mirror of
https://github.com/onionshare/onionshare.git
synced 2024-12-28 00:39:37 -05:00
Expand 'public mode' (optional slugs) to be possible for sharing too, not just receiving, with no rate-limiting/self-destruct on invalid routes.
This commit is contained in:
parent
f5ccfcf2cc
commit
3b45f93dbe
@ -128,7 +128,7 @@ def main(cwd=None):
|
|||||||
print('')
|
print('')
|
||||||
|
|
||||||
# Start OnionShare http service in new thread
|
# Start OnionShare http service in new thread
|
||||||
t = threading.Thread(target=web.start, args=(app.port, stay_open, common.settings.get('slug')))
|
t = threading.Thread(target=web.start, args=(app.port, stay_open, common.settings.get('public_mode'), common.settings.get('slug')))
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ def main(cwd=None):
|
|||||||
common.settings.save()
|
common.settings.save()
|
||||||
|
|
||||||
# Build the URL
|
# Build the URL
|
||||||
if receive and common.settings.get('receive_public_mode'):
|
if common.settings.get('public_mode'):
|
||||||
url = 'http://{0:s}'.format(app.onion_host)
|
url = 'http://{0:s}'.format(app.onion_host)
|
||||||
else:
|
else:
|
||||||
url = 'http://{0:s}/{1:s}'.format(app.onion_host, web.slug)
|
url = 'http://{0:s}/{1:s}'.format(app.onion_host, web.slug)
|
||||||
|
@ -70,11 +70,11 @@ class Settings(object):
|
|||||||
'tor_bridges_use_custom_bridges': '',
|
'tor_bridges_use_custom_bridges': '',
|
||||||
'save_private_key': False,
|
'save_private_key': False,
|
||||||
'private_key': '',
|
'private_key': '',
|
||||||
|
'public_mode': False,
|
||||||
'slug': '',
|
'slug': '',
|
||||||
'hidservauth_string': '',
|
'hidservauth_string': '',
|
||||||
'downloads_dir': self.build_default_downloads_dir(),
|
'downloads_dir': self.build_default_downloads_dir(),
|
||||||
'receive_allow_receiver_shutdown': True,
|
'receive_allow_receiver_shutdown': True
|
||||||
'receive_public_mode': False
|
|
||||||
}
|
}
|
||||||
self._settings = {}
|
self._settings = {}
|
||||||
self.fill_in_defaults()
|
self.fill_in_defaults()
|
||||||
|
@ -143,11 +143,19 @@ class Web(object):
|
|||||||
"""
|
"""
|
||||||
@self.app.route("/<slug_candidate>")
|
@self.app.route("/<slug_candidate>")
|
||||||
def index(slug_candidate):
|
def index(slug_candidate):
|
||||||
|
self.check_slug_candidate(slug_candidate)
|
||||||
|
return index_logic()
|
||||||
|
|
||||||
|
@self.app.route("/")
|
||||||
|
def index_public():
|
||||||
|
if not self.common.settings.get('public_mode'):
|
||||||
|
return self.error404()
|
||||||
|
return index_logic()
|
||||||
|
|
||||||
|
def index_logic(slug_candidate=''):
|
||||||
"""
|
"""
|
||||||
Render the template for the onionshare landing page.
|
Render the template for the onionshare landing page.
|
||||||
"""
|
"""
|
||||||
self.check_slug_candidate(slug_candidate)
|
|
||||||
|
|
||||||
self.add_request(Web.REQUEST_LOAD, request.path)
|
self.add_request(Web.REQUEST_LOAD, request.path)
|
||||||
|
|
||||||
# Deny new downloads if "Stop After First Download" is checked and there is
|
# Deny new downloads if "Stop After First Download" is checked and there is
|
||||||
@ -158,22 +166,39 @@ class Web(object):
|
|||||||
return self.add_security_headers(r)
|
return self.add_security_headers(r)
|
||||||
|
|
||||||
# If download is allowed to continue, serve download page
|
# If download is allowed to continue, serve download page
|
||||||
r = make_response(render_template(
|
if self.slug:
|
||||||
'send.html',
|
r = make_response(render_template(
|
||||||
slug=self.slug,
|
'send.html',
|
||||||
file_info=self.file_info,
|
slug=self.slug,
|
||||||
filename=os.path.basename(self.zip_filename),
|
file_info=self.file_info,
|
||||||
filesize=self.zip_filesize,
|
filename=os.path.basename(self.zip_filename),
|
||||||
filesize_human=self.common.human_readable_filesize(self.zip_filesize)))
|
filesize=self.zip_filesize,
|
||||||
|
filesize_human=self.common.human_readable_filesize(self.zip_filesize)))
|
||||||
|
else:
|
||||||
|
# If download is allowed to continue, serve download page
|
||||||
|
r = make_response(render_template(
|
||||||
|
'send.html',
|
||||||
|
file_info=self.file_info,
|
||||||
|
filename=os.path.basename(self.zip_filename),
|
||||||
|
filesize=self.zip_filesize,
|
||||||
|
filesize_human=self.common.human_readable_filesize(self.zip_filesize)))
|
||||||
return self.add_security_headers(r)
|
return self.add_security_headers(r)
|
||||||
|
|
||||||
@self.app.route("/<slug_candidate>/download")
|
@self.app.route("/<slug_candidate>/download")
|
||||||
def download(slug_candidate):
|
def download(slug_candidate):
|
||||||
|
self.check_slug_candidate(slug_candidate)
|
||||||
|
return download_logic()
|
||||||
|
|
||||||
|
@self.app.route("/download")
|
||||||
|
def download_public():
|
||||||
|
if not self.common.settings.get('public_mode'):
|
||||||
|
return self.error404()
|
||||||
|
return download_logic()
|
||||||
|
|
||||||
|
def download_logic(slug_candidate=''):
|
||||||
"""
|
"""
|
||||||
Download the zip file.
|
Download the zip file.
|
||||||
"""
|
"""
|
||||||
self.check_slug_candidate(slug_candidate)
|
|
||||||
|
|
||||||
# Deny new downloads if "Stop After First Download" is checked and there is
|
# Deny new downloads if "Stop After First Download" is checked and there is
|
||||||
# currently a download
|
# currently a download
|
||||||
deny_download = not self.stay_open and self.download_in_progress
|
deny_download = not self.stay_open and self.download_in_progress
|
||||||
@ -288,7 +313,7 @@ class Web(object):
|
|||||||
def index_logic():
|
def index_logic():
|
||||||
self.add_request(Web.REQUEST_LOAD, request.path)
|
self.add_request(Web.REQUEST_LOAD, request.path)
|
||||||
|
|
||||||
if self.common.settings.get('receive_public_mode'):
|
if self.common.settings.get('public_mode'):
|
||||||
upload_action = '/upload'
|
upload_action = '/upload'
|
||||||
close_action = '/close'
|
close_action = '/close'
|
||||||
else:
|
else:
|
||||||
@ -309,7 +334,7 @@ class Web(object):
|
|||||||
|
|
||||||
@self.app.route("/")
|
@self.app.route("/")
|
||||||
def index_public():
|
def index_public():
|
||||||
if not self.common.settings.get('receive_public_mode'):
|
if not self.common.settings.get('public_mode'):
|
||||||
return self.error404()
|
return self.error404()
|
||||||
return index_logic()
|
return index_logic()
|
||||||
|
|
||||||
@ -332,7 +357,7 @@ class Web(object):
|
|||||||
valid = False
|
valid = False
|
||||||
if not valid:
|
if not valid:
|
||||||
flash('Error uploading, please inform the OnionShare user')
|
flash('Error uploading, please inform the OnionShare user')
|
||||||
if self.common.settings.get('receive_public_mode'):
|
if self.common.settings.get('public_mode'):
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
else:
|
else:
|
||||||
return redirect('/{}'.format(slug_candidate))
|
return redirect('/{}'.format(slug_candidate))
|
||||||
@ -395,7 +420,7 @@ class Web(object):
|
|||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
flash('Uploaded {}'.format(filename))
|
flash('Uploaded {}'.format(filename))
|
||||||
|
|
||||||
if self.common.settings.get('receive_public_mode'):
|
if self.common.settings.get('public_mode'):
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
else:
|
else:
|
||||||
return redirect('/{}'.format(slug_candidate))
|
return redirect('/{}'.format(slug_candidate))
|
||||||
@ -407,7 +432,7 @@ class Web(object):
|
|||||||
|
|
||||||
@self.app.route("/upload", methods=['POST'])
|
@self.app.route("/upload", methods=['POST'])
|
||||||
def upload_public():
|
def upload_public():
|
||||||
if not self.common.settings.get('receive_public_mode'):
|
if not self.common.settings.get('public_mode'):
|
||||||
return self.error404()
|
return self.error404()
|
||||||
return upload_logic()
|
return upload_logic()
|
||||||
|
|
||||||
@ -428,7 +453,7 @@ class Web(object):
|
|||||||
|
|
||||||
@self.app.route("/close", methods=['POST'])
|
@self.app.route("/close", methods=['POST'])
|
||||||
def close_public():
|
def close_public():
|
||||||
if not self.common.settings.get('receive_public_mode'):
|
if not self.common.settings.get('public_mode'):
|
||||||
return self.error404()
|
return self.error404()
|
||||||
return close_logic()
|
return close_logic()
|
||||||
|
|
||||||
@ -458,7 +483,7 @@ class Web(object):
|
|||||||
self.error404_count += 1
|
self.error404_count += 1
|
||||||
|
|
||||||
# In receive mode, with public mode enabled, skip rate limiting 404s
|
# In receive mode, with public mode enabled, skip rate limiting 404s
|
||||||
if not (self.receive_mode and self.common.settings.get('receive_public_mode')):
|
if not self.common.settings.get('public_mode'):
|
||||||
if self.error404_count == 20:
|
if self.error404_count == 20:
|
||||||
self.add_request(Web.REQUEST_RATE_LIMIT, request.path)
|
self.add_request(Web.REQUEST_RATE_LIMIT, request.path)
|
||||||
self.force_shutdown()
|
self.force_shutdown()
|
||||||
@ -563,12 +588,13 @@ class Web(object):
|
|||||||
pass
|
pass
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
def start(self, port, stay_open=False, persistent_slug=None):
|
def start(self, port, stay_open=False, public_mode=False, persistent_slug=None):
|
||||||
"""
|
"""
|
||||||
Start the flask web server.
|
Start the flask web server.
|
||||||
"""
|
"""
|
||||||
self.common.log('Web', 'start', 'port={}, stay_open={}, persistent_slug={}'.format(port, stay_open, persistent_slug))
|
self.common.log('Web', 'start', 'port={}, stay_open={}, persistent_slug={}'.format(port, stay_open, persistent_slug))
|
||||||
self.generate_slug(persistent_slug)
|
if not public_mode:
|
||||||
|
self.generate_slug(persistent_slug)
|
||||||
|
|
||||||
self.stay_open = stay_open
|
self.stay_open = stay_open
|
||||||
|
|
||||||
@ -719,7 +745,7 @@ class ReceiveModeRequest(Request):
|
|||||||
if self.path == '/{}/upload'.format(self.web.slug):
|
if self.path == '/{}/upload'.format(self.web.slug):
|
||||||
self.upload_request = True
|
self.upload_request = True
|
||||||
else:
|
else:
|
||||||
if self.web.common.settings.get('receive_public_mode'):
|
if self.web.common.settings.get('public_mode'):
|
||||||
if self.path == '/upload':
|
if self.path == '/upload':
|
||||||
self.upload_request = True
|
self.upload_request = True
|
||||||
|
|
||||||
|
@ -144,13 +144,14 @@ class Mode(QtWidgets.QWidget):
|
|||||||
self.app.choose_port()
|
self.app.choose_port()
|
||||||
|
|
||||||
# Start http service in new thread
|
# Start http service in new thread
|
||||||
t = threading.Thread(target=self.web.start, args=(self.app.port, not self.common.settings.get('close_after_first_download'), self.common.settings.get('slug')))
|
t = threading.Thread(target=self.web.start, args=(self.app.port, not self.common.settings.get('close_after_first_download'), self.common.settings.get('public_mode'), self.common.settings.get('slug')))
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
# Wait for the web app slug to generate before continuing
|
# Wait for the web app slug to generate before continuing
|
||||||
while self.web.slug == None:
|
if not self.common.settings.get('public_mode'):
|
||||||
time.sleep(0.1)
|
while self.web.slug == None:
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
# Now start the onion service
|
# Now start the onion service
|
||||||
try:
|
try:
|
||||||
|
@ -314,7 +314,7 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
"""
|
"""
|
||||||
Returns the OnionShare URL.
|
Returns the OnionShare URL.
|
||||||
"""
|
"""
|
||||||
if self.mode == ServerStatus.MODE_RECEIVE and self.common.settings.get('receive_public_mode'):
|
if self.common.settings.get('public_mode'):
|
||||||
url = 'http://{0:s}'.format(self.app.onion_host)
|
url = 'http://{0:s}'.format(self.app.onion_host)
|
||||||
else:
|
else:
|
||||||
url = 'http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)
|
url = 'http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)
|
||||||
|
@ -52,6 +52,25 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
|
|
||||||
self.system = platform.system()
|
self.system = platform.system()
|
||||||
|
|
||||||
|
# General options
|
||||||
|
|
||||||
|
# Whether or not to save the Onion private key for reuse (persistent URL mode)
|
||||||
|
self.save_private_key_checkbox = QtWidgets.QCheckBox()
|
||||||
|
self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
self.save_private_key_checkbox.setText(strings._("gui_save_private_key_checkbox", True))
|
||||||
|
|
||||||
|
# Use a slug
|
||||||
|
self.public_mode_checkbox = QtWidgets.QCheckBox()
|
||||||
|
self.public_mode_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
self.public_mode_checkbox.setText(strings._("gui_settings_public_mode_checkbox", True))
|
||||||
|
|
||||||
|
# General options layout
|
||||||
|
general_group_layout = QtWidgets.QVBoxLayout()
|
||||||
|
general_group_layout.addWidget(self.save_private_key_checkbox)
|
||||||
|
general_group_layout.addWidget(self.public_mode_checkbox)
|
||||||
|
general_group = QtWidgets.QGroupBox(strings._("gui_settings_general_label", True))
|
||||||
|
general_group.setLayout(general_group_layout)
|
||||||
|
|
||||||
# Sharing options
|
# Sharing options
|
||||||
|
|
||||||
# Close after first download
|
# Close after first download
|
||||||
@ -64,16 +83,10 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True))
|
self.shutdown_timeout_checkbox.setText(strings._("gui_settings_shutdown_timeout_checkbox", True))
|
||||||
|
|
||||||
# Whether or not to save the Onion private key for reuse
|
|
||||||
self.save_private_key_checkbox = QtWidgets.QCheckBox()
|
|
||||||
self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
|
||||||
self.save_private_key_checkbox.setText(strings._("gui_save_private_key_checkbox", True))
|
|
||||||
|
|
||||||
# Sharing options layout
|
# Sharing options layout
|
||||||
sharing_group_layout = QtWidgets.QVBoxLayout()
|
sharing_group_layout = QtWidgets.QVBoxLayout()
|
||||||
sharing_group_layout.addWidget(self.close_after_first_download_checkbox)
|
sharing_group_layout.addWidget(self.close_after_first_download_checkbox)
|
||||||
sharing_group_layout.addWidget(self.shutdown_timeout_checkbox)
|
sharing_group_layout.addWidget(self.shutdown_timeout_checkbox)
|
||||||
sharing_group_layout.addWidget(self.save_private_key_checkbox)
|
|
||||||
sharing_group = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label", True))
|
sharing_group = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label", True))
|
||||||
sharing_group.setLayout(sharing_group_layout)
|
sharing_group.setLayout(sharing_group_layout)
|
||||||
|
|
||||||
@ -93,16 +106,10 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
self.receive_allow_receiver_shutdown_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.receive_allow_receiver_shutdown_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
self.receive_allow_receiver_shutdown_checkbox.setText(strings._("gui_settings_receive_allow_receiver_shutdown_checkbox", True))
|
self.receive_allow_receiver_shutdown_checkbox.setText(strings._("gui_settings_receive_allow_receiver_shutdown_checkbox", True))
|
||||||
|
|
||||||
# Use a slug
|
|
||||||
self.receive_public_mode_checkbox = QtWidgets.QCheckBox()
|
|
||||||
self.receive_public_mode_checkbox.setCheckState(QtCore.Qt.Checked)
|
|
||||||
self.receive_public_mode_checkbox.setText(strings._("gui_settings_receive_public_mode_checkbox", True))
|
|
||||||
|
|
||||||
# Receiving options layout
|
# Receiving options layout
|
||||||
receiving_group_layout = QtWidgets.QVBoxLayout()
|
receiving_group_layout = QtWidgets.QVBoxLayout()
|
||||||
receiving_group_layout.addLayout(downloads_layout)
|
receiving_group_layout.addLayout(downloads_layout)
|
||||||
receiving_group_layout.addWidget(self.receive_allow_receiver_shutdown_checkbox)
|
receiving_group_layout.addWidget(self.receive_allow_receiver_shutdown_checkbox)
|
||||||
receiving_group_layout.addWidget(self.receive_public_mode_checkbox)
|
|
||||||
receiving_group = QtWidgets.QGroupBox(strings._("gui_settings_receiving_label", True))
|
receiving_group = QtWidgets.QGroupBox(strings._("gui_settings_receiving_label", True))
|
||||||
receiving_group.setLayout(receiving_group_layout)
|
receiving_group.setLayout(receiving_group_layout)
|
||||||
|
|
||||||
@ -377,6 +384,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
|
|
||||||
# Layout
|
# Layout
|
||||||
left_col_layout = QtWidgets.QVBoxLayout()
|
left_col_layout = QtWidgets.QVBoxLayout()
|
||||||
|
left_col_layout.addWidget(general_group)
|
||||||
left_col_layout.addWidget(sharing_group)
|
left_col_layout.addWidget(sharing_group)
|
||||||
left_col_layout.addWidget(receiving_group)
|
left_col_layout.addWidget(receiving_group)
|
||||||
left_col_layout.addWidget(stealth_group)
|
left_col_layout.addWidget(stealth_group)
|
||||||
@ -431,11 +439,11 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
else:
|
else:
|
||||||
self.receive_allow_receiver_shutdown_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
self.receive_allow_receiver_shutdown_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
receive_public_mode = self.old_settings.get('receive_public_mode')
|
public_mode = self.old_settings.get('public_mode')
|
||||||
if receive_public_mode:
|
if public_mode:
|
||||||
self.receive_public_mode_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.public_mode_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
else:
|
else:
|
||||||
self.receive_public_mode_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
self.public_mode_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
use_stealth = self.old_settings.get('use_stealth')
|
use_stealth = self.old_settings.get('use_stealth')
|
||||||
if use_stealth:
|
if use_stealth:
|
||||||
@ -819,7 +827,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
settings.set('hidservauth_string', '')
|
settings.set('hidservauth_string', '')
|
||||||
settings.set('downloads_dir', self.downloads_dir_lineedit.text())
|
settings.set('downloads_dir', self.downloads_dir_lineedit.text())
|
||||||
settings.set('receive_allow_receiver_shutdown', self.receive_allow_receiver_shutdown_checkbox.isChecked())
|
settings.set('receive_allow_receiver_shutdown', self.receive_allow_receiver_shutdown_checkbox.isChecked())
|
||||||
settings.set('receive_public_mode', self.receive_public_mode_checkbox.isChecked())
|
settings.set('public_mode', self.public_mode_checkbox.isChecked())
|
||||||
settings.set('use_stealth', self.stealth_checkbox.isChecked())
|
settings.set('use_stealth', self.stealth_checkbox.isChecked())
|
||||||
# Always unset the HidServAuth if Stealth mode is unset
|
# Always unset the HidServAuth if Stealth mode is unset
|
||||||
if not self.stealth_checkbox.isChecked():
|
if not self.stealth_checkbox.isChecked():
|
||||||
|
@ -91,6 +91,7 @@
|
|||||||
"gui_settings_autoupdate_timestamp": "Last checked: {}",
|
"gui_settings_autoupdate_timestamp": "Last checked: {}",
|
||||||
"gui_settings_autoupdate_timestamp_never": "Never",
|
"gui_settings_autoupdate_timestamp_never": "Never",
|
||||||
"gui_settings_autoupdate_check_button": "Check For Upgrades",
|
"gui_settings_autoupdate_check_button": "Check For Upgrades",
|
||||||
|
"gui_settings_general_label": "General options",
|
||||||
"gui_settings_sharing_label": "Sharing options",
|
"gui_settings_sharing_label": "Sharing options",
|
||||||
"gui_settings_close_after_first_download_option": "Stop sharing after first download",
|
"gui_settings_close_after_first_download_option": "Stop sharing after first download",
|
||||||
"gui_settings_connection_type_label": "How should OnionShare connect to Tor?",
|
"gui_settings_connection_type_label": "How should OnionShare connect to Tor?",
|
||||||
@ -183,7 +184,7 @@
|
|||||||
"gui_settings_downloads_label": "Save files to",
|
"gui_settings_downloads_label": "Save files to",
|
||||||
"gui_settings_downloads_button": "Browse",
|
"gui_settings_downloads_button": "Browse",
|
||||||
"gui_settings_receive_allow_receiver_shutdown_checkbox": "Receive mode can be stopped by the sender",
|
"gui_settings_receive_allow_receiver_shutdown_checkbox": "Receive mode can be stopped by the sender",
|
||||||
"gui_settings_receive_public_mode_checkbox": "Receive mode is open to the public\n(don't prevent people from guessing the OnionShare address)",
|
"gui_settings_public_mode_checkbox": "OnionShare is open to the public\n(don't prevent people from guessing the OnionShare address)",
|
||||||
"systray_close_server_title": "OnionShare Server Closed",
|
"systray_close_server_title": "OnionShare Server Closed",
|
||||||
"systray_close_server_message": "A user closed the server",
|
"systray_close_server_message": "A user closed the server",
|
||||||
"systray_page_loaded_title": "OnionShare Page Loaded",
|
"systray_page_loaded_title": "OnionShare Page Loaded",
|
||||||
|
@ -13,7 +13,11 @@
|
|||||||
<div class="right">
|
<div class="right">
|
||||||
<ul>
|
<ul>
|
||||||
<li>Total size: <strong>{{ filesize_human }}</strong> (compressed)</li>
|
<li>Total size: <strong>{{ filesize_human }}</strong> (compressed)</li>
|
||||||
|
{% if slug %}
|
||||||
<li><a class="button" href='/{{ slug }}/download'>Download Files</a></li>
|
<li><a class="button" href='/{{ slug }}/download'>Download Files</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li><a class="button" href='/download'>Download Files</a></li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<img class="logo" src="/static/img/logo.png" title="OnionShare">
|
<img class="logo" src="/static/img/logo.png" title="OnionShare">
|
||||||
|
Loading…
Reference in New Issue
Block a user