From 0f40e9589c60723d68afb5cbaeb2be100f7d472b Mon Sep 17 00:00:00 2001 From: hiro Date: Wed, 5 Jun 2019 13:47:41 +0200 Subject: [PATCH 1/7] Start code sharing between WebsiteMode and ShareMode --- onionshare/web/base_mode.py | 45 ++++++++++++++++++++++++++++++++++ onionshare/web/share_mode.py | 27 +++----------------- onionshare/web/website_mode.py | 16 +++--------- 3 files changed, 52 insertions(+), 36 deletions(-) create mode 100644 onionshare/web/base_mode.py diff --git a/onionshare/web/base_mode.py b/onionshare/web/base_mode.py new file mode 100644 index 00000000..fb1043d7 --- /dev/null +++ b/onionshare/web/base_mode.py @@ -0,0 +1,45 @@ +import os +import sys +import tempfile +import mimetypes +from flask import Response, request, render_template, make_response + +from .. import strings + +class BaseModeWeb(object): + """ + All of the web logic shared between share and website mode + """ + def __init__(self, common, web): + super(BaseModeWeb, self).__init__() + self.common = common + self.web = web + + # Information about the file to be shared + self.file_info = [] + self.is_zipped = False + self.download_filename = None + self.download_filesize = None + self.gzip_filename = None + self.gzip_filesize = None + self.zip_writer = None + + # Dictionary mapping file paths to filenames on disk + self.files = {} + + self.visit_count = 0 + self.download_count = 0 + + # If "Stop After First Download" is checked (stay_open == False), only allow + # one download at a time. + self.download_in_progress = False + + # Reset assets path + self.web.app.static_folder=self.common.get_resource_path('static') + + + def init(self): + """ + Add custom initialization here. + """ + pass diff --git a/onionshare/web/share_mode.py b/onionshare/web/share_mode.py index 0dfa7e0a..779d0a4b 100644 --- a/onionshare/web/share_mode.py +++ b/onionshare/web/share_mode.py @@ -6,38 +6,17 @@ import mimetypes import gzip from flask import Response, request, render_template, make_response +from .base_mode import BaseModeWeb from .. import strings -class ShareModeWeb(object): +class ShareModeWeb(BaseModeWeb): """ All of the web logic for share mode """ - def __init__(self, common, web): - self.common = common + def init(self): self.common.log('ShareModeWeb', '__init__') - self.web = web - - # Information about the file to be shared - self.file_info = [] - self.is_zipped = False - self.download_filename = None - self.download_filesize = None - self.gzip_filename = None - self.gzip_filesize = None - self.zip_writer = None - - self.download_count = 0 - - # If "Stop After First Download" is checked (stay_open == False), only allow - # one download at a time. - self.download_in_progress = False - - # Reset assets path - self.web.app.static_folder=self.common.get_resource_path('static') - - self.define_routes() def define_routes(self): diff --git a/onionshare/web/website_mode.py b/onionshare/web/website_mode.py index d2cd6db9..f61da569 100644 --- a/onionshare/web/website_mode.py +++ b/onionshare/web/website_mode.py @@ -4,26 +4,18 @@ import tempfile import mimetypes from flask import Response, request, render_template, make_response, send_from_directory +from .base_mode import BaseModeWeb from .. import strings -class WebsiteModeWeb(object): +class WebsiteModeWeb(BaseModeWeb): """ All of the web logic for share mode """ - def __init__(self, common, web): - self.common = common + def init(self): + self.common.log('WebsiteModeWeb', '__init__') - self.web = web - - # Dictionary mapping file paths to filenames on disk - self.files = {} - self.visit_count = 0 - - # Reset assets path - self.web.app.static_folder=self.common.get_resource_path('static') - self.define_routes() def define_routes(self): From 5c0839a5574fa1eceea334994c5cfb2286176577 Mon Sep 17 00:00:00 2001 From: hiro Date: Thu, 13 Jun 2019 12:33:34 +0200 Subject: [PATCH 2/7] Refactor set_file_list between website and share mode --- onionshare/web/base_mode.py | 31 ++++++++++++++++++++++++++++--- onionshare/web/share_mode.py | 15 ++------------- onionshare/web/website_mode.py | 20 ++++++-------------- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/onionshare/web/base_mode.py b/onionshare/web/base_mode.py index fb1043d7..8843d198 100644 --- a/onionshare/web/base_mode.py +++ b/onionshare/web/base_mode.py @@ -26,7 +26,9 @@ class BaseModeWeb(object): # Dictionary mapping file paths to filenames on disk self.files = {} - + self.cleanup_filenames = [] + self.file_info = {'files': [], 'dirs': []} + self.visit_count = 0 self.download_count = 0 @@ -34,8 +36,7 @@ class BaseModeWeb(object): # one download at a time. self.download_in_progress = False - # Reset assets path - self.web.app.static_folder=self.common.get_resource_path('static') + self.define_routes() def init(self): @@ -43,3 +44,27 @@ class BaseModeWeb(object): Add custom initialization here. """ pass + + def set_file_info(self, filenames, processed_size_callback=None): + """ + Build a data structure that describes the list of files + """ + if self.web.mode == 'website': + self.common.log("WebsiteModeWeb", "set_file_info") + self.web.cancel_compression = True + + # This is only the root files and dirs, as opposed to all of them + self.root_files = {} + + # If there's just one folder, replace filenames with a list of files inside that folder + if len(filenames) == 1 and os.path.isdir(filenames[0]): + filenames = [os.path.join(filenames[0], x) for x in os.listdir(filenames[0])] + + self.build_file_list(filenames) + + elif self.web.mode == 'share': + self.common.log("ShareModeWeb", "set_file_info") + self.web.cancel_compression = False + self.build_zipfile_list(filenames, processed_size_callback) + + return True diff --git a/onionshare/web/share_mode.py b/onionshare/web/share_mode.py index 779d0a4b..68763357 100644 --- a/onionshare/web/share_mode.py +++ b/onionshare/web/share_mode.py @@ -177,19 +177,8 @@ class ShareModeWeb(BaseModeWeb): r.headers.set('Content-Type', content_type) return r - def set_file_info(self, filenames, processed_size_callback=None): - """ - Using the list of filenames being shared, fill in details that the web - page will need to display. This includes zipping up the file in order to - get the zip file's name and size. - """ - self.common.log("ShareModeWeb", "set_file_info") - self.web.cancel_compression = False - - self.cleanup_filenames = [] - - # build file info list - self.file_info = {'files': [], 'dirs': []} + def build_zipfile_list(self, filenames, processed_size_callback=None): + self.common.log("ShareModeWeb", "build_file_list") for filename in filenames: info = { 'filename': filename, diff --git a/onionshare/web/website_mode.py b/onionshare/web/website_mode.py index f61da569..4c024849 100644 --- a/onionshare/web/website_mode.py +++ b/onionshare/web/website_mode.py @@ -10,12 +10,14 @@ from .. import strings class WebsiteModeWeb(BaseModeWeb): """ - All of the web logic for share mode + All of the web logic for website mode """ def init(self): - self.common.log('WebsiteModeWeb', '__init__') + # Reset assets path + self.web.app.static_folder=self.common.get_resource_path('share/static') + self.define_routes() def define_routes(self): @@ -127,22 +129,12 @@ class WebsiteModeWeb(BaseModeWeb): static_url_path=self.web.static_url_path)) return self.web.add_security_headers(r) - def set_file_info(self, filenames): + def build_file_list(self, filenames): """ Build a data structure that describes the list of files that make up the static website. """ - self.common.log("WebsiteModeWeb", "set_file_info") - - # This is a dictionary that maps HTTP routes to filenames on disk - self.files = {} - - # This is only the root files and dirs, as opposed to all of them - self.root_files = {} - - # If there's just one folder, replace filenames with a list of files inside that folder - if len(filenames) == 1 and os.path.isdir(filenames[0]): - filenames = [os.path.join(filenames[0], x) for x in os.listdir(filenames[0])] + self.common.log("WebsiteModeWeb", "build_file_list") # Loop through the files for filename in filenames: From 9805919fc7ec395da19979b20226c1de037c7969 Mon Sep 17 00:00:00 2001 From: hiro Date: Thu, 13 Jun 2019 12:41:12 +0200 Subject: [PATCH 3/7] Move directory_listing function --- onionshare/web/base_mode.py | 36 +++++++++++++++++++++++++++++++++- onionshare/web/website_mode.py | 33 +------------------------------ 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/onionshare/web/base_mode.py b/onionshare/web/base_mode.py index 8843d198..46e63e1a 100644 --- a/onionshare/web/base_mode.py +++ b/onionshare/web/base_mode.py @@ -4,7 +4,7 @@ import tempfile import mimetypes from flask import Response, request, render_template, make_response -from .. import strings + from .. import strings class BaseModeWeb(object): """ @@ -45,6 +45,40 @@ class BaseModeWeb(object): """ pass + + def directory_listing(self, path, filenames, filesystem_path=None): + # If filesystem_path is None, this is the root directory listing + files = [] + dirs = [] + + for filename in filenames: + if filesystem_path: + this_filesystem_path = os.path.join(filesystem_path, filename) + else: + this_filesystem_path = self.files[filename] + + is_dir = os.path.isdir(this_filesystem_path) + + if is_dir: + dirs.append({ + 'basename': filename + }) + else: + size = os.path.getsize(this_filesystem_path) + size_human = self.common.human_readable_filesize(size) + files.append({ + 'basename': filename, + 'size_human': size_human + }) + + r = make_response(render_template('listing.html', + path=path, + files=files, + dirs=dirs, + static_url_path=self.web.static_url_path)) + return self.web.add_security_headers(r) + + def set_file_info(self, filenames, processed_size_callback=None): """ Build a data structure that describes the list of files diff --git a/onionshare/web/website_mode.py b/onionshare/web/website_mode.py index 4c024849..287acbd9 100644 --- a/onionshare/web/website_mode.py +++ b/onionshare/web/website_mode.py @@ -97,38 +97,7 @@ class WebsiteModeWeb(BaseModeWeb): # If the path isn't found, throw a 404 return self.web.error404() - def directory_listing(self, path, filenames, filesystem_path=None): - # If filesystem_path is None, this is the root directory listing - files = [] - dirs = [] - - for filename in filenames: - if filesystem_path: - this_filesystem_path = os.path.join(filesystem_path, filename) - else: - this_filesystem_path = self.files[filename] - - is_dir = os.path.isdir(this_filesystem_path) - - if is_dir: - dirs.append({ - 'basename': filename - }) - else: - size = os.path.getsize(this_filesystem_path) - size_human = self.common.human_readable_filesize(size) - files.append({ - 'basename': filename, - 'size_human': size_human - }) - - r = make_response(render_template('listing.html', - path=path, - files=files, - dirs=dirs, - static_url_path=self.web.static_url_path)) - return self.web.add_security_headers(r) - + def build_file_list(self, filenames): """ Build a data structure that describes the list of files that make up From 4d733c224a320a7aa5d430d8440a50cb833cbc16 Mon Sep 17 00:00:00 2001 From: hiro Date: Thu, 13 Jun 2019 21:47:49 +0200 Subject: [PATCH 4/7] Refactor directory_listing function --- onionshare/web/base_mode.py | 43 +++++++++++++++------------------- onionshare/web/share_mode.py | 11 ++------- onionshare/web/website_mode.py | 31 ++++++++++++++++++++---- 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/onionshare/web/base_mode.py b/onionshare/web/base_mode.py index 46e63e1a..ff7e11be 100644 --- a/onionshare/web/base_mode.py +++ b/onionshare/web/base_mode.py @@ -4,7 +4,7 @@ import tempfile import mimetypes from flask import Response, request, render_template, make_response - from .. import strings +from .. import strings class BaseModeWeb(object): """ @@ -46,36 +46,31 @@ class BaseModeWeb(object): pass - def directory_listing(self, path, filenames, filesystem_path=None): + def directory_listing(self, path='', filenames=[], filesystem_path=None): # If filesystem_path is None, this is the root directory listing files = [] dirs = [] + r = '' - for filename in filenames: - if filesystem_path: - this_filesystem_path = os.path.join(filesystem_path, filename) - else: - this_filesystem_path = self.files[filename] + if self.web.mode == 'website': + files, dirs = build_directory_listing(filenames) - is_dir = os.path.isdir(this_filesystem_path) + r = make_response(render_template('listing.html', + path=path, + files=files, + dirs=dirs, + static_url_path=self.web.static_url_path)) - if is_dir: - dirs.append({ - 'basename': filename - }) - else: - size = os.path.getsize(this_filesystem_path) - size_human = self.common.human_readable_filesize(size) - files.append({ - 'basename': filename, - 'size_human': size_human - }) + elif self.web.mode == 'share': + r = make_response(render_template( + 'send.html', + file_info=self.file_info, + filename=os.path.basename(self.download_filename), + filesize=self.filesize, + filesize_human=self.common.human_readable_filesize(self.download_filesize), + is_zipped=self.is_zipped, + static_url_path=self.web.static_url_path)) - r = make_response(render_template('listing.html', - path=path, - files=files, - dirs=dirs, - static_url_path=self.web.static_url_path)) return self.web.add_security_headers(r) diff --git a/onionshare/web/share_mode.py b/onionshare/web/share_mode.py index 68763357..cb3bba50 100644 --- a/onionshare/web/share_mode.py +++ b/onionshare/web/share_mode.py @@ -44,15 +44,8 @@ class ShareModeWeb(BaseModeWeb): else: self.filesize = self.download_filesize - r = make_response(render_template( - 'send.html', - file_info=self.file_info, - filename=os.path.basename(self.download_filename), - filesize=self.filesize, - filesize_human=self.common.human_readable_filesize(self.download_filesize), - is_zipped=self.is_zipped, - static_url_path=self.web.static_url_path)) - return self.web.add_security_headers(r) + return self.directory_listing() + @self.web.app.route("/download") def download(): diff --git a/onionshare/web/website_mode.py b/onionshare/web/website_mode.py index 287acbd9..5183bebc 100644 --- a/onionshare/web/website_mode.py +++ b/onionshare/web/website_mode.py @@ -14,12 +14,9 @@ class WebsiteModeWeb(BaseModeWeb): """ def init(self): self.common.log('WebsiteModeWeb', '__init__') - - # Reset assets path - self.web.app.static_folder=self.common.get_resource_path('share/static') - self.define_routes() + def define_routes(self): """ The web app routes for sharing a website @@ -56,6 +53,7 @@ class WebsiteModeWeb(BaseModeWeb): # Render it dirname = os.path.dirname(self.files[index_path]) basename = os.path.basename(self.files[index_path]) + return send_from_directory(dirname, basename) else: @@ -80,6 +78,7 @@ class WebsiteModeWeb(BaseModeWeb): return self.web.error404() else: # Special case loading / + if path == '': index_path = 'index.html' if index_path in self.files: @@ -97,7 +96,29 @@ class WebsiteModeWeb(BaseModeWeb): # If the path isn't found, throw a 404 return self.web.error404() - + def build_directory_listing(self, filenames): + for filename in filenames: + if filesystem_path: + this_filesystem_path = os.path.join(filesystem_path, filename) + else: + this_filesystem_path = self.files[filename] + + is_dir = os.path.isdir(this_filesystem_path) + + if is_dir: + dirs.append({ + 'basename': filename + }) + else: + size = os.path.getsize(this_filesystem_path) + size_human = self.common.human_readable_filesize(size) + files.append({ + 'basename': filename, + 'size_human': size_human + }) + return files, dirs + + def build_file_list(self, filenames): """ Build a data structure that describes the list of files that make up From 66e85497efb023ab46eda9163bd2f667916bc134 Mon Sep 17 00:00:00 2001 From: hiro Date: Thu, 13 Jun 2019 22:56:48 +0200 Subject: [PATCH 5/7] Revert "Generate a new static_url_path each time the server is stopped and started again" This change creates problems with how website mode renders assets. This reverts commit 50b2311409cd93814324a4570e8bdc5d032748c8. --- onionshare/web/web.py | 18 +++++------------- onionshare_gui/threads.py | 3 --- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/onionshare/web/web.py b/onionshare/web/web.py index 1d2a3fec..17dd8c15 100644 --- a/onionshare/web/web.py +++ b/onionshare/web/web.py @@ -51,12 +51,16 @@ class Web(object): self.common = common self.common.log('Web', '__init__', 'is_gui={}, mode={}'.format(is_gui, mode)) + # The static URL path has a 128-bit random number in it to avoid having name + # collisions with files that might be getting shared + self.static_url_path = '/static_{}'.format(self.common.random_string(16)) + # The flask app self.app = Flask(__name__, + static_url_path=self.static_url_path, static_folder=self.common.get_resource_path('static'), template_folder=self.common.get_resource_path('templates')) self.app.secret_key = self.common.random_string(8) - self.generate_static_url_path() self.auth = HTTPBasicAuth() self.auth.error_handler(self.error401) @@ -225,18 +229,6 @@ class Web(object): self.password = self.common.build_password() self.common.log('Web', 'generate_password', 'built random password: "{}"'.format(self.password)) - def generate_static_url_path(self): - # The static URL path has a 128-bit random number in it to avoid having name - # collisions with files that might be getting shared - self.static_url_path = '/static_{}'.format(self.common.random_string(16)) - self.common.log('Web', 'generate_static_url_path', 'new static_url_path is {}'.format(self.static_url_path)) - - # Update the flask route to handle the new static URL path - self.app.static_url_path = self.static_url_path - self.app.add_url_rule( - self.static_url_path + '/', - endpoint='static', view_func=self.app.send_static_file) - def verbose_mode(self): """ Turn on verbose mode, which will log flask errors to a file. diff --git a/onionshare_gui/threads.py b/onionshare_gui/threads.py index 57e0f0af..bee1b6bc 100644 --- a/onionshare_gui/threads.py +++ b/onionshare_gui/threads.py @@ -42,9 +42,6 @@ class OnionThread(QtCore.QThread): def run(self): self.mode.common.log('OnionThread', 'run') - # Make a new static URL path for each new share - self.mode.web.generate_static_url_path() - # Choose port and password early, because we need them to exist in advance for scheduled shares self.mode.app.stay_open = not self.mode.common.settings.get('close_after_first_download') if not self.mode.app.port: From 35b524439ff0a1bf185dc22c01e92a1a937ac563 Mon Sep 17 00:00:00 2001 From: hiro Date: Thu, 13 Jun 2019 22:58:33 +0200 Subject: [PATCH 6/7] Remove reset of web app path in receive mode --- onionshare/web/receive_mode.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/onionshare/web/receive_mode.py b/onionshare/web/receive_mode.py index 3f848d2f..b444deb2 100644 --- a/onionshare/web/receive_mode.py +++ b/onionshare/web/receive_mode.py @@ -18,9 +18,6 @@ class ReceiveModeWeb(object): self.web = web - # Reset assets path - self.web.app.static_folder=self.common.get_resource_path('static') - self.can_upload = True self.upload_count = 0 self.uploads_in_progress = [] @@ -34,7 +31,7 @@ class ReceiveModeWeb(object): @self.web.app.route("/") def index(): self.web.add_request(self.web.REQUEST_LOAD, request.path) - r = make_response(render_template('receive.html', + r = make_response(render_template('receive.html', static_url_path=self.web.static_url_path)) return self.web.add_security_headers(r) From 2604ef52b51424c42c2a06212ab745242de0fa58 Mon Sep 17 00:00:00 2001 From: hiro Date: Fri, 14 Jun 2019 18:21:12 +0200 Subject: [PATCH 7/7] Clean up rendering logic between share and website mode --- onionshare/web/base_mode.py | 149 +++++++++++++++++++++++++++++---- onionshare/web/share_mode.py | 9 +- onionshare/web/website_mode.py | 114 +------------------------ share/templates/send.html | 17 ++-- 4 files changed, 151 insertions(+), 138 deletions(-) diff --git a/onionshare/web/base_mode.py b/onionshare/web/base_mode.py index ff7e11be..905414f6 100644 --- a/onionshare/web/base_mode.py +++ b/onionshare/web/base_mode.py @@ -2,7 +2,7 @@ import os import sys import tempfile import mimetypes -from flask import Response, request, render_template, make_response +from flask import Response, request, render_template, make_response, send_from_directory from .. import strings @@ -26,6 +26,8 @@ class BaseModeWeb(object): # Dictionary mapping file paths to filenames on disk self.files = {} + # This is only the root files and dirs, as opposed to all of them + self.root_files = {} self.cleanup_filenames = [] self.file_info = {'files': [], 'dirs': []} @@ -46,15 +48,15 @@ class BaseModeWeb(object): pass - def directory_listing(self, path='', filenames=[], filesystem_path=None): + def directory_listing(self, filenames, path='', filesystem_path=None): # If filesystem_path is None, this is the root directory listing files = [] dirs = [] r = '' - if self.web.mode == 'website': - files, dirs = build_directory_listing(filenames) + files, dirs = self.build_directory_listing(filenames, filesystem_path) + if self.web.mode == 'website': r = make_response(render_template('listing.html', path=path, files=files, @@ -65,6 +67,8 @@ class BaseModeWeb(object): r = make_response(render_template( 'send.html', file_info=self.file_info, + files=files, + dirs=dirs, filename=os.path.basename(self.download_filename), filesize=self.filesize, filesize_human=self.common.human_readable_filesize(self.download_filesize), @@ -74,26 +78,141 @@ class BaseModeWeb(object): return self.web.add_security_headers(r) + def build_directory_listing(self, filenames, filesystem_path): + files = [] + dirs = [] + + for filename in filenames: + if filesystem_path: + this_filesystem_path = os.path.join(filesystem_path, filename) + else: + this_filesystem_path = self.files[filename] + + is_dir = os.path.isdir(this_filesystem_path) + + if is_dir: + dirs.append({ + 'basename': filename + }) + else: + size = os.path.getsize(this_filesystem_path) + size_human = self.common.human_readable_filesize(size) + files.append({ + 'basename': filename, + 'size_human': size_human + }) + return files, dirs + + def set_file_info(self, filenames, processed_size_callback=None): """ Build a data structure that describes the list of files """ - if self.web.mode == 'website': - self.common.log("WebsiteModeWeb", "set_file_info") - self.web.cancel_compression = True - # This is only the root files and dirs, as opposed to all of them - self.root_files = {} + # If there's just one folder, replace filenames with a list of files inside that folder + if len(filenames) == 1 and os.path.isdir(filenames[0]): + filenames = [os.path.join(filenames[0], x) for x in os.listdir(filenames[0])] - # If there's just one folder, replace filenames with a list of files inside that folder - if len(filenames) == 1 and os.path.isdir(filenames[0]): - filenames = [os.path.join(filenames[0], x) for x in os.listdir(filenames[0])] + self.build_file_list(filenames) - self.build_file_list(filenames) - - elif self.web.mode == 'share': + if self.web.mode == 'share': self.common.log("ShareModeWeb", "set_file_info") self.web.cancel_compression = False self.build_zipfile_list(filenames, processed_size_callback) + elif self.web.mode == 'website': + self.common.log("WebsiteModeWeb", "set_file_info") + self.web.cancel_compression = True + return True + + + def build_file_list(self, filenames): + """ + Build a data structure that describes the list of files that make up + the static website. + """ + self.common.log("BaseModeWeb", "build_file_list") + + # Loop through the files + for filename in filenames: + basename = os.path.basename(filename.rstrip('/')) + + # If it's a filename, add it + if os.path.isfile(filename): + self.files[basename] = filename + self.root_files[basename] = filename + + # If it's a directory, add it recursively + elif os.path.isdir(filename): + self.root_files[basename + '/'] = filename + + for root, _, nested_filenames in os.walk(filename): + # Normalize the root path. So if the directory name is "/home/user/Documents/some_folder", + # and it has a nested folder foobar, the root is "/home/user/Documents/some_folder/foobar". + # The normalized_root should be "some_folder/foobar" + normalized_root = os.path.join(basename, root[len(filename):].lstrip('/')).rstrip('/') + + # Add the dir itself + self.files[normalized_root + '/'] = root + + # Add the files in this dir + for nested_filename in nested_filenames: + self.files[os.path.join(normalized_root, nested_filename)] = os.path.join(root, nested_filename) + + return True + + def render_logic(self, path=''): + if path in self.files: + filesystem_path = self.files[path] + + # If it's a directory + if os.path.isdir(filesystem_path): + # Is there an index.html? + index_path = os.path.join(path, 'index.html') + if self.web.mode == 'website' and index_path in self.files: + # Render it + dirname = os.path.dirname(self.files[index_path]) + basename = os.path.basename(self.files[index_path]) + + return send_from_directory(dirname, basename) + + else: + # Otherwise, render directory listing + filenames = [] + for filename in os.listdir(filesystem_path): + if os.path.isdir(os.path.join(filesystem_path, filename)): + filenames.append(filename + '/') + else: + filenames.append(filename) + filenames.sort() + return self.directory_listing(filenames, path, filesystem_path) + + # If it's a file + elif os.path.isfile(filesystem_path): + dirname = os.path.dirname(filesystem_path) + basename = os.path.basename(filesystem_path) + return send_from_directory(dirname, basename) + + # If it's not a directory or file, throw a 404 + else: + return self.web.error404() + else: + # Special case loading / + + if path == '': + index_path = 'index.html' + if self.web.mode == 'website' and index_path in self.files: + # Render it + dirname = os.path.dirname(self.files[index_path]) + basename = os.path.basename(self.files[index_path]) + return send_from_directory(dirname, basename) + else: + # Root directory listing + filenames = list(self.root_files) + filenames.sort() + return self.directory_listing(filenames, path) + + else: + # If the path isn't found, throw a 404 + return self.web.error404() diff --git a/onionshare/web/share_mode.py b/onionshare/web/share_mode.py index cb3bba50..afcbdcd9 100644 --- a/onionshare/web/share_mode.py +++ b/onionshare/web/share_mode.py @@ -23,8 +23,9 @@ class ShareModeWeb(BaseModeWeb): """ The web app routes for sharing files """ - @self.web.app.route("/") - def index(): + @self.web.app.route('/', defaults={'path': ''}) + @self.web.app.route('/') + def index(path): """ Render the template for the onionshare landing page. """ @@ -44,7 +45,7 @@ class ShareModeWeb(BaseModeWeb): else: self.filesize = self.download_filesize - return self.directory_listing() + return self.render_logic(path) @self.web.app.route("/download") @@ -171,7 +172,7 @@ class ShareModeWeb(BaseModeWeb): return r def build_zipfile_list(self, filenames, processed_size_callback=None): - self.common.log("ShareModeWeb", "build_file_list") + self.common.log("ShareModeWeb", "build_zipfile_list") for filename in filenames: info = { 'filename': filename, diff --git a/onionshare/web/website_mode.py b/onionshare/web/website_mode.py index 5183bebc..b8e4dfdf 100644 --- a/onionshare/web/website_mode.py +++ b/onionshare/web/website_mode.py @@ -2,7 +2,7 @@ import os import sys import tempfile import mimetypes -from flask import Response, request, render_template, make_response, send_from_directory +from flask import Response, request, render_template, make_response from .base_mode import BaseModeWeb from .. import strings @@ -42,114 +42,4 @@ class WebsiteModeWeb(BaseModeWeb): 'action': 'visit' }) - if path in self.files: - filesystem_path = self.files[path] - - # If it's a directory - if os.path.isdir(filesystem_path): - # Is there an index.html? - index_path = os.path.join(path, 'index.html') - if index_path in self.files: - # Render it - dirname = os.path.dirname(self.files[index_path]) - basename = os.path.basename(self.files[index_path]) - - return send_from_directory(dirname, basename) - - else: - # Otherwise, render directory listing - filenames = [] - for filename in os.listdir(filesystem_path): - if os.path.isdir(os.path.join(filesystem_path, filename)): - filenames.append(filename + '/') - else: - filenames.append(filename) - filenames.sort() - return self.directory_listing(path, filenames, filesystem_path) - - # If it's a file - elif os.path.isfile(filesystem_path): - dirname = os.path.dirname(filesystem_path) - basename = os.path.basename(filesystem_path) - return send_from_directory(dirname, basename) - - # If it's not a directory or file, throw a 404 - else: - return self.web.error404() - else: - # Special case loading / - - if path == '': - index_path = 'index.html' - if index_path in self.files: - # Render it - dirname = os.path.dirname(self.files[index_path]) - basename = os.path.basename(self.files[index_path]) - return send_from_directory(dirname, basename) - else: - # Root directory listing - filenames = list(self.root_files) - filenames.sort() - return self.directory_listing(path, filenames) - - else: - # If the path isn't found, throw a 404 - return self.web.error404() - - def build_directory_listing(self, filenames): - for filename in filenames: - if filesystem_path: - this_filesystem_path = os.path.join(filesystem_path, filename) - else: - this_filesystem_path = self.files[filename] - - is_dir = os.path.isdir(this_filesystem_path) - - if is_dir: - dirs.append({ - 'basename': filename - }) - else: - size = os.path.getsize(this_filesystem_path) - size_human = self.common.human_readable_filesize(size) - files.append({ - 'basename': filename, - 'size_human': size_human - }) - return files, dirs - - - def build_file_list(self, filenames): - """ - Build a data structure that describes the list of files that make up - the static website. - """ - self.common.log("WebsiteModeWeb", "build_file_list") - - # Loop through the files - for filename in filenames: - basename = os.path.basename(filename.rstrip('/')) - - # If it's a filename, add it - if os.path.isfile(filename): - self.files[basename] = filename - self.root_files[basename] = filename - - # If it's a directory, add it recursively - elif os.path.isdir(filename): - self.root_files[basename + '/'] = filename - - for root, _, nested_filenames in os.walk(filename): - # Normalize the root path. So if the directory name is "/home/user/Documents/some_folder", - # and it has a nested folder foobar, the root is "/home/user/Documents/some_folder/foobar". - # The normalized_root should be "some_folder/foobar" - normalized_root = os.path.join(basename, root[len(filename):].lstrip('/')).rstrip('/') - - # Add the dir itself - self.files[normalized_root + '/'] = root - - # Add the files in this dir - for nested_filename in nested_filenames: - self.files[os.path.join(normalized_root, nested_filename)] = os.path.join(root, nested_filename) - - return True + return self.render_logic(path) diff --git a/share/templates/send.html b/share/templates/send.html index e0076c0f..490fddf4 100644 --- a/share/templates/send.html +++ b/share/templates/send.html @@ -28,24 +28,27 @@ Size - {% for info in file_info.dirs %} + {% for info in dirs %} - {{ info.basename }} + + {{ info.basename }} + - {{ info.size_human }} - + — {% endfor %} - {% for info in file_info.files %} + + {% for info in files %} - {{ info.basename }} + + {{ info.basename }} + {{ info.size_human }} - {% endfor %}