summaryrefslogtreecommitdiff
path: root/onionshare_gui/onionshare_gui.py
diff options
context:
space:
mode:
Diffstat (limited to 'onionshare_gui/onionshare_gui.py')
-rw-r--r--onionshare_gui/onionshare_gui.py882
1 files changed, 273 insertions, 609 deletions
diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py
index c05e3323..27abf5e5 100644
--- a/onionshare_gui/onionshare_gui.py
+++ b/onionshare_gui/onionshare_gui.py
@@ -2,7 +2,7 @@
"""
OnionShare | https://onionshare.org/
-Copyright (C) 2018 Micah Lee <micah@micahflee.com>
+Copyright (C) 2014-2018 Micah Lee <micah@micahflee.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,209 +17,171 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-import os, threading, time
+import queue
from PyQt5 import QtCore, QtWidgets, QtGui
-from onionshare import strings, common, web
-from onionshare.settings import Settings
-from onionshare.onion import *
+from onionshare import strings
+from onionshare.web import Web
+
+from .mode.share_mode import ShareMode
+from .mode.receive_mode import ReceiveMode
from .tor_connection_dialog import TorConnectionDialog
from .settings_dialog import SettingsDialog
-from .file_selection import FileSelection
-from .server_status import ServerStatus
-from .downloads import Downloads
-from .alert import Alert
+from .widgets import Alert
from .update_checker import UpdateThread
+from .server_status import ServerStatus
class OnionShareGui(QtWidgets.QMainWindow):
"""
OnionShareGui is the main window for the GUI that contains all of the
GUI elements.
"""
- start_server_finished = QtCore.pyqtSignal()
- stop_server_finished = QtCore.pyqtSignal()
- starting_server_step2 = QtCore.pyqtSignal()
- starting_server_step3 = QtCore.pyqtSignal()
- starting_server_error = QtCore.pyqtSignal(str)
+ MODE_SHARE = 'share'
+ MODE_RECEIVE = 'receive'
- def __init__(self, onion, qtapp, app, filenames, config=False):
+ def __init__(self, common, onion, qtapp, app, filenames, config=False, local_only=False):
super(OnionShareGui, self).__init__()
- self._initSystemTray()
-
- common.log('OnionShareGui', '__init__')
+ self.common = common
+ self.common.log('OnionShareGui', '__init__')
+ self.setMinimumWidth(820)
+ self.setMinimumHeight(660)
self.onion = onion
self.qtapp = qtapp
self.app = app
+ self.local_only = local_only
+
+ self.mode = self.MODE_SHARE
self.setWindowTitle('OnionShare')
- self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png')))
- self.setMinimumWidth(430)
+ self.setWindowIcon(QtGui.QIcon(self.common.get_resource_path('images/logo.png')))
- # Load settings
+ # Load settings, if a custom config was passed in
self.config = config
- self.settings = Settings(self.config)
- self.settings.load()
-
- # File selection
- self.file_selection = FileSelection()
- if filenames:
- for filename in filenames:
- self.file_selection.file_list.add_file(filename)
-
- # Server status
- self.server_status = ServerStatus(self.qtapp, self.app, web, self.file_selection, self.settings)
- self.server_status.server_started.connect(self.file_selection.server_started)
- self.server_status.server_started.connect(self.start_server)
- self.server_status.server_started.connect(self.update_server_status_indicator)
- self.server_status.server_stopped.connect(self.file_selection.server_stopped)
- self.server_status.server_stopped.connect(self.stop_server)
- self.server_status.server_stopped.connect(self.update_server_status_indicator)
- self.server_status.server_stopped.connect(self.update_primary_action)
- self.server_status.server_canceled.connect(self.cancel_server)
- self.server_status.server_canceled.connect(self.file_selection.server_stopped)
- self.server_status.server_canceled.connect(self.update_primary_action)
- self.start_server_finished.connect(self.clear_message)
- self.start_server_finished.connect(self.server_status.start_server_finished)
- self.start_server_finished.connect(self.update_server_status_indicator)
- self.stop_server_finished.connect(self.server_status.stop_server_finished)
- self.stop_server_finished.connect(self.update_server_status_indicator)
- self.file_selection.file_list.files_updated.connect(self.server_status.update)
- self.file_selection.file_list.files_updated.connect(self.update_primary_action)
- self.server_status.url_copied.connect(self.copy_url)
- self.server_status.hidservauth_copied.connect(self.copy_hidservauth)
- self.starting_server_step2.connect(self.start_server_step2)
- self.starting_server_step3.connect(self.start_server_step3)
- self.starting_server_error.connect(self.start_server_error)
- self.server_status.button_clicked.connect(self.clear_message)
-
- # Filesize warning
- self.filesize_warning = QtWidgets.QLabel()
- self.filesize_warning.setWordWrap(True)
- self.filesize_warning.setStyleSheet('padding: 10px 0; font-weight: bold; color: #333333;')
- self.filesize_warning.hide()
-
- # Downloads
- self.downloads = Downloads()
- self.downloads_container = QtWidgets.QScrollArea()
- self.downloads_container.setWidget(self.downloads)
- self.downloads_container.setWidgetResizable(True)
- self.downloads_container.setMaximumHeight(200)
- self.downloads_container.setMinimumHeight(75)
- self.vbar = self.downloads_container.verticalScrollBar()
- self.downloads_container.hide() # downloads start out hidden
- self.new_download = False
- self.downloads_in_progress = 0
- self.downloads_completed = 0
-
- # Info label along top of screen
- self.info_layout = QtWidgets.QHBoxLayout()
- self.info_label = QtWidgets.QLabel()
- self.info_label.setStyleSheet('QLabel { font-size: 12px; color: #666666; }')
-
- self.info_in_progress_downloads_count = QtWidgets.QLabel()
- self.info_in_progress_downloads_count.setStyleSheet('QLabel { font-size: 12px; color: #666666; }')
-
- self.info_completed_downloads_count = QtWidgets.QLabel()
- self.info_completed_downloads_count.setStyleSheet('QLabel { font-size: 12px; color: #666666; }')
-
- self.update_downloads_completed(self.downloads_in_progress)
- self.update_downloads_in_progress(self.downloads_in_progress)
-
- self.info_layout.addWidget(self.info_label)
- self.info_layout.addStretch()
- self.info_layout.addWidget(self.info_in_progress_downloads_count)
- self.info_layout.addWidget(self.info_completed_downloads_count)
-
- self.info_widget = QtWidgets.QWidget()
- self.info_widget.setLayout(self.info_layout)
- self.info_widget.hide()
-
- # Settings button on the status bar
+ if self.config:
+ self.common.load_settings(self.config)
+
+ # System tray
+ menu = QtWidgets.QMenu()
+ self.settings_action = menu.addAction(strings._('gui_settings_window_title'))
+ self.settings_action.triggered.connect(self.open_settings)
+ help_action = menu.addAction(strings._('gui_settings_button_help'))
+ help_action.triggered.connect(SettingsDialog.help_clicked)
+ exit_action = menu.addAction(strings._('systray_menu_exit'))
+ exit_action.triggered.connect(self.close)
+
+ self.system_tray = QtWidgets.QSystemTrayIcon(self)
+ # The convention is Mac systray icons are always grayscale
+ if self.common.platform == 'Darwin':
+ self.system_tray.setIcon(QtGui.QIcon(self.common.get_resource_path('images/logo_grayscale.png')))
+ else:
+ self.system_tray.setIcon(QtGui.QIcon(self.common.get_resource_path('images/logo.png')))
+ self.system_tray.setContextMenu(menu)
+ self.system_tray.show()
+
+ # Mode switcher, to switch between share files and receive files
+ self.share_mode_button = QtWidgets.QPushButton(strings._('gui_mode_share_button'));
+ self.share_mode_button.setFixedHeight(50)
+ self.share_mode_button.clicked.connect(self.share_mode_clicked)
+ self.receive_mode_button = QtWidgets.QPushButton(strings._('gui_mode_receive_button'));
+ self.receive_mode_button.setFixedHeight(50)
+ self.receive_mode_button.clicked.connect(self.receive_mode_clicked)
self.settings_button = QtWidgets.QPushButton()
self.settings_button.setDefault(False)
- self.settings_button.setFlat(True)
self.settings_button.setFixedWidth(40)
- self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings.png')) )
+ self.settings_button.setFixedHeight(50)
+ self.settings_button.setIcon( QtGui.QIcon(self.common.get_resource_path('images/settings.png')) )
self.settings_button.clicked.connect(self.open_settings)
+ self.settings_button.setStyleSheet(self.common.css['settings_button'])
+ mode_switcher_layout = QtWidgets.QHBoxLayout();
+ mode_switcher_layout.setSpacing(0)
+ mode_switcher_layout.addWidget(self.share_mode_button)
+ mode_switcher_layout.addWidget(self.receive_mode_button)
+ mode_switcher_layout.addWidget(self.settings_button)
# Server status indicator on the status bar
- self.server_status_image_stopped = QtGui.QImage(common.get_resource_path('images/server_stopped.png'))
- self.server_status_image_working = QtGui.QImage(common.get_resource_path('images/server_working.png'))
- self.server_status_image_started = QtGui.QImage(common.get_resource_path('images/server_started.png'))
+ self.server_status_image_stopped = QtGui.QImage(self.common.get_resource_path('images/server_stopped.png'))
+ self.server_status_image_working = QtGui.QImage(self.common.get_resource_path('images/server_working.png'))
+ self.server_status_image_started = QtGui.QImage(self.common.get_resource_path('images/server_started.png'))
self.server_status_image_label = QtWidgets.QLabel()
self.server_status_image_label.setFixedWidth(20)
- self.server_status_label = QtWidgets.QLabel()
- self.server_status_label.setStyleSheet('QLabel { font-style: italic; color: #666666; }')
+ self.server_status_label = QtWidgets.QLabel('')
+ self.server_status_label.setStyleSheet(self.common.css['server_status_indicator_label'])
server_status_indicator_layout = QtWidgets.QHBoxLayout()
server_status_indicator_layout.addWidget(self.server_status_image_label)
server_status_indicator_layout.addWidget(self.server_status_label)
self.server_status_indicator = QtWidgets.QWidget()
self.server_status_indicator.setLayout(server_status_indicator_layout)
- self.update_server_status_indicator()
# Status bar
self.status_bar = QtWidgets.QStatusBar()
self.status_bar.setSizeGripEnabled(False)
- statusBar_cssStyleData ="""
- QStatusBar {
- font-style: italic;
- color: #666666;
- }
-
- QStatusBar::item {
- border: 0px;
- }"""
-
- self.status_bar.setStyleSheet(statusBar_cssStyleData)
+ self.status_bar.setStyleSheet(self.common.css['status_bar'])
self.status_bar.addPermanentWidget(self.server_status_indicator)
- self.status_bar.addPermanentWidget(self.settings_button)
self.setStatusBar(self.status_bar)
- # Status bar, zip progress bar
- self._zip_progress_bar = None
- # Status bar, sharing messages
- self.server_share_status_label = QtWidgets.QLabel('')
- self.server_share_status_label.setStyleSheet('QLabel { font-style: italic; color: #666666; padding: 2px; }')
- self.status_bar.insertWidget(0, self.server_share_status_label)
-
- # Primary action layout
- primary_action_layout = QtWidgets.QVBoxLayout()
- primary_action_layout.addWidget(self.server_status)
- primary_action_layout.addWidget(self.filesize_warning)
- primary_action_layout.addWidget(self.downloads_container)
- self.primary_action = QtWidgets.QWidget()
- self.primary_action.setLayout(primary_action_layout)
- self.primary_action.hide()
- self.update_primary_action()
-
- # Main layout
- self.layout = QtWidgets.QVBoxLayout()
- self.layout.addWidget(self.info_widget)
- self.layout.addLayout(self.file_selection)
- self.layout.addWidget(self.primary_action)
+ # Share mode
+ self.share_mode = ShareMode(self.common, qtapp, app, self.status_bar, self.server_status_label, self.system_tray, filenames, self.local_only)
+ self.share_mode.init()
+ self.share_mode.server_status.server_started.connect(self.update_server_status_indicator)
+ self.share_mode.server_status.server_stopped.connect(self.update_server_status_indicator)
+ self.share_mode.start_server_finished.connect(self.update_server_status_indicator)
+ self.share_mode.stop_server_finished.connect(self.update_server_status_indicator)
+ self.share_mode.stop_server_finished.connect(self.stop_server_finished)
+ self.share_mode.start_server_finished.connect(self.clear_message)
+ self.share_mode.server_status.button_clicked.connect(self.clear_message)
+ self.share_mode.server_status.url_copied.connect(self.copy_url)
+ self.share_mode.server_status.hidservauth_copied.connect(self.copy_hidservauth)
+ self.share_mode.set_server_active.connect(self.set_server_active)
+
+ # Receive mode
+ self.receive_mode = ReceiveMode(self.common, qtapp, app, self.status_bar, self.server_status_label, self.system_tray, None, self.local_only)
+ self.receive_mode.init()
+ self.receive_mode.server_status.server_started.connect(self.update_server_status_indicator)
+ self.receive_mode.server_status.server_stopped.connect(self.update_server_status_indicator)
+ self.receive_mode.start_server_finished.connect(self.update_server_status_indicator)
+ self.receive_mode.stop_server_finished.connect(self.update_server_status_indicator)
+ self.receive_mode.stop_server_finished.connect(self.stop_server_finished)
+ self.receive_mode.start_server_finished.connect(self.clear_message)
+ self.receive_mode.server_status.button_clicked.connect(self.clear_message)
+ self.receive_mode.server_status.url_copied.connect(self.copy_url)
+ self.receive_mode.server_status.hidservauth_copied.connect(self.copy_hidservauth)
+ self.receive_mode.set_server_active.connect(self.set_server_active)
+
+ self.update_mode_switcher()
+ self.update_server_status_indicator()
+
+ # Layouts
+ contents_layout = QtWidgets.QVBoxLayout()
+ contents_layout.setContentsMargins(10, 0, 10, 0)
+ contents_layout.addWidget(self.receive_mode)
+ contents_layout.addWidget(self.share_mode)
+
+ layout = QtWidgets.QVBoxLayout()
+ layout.setContentsMargins(0, 0, 0, 0)
+ layout.addLayout(mode_switcher_layout)
+ layout.addLayout(contents_layout)
+
central_widget = QtWidgets.QWidget()
- central_widget.setLayout(self.layout)
+ central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
self.show()
- # Always start with focus on file selection
- self.file_selection.setFocus()
-
# The server isn't active yet
self.set_server_active(False)
# Create the timer
self.timer = QtCore.QTimer()
- self.timer.timeout.connect(self.check_for_requests)
+ self.timer.timeout.connect(self.timer_callback)
# Start the "Connecting to Tor" dialog, which calls onion.connect()
- tor_con = TorConnectionDialog(self.qtapp, self.settings, self.onion)
+ tor_con = TorConnectionDialog(self.common, self.qtapp, self.onion)
tor_con.canceled.connect(self._tor_connection_canceled)
tor_con.open_settings.connect(self._tor_connection_open_settings)
- tor_con.start()
+ if not self.local_only:
+ tor_con.start()
# Start the timer
self.timer.start(500)
@@ -227,77 +189,76 @@ class OnionShareGui(QtWidgets.QMainWindow):
# After connecting to Tor, check for updates
self.check_for_updates()
- def update_primary_action(self):
- # Show or hide primary action layout
- file_count = self.file_selection.file_list.count()
- if file_count > 0:
- self.primary_action.show()
- self.info_widget.show()
-
- # Update the file count in the info label
- total_size_bytes = 0
- for index in range(self.file_selection.file_list.count()):
- item = self.file_selection.file_list.item(index)
- total_size_bytes += item.size_bytes
- total_size_readable = common.human_readable_filesize(total_size_bytes)
-
- if file_count > 1:
- self.info_label.setText(strings._('gui_file_info', True).format(file_count, total_size_readable))
- else:
- self.info_label.setText(strings._('gui_file_info_single', True).format(file_count, total_size_readable))
+ def update_mode_switcher(self):
+ # Based on the current mode, switch the mode switcher button styles,
+ # and show and hide widgets to switch modes
+ if self.mode == self.MODE_SHARE:
+ self.share_mode_button.setStyleSheet(self.common.css['mode_switcher_selected_style'])
+ self.receive_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
+ self.receive_mode.hide()
+ self.share_mode.show()
else:
- self.primary_action.hide()
- self.info_widget.hide()
+ self.share_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
+ self.receive_mode_button.setStyleSheet(self.common.css['mode_switcher_selected_style'])
- # Resize window
- self.adjustSize()
+ self.share_mode.hide()
+ self.receive_mode.show()
- def update_server_status_indicator(self):
- common.log('OnionShareGui', 'update_server_status_indicator')
+ self.update_server_status_indicator()
- # Set the status image
- if self.server_status.status == self.server_status.STATUS_STOPPED:
- self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_stopped))
- self.server_status_label.setText(strings._('gui_status_indicator_stopped', True))
- elif self.server_status.status == self.server_status.STATUS_WORKING:
- self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_working))
- self.server_status_label.setText(strings._('gui_status_indicator_working', True))
- elif self.server_status.status == self.server_status.STATUS_STARTED:
- self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_started))
- self.server_status_label.setText(strings._('gui_status_indicator_started', True))
-
- def _initSystemTray(self):
- system = common.get_platform()
+ def share_mode_clicked(self):
+ if self.mode != self.MODE_SHARE:
+ self.common.log('OnionShareGui', 'share_mode_clicked')
+ self.mode = self.MODE_SHARE
+ self.update_mode_switcher()
- menu = QtWidgets.QMenu()
- self.settingsAction = menu.addAction(strings._('gui_settings_window_title', True))
- self.settingsAction.triggered.connect(self.open_settings)
- self.helpAction = menu.addAction(strings._('gui_settings_button_help', True))
- self.helpAction.triggered.connect(SettingsDialog.help_clicked)
- self.exitAction = menu.addAction(strings._('systray_menu_exit', True))
- self.exitAction.triggered.connect(self.close)
-
- self.systemTray = QtWidgets.QSystemTrayIcon(self)
- # The convention is Mac systray icons are always grayscale
- if system == 'Darwin':
- self.systemTray.setIcon(QtGui.QIcon(common.get_resource_path('images/logo_grayscale.png')))
+ def receive_mode_clicked(self):
+ if self.mode != self.MODE_RECEIVE:
+ self.common.log('OnionShareGui', 'receive_mode_clicked')
+ self.mode = self.MODE_RECEIVE
+ self.update_mode_switcher()
+
+ def update_server_status_indicator(self):
+ # Set the status image
+ if self.mode == self.MODE_SHARE:
+ # Share mode
+ if self.share_mode.server_status.status == ServerStatus.STATUS_STOPPED:
+ self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_stopped))
+ self.server_status_label.setText(strings._('gui_status_indicator_share_stopped'))
+ elif self.share_mode.server_status.status == ServerStatus.STATUS_WORKING:
+ self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_working))
+ self.server_status_label.setText(strings._('gui_status_indicator_share_working'))
+ elif self.share_mode.server_status.status == ServerStatus.STATUS_STARTED:
+ self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_started))
+ self.server_status_label.setText(strings._('gui_status_indicator_share_started'))
else:
- self.systemTray.setIcon(QtGui.QIcon(common.get_resource_path('images/logo.png')))
- self.systemTray.setContextMenu(menu)
- self.systemTray.show()
+ # Receive mode
+ if self.receive_mode.server_status.status == ServerStatus.STATUS_STOPPED:
+ self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_stopped))
+ self.server_status_label.setText(strings._('gui_status_indicator_receive_stopped'))
+ elif self.receive_mode.server_status.status == ServerStatus.STATUS_WORKING:
+ self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_working))
+ self.server_status_label.setText(strings._('gui_status_indicator_receive_working'))
+ elif self.receive_mode.server_status.status == ServerStatus.STATUS_STARTED:
+ self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_started))
+ self.server_status_label.setText(strings._('gui_status_indicator_receive_started'))
+
+ def stop_server_finished(self):
+ # When the server stopped, cleanup the ephemeral onion service
+ self.onion.cleanup(stop_tor=False)
def _tor_connection_canceled(self):
"""
If the user cancels before Tor finishes connecting, ask if they want to
quit, or open settings.
"""
- common.log('OnionShareGui', '_tor_connection_canceled')
+ self.common.log('OnionShareGui', '_tor_connection_canceled')
def ask():
- a = Alert(strings._('gui_tor_connection_ask', True), QtWidgets.QMessageBox.Question, buttons=QtWidgets.QMessageBox.NoButton, autostart=False)
- settings_button = QtWidgets.QPushButton(strings._('gui_tor_connection_ask_open_settings', True))
- quit_button = QtWidgets.QPushButton(strings._('gui_tor_connection_ask_quit', True))
+ a = Alert(self.common, strings._('gui_tor_connection_ask'), QtWidgets.QMessageBox.Question, buttons=QtWidgets.QMessageBox.NoButton, autostart=False)
+ settings_button = QtWidgets.QPushButton(strings._('gui_tor_connection_ask_open_settings'))
+ quit_button = QtWidgets.QPushButton(strings._('gui_tor_connection_ask_quit'))
a.addButton(settings_button, QtWidgets.QMessageBox.AcceptRole)
a.addButton(quit_button, QtWidgets.QMessageBox.RejectRole)
a.setDefaultButton(settings_button)
@@ -305,12 +266,12 @@ class OnionShareGui(QtWidgets.QMainWindow):
if a.clickedButton() == settings_button:
# Open settings
- common.log('OnionShareGui', '_tor_connection_canceled', 'Settings button clicked')
+ self.common.log('OnionShareGui', '_tor_connection_canceled', 'Settings button clicked')
self.open_settings()
if a.clickedButton() == quit_button:
# Quit
- common.log('OnionShareGui', '_tor_connection_canceled', 'Quit button clicked')
+ self.common.log('OnionShareGui', '_tor_connection_canceled', 'Quit button clicked')
# Wait 1ms for the event loop to finish, then quit
QtCore.QTimer.singleShot(1, self.qtapp.quit)
@@ -322,7 +283,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
The TorConnectionDialog wants to open the Settings dialog
"""
- common.log('OnionShareGui', '_tor_connection_open_settings')
+ self.common.log('OnionShareGui', '_tor_connection_open_settings')
# Wait 1ms for the event loop to finish closing the TorConnectionDialog
QtCore.QTimer.singleShot(1, self.open_settings)
@@ -331,337 +292,132 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
Open the SettingsDialog.
"""
- common.log('OnionShareGui', 'open_settings')
+ self.common.log('OnionShareGui', 'open_settings')
def reload_settings():
- common.log('OnionShareGui', 'open_settings', 'settings have changed, reloading')
- self.settings.load()
+ self.common.log('OnionShareGui', 'open_settings', 'settings have changed, reloading')
+ self.common.settings.load()
+
# We might've stopped the main requests timer if a Tor connection failed.
# If we've reloaded settings, we probably succeeded in obtaining a new
# connection. If so, restart the timer.
- if self.onion.is_authenticated():
- if not self.timer.isActive():
- self.timer.start(500)
- # If there were some files listed for sharing, we should be ok to
- # re-enable the 'Start Sharing' button now.
- if self.server_status.file_selection.get_num_files() > 0:
- self.server_status.server_button.setEnabled(True)
- self.status_bar.clearMessage()
+ if not self.local_only:
+ if self.onion.is_authenticated():
+ if not self.timer.isActive():
+ self.timer.start(500)
+ self.share_mode.on_reload_settings()
+ self.receive_mode.on_reload_settings()
+ self.status_bar.clearMessage()
+
# If we switched off the shutdown timeout setting, ensure the widget is hidden.
- if not self.settings.get('shutdown_timeout'):
- self.server_status.shutdown_timeout_container.hide()
+ if not self.common.settings.get('shutdown_timeout'):
+ self.share_mode.server_status.shutdown_timeout_container.hide()
+ self.receive_mode.server_status.shutdown_timeout_container.hide()
- d = SettingsDialog(self.onion, self.qtapp, self.config)
+ d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only)
d.settings_saved.connect(reload_settings)
d.exec_()
# When settings close, refresh the server status UI
- self.server_status.update()
-
- def start_server(self):
- """
- Start the onionshare server. This uses multiple threads to start the Tor onion
- server and the web app.
- """
- common.log('OnionShareGui', 'start_server')
-
- self.set_server_active(True)
-
- self.app.set_stealth(self.settings.get('use_stealth'))
-
- # Hide and reset the downloads if we have previously shared
- self.downloads_container.hide()
- self.downloads.reset_downloads()
- self.reset_info_counters()
- self.status_bar.clearMessage()
- self.server_share_status_label.setText('')
-
- # Reset web counters
- web.download_count = 0
- web.error404_count = 0
- web.set_gui_mode()
-
- # start the onion service in a new thread
- def start_onion_service(self):
- try:
- self.app.start_onion_service()
- self.starting_server_step2.emit()
-
- except (TorTooOld, TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorTimeout, OSError) as e:
- self.starting_server_error.emit(e.args[0])
- return
-
-
- self.app.stay_open = not self.settings.get('close_after_first_download')
-
- # start onionshare http service in new thread
- t = threading.Thread(target=web.start, args=(self.app.port, self.app.stay_open, self.settings.get('slug')))
- t.daemon = True
- t.start()
- # wait for modules in thread to load, preventing a thread-related cx_Freeze crash
- time.sleep(0.2)
-
- common.log('OnionshareGui', 'start_server', 'Starting an onion thread')
- self.t = OnionThread(function=start_onion_service, kwargs={'self': self})
- self.t.daemon = True
- self.t.start()
-
- def start_server_step2(self):
- """
- Step 2 in starting the onionshare server. Zipping up files.
- """
- common.log('OnionShareGui', 'start_server_step2')
-
- # add progress bar to the status bar, indicating the crunching of files.
- self._zip_progress_bar = ZipProgressBar(0)
- self.filenames = []
- for index in range(self.file_selection.file_list.count()):
- self.filenames.append(self.file_selection.file_list.item(index).filename)
-
- self._zip_progress_bar.total_files_size = OnionShareGui._compute_total_size(self.filenames)
- self.status_bar.insertWidget(0, self._zip_progress_bar)
-
- # prepare the files for sending in a new thread
- def finish_starting_server(self):
- # prepare files to share
- def _set_processed_size(x):
- if self._zip_progress_bar != None:
- self._zip_progress_bar.update_processed_size_signal.emit(x)
- try:
- web.set_file_info(self.filenames, processed_size_callback=_set_processed_size)
- self.app.cleanup_filenames.append(web.zip_filename)
- self.starting_server_step3.emit()
-
- # done
- self.start_server_finished.emit()
- except OSError as e:
- self.starting_server_error.emit(e.strerror)
- return
-
- t = threading.Thread(target=finish_starting_server, kwargs={'self': self})
- t.daemon = True
- t.start()
-
- def start_server_step3(self):
- """
- Step 3 in starting the onionshare server. This displays the large filesize
- warning, if applicable.
- """
- common.log('OnionShareGui', 'start_server_step3')
-
- # Remove zip progress bar
- if self._zip_progress_bar is not None:
- self.status_bar.removeWidget(self._zip_progress_bar)
- self._zip_progress_bar = None
-
- # warn about sending large files over Tor
- if web.zip_filesize >= 157286400: # 150mb
- self.filesize_warning.setText(strings._("large_filesize", True))
- self.filesize_warning.show()
-
- if self.settings.get('shutdown_timeout'):
- # Convert the date value to seconds between now and then
- now = QtCore.QDateTime.currentDateTime()
- self.timeout = now.secsTo(self.server_status.timeout)
- # Set the shutdown timeout value
- if self.timeout > 0:
- self.app.shutdown_timer = common.close_after_seconds(self.timeout)
- self.app.shutdown_timer.start()
- # The timeout has actually already passed since the user clicked Start. Probably the Onion service took too long to start.
- else:
- self.stop_server()
- self.start_server_error(strings._('gui_server_started_after_timeout'))
-
- def start_server_error(self, error):
- """
- If there's an error when trying to start the onion service
- """
- common.log('OnionShareGui', 'start_server_error')
-
- self.set_server_active(False)
-
- Alert(error, QtWidgets.QMessageBox.Warning)
- self.server_status.stop_server()
- if self._zip_progress_bar is not None:
- self.status_bar.removeWidget(self._zip_progress_bar)
- self._zip_progress_bar = None
- self.status_bar.clearMessage()
-
- def cancel_server(self):
- """
- Cancel the server while it is preparing to start
- """
- if self.t:
- self.t.quit()
- self.stop_server()
-
- def stop_server(self):
- """
- Stop the onionshare server.
- """
- common.log('OnionShareGui', 'stop_server')
-
- if self.server_status.status != self.server_status.STATUS_STOPPED:
- try:
- web.stop(self.app.port)
- except:
- # Probably we had no port to begin with (Onion service didn't start)
- pass
- self.app.cleanup()
- # Remove ephemeral service, but don't disconnect from Tor
- self.onion.cleanup(stop_tor=False)
- self.filesize_warning.hide()
- self.downloads_in_progress = 0
- self.downloads_completed = 0
- self.update_downloads_in_progress(0)
- self.file_selection.file_list.adjustSize()
-
- self.set_server_active(False)
- self.stop_server_finished.emit()
+ self.share_mode.server_status.update()
+ self.receive_mode.server_status.update()
def check_for_updates(self):
"""
Check for updates in a new thread, if enabled.
"""
- system = common.get_platform()
- if system == 'Windows' or system == 'Darwin':
- if self.settings.get('use_autoupdate'):
+ if self.common.platform == 'Windows' or self.common.platform == 'Darwin':
+ if self.common.settings.get('use_autoupdate'):
def update_available(update_url, installed_version, latest_version):
- Alert(strings._("update_available", True).format(update_url, installed_version, latest_version))
+ Alert(self.common, strings._("update_available").format(update_url, installed_version, latest_version))
- self.update_thread = UpdateThread(self.onion, self.config)
+ self.update_thread = UpdateThread(self.common, self.onion, self.config)
self.update_thread.update_available.connect(update_available)
self.update_thread.start()
- @staticmethod
- def _compute_total_size(filenames):
- total_size = 0
- for filename in filenames:
- if os.path.isfile(filename):
- total_size += os.path.getsize(filename)
- if os.path.isdir(filename):
- total_size += common.dir_size(filename)
- return total_size
-
- def check_for_requests(self):
+ def timer_callback(self):
"""
- Check for messages communicated from the web app, and update the GUI accordingly.
+ Check for messages communicated from the web app, and update the GUI accordingly. Also,
+ call ShareMode and ReceiveMode's timer_callbacks.
"""
self.update()
- # Have we lost connection to Tor somehow?
- if not self.onion.is_authenticated():
- self.timer.stop()
- if self.server_status.status != self.server_status.STATUS_STOPPED:
- self.server_status.stop_server()
- self.server_status.server_button.setEnabled(False)
- self.status_bar.showMessage(strings._('gui_tor_connection_lost', True))
- if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'):
- self.systemTray.showMessage(strings._('gui_tor_connection_lost', True), strings._('gui_tor_connection_error_settings', True))
-
- # scroll to the bottom of the dl progress bar log pane
- # if a new download has been added
- if self.new_download:
- self.vbar.setValue(self.vbar.maximum())
- self.new_download = False
+ if not self.local_only:
+ # Have we lost connection to Tor somehow?
+ if not self.onion.is_authenticated():
+ self.timer.stop()
+ self.status_bar.showMessage(strings._('gui_tor_connection_lost'))
+ self.system_tray.showMessage(strings._('gui_tor_connection_lost'), strings._('gui_tor_connection_error_settings'))
+
+ self.share_mode.handle_tor_broke()
+ self.receive_mode.handle_tor_broke()
+
+ # Process events from the web object
+ if self.mode == self.MODE_SHARE:
+ mode = self.share_mode
+ else:
+ mode = self.receive_mode
events = []
done = False
while not done:
try:
- r = web.q.get(False)
+ r = mode.web.q.get(False)
events.append(r)
- except web.queue.Empty:
+ except queue.Empty:
done = True
for event in events:
- if event["type"] == web.REQUEST_LOAD:
- self.status_bar.showMessage(strings._('download_page_loaded', True))
-
- elif event["type"] == web.REQUEST_DOWNLOAD:
- self.downloads_container.show() # show the downloads layout
- self.downloads.add_download(event["data"]["id"], web.zip_filesize)
- self.new_download = True
- self.downloads_in_progress += 1
- self.update_downloads_in_progress(self.downloads_in_progress)
- if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'):
- self.systemTray.showMessage(strings._('systray_download_started_title', True), strings._('systray_download_started_message', True))
-
- elif event["type"] == web.REQUEST_RATE_LIMIT:
- self.stop_server()
- Alert(strings._('error_rate_limit'), QtWidgets.QMessageBox.Critical)
-
- elif event["type"] == web.REQUEST_PROGRESS:
- self.downloads.update_download(event["data"]["id"], event["data"]["bytes"])
-
- # is the download complete?
- if event["data"]["bytes"] == web.zip_filesize:
- if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'):
- self.systemTray.showMessage(strings._('systray_download_completed_title', True), strings._('systray_download_completed_message', True))
- # Update the total 'completed downloads' info
- self.downloads_completed += 1
- self.update_downloads_completed(self.downloads_completed)
- # Update the 'in progress downloads' info
- self.downloads_in_progress -= 1
- self.update_downloads_in_progress(self.downloads_in_progress)
-
- # close on finish?
- if not web.get_stay_open():
- self.server_status.stop_server()
- self.status_bar.clearMessage()
- self.server_share_status_label.setText(strings._('closing_automatically', True))
- else:
- if self.server_status.status == self.server_status.STATUS_STOPPED:
- self.downloads.cancel_download(event["data"]["id"])
- self.downloads_in_progress = 0
- self.update_downloads_in_progress(self.downloads_in_progress)
-
-
- elif event["type"] == web.REQUEST_CANCELED:
- self.downloads.cancel_download(event["data"]["id"])
- # Update the 'in progress downloads' info
- self.downloads_in_progress -= 1
- self.update_downloads_in_progress(self.downloads_in_progress)
- if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'):
- self.systemTray.showMessage(strings._('systray_download_canceled_title', True), strings._('systray_download_canceled_message', True))
-
- elif event["path"] != '/favicon.ico':
- self.status_bar.showMessage('[#{0:d}] {1:s}: {2:s}'.format(web.error404_count, strings._('other_page_loaded', True), event["path"]))
-
- # If the auto-shutdown timer has stopped, stop the server
- if self.server_status.status == self.server_status.STATUS_STARTED:
- if self.app.shutdown_timer and self.settings.get('shutdown_timeout'):
- if self.timeout > 0:
- now = QtCore.QDateTime.currentDateTime()
- seconds_remaining = now.secsTo(self.server_status.timeout)
- self.server_status.server_button.setText(strings._('gui_stop_server_shutdown_timeout', True).format(seconds_remaining))
- if not self.app.shutdown_timer.is_alive():
- # If there were no attempts to download the share, or all downloads are done, we can stop
- if web.download_count == 0 or web.done:
- self.server_status.stop_server()
- self.status_bar.clearMessage()
- self.server_share_status_label.setText(strings._('close_on_timeout', True))
- # A download is probably still running - hold off on stopping the share
- else:
- self.status_bar.clearMessage()
- self.server_share_status_label.setText(strings._('timeout_download_still_running', True))
+ if event["type"] == Web.REQUEST_LOAD:
+ mode.handle_request_load(event)
+
+ elif event["type"] == Web.REQUEST_STARTED:
+ mode.handle_request_started(event)
+
+ elif event["type"] == Web.REQUEST_RATE_LIMIT:
+ mode.handle_request_rate_limit(event)
+
+ elif event["type"] == Web.REQUEST_PROGRESS:
+ mode.handle_request_progress(event)
+
+ elif event["type"] == Web.REQUEST_CANCELED:
+ mode.handle_request_canceled(event)
+
+ elif event["type"] == Web.REQUEST_UPLOAD_FILE_RENAMED:
+ mode.handle_request_upload_file_renamed(event)
+
+ elif event["type"] == Web.REQUEST_UPLOAD_SET_DIR:
+ mode.handle_request_upload_set_dir(event)
+
+ elif event["type"] == Web.REQUEST_UPLOAD_FINISHED:
+ mode.handle_request_upload_finished(event)
+
+ elif event["type"] == Web.REQUEST_UPLOAD_CANCELED:
+ mode.handle_request_upload_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"]))
+
+ if event["type"] == Web.REQUEST_OTHER:
+ if event["path"] != '/favicon.ico' and event["path"] != "/{}/shutdown".format(mode.web.shutdown_slug):
+ self.status_bar.showMessage('[#{0:d}] {1:s}: {2:s}'.format(mode.web.error404_count, strings._('other_page_loaded'), event["path"]))
+
+ mode.timer_callback()
def copy_url(self):
"""
When the URL gets copied to the clipboard, display this in the status bar.
"""
- common.log('OnionShareGui', 'copy_url')
- if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'):
- self.systemTray.showMessage(strings._('gui_copied_url_title', True), strings._('gui_copied_url', True))
+ self.common.log('OnionShareGui', 'copy_url')
+ self.system_tray.showMessage(strings._('gui_copied_url_title'), strings._('gui_copied_url'))
def copy_hidservauth(self):
"""
When the stealth onion service HidServAuth gets copied to the clipboard, display this in the status bar.
"""
- common.log('OnionShareGui', 'copy_hidservauth')
- if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'):
- self.systemTray.showMessage(strings._('gui_copied_hidservauth_title', True), strings._('gui_copied_hidservauth', True))
+ self.common.log('OnionShareGui', 'copy_hidservauth')
+ self.system_tray.showMessage(strings._('gui_copied_hidservauth_title'), strings._('gui_copied_hidservauth'))
def clear_message(self):
"""
@@ -671,56 +427,42 @@ class OnionShareGui(QtWidgets.QMainWindow):
def set_server_active(self, active):
"""
- Disable the Settings button while an OnionShare server is active.
+ Disable the Settings and Receive Files buttons while an Share Files server is active.
"""
if active:
self.settings_button.hide()
+ if self.mode == self.MODE_SHARE:
+ self.share_mode_button.show()
+ self.receive_mode_button.hide()
+ else:
+ self.share_mode_button.hide()
+ self.receive_mode_button.show()
else:
self.settings_button.show()
+ self.share_mode_button.show()
+ self.receive_mode_button.show()
# Disable settings menu action when server is active
- self.settingsAction.setEnabled(not active)
-
- def reset_info_counters(self):
- """
- Set the info counters back to zero.
- """
- self.update_downloads_completed(0)
- self.update_downloads_in_progress(0)
-
- def update_downloads_completed(self, count):
- """
- Update the 'Downloads completed' info widget.
- """
- if count == 0:
- self.info_completed_downloads_image = common.get_resource_path('images/download_completed_none.png')
- else:
- self.info_completed_downloads_image = common.get_resource_path('images/download_completed.png')
- self.info_completed_downloads_count.setText('<img src="{0:s}" /> {1:d}'.format(self.info_completed_downloads_image, count))
- self.info_completed_downloads_count.setToolTip(strings._('info_completed_downloads_tooltip', True).format(count))
-
- def update_downloads_in_progress(self, count):
- """
- Update the 'Downloads in progress' info widget.
- """
- if count == 0:
- self.info_in_progress_downloads_image = common.get_resource_path('images/download_in_progress_none.png')
- else:
- self.info_in_progress_downloads_image = common.get_resource_path('images/download_in_progress.png')
- self.info_in_progress_downloads_count.setText('<img src="{0:s}" /> {1:d}'.format(self.info_in_progress_downloads_image, count))
- self.info_in_progress_downloads_count.setToolTip(strings._('info_in_progress_downloads_tooltip', True).format(count))
+ self.settings_action.setEnabled(not active)
def closeEvent(self, e):
- common.log('OnionShareGui', 'closeEvent')
+ self.common.log('OnionShareGui', 'closeEvent')
try:
- if self.server_status.status != self.server_status.STATUS_STOPPED:
- common.log('OnionShareGui', 'closeEvent, opening warning dialog')
+ if self.mode == OnionShareGui.MODE_SHARE:
+ server_status = self.share_mode.server_status
+ else:
+ server_status = self.receive_mode.server_status
+ if server_status.status != server_status.STATUS_STOPPED:
+ self.common.log('OnionShareGui', 'closeEvent, opening warning dialog')
dialog = QtWidgets.QMessageBox()
- dialog.setWindowTitle(strings._('gui_quit_title', True))
- dialog.setText(strings._('gui_quit_warning', True))
+ dialog.setWindowTitle(strings._('gui_quit_title'))
+ if self.mode == OnionShareGui.MODE_SHARE:
+ dialog.setText(strings._('gui_share_quit_warning'))
+ else:
+ dialog.setText(strings._('gui_receive_quit_warning'))
dialog.setIcon(QtWidgets.QMessageBox.Critical)
- quit_button = dialog.addButton(strings._('gui_quit_warning_quit', True), QtWidgets.QMessageBox.YesRole)
- dont_quit_button = dialog.addButton(strings._('gui_quit_warning_dont_quit', True), QtWidgets.QMessageBox.NoRole)
+ quit_button = dialog.addButton(strings._('gui_quit_warning_quit'), QtWidgets.QMessageBox.YesRole)
+ dont_quit_button = dialog.addButton(strings._('gui_quit_warning_dont_quit'), QtWidgets.QMessageBox.NoRole)
dialog.setDefaultButton(dont_quit_button)
reply = dialog.exec_()
@@ -734,81 +476,3 @@ class OnionShareGui(QtWidgets.QMainWindow):
except:
e.accept()
-
-
-class ZipProgressBar(QtWidgets.QProgressBar):
- update_processed_size_signal = QtCore.pyqtSignal(int)
-
- def __init__(self, total_files_size):
- super(ZipProgressBar, self).__init__()
- self.setMaximumHeight(20)
- self.setMinimumWidth(200)
- self.setValue(0)
- self.setFormat(strings._('zip_progress_bar_format'))
- cssStyleData ="""
- QProgressBar {
- border: 1px solid #4e064f;
- background-color: #ffffff !important;
- text-align: center;
- color: #9b9b9b;
- }
-
- QProgressBar::chunk {
- border: 0px;
- background-color: #4e064f;
- width: 10px;
- }"""
- self.setStyleSheet(cssStyleData)
-
- self._total_files_size = total_files_size
- self._processed_size = 0
-
- self.update_processed_size_signal.connect(self.update_processed_size)
-
- @property
- def total_files_size(self):
- return self._total_files_size
-
- @total_files_size.setter
- def total_files_size(self, val):
- self._total_files_size = val
-
- @property
- def processed_size(self):
- return self._processed_size
-
- @processed_size.setter
- def processed_size(self, val):
- self.update_processed_size(val)
-
- def update_processed_size(self, val):
- self._processed_size = val
- if self.processed_size < self.total_files_size:
- self.setValue(int((self.processed_size * 100) / self.total_files_size))
- elif self.total_files_size != 0:
- self.setValue(100)
- else:
- self.setValue(0)
-
-
-class OnionThread(QtCore.QThread):
- """
- A QThread for starting our Onion Service.
- By using QThread rather than threading.Thread, we are able
- to call quit() or terminate() on the startup if the user
- decided to cancel (in which case do not proceed with obtaining
- the Onion address and starting the web server).
- """
- def __init__(self, function, kwargs=None):
- super(OnionThread, self).__init__()
- common.log('OnionThread', '__init__')
- self.function = function
- if not kwargs:
- self.kwargs = {}
- else:
- self.kwargs = kwargs
-
- def run(self):
- common.log('OnionThread', 'run')
-
- self.function(**self.kwargs)