mirror of
https://github.com/onionshare/onionshare.git
synced 2024-10-01 01:35:40 -04:00
Start making IndividualFileHistoryItem widgets appear in the history, and make non-GET requests return 405 Method Not Allowed
This commit is contained in:
parent
c55925c1ce
commit
644b47082a
@ -132,18 +132,28 @@ class SendBaseModeWeb:
|
|||||||
file_to_download = filesystem_path
|
file_to_download = filesystem_path
|
||||||
filesize = os.path.getsize(filesystem_path)
|
filesize = os.path.getsize(filesystem_path)
|
||||||
|
|
||||||
# TODO: Tell GUI the download started
|
# Each download has a unique id
|
||||||
#self.web.add_request(self.web.REQUEST_STARTED, path, {
|
download_id = self.download_count
|
||||||
# 'id': download_id,
|
self.download_count += 1
|
||||||
# 'use_gzip': use_gzip
|
|
||||||
#})
|
path = request.path
|
||||||
|
|
||||||
|
# Tell GUI the individual file started
|
||||||
|
self.web.add_request(self.web.REQUEST_INDIVIDUAL_FILE_STARTED, path, {
|
||||||
|
'id': download_id,
|
||||||
|
'filesize': filesize,
|
||||||
|
'method': request.method
|
||||||
|
})
|
||||||
|
|
||||||
|
# Only GET requests are allowed, any other method should fail
|
||||||
|
if request.method != "GET":
|
||||||
|
return self.web.error405()
|
||||||
|
|
||||||
def generate():
|
def generate():
|
||||||
chunk_size = 102400 # 100kb
|
chunk_size = 102400 # 100kb
|
||||||
|
|
||||||
fp = open(file_to_download, 'rb')
|
fp = open(file_to_download, 'rb')
|
||||||
done = False
|
done = False
|
||||||
canceled = False
|
|
||||||
while not done:
|
while not done:
|
||||||
chunk = fp.read(chunk_size)
|
chunk = fp.read(chunk_size)
|
||||||
if chunk == b'':
|
if chunk == b'':
|
||||||
@ -152,7 +162,7 @@ class SendBaseModeWeb:
|
|||||||
try:
|
try:
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
# TODO: Tell GUI the progress
|
# Tell GUI the progress
|
||||||
downloaded_bytes = fp.tell()
|
downloaded_bytes = fp.tell()
|
||||||
percent = (1.0 * downloaded_bytes / filesize) * 100
|
percent = (1.0 * downloaded_bytes / filesize) * 100
|
||||||
if not self.web.is_gui or self.common.platform == 'Linux' or self.common.platform == 'BSD':
|
if not self.web.is_gui or self.common.platform == 'Linux' or self.common.platform == 'BSD':
|
||||||
@ -160,20 +170,19 @@ class SendBaseModeWeb:
|
|||||||
"\r{0:s}, {1:.2f}% ".format(self.common.human_readable_filesize(downloaded_bytes), percent))
|
"\r{0:s}, {1:.2f}% ".format(self.common.human_readable_filesize(downloaded_bytes), percent))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
#self.web.add_request(self.web.REQUEST_PROGRESS, path, {
|
self.web.add_request(self.web.REQUEST_INDIVIDUAL_FILE_PROGRESS, path, {
|
||||||
# 'id': download_id,
|
'id': download_id,
|
||||||
# 'bytes': downloaded_bytes
|
'bytes': downloaded_bytes
|
||||||
# })
|
})
|
||||||
done = False
|
done = False
|
||||||
except:
|
except:
|
||||||
# Looks like the download was canceled
|
# Looks like the download was canceled
|
||||||
done = True
|
done = True
|
||||||
canceled = True
|
|
||||||
|
|
||||||
# TODO: Tell the GUI the download has canceled
|
# Tell the GUI the individual file was canceled
|
||||||
#self.web.add_request(self.web.REQUEST_CANCELED, path, {
|
self.web.add_request(self.web.REQUEST_INDIVIDUAL_FILE_CANCELED, path, {
|
||||||
# 'id': download_id
|
'id': download_id
|
||||||
#})
|
})
|
||||||
|
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
|
@ -37,15 +37,18 @@ class Web:
|
|||||||
REQUEST_LOAD = 0
|
REQUEST_LOAD = 0
|
||||||
REQUEST_STARTED = 1
|
REQUEST_STARTED = 1
|
||||||
REQUEST_PROGRESS = 2
|
REQUEST_PROGRESS = 2
|
||||||
REQUEST_OTHER = 3
|
REQUEST_CANCELED = 3
|
||||||
REQUEST_CANCELED = 4
|
REQUEST_RATE_LIMIT = 4
|
||||||
REQUEST_RATE_LIMIT = 5
|
REQUEST_UPLOAD_FILE_RENAMED = 5
|
||||||
REQUEST_UPLOAD_FILE_RENAMED = 6
|
REQUEST_UPLOAD_SET_DIR = 6
|
||||||
REQUEST_UPLOAD_SET_DIR = 7
|
REQUEST_UPLOAD_FINISHED = 7
|
||||||
REQUEST_UPLOAD_FINISHED = 8
|
REQUEST_UPLOAD_CANCELED = 8
|
||||||
REQUEST_UPLOAD_CANCELED = 9
|
REQUEST_INDIVIDUAL_FILE_STARTED = 9
|
||||||
REQUEST_ERROR_DATA_DIR_CANNOT_CREATE = 10
|
REQUEST_INDIVIDUAL_FILE_PROGRESS = 10
|
||||||
REQUEST_INVALID_PASSWORD = 11
|
REQUEST_INDIVIDUAL_FILE_CANCELED = 11
|
||||||
|
REQUEST_ERROR_DATA_DIR_CANNOT_CREATE = 12
|
||||||
|
REQUEST_OTHER = 13
|
||||||
|
REQUEST_INVALID_PASSWORD = 14
|
||||||
|
|
||||||
def __init__(self, common, is_gui, mode='share'):
|
def __init__(self, common, is_gui, mode='share'):
|
||||||
self.common = common
|
self.common = common
|
||||||
@ -193,15 +196,18 @@ class Web:
|
|||||||
r = make_response(render_template('401.html', static_url_path=self.static_url_path), 401)
|
r = make_response(render_template('401.html', static_url_path=self.static_url_path), 401)
|
||||||
return self.add_security_headers(r)
|
return self.add_security_headers(r)
|
||||||
|
|
||||||
|
def error403(self):
|
||||||
|
self.add_request(Web.REQUEST_OTHER, request.path)
|
||||||
|
r = make_response(render_template('403.html', static_url_path=self.static_url_path), 403)
|
||||||
|
return self.add_security_headers(r)
|
||||||
|
|
||||||
def error404(self):
|
def error404(self):
|
||||||
self.add_request(Web.REQUEST_OTHER, request.path)
|
self.add_request(Web.REQUEST_OTHER, request.path)
|
||||||
r = make_response(render_template('404.html', static_url_path=self.static_url_path), 404)
|
r = make_response(render_template('404.html', static_url_path=self.static_url_path), 404)
|
||||||
return self.add_security_headers(r)
|
return self.add_security_headers(r)
|
||||||
|
|
||||||
def error403(self):
|
def error405(self):
|
||||||
self.add_request(Web.REQUEST_OTHER, request.path)
|
r = make_response(render_template('405.html', static_url_path=self.static_url_path), 405)
|
||||||
|
|
||||||
r = make_response(render_template('403.html', static_url_path=self.static_url_path), 403)
|
|
||||||
return self.add_security_headers(r)
|
return self.add_security_headers(r)
|
||||||
|
|
||||||
def add_security_headers(self, r):
|
def add_security_headers(self, r):
|
||||||
|
@ -22,6 +22,8 @@ from PyQt5 import QtCore, QtWidgets, QtGui
|
|||||||
from onionshare import strings
|
from onionshare import strings
|
||||||
from onionshare.common import AutoStopTimer
|
from onionshare.common import AutoStopTimer
|
||||||
|
|
||||||
|
from .history import IndividualFileHistoryItem
|
||||||
|
|
||||||
from ..server_status import ServerStatus
|
from ..server_status import ServerStatus
|
||||||
from ..threads import OnionThread
|
from ..threads import OnionThread
|
||||||
from ..threads import AutoStartTimer
|
from ..threads import AutoStartTimer
|
||||||
@ -29,7 +31,7 @@ from ..widgets import Alert
|
|||||||
|
|
||||||
class Mode(QtWidgets.QWidget):
|
class Mode(QtWidgets.QWidget):
|
||||||
"""
|
"""
|
||||||
The class that ShareMode and ReceiveMode inherit from.
|
The class that all modes inherit from
|
||||||
"""
|
"""
|
||||||
start_server_finished = QtCore.pyqtSignal()
|
start_server_finished = QtCore.pyqtSignal()
|
||||||
stop_server_finished = QtCore.pyqtSignal()
|
stop_server_finished = QtCore.pyqtSignal()
|
||||||
@ -417,3 +419,46 @@ class Mode(QtWidgets.QWidget):
|
|||||||
Handle REQUEST_UPLOAD_CANCELED event.
|
Handle REQUEST_UPLOAD_CANCELED event.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def handle_request_individual_file_started(self, event):
|
||||||
|
"""
|
||||||
|
Handle REQUEST_INDVIDIDUAL_FILES_STARTED event.
|
||||||
|
Used in both Share and Website modes, so implemented here.
|
||||||
|
"""
|
||||||
|
item = IndividualFileHistoryItem(self.common, event["data"], event["path"])
|
||||||
|
self.history.add(event["data"]["id"], item)
|
||||||
|
self.toggle_history.update_indicator(True)
|
||||||
|
self.history.in_progress_count += 1
|
||||||
|
self.history.update_in_progress()
|
||||||
|
|
||||||
|
def handle_request_individual_file_progress(self, event):
|
||||||
|
"""
|
||||||
|
Handle REQUEST_INDVIDIDUAL_FILES_PROGRESS event.
|
||||||
|
Used in both Share and Website modes, so implemented here.
|
||||||
|
"""
|
||||||
|
self.history.update(event["data"]["id"], event["data"]["bytes"])
|
||||||
|
|
||||||
|
# Is the download complete?
|
||||||
|
if event["data"]["bytes"] == self.web.share_mode.filesize:
|
||||||
|
# Update completed and in progress labels
|
||||||
|
self.history.completed_count += 1
|
||||||
|
self.history.in_progress_count -= 1
|
||||||
|
self.history.update_completed()
|
||||||
|
self.history.update_in_progress()
|
||||||
|
|
||||||
|
else:
|
||||||
|
if self.server_status.status == self.server_status.STATUS_STOPPED:
|
||||||
|
self.history.cancel(event["data"]["id"])
|
||||||
|
self.history.in_progress_count = 0
|
||||||
|
self.history.update_in_progress()
|
||||||
|
|
||||||
|
def handle_request_individual_file_canceled(self, event):
|
||||||
|
"""
|
||||||
|
Handle REQUEST_INDVIDIDUAL_FILES_CANCELED event.
|
||||||
|
Used in both Share and Website modes, so implemented here.
|
||||||
|
"""
|
||||||
|
self.history.cancel(event["data"]["id"])
|
||||||
|
|
||||||
|
# Update in progress count
|
||||||
|
self.history.in_progress_count -= 1
|
||||||
|
self.history.update_in_progress()
|
||||||
|
@ -345,61 +345,88 @@ class IndividualFileHistoryItem(HistoryItem):
|
|||||||
"""
|
"""
|
||||||
Individual file history item, for share mode viewing of individual files
|
Individual file history item, for share mode viewing of individual files
|
||||||
"""
|
"""
|
||||||
def __init__(self, common, path):
|
def __init__(self, common, data, path):
|
||||||
super(IndividualFileHistoryItem, self).__init__()
|
super(IndividualFileHistoryItem, self).__init__()
|
||||||
self.status = HistoryItem.STATUS_STARTED
|
self.status = HistoryItem.STATUS_STARTED
|
||||||
self.common = common
|
self.common = common
|
||||||
|
|
||||||
self.visited = time.time()
|
self.id = id
|
||||||
self.visited_dt = datetime.fromtimestamp(self.visited)
|
self.path = path
|
||||||
|
self.method = data['method']
|
||||||
|
self.total_bytes = data['filesize']
|
||||||
|
self.downloaded_bytes = 0
|
||||||
|
self.started = time.time()
|
||||||
|
self.started_dt = datetime.fromtimestamp(self.started)
|
||||||
|
self.status = HistoryItem.STATUS_STARTED
|
||||||
|
|
||||||
# Labels
|
# Labels
|
||||||
self.timestamp_label = QtWidgets.QLabel(self.visited_dt.strftime("%b %d, %I:%M%p"))
|
self.timestamp_label = QtWidgets.QLabel(self.started_dt.strftime("%b %d, %I:%M%p"))
|
||||||
self.path_viewed_label = QtWidgets.QLabel(strings._('gui_individual_file_download').format(path))
|
self.method_label = QtWidgets.QLabel("{} {}".format(self.method, self.path))
|
||||||
|
self.status_label = QtWidgets.QLabel()
|
||||||
|
|
||||||
|
# Progress bar
|
||||||
|
self.progress_bar = QtWidgets.QProgressBar()
|
||||||
|
self.progress_bar.setTextVisible(True)
|
||||||
|
self.progress_bar.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||||
|
self.progress_bar.setAlignment(QtCore.Qt.AlignHCenter)
|
||||||
|
self.progress_bar.setMinimum(0)
|
||||||
|
self.progress_bar.setMaximum(data['filesize'])
|
||||||
|
self.progress_bar.setValue(0)
|
||||||
|
self.progress_bar.setStyleSheet(self.common.css['downloads_uploads_progress_bar'])
|
||||||
|
self.progress_bar.total_bytes = data['filesize']
|
||||||
|
|
||||||
|
# Text layout
|
||||||
|
labels_layout = QtWidgets.QHBoxLayout()
|
||||||
|
labels_layout.addWidget(self.timestamp_label)
|
||||||
|
labels_layout.addWidget(self.method_label)
|
||||||
|
labels_layout.addWidget(self.status_label)
|
||||||
|
|
||||||
# Layout
|
# Layout
|
||||||
layout = QtWidgets.QVBoxLayout()
|
layout = QtWidgets.QVBoxLayout()
|
||||||
layout.addWidget(self.timestamp_label)
|
layout.addLayout(labels_layout)
|
||||||
layout.addWidget(self.path_viewed_label)
|
layout.addWidget(self.progress_bar)
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
# All non-GET requests are error 405 Method Not Allowed
|
||||||
|
if self.method.lower() != 'get':
|
||||||
|
self.status_label.setText("405")
|
||||||
|
self.progress_bar.hide()
|
||||||
|
else:
|
||||||
|
# Start at 0
|
||||||
|
self.update(0)
|
||||||
|
|
||||||
def update(self):
|
def update(self, downloaded_bytes):
|
||||||
self.label.setText(self.get_finished_label_text(self.started_dt))
|
self.downloaded_bytes = downloaded_bytes
|
||||||
self.status = HistoryItem.STATUS_FINISHED
|
|
||||||
|
self.progress_bar.setValue(downloaded_bytes)
|
||||||
|
if downloaded_bytes == self.progress_bar.total_bytes:
|
||||||
|
self.progress_bar.hide()
|
||||||
|
self.status = HistoryItem.STATUS_FINISHED
|
||||||
|
|
||||||
|
else:
|
||||||
|
elapsed = time.time() - self.started
|
||||||
|
if elapsed < 10:
|
||||||
|
# Wait a couple of seconds for the download rate to stabilize.
|
||||||
|
# This prevents a "Windows copy dialog"-esque experience at
|
||||||
|
# the beginning of the download.
|
||||||
|
pb_fmt = strings._('gui_all_modes_progress_starting').format(
|
||||||
|
self.common.human_readable_filesize(downloaded_bytes))
|
||||||
|
else:
|
||||||
|
pb_fmt = strings._('gui_all_modes_progress_eta').format(
|
||||||
|
self.common.human_readable_filesize(downloaded_bytes),
|
||||||
|
self.estimated_time_remaining)
|
||||||
|
|
||||||
|
self.progress_bar.setFormat(pb_fmt)
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self.progress_bar.setFormat(strings._('gui_canceled'))
|
self.progress_bar.setFormat(strings._('gui_canceled'))
|
||||||
self.status = HistoryItem.STATUS_CANCELED
|
self.status = HistoryItem.STATUS_CANCELED
|
||||||
|
|
||||||
class VisitHistoryItem(HistoryItem):
|
@property
|
||||||
"""
|
def estimated_time_remaining(self):
|
||||||
Download history item, for share mode
|
return self.common.estimated_time_remaining(self.downloaded_bytes,
|
||||||
"""
|
self.total_bytes,
|
||||||
def __init__(self, common, id, total_bytes):
|
self.started)
|
||||||
super(VisitHistoryItem, self).__init__()
|
|
||||||
self.status = HistoryItem.STATUS_STARTED
|
|
||||||
self.common = common
|
|
||||||
|
|
||||||
self.id = id
|
|
||||||
self.visited = time.time()
|
|
||||||
self.visited_dt = datetime.fromtimestamp(self.visited)
|
|
||||||
|
|
||||||
# Label
|
|
||||||
self.label = QtWidgets.QLabel(strings._('gui_visit_started').format(self.visited_dt.strftime("%b %d, %I:%M%p")))
|
|
||||||
|
|
||||||
# Layout
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
self.label.setText(self.get_finished_label_text(self.started_dt))
|
|
||||||
self.status = HistoryItem.STATUS_FINISHED
|
|
||||||
|
|
||||||
def cancel(self):
|
|
||||||
self.progress_bar.setFormat(strings._('gui_canceled'))
|
|
||||||
self.status = HistoryItem.STATUS_CANCELED
|
|
||||||
|
|
||||||
class HistoryItemList(QtWidgets.QScrollArea):
|
class HistoryItemList(QtWidgets.QScrollArea):
|
||||||
"""
|
"""
|
||||||
|
@ -225,21 +225,6 @@ class ShareMode(Mode):
|
|||||||
"""
|
"""
|
||||||
self.primary_action.hide()
|
self.primary_action.hide()
|
||||||
|
|
||||||
def handle_request_load(self, event):
|
|
||||||
"""
|
|
||||||
Handle REQUEST_LOAD event.
|
|
||||||
"""
|
|
||||||
self.system_tray.showMessage(strings._('systray_page_loaded_title'), strings._('systray_page_loaded_message'))
|
|
||||||
if not self.common.settings.get('close_after_first_download') and not event["path"].startswith(('/favicon.ico', '/download', self.web.static_url_path)) and event["path"] != '/':
|
|
||||||
|
|
||||||
item = IndividualFileHistoryItem(self.common, event["path"])
|
|
||||||
|
|
||||||
self.history.add(0, item)
|
|
||||||
self.toggle_history.update_indicator(True)
|
|
||||||
self.history.completed_count += 1
|
|
||||||
self.history.update_completed()
|
|
||||||
self.system_tray.showMessage(strings._('systray_individual_file_downloaded_title'), strings._('systray_individual_file_downloaded_message').format(event["path"]))
|
|
||||||
|
|
||||||
def handle_request_started(self, event):
|
def handle_request_started(self, event):
|
||||||
"""
|
"""
|
||||||
Handle REQUEST_STARTED event.
|
Handle REQUEST_STARTED event.
|
||||||
|
@ -30,7 +30,7 @@ from onionshare.web import Web
|
|||||||
|
|
||||||
from ..file_selection import FileSelection
|
from ..file_selection import FileSelection
|
||||||
from .. import Mode
|
from .. import Mode
|
||||||
from ..history import History, ToggleHistory, VisitHistoryItem
|
from ..history import History, ToggleHistory
|
||||||
from ...widgets import Alert
|
from ...widgets import Alert
|
||||||
|
|
||||||
class WebsiteMode(Mode):
|
class WebsiteMode(Mode):
|
||||||
@ -204,21 +204,6 @@ class WebsiteMode(Mode):
|
|||||||
"""
|
"""
|
||||||
self.system_tray.showMessage(strings._('systray_site_loaded_title'), strings._('systray_site_loaded_message'))
|
self.system_tray.showMessage(strings._('systray_site_loaded_title'), strings._('systray_site_loaded_message'))
|
||||||
|
|
||||||
def handle_request_started(self, event):
|
|
||||||
"""
|
|
||||||
Handle REQUEST_STARTED event.
|
|
||||||
"""
|
|
||||||
if ( (event["path"] == '') or (event["path"].find(".htm") != -1 ) ):
|
|
||||||
item = VisitHistoryItem(self.common, event["data"]["id"], 0)
|
|
||||||
|
|
||||||
self.history.add(event["data"]["id"], item)
|
|
||||||
self.toggle_history.update_indicator(True)
|
|
||||||
self.history.completed_count += 1
|
|
||||||
self.history.update_completed()
|
|
||||||
|
|
||||||
self.system_tray.showMessage(strings._('systray_website_started_title'), strings._('systray_website_started_message'))
|
|
||||||
|
|
||||||
|
|
||||||
def on_reload_settings(self):
|
def on_reload_settings(self):
|
||||||
"""
|
"""
|
||||||
If there were some files listed for sharing, we should be ok to re-enable
|
If there were some files listed for sharing, we should be ok to re-enable
|
||||||
|
@ -383,7 +383,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
self.share_mode.server_status.autostart_timer_container.hide()
|
self.share_mode.server_status.autostart_timer_container.hide()
|
||||||
self.receive_mode.server_status.autostart_timer_container.hide()
|
self.receive_mode.server_status.autostart_timer_container.hide()
|
||||||
self.website_mode.server_status.autostart_timer_container.hide()
|
self.website_mode.server_status.autostart_timer_container.hide()
|
||||||
|
|
||||||
d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only)
|
d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only)
|
||||||
d.settings_saved.connect(reload_settings)
|
d.settings_saved.connect(reload_settings)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
@ -470,6 +470,15 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
elif event["type"] == Web.REQUEST_UPLOAD_CANCELED:
|
elif event["type"] == Web.REQUEST_UPLOAD_CANCELED:
|
||||||
mode.handle_request_upload_canceled(event)
|
mode.handle_request_upload_canceled(event)
|
||||||
|
|
||||||
|
elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_STARTED:
|
||||||
|
mode.handle_request_individual_file_started(event)
|
||||||
|
|
||||||
|
elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_PROGRESS:
|
||||||
|
mode.handle_request_individual_file_progress(event)
|
||||||
|
|
||||||
|
elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_CANCELED:
|
||||||
|
mode.handle_request_individual_file_canceled(event)
|
||||||
|
|
||||||
if event["type"] == Web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE:
|
if event["type"] == Web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE:
|
||||||
Alert(self.common, strings._('error_cannot_create_data_dir').format(event["data"]["receive_mode_dir"]))
|
Alert(self.common, strings._('error_cannot_create_data_dir').format(event["data"]["receive_mode_dir"]))
|
||||||
|
|
||||||
|
@ -178,7 +178,6 @@
|
|||||||
"gui_receive_mode_no_files": "No Files Received Yet",
|
"gui_receive_mode_no_files": "No Files Received Yet",
|
||||||
"gui_receive_mode_autostop_timer_waiting": "Waiting to finish receiving",
|
"gui_receive_mode_autostop_timer_waiting": "Waiting to finish receiving",
|
||||||
"gui_visit_started": "Someone has visited your website {}",
|
"gui_visit_started": "Someone has visited your website {}",
|
||||||
"gui_individual_file_download": "Viewed {}",
|
|
||||||
"receive_mode_upload_starting": "Upload of total size {} is starting",
|
"receive_mode_upload_starting": "Upload of total size {} is starting",
|
||||||
"days_first_letter": "d",
|
"days_first_letter": "d",
|
||||||
"hours_first_letter": "h",
|
"hours_first_letter": "h",
|
||||||
|
19
share/templates/405.html
Normal file
19
share/templates/405.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>OnionShare: 405 Method Not Allowed</title>
|
||||||
|
<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>
|
||||||
|
<div class="info-wrapper">
|
||||||
|
<div class="info">
|
||||||
|
<p><img class="logo" src="{{ static_url_path }}/img/logo_large.png" title="OnionShare"></p>
|
||||||
|
<p class="info-header">405 Method Not Allowed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user