Merge branch 'develop' of github.com:micahflee/onionshare into chat

This commit is contained in:
Saptak S 2020-07-04 13:42:20 +05:30
commit 41681f82ec
No known key found for this signature in database
GPG Key ID: 2D9B32E54C68A3FB
25 changed files with 316 additions and 70 deletions

View File

@ -103,7 +103,7 @@ Create a .rpm on Fedora-like distros: `./install/build_rpm.sh`
For openSUSE: There are instructions for building [in the wiki](https://github.com/micahflee/onionshare/wiki/Linux-Distribution-Support#opensuse-leap-150).
For ArchLinux: There is a PKBUILD available [here](https://aur.archlinux.org/packages/onionshare/) that can be used to install OnionShare.
For ArchLinux: There is a PKBUILD available [here](https://www.archlinux.org/packages/community/any/onionshare/) that can be used to install OnionShare.
If you find that these instructions don't work for your Linux distribution or version, consult the [Linux Distribution Support wiki guide](https://github.com/micahflee/onionshare/wiki/Linux-Distribution-Support), which might contain extra instructions.

View File

@ -209,6 +209,11 @@ class GuiCommon:
color: #cc0000;
}""",
# Share mode and child widget styles
"share_delete_all_files_button": """
QPushButton {
color: #3f7fcf;
}
""",
"share_zip_progess_bar": """
QProgressBar {
border: 1px solid #4e064f;

View File

@ -138,7 +138,7 @@ class Mode(QtWidgets.QWidget):
"""
# If this is a scheduled share, display the countdown til the share starts
if self.server_status.status == ServerStatus.STATUS_WORKING:
if self.server_status.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
now = QtCore.QDateTime.currentDateTime()
if self.server_status.local_only:
seconds_remaining = now.secsTo(
@ -227,13 +227,8 @@ class Mode(QtWidgets.QWidget):
# Start the onion thread. If this share was scheduled for a future date,
# the OnionThread will start and exit 'early' to obtain the port, password
# and onion address, but it will not start the WebThread yet.
if self.server_status.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
self.start_onion_thread(obtain_onion_early=True)
else:
self.start_onion_thread()
# If scheduling a share, delay starting the real share
if self.server_status.autostart_timer_datetime:
self.common.log("Mode", "start_server", "Starting auto-start timer")
self.startup_thread = AutoStartTimer(self)
# Once the timer has finished, start the real share, with a WebThread
@ -241,6 +236,8 @@ class Mode(QtWidgets.QWidget):
self.startup_thread.error.connect(self.start_server_error)
self.startup_thread.canceled = False
self.startup_thread.start()
else:
self.start_onion_thread()
def start_onion_thread(self, obtain_onion_early=False):
self.common.log("Mode", "start_server", "Starting an onion thread")

View File

@ -338,8 +338,8 @@ class FileSelection(QtWidgets.QVBoxLayout):
else:
self.add_button = QtWidgets.QPushButton(strings._("gui_add"))
self.add_button.clicked.connect(self.add)
self.delete_button = QtWidgets.QPushButton(strings._("gui_delete"))
self.delete_button.clicked.connect(self.delete)
self.remove_button = QtWidgets.QPushButton(strings._("gui_remove"))
self.remove_button.clicked.connect(self.delete)
button_layout = QtWidgets.QHBoxLayout()
button_layout.addStretch()
if self.common.platform == "Darwin":
@ -347,7 +347,7 @@ class FileSelection(QtWidgets.QVBoxLayout):
button_layout.addWidget(self.add_folder_button)
else:
button_layout.addWidget(self.add_button)
button_layout.addWidget(self.delete_button)
button_layout.addWidget(self.remove_button)
# Add the widgets
self.addWidget(self.file_list)
@ -366,7 +366,7 @@ class FileSelection(QtWidgets.QVBoxLayout):
self.add_folder_button.hide()
else:
self.add_button.hide()
self.delete_button.hide()
self.remove_button.hide()
else:
if self.common.platform == "Darwin":
self.add_files_button.show()
@ -376,9 +376,9 @@ class FileSelection(QtWidgets.QVBoxLayout):
# Delete button should be hidden if item isn't selected
if len(self.file_list.selectedItems()) == 0:
self.delete_button.hide()
self.remove_button.hide()
else:
self.delete_button.show()
self.remove_button.show()
# Update the file list
self.file_list.update()

View File

@ -76,7 +76,10 @@ class ModeSettingsWidget(QtWidgets.QWidget):
self.autostart_timer_widget.setCurrentSection(
QtWidgets.QDateTimeEdit.MinuteSection
)
self.autostart_timer_widget.hide()
if self.settings.get("general", "autostart_timer"):
self.autostart_timer_widget.show()
else:
self.autostart_timer_widget.hide()
# Autostart timer layout
autostart_timer_layout = QtWidgets.QHBoxLayout()
@ -104,7 +107,10 @@ class ModeSettingsWidget(QtWidgets.QWidget):
self.autostop_timer_widget.setCurrentSection(
QtWidgets.QDateTimeEdit.MinuteSection
)
self.autostop_timer_widget.hide()
if self.settings.get("general", "autostop_timer"):
self.autostop_timer_widget.show()
else:
self.autostop_timer_widget.hide()
# Autostop timer layout
autostop_timer_layout = QtWidgets.QHBoxLayout()

View File

@ -141,7 +141,7 @@ class ReceiveMode(Mode):
f"selected dir: {selected_dir}",
)
self.data_dir_lineedit.setText(selected_dir)
self.settings.set("receive", "data_dir", data_dir)
self.settings.set("receive", "data_dir", selected_dir)
def get_stop_server_autostop_timer_text(self):
"""

View File

@ -111,6 +111,17 @@ class ShareMode(Mode):
self.info_label = QtWidgets.QLabel()
self.info_label.hide()
# Delete all files button
self.remove_all_button = QtWidgets.QPushButton(
strings._("gui_file_selection_remove_all")
)
self.remove_all_button.setFlat(True)
self.remove_all_button.setStyleSheet(
self.common.gui.css["share_delete_all_files_button"]
)
self.remove_all_button.clicked.connect(self.delete_all)
self.remove_all_button.hide()
# Toggle history
self.toggle_history = ToggleHistory(
self.common,
@ -126,6 +137,7 @@ class ShareMode(Mode):
top_bar_layout = QtWidgets.QHBoxLayout()
top_bar_layout.addWidget(self.info_label)
top_bar_layout.addStretch()
top_bar_layout.addWidget(self.remove_all_button)
top_bar_layout.addWidget(self.toggle_history)
# Primary action layout
@ -198,6 +210,8 @@ class ShareMode(Mode):
# Hide and reset the downloads if we have previously shared
self.reset_info_counters()
self.remove_all_button.hide()
def start_server_step2_custom(self):
"""
Step 2 in starting the server. Zipping up files.
@ -257,6 +271,8 @@ class ShareMode(Mode):
self.history.update_in_progress()
self.file_selection.file_list.adjustSize()
self.remove_all_button.show()
def cancel_server_custom(self):
"""
Stop the compression thread on cancel
@ -343,6 +359,7 @@ class ShareMode(Mode):
if self.server_status.file_selection.get_num_files() > 0:
self.primary_action.show()
self.info_label.show()
self.remove_all_button.show()
def update_primary_action(self):
self.common.log("ShareMode", "update_primary_action")
@ -352,6 +369,7 @@ class ShareMode(Mode):
if file_count > 0:
self.primary_action.show()
self.info_label.show()
self.remove_all_button.show()
# Update the file count in the info label
total_size_bytes = 0
@ -374,6 +392,7 @@ class ShareMode(Mode):
else:
self.primary_action.hide()
self.info_label.hide()
self.remove_all_button.hide()
def reset_info_counters(self):
"""
@ -383,6 +402,15 @@ class ShareMode(Mode):
self.toggle_history.indicator_count = 0
self.toggle_history.update_indicator()
def delete_all(self):
"""
Delete All button clicked
"""
self.file_selection.file_list.clear()
self.file_selection.file_list.files_updated.emit()
self.file_selection.file_list.setCurrentItem(None)
@staticmethod
def _compute_total_size(filenames):
total_size = 0

View File

@ -114,6 +114,17 @@ class WebsiteMode(Mode):
self.info_label = QtWidgets.QLabel()
self.info_label.hide()
# Delete all files button
self.remove_all_button = QtWidgets.QPushButton(
strings._("gui_file_selection_remove_all")
)
self.remove_all_button.setFlat(True)
self.remove_all_button.setStyleSheet(
self.common.gui.css["share_delete_all_files_button"]
)
self.remove_all_button.clicked.connect(self.delete_all)
self.remove_all_button.hide()
# Toggle history
self.toggle_history = ToggleHistory(
self.common,
@ -129,6 +140,7 @@ class WebsiteMode(Mode):
top_bar_layout = QtWidgets.QHBoxLayout()
top_bar_layout.addWidget(self.info_label)
top_bar_layout.addStretch()
top_bar_layout.addWidget(self.remove_all_button)
top_bar_layout.addWidget(self.toggle_history)
# Primary action layout
@ -191,6 +203,8 @@ class WebsiteMode(Mode):
# Hide and reset the downloads if we have previously shared
self.reset_info_counters()
self.remove_all_button.hide()
def start_server_step2_custom(self):
"""
Step 2 in starting the server. Zipping up files.
@ -228,6 +242,8 @@ class WebsiteMode(Mode):
self.history.completed_count = 0
self.file_selection.file_list.adjustSize()
self.remove_all_button.show()
def cancel_server_custom(self):
"""
Log that the server has been cancelled
@ -248,6 +264,7 @@ class WebsiteMode(Mode):
if self.server_status.file_selection.get_num_files() > 0:
self.primary_action.show()
self.info_label.show()
self.remove_all_button.show()
def update_primary_action(self):
self.common.log("WebsiteMode", "update_primary_action")
@ -257,6 +274,7 @@ class WebsiteMode(Mode):
if file_count > 0:
self.primary_action.show()
self.info_label.show()
self.remove_all_button.show()
# Update the file count in the info label
total_size_bytes = 0
@ -279,6 +297,7 @@ class WebsiteMode(Mode):
else:
self.primary_action.hide()
self.info_label.hide()
self.remove_all_button.hide()
def reset_info_counters(self):
"""
@ -288,6 +307,15 @@ class WebsiteMode(Mode):
self.toggle_history.indicator_count = 0
self.toggle_history.update_indicator()
def delete_all(self):
"""
Delete All button clicked
"""
self.file_selection.file_list.clear()
self.file_selection.file_list.files_updated.emit()
self.file_selection.file_list.setCurrentItem(None)
@staticmethod
def _compute_total_size(filenames):
total_size = 0

View File

@ -20,10 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import platform
import textwrap
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import Qt
from onionshare import strings
from ..widgets import Alert
from ..widgets import QRCodeDialog
class ServerStatus(QtWidgets.QWidget):
@ -85,17 +87,31 @@ class ServerStatus(QtWidgets.QWidget):
self.url.setWordWrap(True)
self.url.setMinimumSize(self.url.sizeHint())
self.url.setStyleSheet(self.common.gui.css["server_status_url"])
self.url.setTextInteractionFlags(
Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard
)
self.copy_url_button = QtWidgets.QPushButton(strings._("gui_copy_url"))
self.copy_url_button.setFlat(True)
self.copy_url_button.setStyleSheet(
self.common.gui.css["server_status_url_buttons"]
)
self.copy_url_button.setMinimumHeight(65)
self.copy_url_button.clicked.connect(self.copy_url)
self.copy_hidservauth_button = QtWidgets.QPushButton(
strings._("gui_copy_hidservauth")
)
self.show_url_qr_code_button = QtWidgets.QPushButton(
strings._("gui_show_url_qr_code")
)
self.show_url_qr_code_button.hide()
self.show_url_qr_code_button.clicked.connect(
self.show_url_qr_code_button_clicked
)
self.show_url_qr_code_button.setFlat(True)
self.show_url_qr_code_button.setStyleSheet(
self.common.gui.css["server_status_url_buttons"]
)
self.copy_hidservauth_button.setFlat(True)
self.copy_hidservauth_button.setStyleSheet(
self.common.gui.css["server_status_url_buttons"]
@ -103,6 +119,7 @@ class ServerStatus(QtWidgets.QWidget):
self.copy_hidservauth_button.clicked.connect(self.copy_hidservauth)
url_buttons_layout = QtWidgets.QHBoxLayout()
url_buttons_layout.addWidget(self.copy_url_button)
url_buttons_layout.addWidget(self.show_url_qr_code_button)
url_buttons_layout.addWidget(self.copy_hidservauth_button)
url_buttons_layout.addStretch()
@ -190,6 +207,8 @@ class ServerStatus(QtWidgets.QWidget):
self.url.show()
self.copy_url_button.show()
self.show_url_qr_code_button.show()
if self.settings.get("general", "client_auth"):
self.copy_hidservauth_button.show()
else:
@ -273,7 +292,7 @@ class ServerStatus(QtWidgets.QWidget):
self.common.gui.css["server_status_button_working"]
)
self.server_button.setEnabled(True)
if self.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
self.server_button.setToolTip(
strings._("gui_start_server_autostart_timer_tooltip").format(
self.mode_settings_widget.autostart_timer_widget.dateTime().toString(
@ -283,15 +302,6 @@ class ServerStatus(QtWidgets.QWidget):
)
else:
self.server_button.setText(strings._("gui_please_wait"))
if self.settings.get("general", "autostart_timer"):
self.server_button.setToolTip(
strings._("gui_start_server_autostart_timer_tooltip").format(
self.mode_settings_widget.autostart_timer_widget.dateTime().toString(
"h:mm AP, MMMM dd, yyyy"
)
)
)
else:
self.server_button.setStyleSheet(
self.common.gui.css["server_status_button_working"]
@ -368,6 +378,12 @@ class ServerStatus(QtWidgets.QWidget):
self.cancel_server()
self.button_clicked.emit()
def show_url_qr_code_button_clicked(self):
"""
Show a QR code of the onion URL.
"""
self.qr_code_dialog = QRCodeDialog(self.common, self.get_url())
def start_server(self):
"""
Start the server.
@ -381,7 +397,7 @@ class ServerStatus(QtWidgets.QWidget):
The server has finished starting.
"""
self.status = self.STATUS_STARTED
self.copy_url()
# self.copy_url()
self.update()
self.server_started_finished.emit()

View File

@ -338,7 +338,7 @@ class Tab(QtWidgets.QWidget):
strings._("gui_status_indicator_share_stopped")
)
elif self.share_mode.server_status.status == ServerStatus.STATUS_WORKING:
if self.share_mode.server_status.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
self.set_server_status_indicator_working(
strings._("gui_status_indicator_share_scheduled")
)
@ -357,9 +357,14 @@ class Tab(QtWidgets.QWidget):
strings._("gui_status_indicator_share_stopped")
)
elif self.website_mode.server_status.status == ServerStatus.STATUS_WORKING:
self.set_server_status_indicator_working(
strings._("gui_status_indicator_share_working")
)
if self.website_mode.server_status.autostart_timer_datetime:
self.set_server_status_indicator_working(
strings._("gui_status_indicator_share_scheduled")
)
else:
self.set_server_status_indicator_working(
strings._("gui_status_indicator_share_working")
)
elif self.website_mode.server_status.status == ServerStatus.STATUS_STARTED:
self.set_server_status_indicator_started(
strings._("gui_status_indicator_share_started")
@ -371,7 +376,7 @@ class Tab(QtWidgets.QWidget):
strings._("gui_status_indicator_receive_stopped")
)
elif self.receive_mode.server_status.status == ServerStatus.STATUS_WORKING:
if self.receive_mode.server_status.autostart_timer_datetime:
if self.settings.get("general", "autostart_timer"):
self.set_server_status_indicator_working(
strings._("gui_status_indicator_receive_scheduled")
)

View File

@ -60,6 +60,7 @@ class TabWidget(QtWidgets.QTabWidget):
# Use a custom tab bar
tab_bar = TabBar()
tab_bar.move_new_tab_button.connect(self.move_new_tab_button)
tab_bar.currentChanged.connect(self.tab_changed)
self.setTabBar(tab_bar)
# Set up the tab widget
@ -108,6 +109,24 @@ class TabWidget(QtWidgets.QTabWidget):
self.new_tab_button.move(pos)
self.new_tab_button.raise_()
def tab_changed(self):
# Active tab was changed
tab_id = self.currentIndex()
self.common.log("TabWidget", "tab_changed", f"Tab was changed to {tab_id}")
try:
mode = self.tabs[tab_id].get_mode()
if mode:
# Update the server status indicator to reflect that of the current tab
self.tabs[tab_id].update_server_status_indicator()
else:
# If this tab doesn't have a mode set yet, blank the server status indicator
self.status_bar.server_status_image_label.clear()
self.status_bar.server_status_label.clear()
except KeyError:
# When all current tabs are closed, index briefly drops to -1 before resetting to 0
# which will otherwise trigger a KeyError on tab.get_mode() above.
pass
def new_tab_clicked(self):
# Create a new tab
self.add_tab()

View File

@ -18,7 +18,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings
import qrcode
class Alert(QtWidgets.QMessageBox):
"""
@ -90,3 +91,64 @@ class MinimumWidthWidget(QtWidgets.QWidget):
super(MinimumWidthWidget, self).__init__()
self.setMinimumWidth(width)
class Image(qrcode.image.base.BaseImage):
"""
A custom Image class, for use with the QR Code pixmap.
"""
def __init__(self, border, width, box_size):
self.border = border
self.width = width
self.box_size = box_size
size = (width + border * 2) * box_size
self._image = QtGui.QImage(
size, size, QtGui.QImage.Format_RGB16)
self._image.fill(QtCore.Qt.white)
def pixmap(self):
return QtGui.QPixmap.fromImage(self._image)
def drawrect(self, row, col):
painter = QtGui.QPainter(self._image)
painter.fillRect(
(col + self.border) * self.box_size,
(row + self.border) * self.box_size,
self.box_size, self.box_size,
QtCore.Qt.black)
def save(self, stream, kind=None):
pass
class QRCodeDialog(QtWidgets.QDialog):
"""
A dialog showing a QR code.
"""
def __init__(self, common, text):
super(QRCodeDialog, self).__init__()
self.common = common
self.text = text
self.common.log("QrCode", "__init__")
self.qr_label = QtWidgets.QLabel(self)
self.qr_label.setPixmap(
qrcode.make(self.text, image_factory=Image).pixmap())
self.qr_label_description = QtWidgets.QLabel(self)
self.qr_label_description.setText(strings._("gui_qr_code_description"))
self.qr_label_description.setWordWrap(True)
self.setWindowTitle(strings._("gui_qr_code_dialog_title"))
self.setWindowIcon(
QtGui.QIcon(self.common.get_resource_path("images/logo.png"))
)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.qr_label)
layout.addWidget(self.qr_label_description)
self.exec_()

32
poetry.lock generated
View File

@ -53,9 +53,9 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "7.1.1"
[[package]]
category = "dev"
category = "main"
description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\""
marker = "platform_system == \"Windows\" or sys_platform == \"win32\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@ -412,6 +412,7 @@ doc = ["sphinx", "sphinx-rtd-theme"]
[[package]]
category = "main"
<<<<<<< HEAD
description = "Engine.IO server"
name = "python-engineio"
optional = false
@ -440,6 +441,23 @@ six = ">=1.9.0"
[package.extras]
asyncio_client = ["aiohttp (>=3.4)", "websockets (>=7.0)"]
client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
=======
description = "QR Code image generator"
name = "qrcode"
optional = false
python-versions = "*"
version = "6.1"
[package.dependencies]
colorama = "*"
six = "*"
[package.extras]
dev = ["tox", "pytest", "mock"]
maintainer = ["zest.releaser"]
pil = ["pillow"]
test = ["pytest", "pytest-cov", "mock"]
>>>>>>> 172a30d5c9facbf50fe3b11f663b338a8d9a2c73
[[package]]
category = "main"
@ -536,7 +554,11 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["jaraco.itertools", "func-timeout"]
[metadata]
<<<<<<< HEAD
content-hash = "181ea24287292c0ab933307bbff258d4f2791642116c7c39d3bde83d573c089b"
=======
content-hash = "3f46cfec01bcb5166c9f354aaf4439064b477955f3ea2373fcfdb65d5b89276e"
>>>>>>> 172a30d5c9facbf50fe3b11f663b338a8d9a2c73
python-versions = "^3.7"
[metadata.files]
@ -796,6 +818,7 @@ pytest-qt = [
{file = "pytest-qt-3.3.0.tar.gz", hash = "sha256:714b0bf86c5313413f2d300ac613515db3a1aef595051ab8ba2ffe619dbe8925"},
{file = "pytest_qt-3.3.0-py2.py3-none-any.whl", hash = "sha256:5f8928288f50489d83f5d38caf2d7d9fcd6e7cf769947902caa4661dc7c851e3"},
]
<<<<<<< HEAD
python-engineio = [
{file = "python-engineio-3.12.1.tar.gz", hash = "sha256:2481732d93646998f7372ef0ecf003af7817b82720b881db173c3d50b4887916"},
{file = "python_engineio-3.12.1-py2.py3-none-any.whl", hash = "sha256:222926adb4bc6e03a8fc8e0ef2a3309f030c1c3f8e0fcc94c9ba214574565f02"},
@ -803,6 +826,11 @@ python-engineio = [
python-socketio = [
{file = "python-socketio-4.5.1.tar.gz", hash = "sha256:149b98c33f8c3d09273fb4ebeb83781e4dc9411b56b27d9f058bec1bd1ed74b7"},
{file = "python_socketio-4.5.1-py2.py3-none-any.whl", hash = "sha256:81280cbbb7018d8ecdd006bf6025979733d347c0f2612282c1e21f6ed7d3b55b"},
=======
qrcode = [
{file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"},
{file = "qrcode-6.1.tar.gz", hash = "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369"},
>>>>>>> 172a30d5c9facbf50fe3b11f663b338a8d9a2c73
]
requests = [
{file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"},

View File

@ -32,6 +32,7 @@ watchdog = "*"
psutil = "*"
flask-socketio = "^4.3.0"
eventlet = "^0.25.2"
qrcode = "^6.1"
[tool.poetry.dev-dependencies]
atomicwrites = "*"

View File

@ -11,7 +11,8 @@
"gui_add": "Add",
"gui_add_files": "Add Files",
"gui_add_folder": "Add Folder",
"gui_delete": "Delete",
"gui_remove": "Remove",
"gui_file_selection_remove_all": "Remove All",
"gui_choose_items": "Choose",
"gui_share_start_server": "Start sharing",
"gui_share_stop_server": "Stop sharing",
@ -31,6 +32,9 @@
"gui_copied_url": "OnionShare address copied to clipboard",
"gui_copied_hidservauth_title": "Copied HidServAuth",
"gui_copied_hidservauth": "HidServAuth line copied to clipboard",
"gui_show_url_qr_code": "Show QR code",
"gui_qr_code_dialog_title": "OnionShare QR Code",
"gui_qr_code_description": "Scan this QR code with a QR reader, such as the camera on your phone, in order to more easily share the OnionShare address with someone.",
"gui_waiting_to_start": "Scheduled to start in {}. Click to cancel.",
"gui_please_wait": "Starting… Click to cancel.",
"error_rate_limit": "Someone has made too many wrong attempts to guess your password, so OnionShare has stopped the server. Start sharing again and send the recipient a new address to share.",
@ -210,4 +214,4 @@
"mode_settings_receive_data_dir_label": "Save files to",
"mode_settings_receive_data_dir_browse_button": "Browse",
"mode_settings_website_disable_csp_checkbox": "Disable Content Security Policy header (allows your website to use third-party resources)"
}
}

File diff suppressed because one or more lines are too long

2
share/static/js/jquery-3.5.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,9 @@ $(function(){
$('#flashes').append($('<li>').addClass(category).text(message));
};
var scriptSrc = document.getElementById('receive-script').src;
var staticImgPath = scriptSrc.substr(0, scriptSrc.lastIndexOf( '/' )+1).replace('js', 'img');
// Intercept submitting the form
$('#send').submit(function(event){
event.preventDefault();
@ -38,7 +41,7 @@ $(function(){
// and update the status
if(event.loaded == event.total) {
$('.cancel', ajax.$upload_div).remove();
$('.upload-status', ajax.$upload_div).html('<img src="/static/img/ajax.gif" alt="" /> Waiting for data to finish traversing Tor network ...');
$('.upload-status', ajax.$upload_div).html('<img src="' + staticImgPath + '/ajax.gif" alt="" /> Waiting for data to finish traversing Tor network ...');
}
}, false);

View File

@ -38,7 +38,7 @@
</form>
</div>
<script src="{{ static_url_path }}/js/jquery-3.4.0.min.js"></script>
<script async src="{{ static_url_path }}/js/receive.js"></script>
<script src="{{ static_url_path }}/js/jquery-3.5.1.min.js"></script>
<script async src="{{ static_url_path }}/js/receive.js" id="receive-script"></script>
</body>
</html>

View File

@ -297,6 +297,17 @@ class GuiBaseTest(unittest.TestCase):
f"http://onionshare:{tab.get_mode().server_status.web.password}@127.0.0.1:{tab.app.port}",
)
def have_show_qr_code_button(self, tab):
"""Test that the Show QR Code URL button is shown and that it loads a QR Code Dialog"""
self.assertTrue(tab.get_mode().server_status.show_url_qr_code_button.isVisible())
def accept_dialog():
window = tab.common.gui.qtapp.activeWindow()
if window:
window.close()
QtCore.QTimer.singleShot(500, accept_dialog)
tab.get_mode().server_status.show_url_qr_code_button.click()
def server_status_indicator_says_started(self, tab):
"""Test that the Server Status indicator shows we are started"""
if type(tab.get_mode()) == ReceiveMode:
@ -388,13 +399,13 @@ class GuiBaseTest(unittest.TestCase):
tab.get_mode().server_status.file_selection.get_num_files(), num
)
def add_delete_buttons_hidden(self, tab):
"""Test that the add and delete buttons are hidden when the server starts"""
def add_remove_buttons_hidden(self, tab):
"""Test that the add and remove buttons are hidden when the server starts"""
self.assertFalse(
tab.get_mode().server_status.file_selection.add_button.isVisible()
)
self.assertFalse(
tab.get_mode().server_status.file_selection.delete_button.isVisible()
tab.get_mode().server_status.file_selection.remove_button.isVisible()
)
# Auto-stop timer tests

View File

@ -20,7 +20,7 @@ do
shift
done
pytest $PARAMS -vvv ./tests/test_cli*.py
pytest $PARAMS -vvv ./tests/test_cli*.py || exit 1
for filename in ./tests/test_gui_*.py; do
pytest $PARAMS -vvv --no-qt-log $filename
pytest $PARAMS -vvv --no-qt-log $filename || exit 1
done

View File

@ -112,6 +112,7 @@ class TestReceive(GuiBaseTest):
self.have_a_password(tab)
self.url_description_shown(tab)
self.have_copy_url_button(tab)
self.have_show_qr_code_button(tab)
self.server_status_indicator_says_started(tab)
self.web_page(tab, "Select the files you want to send, then click")

View File

@ -12,8 +12,8 @@ from .gui_base_test import GuiBaseTest
class TestShare(GuiBaseTest):
# Shared test methods
def deleting_all_files_hides_delete_button(self, tab):
"""Test that clicking on the file item shows the delete button. Test that deleting the only item in the list hides the delete button"""
def removing_all_files_hides_remove_button(self, tab):
"""Test that clicking on the file item shows the remove button. Test that removing the only item in the list hides the remove button"""
rect = tab.get_mode().server_status.file_selection.file_list.visualItemRect(
tab.get_mode().server_status.file_selection.file_list.item(0)
)
@ -22,12 +22,12 @@ class TestShare(GuiBaseTest):
QtCore.Qt.LeftButton,
pos=rect.center(),
)
# Delete button should be visible
# Remove button should be visible
self.assertTrue(
tab.get_mode().server_status.file_selection.delete_button.isVisible()
tab.get_mode().server_status.file_selection.remove_button.isVisible()
)
# Click delete, delete button should still be visible since we have one more file
tab.get_mode().server_status.file_selection.delete_button.click()
# Click remove, remove button should still be visible since we have one more file
tab.get_mode().server_status.file_selection.remove_button.click()
rect = tab.get_mode().server_status.file_selection.file_list.visualItemRect(
tab.get_mode().server_status.file_selection.file_list.item(0)
)
@ -37,17 +37,17 @@ class TestShare(GuiBaseTest):
pos=rect.center(),
)
self.assertTrue(
tab.get_mode().server_status.file_selection.delete_button.isVisible()
tab.get_mode().server_status.file_selection.remove_button.isVisible()
)
tab.get_mode().server_status.file_selection.delete_button.click()
tab.get_mode().server_status.file_selection.remove_button.click()
# No more files, the delete button should be hidden
# No more files, the remove button should be hidden
self.assertFalse(
tab.get_mode().server_status.file_selection.delete_button.isVisible()
tab.get_mode().server_status.file_selection.remove_button.isVisible()
)
def add_a_file_and_delete_using_its_delete_widget(self, tab):
"""Test that we can also delete a file by clicking on its [X] widget"""
def add_a_file_and_remove_using_its_remove_widget(self, tab):
"""Test that we can also remove a file by clicking on its [X] widget"""
num_files = tab.get_mode().server_status.file_selection.get_num_files()
tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[0])
tab.get_mode().server_status.file_selection.file_list.item(
@ -55,6 +55,14 @@ class TestShare(GuiBaseTest):
).item_button.click()
self.file_selection_widget_has_files(tab, num_files)
def add_a_file_and_remove_using_remove_all_widget(self, tab):
"""Test that we can also remove all files by clicking on the Remove All widget"""
tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[0])
tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[1])
tab.get_mode().remove_all_button.click()
# Should be no files after clearing all
self.file_selection_widget_has_files(tab, 0)
def file_selection_widget_read_files(self, tab):
"""Re-add some files to the list so we can share"""
num_files = tab.get_mode().server_status.file_selection.get_num_files()
@ -189,7 +197,7 @@ class TestShare(GuiBaseTest):
"""Test that we can cancel a share before it's started up """
self.server_working_on_start_button_pressed(tab)
self.server_status_indicator_says_scheduled(tab)
self.add_delete_buttons_hidden(tab)
self.add_remove_buttons_hidden(tab)
self.mode_settings_widget_is_hidden(tab)
self.set_autostart_timer(tab, 10)
QtTest.QTest.mousePress(
@ -216,21 +224,22 @@ class TestShare(GuiBaseTest):
self.history_is_not_visible(tab)
self.click_toggle_history(tab)
self.history_is_visible(tab)
self.deleting_all_files_hides_delete_button(tab)
self.add_a_file_and_delete_using_its_delete_widget(tab)
self.removing_all_files_hides_remove_button(tab)
self.add_a_file_and_remove_using_its_remove_widget(tab)
self.file_selection_widget_read_files(tab)
def run_all_share_mode_started_tests(self, tab, startup_time=2000):
"""Tests in share mode after starting a share"""
self.server_working_on_start_button_pressed(tab)
self.server_status_indicator_says_starting(tab)
self.add_delete_buttons_hidden(tab)
self.add_remove_buttons_hidden(tab)
self.mode_settings_widget_is_hidden(tab)
self.server_is_started(tab, startup_time)
self.web_server_is_running(tab)
self.have_a_password(tab)
self.url_description_shown(tab)
self.have_copy_url_button(tab)
self.have_show_qr_code_button(tab)
self.server_status_indicator_says_started(tab)
def run_all_share_mode_download_tests(self, tab):
@ -269,7 +278,7 @@ class TestShare(GuiBaseTest):
self.run_all_share_mode_started_tests(tab)
self.run_all_share_mode_download_tests(tab)
def run_all_clear_all_button_tests(self, tab):
def run_all_clear_all_history_button_tests(self, tab):
"""Test the Clear All history button"""
self.run_all_share_mode_setup_tests(tab)
self.run_all_share_mode_started_tests(tab)
@ -279,6 +288,11 @@ class TestShare(GuiBaseTest):
self.individual_file_is_viewable_or_not(tab)
self.clear_all_history_items(tab, 2)
def run_all_remove_all_file_selection_button_tests(self, tab):
"""Test the Remove All File Selection button"""
self.run_all_share_mode_setup_tests(tab)
self.add_a_file_and_remove_using_remove_all_widget(tab)
def run_all_share_mode_individual_file_tests(self, tab):
"""Tests in share mode when viewing an individual file"""
self.run_all_share_mode_setup_tests(tab)
@ -375,15 +389,27 @@ class TestShare(GuiBaseTest):
self.close_all_tabs()
@pytest.mark.gui
def test_clear_all_button(self):
def test_clear_all_history_button(self):
"""
Test canceling a scheduled share
Test clearing all history items
"""
tab = self.new_share_tab()
tab.get_mode().autostop_sharing_checkbox.click()
self.run_all_common_setup_tests()
self.run_all_clear_all_button_tests(tab)
self.run_all_clear_all_history_button_tests(tab)
self.close_all_tabs()
@pytest.mark.gui
def test_remove_all_file_selection_button(self):
"""
Test remove all file items at once
"""
tab = self.new_share_tab()
self.run_all_common_setup_tests()
self.run_all_remove_all_file_selection_button_tests(tab)
self.close_all_tabs()
@ -561,6 +587,7 @@ class TestShare(GuiBaseTest):
Rate limit should be triggered
"""
tab = self.new_share_tab()
def accept_dialog():
window = tab.common.gui.qtapp.activeWindow()
if window:
@ -580,6 +607,7 @@ class TestShare(GuiBaseTest):
Public mode should skip the rate limit
"""
tab = self.new_share_tab()
def accept_dialog():
window = tab.common.gui.qtapp.activeWindow()
if window:

View File

@ -138,18 +138,21 @@ class TestTabs(GuiBaseTest):
self.gui.tabs.widget(1).share_button.click()
self.assertFalse(self.gui.tabs.widget(1).new_tab.isVisible())
self.assertTrue(self.gui.tabs.widget(1).share_mode.isVisible())
self.assertEqual(self.gui.status_bar.server_status_label.text(), 'Ready to share')
# New tab, receive files
self.gui.tabs.new_tab_button.click()
self.gui.tabs.widget(2).receive_button.click()
self.assertFalse(self.gui.tabs.widget(2).new_tab.isVisible())
self.assertTrue(self.gui.tabs.widget(2).receive_mode.isVisible())
self.assertEqual(self.gui.status_bar.server_status_label.text(), 'Ready to receive')
# New tab, publish website
self.gui.tabs.new_tab_button.click()
self.gui.tabs.widget(3).website_button.click()
self.assertFalse(self.gui.tabs.widget(3).new_tab.isVisible())
self.assertTrue(self.gui.tabs.widget(3).website_mode.isVisible())
self.assertEqual(self.gui.status_bar.server_status_label.text(), 'Ready to share')
# Close tabs
self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide).click()

View File

@ -64,12 +64,13 @@ class TestWebsite(GuiBaseTest):
"""Tests in website mode after starting a share"""
self.server_working_on_start_button_pressed(tab)
self.server_status_indicator_says_starting(tab)
self.add_delete_buttons_hidden(tab)
self.add_remove_buttons_hidden(tab)
self.server_is_started(tab, startup_time)
self.web_server_is_running(tab)
self.have_a_password(tab)
self.url_description_shown(tab)
self.have_copy_url_button(tab)
self.have_show_qr_code_button(tab)
self.server_status_indicator_says_started(tab)
def run_all_website_mode_download_tests(self, tab):