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
|
||||
filesize = os.path.getsize(filesystem_path)
|
||||
|
||||
# TODO: Tell GUI the download started
|
||||
#self.web.add_request(self.web.REQUEST_STARTED, path, {
|
||||
# 'id': download_id,
|
||||
# 'use_gzip': use_gzip
|
||||
#})
|
||||
# Each download has a unique id
|
||||
download_id = self.download_count
|
||||
self.download_count += 1
|
||||
|
||||
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():
|
||||
chunk_size = 102400 # 100kb
|
||||
|
||||
fp = open(file_to_download, 'rb')
|
||||
done = False
|
||||
canceled = False
|
||||
while not done:
|
||||
chunk = fp.read(chunk_size)
|
||||
if chunk == b'':
|
||||
@ -152,7 +162,7 @@ class SendBaseModeWeb:
|
||||
try:
|
||||
yield chunk
|
||||
|
||||
# TODO: Tell GUI the progress
|
||||
# Tell GUI the progress
|
||||
downloaded_bytes = fp.tell()
|
||||
percent = (1.0 * downloaded_bytes / filesize) * 100
|
||||
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))
|
||||
sys.stdout.flush()
|
||||
|
||||
#self.web.add_request(self.web.REQUEST_PROGRESS, path, {
|
||||
# 'id': download_id,
|
||||
# 'bytes': downloaded_bytes
|
||||
# })
|
||||
self.web.add_request(self.web.REQUEST_INDIVIDUAL_FILE_PROGRESS, path, {
|
||||
'id': download_id,
|
||||
'bytes': downloaded_bytes
|
||||
})
|
||||
done = False
|
||||
except:
|
||||
# Looks like the download was canceled
|
||||
done = True
|
||||
canceled = True
|
||||
|
||||
# TODO: Tell the GUI the download has canceled
|
||||
#self.web.add_request(self.web.REQUEST_CANCELED, path, {
|
||||
# 'id': download_id
|
||||
#})
|
||||
# Tell the GUI the individual file was canceled
|
||||
self.web.add_request(self.web.REQUEST_INDIVIDUAL_FILE_CANCELED, path, {
|
||||
'id': download_id
|
||||
})
|
||||
|
||||
fp.close()
|
||||
|
||||
|
@ -37,15 +37,18 @@ class Web:
|
||||
REQUEST_LOAD = 0
|
||||
REQUEST_STARTED = 1
|
||||
REQUEST_PROGRESS = 2
|
||||
REQUEST_OTHER = 3
|
||||
REQUEST_CANCELED = 4
|
||||
REQUEST_RATE_LIMIT = 5
|
||||
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
|
||||
REQUEST_INVALID_PASSWORD = 11
|
||||
REQUEST_CANCELED = 3
|
||||
REQUEST_RATE_LIMIT = 4
|
||||
REQUEST_UPLOAD_FILE_RENAMED = 5
|
||||
REQUEST_UPLOAD_SET_DIR = 6
|
||||
REQUEST_UPLOAD_FINISHED = 7
|
||||
REQUEST_UPLOAD_CANCELED = 8
|
||||
REQUEST_INDIVIDUAL_FILE_STARTED = 9
|
||||
REQUEST_INDIVIDUAL_FILE_PROGRESS = 10
|
||||
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'):
|
||||
self.common = common
|
||||
@ -193,15 +196,18 @@ class Web:
|
||||
r = make_response(render_template('401.html', static_url_path=self.static_url_path), 401)
|
||||
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):
|
||||
self.add_request(Web.REQUEST_OTHER, request.path)
|
||||
r = make_response(render_template('404.html', static_url_path=self.static_url_path), 404)
|
||||
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)
|
||||
def error405(self):
|
||||
r = make_response(render_template('405.html', static_url_path=self.static_url_path), 405)
|
||||
return self.add_security_headers(r)
|
||||
|
||||
def add_security_headers(self, r):
|
||||
|
@ -22,6 +22,8 @@ from PyQt5 import QtCore, QtWidgets, QtGui
|
||||
from onionshare import strings
|
||||
from onionshare.common import AutoStopTimer
|
||||
|
||||
from .history import IndividualFileHistoryItem
|
||||
|
||||
from ..server_status import ServerStatus
|
||||
from ..threads import OnionThread
|
||||
from ..threads import AutoStartTimer
|
||||
@ -29,7 +31,7 @@ from ..widgets import Alert
|
||||
|
||||
class Mode(QtWidgets.QWidget):
|
||||
"""
|
||||
The class that ShareMode and ReceiveMode inherit from.
|
||||
The class that all modes inherit from
|
||||
"""
|
||||
start_server_finished = QtCore.pyqtSignal()
|
||||
stop_server_finished = QtCore.pyqtSignal()
|
||||
@ -417,3 +419,46 @@ class Mode(QtWidgets.QWidget):
|
||||
Handle REQUEST_UPLOAD_CANCELED event.
|
||||
"""
|
||||
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
|
||||
"""
|
||||
def __init__(self, common, path):
|
||||
def __init__(self, common, data, path):
|
||||
super(IndividualFileHistoryItem, self).__init__()
|
||||
self.status = HistoryItem.STATUS_STARTED
|
||||
self.common = common
|
||||
|
||||
self.visited = time.time()
|
||||
self.visited_dt = datetime.fromtimestamp(self.visited)
|
||||
self.id = id
|
||||
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
|
||||
self.timestamp_label = QtWidgets.QLabel(self.visited_dt.strftime("%b %d, %I:%M%p"))
|
||||
self.path_viewed_label = QtWidgets.QLabel(strings._('gui_individual_file_download').format(path))
|
||||
self.timestamp_label = QtWidgets.QLabel(self.started_dt.strftime("%b %d, %I:%M%p"))
|
||||
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 = QtWidgets.QVBoxLayout()
|
||||
layout.addWidget(self.timestamp_label)
|
||||
layout.addWidget(self.path_viewed_label)
|
||||
layout.addLayout(labels_layout)
|
||||
layout.addWidget(self.progress_bar)
|
||||
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):
|
||||
self.label.setText(self.get_finished_label_text(self.started_dt))
|
||||
self.status = HistoryItem.STATUS_FINISHED
|
||||
def update(self, downloaded_bytes):
|
||||
self.downloaded_bytes = downloaded_bytes
|
||||
|
||||
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):
|
||||
self.progress_bar.setFormat(strings._('gui_canceled'))
|
||||
self.status = HistoryItem.STATUS_CANCELED
|
||||
|
||||
class VisitHistoryItem(HistoryItem):
|
||||
"""
|
||||
Download history item, for share mode
|
||||
"""
|
||||
def __init__(self, common, id, total_bytes):
|
||||
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
|
||||
@property
|
||||
def estimated_time_remaining(self):
|
||||
return self.common.estimated_time_remaining(self.downloaded_bytes,
|
||||
self.total_bytes,
|
||||
self.started)
|
||||
|
||||
class HistoryItemList(QtWidgets.QScrollArea):
|
||||
"""
|
||||
|
@ -225,21 +225,6 @@ class ShareMode(Mode):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Handle REQUEST_STARTED event.
|
||||
|
@ -30,7 +30,7 @@ from onionshare.web import Web
|
||||
|
||||
from ..file_selection import FileSelection
|
||||
from .. import Mode
|
||||
from ..history import History, ToggleHistory, VisitHistoryItem
|
||||
from ..history import History, ToggleHistory
|
||||
from ...widgets import Alert
|
||||
|
||||
class WebsiteMode(Mode):
|
||||
@ -204,21 +204,6 @@ class WebsiteMode(Mode):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
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.receive_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.settings_saved.connect(reload_settings)
|
||||
d.exec_()
|
||||
@ -470,6 +470,15 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
||||
elif event["type"] == Web.REQUEST_UPLOAD_CANCELED:
|
||||
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:
|
||||
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_autostop_timer_waiting": "Waiting to finish receiving",
|
||||
"gui_visit_started": "Someone has visited your website {}",
|
||||
"gui_individual_file_download": "Viewed {}",
|
||||
"receive_mode_upload_starting": "Upload of total size {} is starting",
|
||||
"days_first_letter": "d",
|
||||
"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