mirror of
https://github.com/onionshare/onionshare.git
synced 2025-02-14 21:51:32 -05:00
Add a Startup Timer feature (scheduled start / dead man's switch)
This commit is contained in:
parent
d86b13d91c
commit
26d262ccfc
@ -53,6 +53,7 @@ def main(cwd=None):
|
|||||||
parser = argparse.ArgumentParser(formatter_class=lambda prog: argparse.HelpFormatter(prog,max_help_position=28))
|
parser = argparse.ArgumentParser(formatter_class=lambda prog: argparse.HelpFormatter(prog,max_help_position=28))
|
||||||
parser.add_argument('--local-only', action='store_true', dest='local_only', help=strings._("help_local_only"))
|
parser.add_argument('--local-only', action='store_true', dest='local_only', help=strings._("help_local_only"))
|
||||||
parser.add_argument('--stay-open', action='store_true', dest='stay_open', help=strings._("help_stay_open"))
|
parser.add_argument('--stay-open', action='store_true', dest='stay_open', help=strings._("help_stay_open"))
|
||||||
|
parser.add_argument('--startup-timer', metavar='<int>', dest='startup_timer', default=0, help=strings._("help_startup_timer"))
|
||||||
parser.add_argument('--shutdown-timeout', metavar='<int>', dest='shutdown_timeout', default=0, help=strings._("help_shutdown_timeout"))
|
parser.add_argument('--shutdown-timeout', metavar='<int>', dest='shutdown_timeout', default=0, help=strings._("help_shutdown_timeout"))
|
||||||
parser.add_argument('--stealth', action='store_true', dest='stealth', help=strings._("help_stealth"))
|
parser.add_argument('--stealth', action='store_true', dest='stealth', help=strings._("help_stealth"))
|
||||||
parser.add_argument('--receive', action='store_true', dest='receive', help=strings._("help_receive"))
|
parser.add_argument('--receive', action='store_true', dest='receive', help=strings._("help_receive"))
|
||||||
@ -68,6 +69,7 @@ def main(cwd=None):
|
|||||||
local_only = bool(args.local_only)
|
local_only = bool(args.local_only)
|
||||||
debug = bool(args.debug)
|
debug = bool(args.debug)
|
||||||
stay_open = bool(args.stay_open)
|
stay_open = bool(args.stay_open)
|
||||||
|
startup_timer = int(args.startup_timer)
|
||||||
shutdown_timeout = int(args.shutdown_timeout)
|
shutdown_timeout = int(args.shutdown_timeout)
|
||||||
stealth = bool(args.stealth)
|
stealth = bool(args.stealth)
|
||||||
receive = bool(args.receive)
|
receive = bool(args.receive)
|
||||||
@ -120,10 +122,28 @@ def main(cwd=None):
|
|||||||
|
|
||||||
# Start the onionshare app
|
# Start the onionshare app
|
||||||
try:
|
try:
|
||||||
|
common.settings.load()
|
||||||
|
if not common.settings.get('public_mode'):
|
||||||
|
web.generate_slug(common.settings.get('slug'))
|
||||||
|
else:
|
||||||
|
web.slug = None
|
||||||
app = OnionShare(common, onion, local_only, shutdown_timeout)
|
app = OnionShare(common, onion, local_only, shutdown_timeout)
|
||||||
app.set_stealth(stealth)
|
app.set_stealth(stealth)
|
||||||
app.choose_port()
|
app.choose_port()
|
||||||
app.start_onion_service()
|
# Delay the startup if a startup timer was set
|
||||||
|
if startup_timer > 0:
|
||||||
|
app.start_onion_service(False, True)
|
||||||
|
if common.settings.get('public_mode'):
|
||||||
|
url = 'http://{0:s}'.format(app.onion_host)
|
||||||
|
else:
|
||||||
|
url = 'http://{0:s}/{1:s}'.format(app.onion_host, web.slug)
|
||||||
|
print(strings._("scheduled_onion_service").format(url))
|
||||||
|
app.onion.cleanup()
|
||||||
|
print(strings._("waiting_for_startup_timer"))
|
||||||
|
time.sleep(startup_timer)
|
||||||
|
app.start_onion_service()
|
||||||
|
else:
|
||||||
|
app.start_onion_service()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("")
|
print("")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
@ -149,7 +169,7 @@ def main(cwd=None):
|
|||||||
print('')
|
print('')
|
||||||
|
|
||||||
# Start OnionShare http service in new thread
|
# Start OnionShare http service in new thread
|
||||||
t = threading.Thread(target=web.start, args=(app.port, stay_open, common.settings.get('public_mode'), common.settings.get('slug')))
|
t = threading.Thread(target=web.start, args=(app.port, stay_open, common.settings.get('public_mode'), web.slug))
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
|
@ -133,6 +133,7 @@ class Onion(object):
|
|||||||
|
|
||||||
self.stealth = False
|
self.stealth = False
|
||||||
self.service_id = None
|
self.service_id = None
|
||||||
|
self.scheduled_key = None
|
||||||
|
|
||||||
# Is bundled tor supported?
|
# Is bundled tor supported?
|
||||||
if (self.common.platform == 'Windows' or self.common.platform == 'Darwin') and getattr(sys, 'onionshare_dev_mode', False):
|
if (self.common.platform == 'Windows' or self.common.platform == 'Darwin') and getattr(sys, 'onionshare_dev_mode', False):
|
||||||
@ -423,7 +424,7 @@ class Onion(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def start_onion_service(self, port):
|
def start_onion_service(self, port, await_publication, save_scheduled_key=False):
|
||||||
"""
|
"""
|
||||||
Start a onion service on port 80, pointing to the given port, and
|
Start a onion service on port 80, pointing to the given port, and
|
||||||
return the onion hostname.
|
return the onion hostname.
|
||||||
@ -455,6 +456,14 @@ class Onion(object):
|
|||||||
# Assume it was a v3 key. Stem will throw an error if it's something illegible
|
# Assume it was a v3 key. Stem will throw an error if it's something illegible
|
||||||
key_type = "ED25519-V3"
|
key_type = "ED25519-V3"
|
||||||
|
|
||||||
|
elif self.scheduled_key:
|
||||||
|
key_content = self.scheduled_key
|
||||||
|
if self.is_v2_key(key_content):
|
||||||
|
key_type = "RSA1024"
|
||||||
|
else:
|
||||||
|
# Assume it was a v3 key. Stem will throw an error if it's something illegible
|
||||||
|
key_type = "ED25519-V3"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
key_type = "NEW"
|
key_type = "NEW"
|
||||||
# Work out if we can support v3 onion services, which are preferred
|
# Work out if we can support v3 onion services, which are preferred
|
||||||
@ -474,7 +483,6 @@ class Onion(object):
|
|||||||
if key_type == "NEW":
|
if key_type == "NEW":
|
||||||
debug_message += ', key_content={}'.format(key_content)
|
debug_message += ', key_content={}'.format(key_content)
|
||||||
self.common.log('Onion', 'start_onion_service', '{}'.format(debug_message))
|
self.common.log('Onion', 'start_onion_service', '{}'.format(debug_message))
|
||||||
await_publication = True
|
|
||||||
try:
|
try:
|
||||||
if basic_auth != None:
|
if basic_auth != None:
|
||||||
res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=await_publication, basic_auth=basic_auth, key_type=key_type, key_content=key_content)
|
res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=await_publication, basic_auth=basic_auth, key_type=key_type, key_content=key_content)
|
||||||
@ -493,6 +501,12 @@ class Onion(object):
|
|||||||
if not self.settings.get('private_key'):
|
if not self.settings.get('private_key'):
|
||||||
self.settings.set('private_key', res.private_key)
|
self.settings.set('private_key', res.private_key)
|
||||||
|
|
||||||
|
# If we were scheduling a future share, register the private key for later re-use
|
||||||
|
if save_scheduled_key:
|
||||||
|
self.scheduled_key = res.private_key
|
||||||
|
else:
|
||||||
|
self.scheduled_key = None
|
||||||
|
|
||||||
if self.stealth:
|
if self.stealth:
|
||||||
# Similar to the PrivateKey, the Control port only returns the ClientAuth
|
# Similar to the PrivateKey, the Control port only returns the ClientAuth
|
||||||
# in the response if it was responsible for creating the basic_auth password
|
# in the response if it was responsible for creating the basic_auth password
|
||||||
|
@ -41,6 +41,7 @@ class OnionShare(object):
|
|||||||
self.onion_host = None
|
self.onion_host = None
|
||||||
self.port = None
|
self.port = None
|
||||||
self.stealth = None
|
self.stealth = None
|
||||||
|
self.scheduled_key = None
|
||||||
|
|
||||||
# files and dirs to delete on shutdown
|
# files and dirs to delete on shutdown
|
||||||
self.cleanup_filenames = []
|
self.cleanup_filenames = []
|
||||||
@ -68,7 +69,7 @@ class OnionShare(object):
|
|||||||
except:
|
except:
|
||||||
raise OSError(strings._('no_available_port'))
|
raise OSError(strings._('no_available_port'))
|
||||||
|
|
||||||
def start_onion_service(self):
|
def start_onion_service(self, await_publication=True, save_scheduled_key=False):
|
||||||
"""
|
"""
|
||||||
Start the onionshare onion service.
|
Start the onionshare onion service.
|
||||||
"""
|
"""
|
||||||
@ -84,16 +85,20 @@ class OnionShare(object):
|
|||||||
self.onion_host = '127.0.0.1:{0:d}'.format(self.port)
|
self.onion_host = '127.0.0.1:{0:d}'.format(self.port)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.onion_host = self.onion.start_onion_service(self.port)
|
self.onion_host = self.onion.start_onion_service(self.port, await_publication, save_scheduled_key)
|
||||||
|
|
||||||
if self.stealth:
|
if self.stealth:
|
||||||
self.auth_string = self.onion.auth_string
|
self.auth_string = self.onion.auth_string
|
||||||
|
|
||||||
|
if self.onion.scheduled_key:
|
||||||
|
self.scheduled_key = self.onion.scheduled_key
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
"""
|
"""
|
||||||
Shut everything down and clean up temporary files, etc.
|
Shut everything down and clean up temporary files, etc.
|
||||||
"""
|
"""
|
||||||
self.common.log('OnionShare', 'cleanup')
|
self.common.log('OnionShare', 'cleanup')
|
||||||
|
self.scheduled_key = None
|
||||||
|
|
||||||
# Cleanup files
|
# Cleanup files
|
||||||
try:
|
try:
|
||||||
|
@ -85,6 +85,7 @@ class Settings(object):
|
|||||||
'auth_password': '',
|
'auth_password': '',
|
||||||
'close_after_first_download': True,
|
'close_after_first_download': True,
|
||||||
'shutdown_timeout': False,
|
'shutdown_timeout': False,
|
||||||
|
'startup_timer': False,
|
||||||
'use_stealth': False,
|
'use_stealth': False,
|
||||||
'use_autoupdate': True,
|
'use_autoupdate': True,
|
||||||
'autoupdate_timestamp': None,
|
'autoupdate_timestamp': None,
|
||||||
|
@ -228,13 +228,11 @@ class Web(object):
|
|||||||
pass
|
pass
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
def start(self, port, stay_open=False, public_mode=False, persistent_slug=None):
|
def start(self, port, stay_open=False, public_mode=False, slug=None):
|
||||||
"""
|
"""
|
||||||
Start the flask web server.
|
Start the flask web server.
|
||||||
"""
|
"""
|
||||||
self.common.log('Web', 'start', 'port={}, stay_open={}, public_mode={}, persistent_slug={}'.format(port, stay_open, public_mode, persistent_slug))
|
self.common.log('Web', 'start', 'port={}, stay_open={}, public_mode={}, slug={}'.format(port, stay_open, public_mode, slug))
|
||||||
if not public_mode:
|
|
||||||
self.generate_slug(persistent_slug)
|
|
||||||
|
|
||||||
self.stay_open = stay_open
|
self.stay_open = stay_open
|
||||||
|
|
||||||
@ -264,7 +262,7 @@ class Web(object):
|
|||||||
self.stop_q.put(True)
|
self.stop_q.put(True)
|
||||||
|
|
||||||
# Reset any slug that was in use
|
# Reset any slug that was in use
|
||||||
self.slug = ''
|
self.slug = None
|
||||||
|
|
||||||
# To stop flask, load http://127.0.0.1:<port>/<shutdown_slug>/shutdown
|
# To stop flask, load http://127.0.0.1:<port>/<shutdown_slug>/shutdown
|
||||||
if self.running:
|
if self.running:
|
||||||
|
@ -24,6 +24,7 @@ from onionshare.common import ShutdownTimer
|
|||||||
|
|
||||||
from ..server_status import ServerStatus
|
from ..server_status import ServerStatus
|
||||||
from ..threads import OnionThread
|
from ..threads import OnionThread
|
||||||
|
from ..threads import StartupTimer
|
||||||
from ..widgets import Alert
|
from ..widgets import Alert
|
||||||
|
|
||||||
class Mode(QtWidgets.QWidget):
|
class Mode(QtWidgets.QWidget):
|
||||||
@ -35,6 +36,7 @@ class Mode(QtWidgets.QWidget):
|
|||||||
starting_server_step2 = QtCore.pyqtSignal()
|
starting_server_step2 = QtCore.pyqtSignal()
|
||||||
starting_server_step3 = QtCore.pyqtSignal()
|
starting_server_step3 = QtCore.pyqtSignal()
|
||||||
starting_server_error = QtCore.pyqtSignal(str)
|
starting_server_error = QtCore.pyqtSignal(str)
|
||||||
|
starting_server_early = QtCore.pyqtSignal()
|
||||||
set_server_active = QtCore.pyqtSignal(bool)
|
set_server_active = QtCore.pyqtSignal(bool)
|
||||||
|
|
||||||
def __init__(self, common, qtapp, app, status_bar, server_status_label, system_tray, filenames=None, local_only=False):
|
def __init__(self, common, qtapp, app, status_bar, server_status_label, system_tray, filenames=None, local_only=False):
|
||||||
@ -58,6 +60,7 @@ class Mode(QtWidgets.QWidget):
|
|||||||
# Threads start out as None
|
# Threads start out as None
|
||||||
self.onion_thread = None
|
self.onion_thread = None
|
||||||
self.web_thread = None
|
self.web_thread = None
|
||||||
|
self.startup_thread = None
|
||||||
|
|
||||||
# Server status
|
# Server status
|
||||||
self.server_status = ServerStatus(self.common, self.qtapp, self.app, None, self.local_only)
|
self.server_status = ServerStatus(self.common, self.qtapp, self.app, None, self.local_only)
|
||||||
@ -68,6 +71,7 @@ class Mode(QtWidgets.QWidget):
|
|||||||
self.stop_server_finished.connect(self.server_status.stop_server_finished)
|
self.stop_server_finished.connect(self.server_status.stop_server_finished)
|
||||||
self.starting_server_step2.connect(self.start_server_step2)
|
self.starting_server_step2.connect(self.start_server_step2)
|
||||||
self.starting_server_step3.connect(self.start_server_step3)
|
self.starting_server_step3.connect(self.start_server_step3)
|
||||||
|
self.starting_server_early.connect(self.start_server_early)
|
||||||
self.starting_server_error.connect(self.start_server_error)
|
self.starting_server_error.connect(self.start_server_error)
|
||||||
|
|
||||||
# Primary action
|
# Primary action
|
||||||
@ -142,7 +146,41 @@ class Mode(QtWidgets.QWidget):
|
|||||||
self.status_bar.clearMessage()
|
self.status_bar.clearMessage()
|
||||||
self.server_status_label.setText('')
|
self.server_status_label.setText('')
|
||||||
|
|
||||||
|
# Ensure we always get a new random port each time we might launch an OnionThread
|
||||||
|
self.app.port = None
|
||||||
|
|
||||||
|
# Start the onion thread. If this share was scheduled for a future date,
|
||||||
|
# the OnionThread will start and exit 'early' to obtain the port, slug
|
||||||
|
# and onion address, but it will not start the WebThread yet.
|
||||||
|
if self.server_status.scheduled_start:
|
||||||
|
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.scheduled_start:
|
||||||
|
self.common.log('Mode', 'start_server', 'Starting startup timer')
|
||||||
|
self.startup_thread = StartupTimer(self)
|
||||||
|
# Once the timer has finished, start the real share, with a WebThread
|
||||||
|
self.startup_thread.success.connect(self.start_scheduled_service)
|
||||||
|
self.startup_thread.error.connect(self.start_server_error)
|
||||||
|
self.startup_thread.canceled = False
|
||||||
|
self.startup_thread.start()
|
||||||
|
|
||||||
|
def start_onion_thread(self, obtain_onion_early=False):
|
||||||
self.common.log('Mode', 'start_server', 'Starting an onion thread')
|
self.common.log('Mode', 'start_server', 'Starting an onion thread')
|
||||||
|
self.obtain_onion_early = obtain_onion_early
|
||||||
|
self.onion_thread = OnionThread(self)
|
||||||
|
self.onion_thread.success.connect(self.starting_server_step2.emit)
|
||||||
|
self.onion_thread.success_early.connect(self.starting_server_early.emit)
|
||||||
|
self.onion_thread.error.connect(self.starting_server_error.emit)
|
||||||
|
self.onion_thread.start()
|
||||||
|
|
||||||
|
def start_scheduled_service(self, obtain_onion_early=False):
|
||||||
|
# We start a new OnionThread with the saved scheduled key from settings
|
||||||
|
self.common.settings.load()
|
||||||
|
self.obtain_onion_early = obtain_onion_early
|
||||||
|
self.common.log('Mode', 'start_server', 'Starting a scheduled onion thread')
|
||||||
self.onion_thread = OnionThread(self)
|
self.onion_thread = OnionThread(self)
|
||||||
self.onion_thread.success.connect(self.starting_server_step2.emit)
|
self.onion_thread.success.connect(self.starting_server_step2.emit)
|
||||||
self.onion_thread.error.connect(self.starting_server_error.emit)
|
self.onion_thread.error.connect(self.starting_server_error.emit)
|
||||||
@ -154,6 +192,14 @@ class Mode(QtWidgets.QWidget):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def start_server_early(self):
|
||||||
|
"""
|
||||||
|
An 'early' start of an onion service in order to obtain the onion
|
||||||
|
address for a scheduled start. Shows the onion address in the UI
|
||||||
|
in advance of actually starting the share.
|
||||||
|
"""
|
||||||
|
self.server_status.show_url()
|
||||||
|
|
||||||
def start_server_step2(self):
|
def start_server_step2(self):
|
||||||
"""
|
"""
|
||||||
Step 2 in starting the onionshare server.
|
Step 2 in starting the onionshare server.
|
||||||
@ -225,7 +271,11 @@ class Mode(QtWidgets.QWidget):
|
|||||||
Cancel the server while it is preparing to start
|
Cancel the server while it is preparing to start
|
||||||
"""
|
"""
|
||||||
self.cancel_server_custom()
|
self.cancel_server_custom()
|
||||||
|
if self.startup_thread:
|
||||||
|
self.common.log('Mode', 'cancel_server: quitting startup thread')
|
||||||
|
self.startup_thread.canceled = True
|
||||||
|
self.app.onion.scheduled_key = None
|
||||||
|
self.startup_thread.quit()
|
||||||
if self.onion_thread:
|
if self.onion_thread:
|
||||||
self.common.log('Mode', 'cancel_server: quitting onion thread')
|
self.common.log('Mode', 'cancel_server: quitting onion thread')
|
||||||
self.onion_thread.quit()
|
self.onion_thread.quit()
|
||||||
|
@ -228,7 +228,10 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
self.server_status_label.setText(strings._('gui_status_indicator_share_stopped'))
|
self.server_status_label.setText(strings._('gui_status_indicator_share_stopped'))
|
||||||
elif self.share_mode.server_status.status == ServerStatus.STATUS_WORKING:
|
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_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_working))
|
||||||
self.server_status_label.setText(strings._('gui_status_indicator_share_working'))
|
if self.share_mode.server_status.scheduled_start:
|
||||||
|
self.server_status_label.setText(strings._('gui_status_indicator_share_scheduled'))
|
||||||
|
else:
|
||||||
|
self.server_status_label.setText(strings._('gui_status_indicator_share_working'))
|
||||||
elif self.share_mode.server_status.status == ServerStatus.STATUS_STARTED:
|
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_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_started))
|
||||||
self.server_status_label.setText(strings._('gui_status_indicator_share_started'))
|
self.server_status_label.setText(strings._('gui_status_indicator_share_started'))
|
||||||
@ -239,7 +242,10 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
self.server_status_label.setText(strings._('gui_status_indicator_receive_stopped'))
|
self.server_status_label.setText(strings._('gui_status_indicator_receive_stopped'))
|
||||||
elif self.receive_mode.server_status.status == ServerStatus.STATUS_WORKING:
|
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_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_working))
|
||||||
self.server_status_label.setText(strings._('gui_status_indicator_receive_working'))
|
if self.receive_mode.server_status.scheduled_start:
|
||||||
|
self.server_status_label.setText(strings._('gui_status_indicator_receive_scheduled'))
|
||||||
|
else:
|
||||||
|
self.server_status_label.setText(strings._('gui_status_indicator_receive_working'))
|
||||||
elif self.receive_mode.server_status.status == ServerStatus.STATUS_STARTED:
|
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_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_started))
|
||||||
self.server_status_label.setText(strings._('gui_status_indicator_receive_started'))
|
self.server_status_label.setText(strings._('gui_status_indicator_receive_started'))
|
||||||
@ -313,6 +319,10 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
if not self.common.settings.get('shutdown_timeout'):
|
if not self.common.settings.get('shutdown_timeout'):
|
||||||
self.share_mode.server_status.shutdown_timeout_container.hide()
|
self.share_mode.server_status.shutdown_timeout_container.hide()
|
||||||
self.receive_mode.server_status.shutdown_timeout_container.hide()
|
self.receive_mode.server_status.shutdown_timeout_container.hide()
|
||||||
|
# If we switched off the startup timer setting, ensure the widget is hidden.
|
||||||
|
if not self.common.settings.get('startup_timer'):
|
||||||
|
self.share_mode.server_status.startup_timer_container.hide()
|
||||||
|
self.receive_mode.server_status.startup_timer_container.hide()
|
||||||
|
|
||||||
d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only)
|
d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only)
|
||||||
d.settings_saved.connect(reload_settings)
|
d.settings_saved.connect(reload_settings)
|
||||||
|
@ -56,10 +56,36 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
self.web = None
|
self.web = None
|
||||||
|
self.scheduled_start = None
|
||||||
self.local_only = local_only
|
self.local_only = local_only
|
||||||
|
|
||||||
self.resizeEvent(None)
|
self.resizeEvent(None)
|
||||||
|
|
||||||
|
# Startup timer layout
|
||||||
|
self.startup_timer_label = QtWidgets.QLabel(strings._('gui_settings_startup_timer'))
|
||||||
|
self.startup_timer = QtWidgets.QDateTimeEdit()
|
||||||
|
self.startup_timer.setDisplayFormat("hh:mm A MMM d, yy")
|
||||||
|
if self.local_only:
|
||||||
|
# For testing
|
||||||
|
self.startup_timer.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(15))
|
||||||
|
self.startup_timer.setMinimumDateTime(QtCore.QDateTime.currentDateTime())
|
||||||
|
else:
|
||||||
|
# Set proposed timer to be 5 minutes into the future
|
||||||
|
self.startup_timer.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300))
|
||||||
|
# Onion services can take a little while to start, so reduce the risk of it expiring too soon by setting the minimum to 60s from now
|
||||||
|
self.startup_timer.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(60))
|
||||||
|
self.startup_timer.setCurrentSection(QtWidgets.QDateTimeEdit.MinuteSection)
|
||||||
|
startup_timer_layout = QtWidgets.QHBoxLayout()
|
||||||
|
startup_timer_layout.addWidget(self.startup_timer_label)
|
||||||
|
startup_timer_layout.addWidget(self.startup_timer)
|
||||||
|
|
||||||
|
# Startup timer container, so it can all be hidden and shown as a group
|
||||||
|
startup_timer_container_layout = QtWidgets.QVBoxLayout()
|
||||||
|
startup_timer_container_layout.addLayout(startup_timer_layout)
|
||||||
|
self.startup_timer_container = QtWidgets.QWidget()
|
||||||
|
self.startup_timer_container.setLayout(startup_timer_container_layout)
|
||||||
|
self.startup_timer_container.hide()
|
||||||
|
|
||||||
# Shutdown timeout layout
|
# Shutdown timeout layout
|
||||||
self.shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout'))
|
self.shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout'))
|
||||||
self.shutdown_timeout = QtWidgets.QDateTimeEdit()
|
self.shutdown_timeout = QtWidgets.QDateTimeEdit()
|
||||||
@ -123,6 +149,7 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
layout = QtWidgets.QVBoxLayout()
|
layout = QtWidgets.QVBoxLayout()
|
||||||
layout.addWidget(self.server_button)
|
layout.addWidget(self.server_button)
|
||||||
layout.addLayout(url_layout)
|
layout.addLayout(url_layout)
|
||||||
|
layout.addWidget(self.startup_timer_container)
|
||||||
layout.addWidget(self.shutdown_timeout_container)
|
layout.addWidget(self.shutdown_timeout_container)
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
@ -154,6 +181,13 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def startup_timer_reset(self):
|
||||||
|
"""
|
||||||
|
Reset the timer in the UI after stopping a share
|
||||||
|
"""
|
||||||
|
self.startup_timer.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300))
|
||||||
|
if not self.local_only:
|
||||||
|
self.startup_timer.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(60))
|
||||||
|
|
||||||
def shutdown_timeout_reset(self):
|
def shutdown_timeout_reset(self):
|
||||||
"""
|
"""
|
||||||
@ -163,6 +197,14 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
if not self.local_only:
|
if not self.local_only:
|
||||||
self.shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(60))
|
self.shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(60))
|
||||||
|
|
||||||
|
def show_url(self):
|
||||||
|
"""
|
||||||
|
Show the URL in the UI.
|
||||||
|
"""
|
||||||
|
self.url.setText(self.get_url())
|
||||||
|
self.url.show()
|
||||||
|
self.copy_url_button.show()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""
|
"""
|
||||||
Update the GUI elements based on the current state.
|
Update the GUI elements based on the current state.
|
||||||
@ -190,16 +232,16 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
else:
|
else:
|
||||||
self.url_description.setToolTip(strings._('gui_url_label_stay_open'))
|
self.url_description.setToolTip(strings._('gui_url_label_stay_open'))
|
||||||
|
|
||||||
self.url.setText(self.get_url())
|
self.show_url()
|
||||||
self.url.show()
|
|
||||||
|
|
||||||
self.copy_url_button.show()
|
|
||||||
|
|
||||||
if self.common.settings.get('save_private_key'):
|
if self.common.settings.get('save_private_key'):
|
||||||
if not self.common.settings.get('slug'):
|
if not self.common.settings.get('slug'):
|
||||||
self.common.settings.set('slug', self.web.slug)
|
self.common.settings.set('slug', self.web.slug)
|
||||||
self.common.settings.save()
|
self.common.settings.save()
|
||||||
|
|
||||||
|
if self.common.settings.get('startup_timer'):
|
||||||
|
self.startup_timer_container.hide()
|
||||||
|
|
||||||
if self.common.settings.get('shutdown_timeout'):
|
if self.common.settings.get('shutdown_timeout'):
|
||||||
self.shutdown_timeout_container.hide()
|
self.shutdown_timeout_container.hide()
|
||||||
|
|
||||||
@ -227,6 +269,8 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
else:
|
else:
|
||||||
self.server_button.setText(strings._('gui_receive_start_server'))
|
self.server_button.setText(strings._('gui_receive_start_server'))
|
||||||
self.server_button.setToolTip('')
|
self.server_button.setToolTip('')
|
||||||
|
if self.common.settings.get('startup_timer'):
|
||||||
|
self.startup_timer_container.show()
|
||||||
if self.common.settings.get('shutdown_timeout'):
|
if self.common.settings.get('shutdown_timeout'):
|
||||||
self.shutdown_timeout_container.show()
|
self.shutdown_timeout_container.show()
|
||||||
elif self.status == self.STATUS_STARTED:
|
elif self.status == self.STATUS_STARTED:
|
||||||
@ -236,23 +280,30 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
self.server_button.setText(strings._('gui_share_stop_server'))
|
self.server_button.setText(strings._('gui_share_stop_server'))
|
||||||
else:
|
else:
|
||||||
self.server_button.setText(strings._('gui_receive_stop_server'))
|
self.server_button.setText(strings._('gui_receive_stop_server'))
|
||||||
|
if self.common.settings.get('startup_timer'):
|
||||||
|
self.startup_timer_container.hide()
|
||||||
if self.common.settings.get('shutdown_timeout'):
|
if self.common.settings.get('shutdown_timeout'):
|
||||||
self.shutdown_timeout_container.hide()
|
self.shutdown_timeout_container.hide()
|
||||||
if self.mode == ServerStatus.MODE_SHARE:
|
if self.mode == ServerStatus.MODE_SHARE:
|
||||||
self.server_button.setToolTip(strings._('gui_share_stop_server_shutdown_timeout_tooltip').format(self.timeout))
|
self.server_button.setToolTip(strings._('gui_share_stop_server_shutdown_timeout_tooltip').format(self.timeout))
|
||||||
else:
|
else:
|
||||||
self.server_button.setToolTip(strings._('gui_receive_stop_server_shutdown_timeout_tooltip').format(self.timeout))
|
self.server_button.setToolTip(strings._('gui_receive_stop_server_shutdown_timeout_tooltip').format(self.timeout))
|
||||||
|
|
||||||
elif self.status == self.STATUS_WORKING:
|
elif self.status == self.STATUS_WORKING:
|
||||||
self.server_button.setStyleSheet(self.common.css['server_status_button_working'])
|
self.server_button.setStyleSheet(self.common.css['server_status_button_working'])
|
||||||
self.server_button.setEnabled(True)
|
self.server_button.setEnabled(True)
|
||||||
self.server_button.setText(strings._('gui_please_wait'))
|
if self.scheduled_start:
|
||||||
|
self.server_button.setText(strings._('gui_waiting_to_start').format(self.scheduled_start))
|
||||||
|
self.startup_timer_container.hide()
|
||||||
|
else:
|
||||||
|
self.server_button.setText(strings._('gui_please_wait'))
|
||||||
if self.common.settings.get('shutdown_timeout'):
|
if self.common.settings.get('shutdown_timeout'):
|
||||||
self.shutdown_timeout_container.hide()
|
self.shutdown_timeout_container.hide()
|
||||||
else:
|
else:
|
||||||
self.server_button.setStyleSheet(self.common.css['server_status_button_working'])
|
self.server_button.setStyleSheet(self.common.css['server_status_button_working'])
|
||||||
self.server_button.setEnabled(False)
|
self.server_button.setEnabled(False)
|
||||||
self.server_button.setText(strings._('gui_please_wait'))
|
self.server_button.setText(strings._('gui_please_wait'))
|
||||||
|
if self.common.settings.get('startup_timer'):
|
||||||
|
self.startup_timer_container.hide()
|
||||||
if self.common.settings.get('shutdown_timeout'):
|
if self.common.settings.get('shutdown_timeout'):
|
||||||
self.shutdown_timeout_container.hide()
|
self.shutdown_timeout_container.hide()
|
||||||
|
|
||||||
@ -261,6 +312,11 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
Toggle starting or stopping the server.
|
Toggle starting or stopping the server.
|
||||||
"""
|
"""
|
||||||
if self.status == self.STATUS_STOPPED:
|
if self.status == self.STATUS_STOPPED:
|
||||||
|
if self.common.settings.get('startup_timer'):
|
||||||
|
if self.local_only:
|
||||||
|
self.scheduled_start = self.startup_timer.dateTime().toPyDateTime()
|
||||||
|
else:
|
||||||
|
self.scheduled_start = self.startup_timer.dateTime().toPyDateTime().replace(second=0, microsecond=0)
|
||||||
if self.common.settings.get('shutdown_timeout'):
|
if self.common.settings.get('shutdown_timeout'):
|
||||||
if self.local_only:
|
if self.local_only:
|
||||||
self.timeout = self.shutdown_timeout.dateTime().toPyDateTime()
|
self.timeout = self.shutdown_timeout.dateTime().toPyDateTime()
|
||||||
@ -302,6 +358,7 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
Stop the server.
|
Stop the server.
|
||||||
"""
|
"""
|
||||||
self.status = self.STATUS_WORKING
|
self.status = self.STATUS_WORKING
|
||||||
|
self.startup_timer_reset()
|
||||||
self.shutdown_timeout_reset()
|
self.shutdown_timeout_reset()
|
||||||
self.update()
|
self.update()
|
||||||
self.server_stopped.emit()
|
self.server_stopped.emit()
|
||||||
@ -312,6 +369,7 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
"""
|
"""
|
||||||
self.common.log('ServerStatus', 'cancel_server', 'Canceling the server mid-startup')
|
self.common.log('ServerStatus', 'cancel_server', 'Canceling the server mid-startup')
|
||||||
self.status = self.STATUS_WORKING
|
self.status = self.STATUS_WORKING
|
||||||
|
self.startup_timer_reset()
|
||||||
self.shutdown_timeout_reset()
|
self.shutdown_timeout_reset()
|
||||||
self.update()
|
self.update()
|
||||||
self.server_canceled.emit()
|
self.server_canceled.emit()
|
||||||
|
@ -71,6 +71,23 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
self.public_mode_widget = QtWidgets.QWidget()
|
self.public_mode_widget = QtWidgets.QWidget()
|
||||||
self.public_mode_widget.setLayout(public_mode_layout)
|
self.public_mode_widget.setLayout(public_mode_layout)
|
||||||
|
|
||||||
|
# Whether or not to use a startup ('auto-start') timer
|
||||||
|
self.startup_timer_checkbox = QtWidgets.QCheckBox()
|
||||||
|
self.startup_timer_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
|
self.startup_timer_checkbox.setText(strings._("gui_settings_startup_timer_checkbox"))
|
||||||
|
startup_timer_label = QtWidgets.QLabel(strings._("gui_settings_whats_this").format("https://github.com/micahflee/onionshare/wiki/Using-the-Auto-Stop-Timer"))
|
||||||
|
startup_timer_label.setStyleSheet(self.common.css['settings_whats_this'])
|
||||||
|
startup_timer_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
|
||||||
|
startup_timer_label.setOpenExternalLinks(True)
|
||||||
|
startup_timer_label.setMinimumSize(public_mode_label.sizeHint())
|
||||||
|
startup_timer_layout = QtWidgets.QHBoxLayout()
|
||||||
|
startup_timer_layout.addWidget(self.startup_timer_checkbox)
|
||||||
|
startup_timer_layout.addWidget(startup_timer_label)
|
||||||
|
startup_timer_layout.addStretch()
|
||||||
|
startup_timer_layout.setContentsMargins(0,0,0,0)
|
||||||
|
self.startup_timer_widget = QtWidgets.QWidget()
|
||||||
|
self.startup_timer_widget.setLayout(startup_timer_layout)
|
||||||
|
|
||||||
# Whether or not to use a shutdown ('auto-stop') timer
|
# Whether or not to use a shutdown ('auto-stop') timer
|
||||||
self.shutdown_timeout_checkbox = QtWidgets.QCheckBox()
|
self.shutdown_timeout_checkbox = QtWidgets.QCheckBox()
|
||||||
self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
@ -91,6 +108,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
# General settings layout
|
# General settings layout
|
||||||
general_group_layout = QtWidgets.QVBoxLayout()
|
general_group_layout = QtWidgets.QVBoxLayout()
|
||||||
general_group_layout.addWidget(self.public_mode_widget)
|
general_group_layout.addWidget(self.public_mode_widget)
|
||||||
|
general_group_layout.addWidget(self.startup_timer_widget)
|
||||||
general_group_layout.addWidget(self.shutdown_timeout_widget)
|
general_group_layout.addWidget(self.shutdown_timeout_widget)
|
||||||
general_group = QtWidgets.QGroupBox(strings._("gui_settings_general_label"))
|
general_group = QtWidgets.QGroupBox(strings._("gui_settings_general_label"))
|
||||||
general_group.setLayout(general_group_layout)
|
general_group.setLayout(general_group_layout)
|
||||||
@ -488,6 +506,12 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
else:
|
else:
|
||||||
self.close_after_first_download_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
self.close_after_first_download_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
|
startup_timer = self.old_settings.get('startup_timer')
|
||||||
|
if startup_timer:
|
||||||
|
self.startup_timer_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
|
else:
|
||||||
|
self.startup_timer_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
shutdown_timeout = self.old_settings.get('shutdown_timeout')
|
shutdown_timeout = self.old_settings.get('shutdown_timeout')
|
||||||
if shutdown_timeout:
|
if shutdown_timeout:
|
||||||
self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked)
|
self.shutdown_timeout_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||||
@ -932,6 +956,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||||||
settings.load() # To get the last update timestamp
|
settings.load() # To get the last update timestamp
|
||||||
|
|
||||||
settings.set('close_after_first_download', self.close_after_first_download_checkbox.isChecked())
|
settings.set('close_after_first_download', self.close_after_first_download_checkbox.isChecked())
|
||||||
|
settings.set('startup_timer', self.startup_timer_checkbox.isChecked())
|
||||||
settings.set('shutdown_timeout', self.shutdown_timeout_checkbox.isChecked())
|
settings.set('shutdown_timeout', self.shutdown_timeout_checkbox.isChecked())
|
||||||
|
|
||||||
# Complicated logic here to force v2 onion mode on or off depending on other settings
|
# Complicated logic here to force v2 onion mode on or off depending on other settings
|
||||||
|
@ -28,6 +28,7 @@ class OnionThread(QtCore.QThread):
|
|||||||
Starts the onion service, and waits for it to finish
|
Starts the onion service, and waits for it to finish
|
||||||
"""
|
"""
|
||||||
success = QtCore.pyqtSignal()
|
success = QtCore.pyqtSignal()
|
||||||
|
success_early = QtCore.pyqtSignal()
|
||||||
error = QtCore.pyqtSignal(str)
|
error = QtCore.pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, mode):
|
def __init__(self, mode):
|
||||||
@ -41,18 +42,30 @@ class OnionThread(QtCore.QThread):
|
|||||||
def run(self):
|
def run(self):
|
||||||
self.mode.common.log('OnionThread', 'run')
|
self.mode.common.log('OnionThread', 'run')
|
||||||
|
|
||||||
|
# Choose port and slug early, because we need them to exist in advance for scheduled shares
|
||||||
self.mode.app.stay_open = not self.mode.common.settings.get('close_after_first_download')
|
self.mode.app.stay_open = not self.mode.common.settings.get('close_after_first_download')
|
||||||
|
if not self.mode.app.port:
|
||||||
# start onionshare http service in new thread
|
self.mode.app.choose_port()
|
||||||
self.mode.web_thread = WebThread(self.mode)
|
if not self.mode.common.settings.get('public_mode'):
|
||||||
self.mode.web_thread.start()
|
if not self.mode.web.slug:
|
||||||
|
self.mode.web.generate_slug(self.mode.common.settings.get('slug'))
|
||||||
# wait for modules in thread to load, preventing a thread-related cx_Freeze crash
|
|
||||||
time.sleep(0.2)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.mode.app.start_onion_service()
|
if self.mode.obtain_onion_early:
|
||||||
self.success.emit()
|
self.mode.app.start_onion_service(await_publication=False, save_scheduled_key=True)
|
||||||
|
# wait for modules in thread to load, preventing a thread-related cx_Freeze crash
|
||||||
|
time.sleep(0.2)
|
||||||
|
self.success_early.emit()
|
||||||
|
# Unregister the onion so we can use it in the next OnionThread
|
||||||
|
self.mode.app.onion.cleanup()
|
||||||
|
else:
|
||||||
|
self.mode.app.start_onion_service(await_publication=True)
|
||||||
|
# wait for modules in thread to load, preventing a thread-related cx_Freeze crash
|
||||||
|
time.sleep(0.2)
|
||||||
|
# start onionshare http service in new thread
|
||||||
|
self.mode.web_thread = WebThread(self.mode)
|
||||||
|
self.mode.web_thread.start()
|
||||||
|
self.success.emit()
|
||||||
|
|
||||||
except (TorTooOld, TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorTimeout, OSError) as e:
|
except (TorTooOld, TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorTimeout, OSError) as e:
|
||||||
self.error.emit(e.args[0])
|
self.error.emit(e.args[0])
|
||||||
@ -73,5 +86,39 @@ class WebThread(QtCore.QThread):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.mode.common.log('WebThread', 'run')
|
self.mode.common.log('WebThread', 'run')
|
||||||
self.mode.app.choose_port()
|
self.mode.web.start(self.mode.app.port, self.mode.app.stay_open, self.mode.common.settings.get('public_mode'), self.mode.web.slug)
|
||||||
self.mode.web.start(self.mode.app.port, self.mode.app.stay_open, self.mode.common.settings.get('public_mode'), self.mode.common.settings.get('slug'))
|
self.success.emit()
|
||||||
|
|
||||||
|
|
||||||
|
class StartupTimer(QtCore.QThread):
|
||||||
|
"""
|
||||||
|
Waits for a prescribed time before allowing a share to start
|
||||||
|
"""
|
||||||
|
success = QtCore.pyqtSignal()
|
||||||
|
error = QtCore.pyqtSignal(str)
|
||||||
|
def __init__(self, mode, canceled=False):
|
||||||
|
super(StartupTimer, self).__init__()
|
||||||
|
self.mode = mode
|
||||||
|
self.canceled = canceled
|
||||||
|
self.mode.common.log('StartupTimer', '__init__')
|
||||||
|
|
||||||
|
# allow this thread to be terminated
|
||||||
|
self.setTerminationEnabled()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
now = QtCore.QDateTime.currentDateTime()
|
||||||
|
scheduled_start = now.secsTo(self.mode.server_status.scheduled_start)
|
||||||
|
try:
|
||||||
|
# Sleep until scheduled time
|
||||||
|
while scheduled_start > 0 and self.canceled == False:
|
||||||
|
time.sleep(0.1)
|
||||||
|
now = QtCore.QDateTime.currentDateTime()
|
||||||
|
scheduled_start = now.secsTo(self.mode.server_status.scheduled_start)
|
||||||
|
# Timer has now finished
|
||||||
|
self.mode.server_status.server_button.setText(strings._('gui_please_wait'))
|
||||||
|
self.mode.server_status_label.setText(strings._('gui_status_indicator_share_working'))
|
||||||
|
if self.canceled == False:
|
||||||
|
self.success.emit()
|
||||||
|
except ValueError as e:
|
||||||
|
self.error.emit(e.args[0])
|
||||||
|
return
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"large_filesize": "Warning: Sending a large share could take hours",
|
"large_filesize": "Warning: Sending a large share could take hours",
|
||||||
"help_local_only": "Don't use Tor (only for development)",
|
"help_local_only": "Don't use Tor (only for development)",
|
||||||
"help_stay_open": "Continue sharing after files have been sent",
|
"help_stay_open": "Continue sharing after files have been sent",
|
||||||
|
"help_startup_timer": "Schedule this share to start N seconds from now",
|
||||||
"help_shutdown_timeout": "Stop sharing after a given amount of seconds",
|
"help_shutdown_timeout": "Stop sharing after a given amount of seconds",
|
||||||
"help_stealth": "Use client authorization (advanced)",
|
"help_stealth": "Use client authorization (advanced)",
|
||||||
"help_receive": "Receive shares instead of sending them",
|
"help_receive": "Receive shares instead of sending them",
|
||||||
@ -42,6 +43,7 @@
|
|||||||
"gui_copied_url": "OnionShare address copied to clipboard",
|
"gui_copied_url": "OnionShare address copied to clipboard",
|
||||||
"gui_copied_hidservauth_title": "Copied HidServAuth",
|
"gui_copied_hidservauth_title": "Copied HidServAuth",
|
||||||
"gui_copied_hidservauth": "HidServAuth line copied to clipboard",
|
"gui_copied_hidservauth": "HidServAuth line copied to clipboard",
|
||||||
|
"gui_waiting_to_start": "Scheduled for {}. Click to cancel.",
|
||||||
"gui_please_wait": "Starting… Click to cancel.",
|
"gui_please_wait": "Starting… Click to cancel.",
|
||||||
"version_string": "OnionShare {0:s} | https://onionshare.org/",
|
"version_string": "OnionShare {0:s} | https://onionshare.org/",
|
||||||
"gui_quit_title": "Not so fast",
|
"gui_quit_title": "Not so fast",
|
||||||
@ -94,6 +96,8 @@
|
|||||||
"gui_settings_button_help": "Help",
|
"gui_settings_button_help": "Help",
|
||||||
"gui_settings_shutdown_timeout_checkbox": "Use auto-stop timer",
|
"gui_settings_shutdown_timeout_checkbox": "Use auto-stop timer",
|
||||||
"gui_settings_shutdown_timeout": "Stop the share at:",
|
"gui_settings_shutdown_timeout": "Stop the share at:",
|
||||||
|
"gui_settings_startup_timer_checkbox": "Use auto-start timer",
|
||||||
|
"gui_settings_startup_timer": "Start the share at:",
|
||||||
"settings_error_unknown": "Can't connect to Tor controller because your settings don't make sense.",
|
"settings_error_unknown": "Can't connect to Tor controller because your settings don't make sense.",
|
||||||
"settings_error_automatic": "Could not connect to the Tor controller. Is Tor Browser (available from torproject.org) running in the background?",
|
"settings_error_automatic": "Could not connect to the Tor controller. Is Tor Browser (available from torproject.org) running in the background?",
|
||||||
"settings_error_socket_port": "Can't connect to the Tor controller at {}:{}.",
|
"settings_error_socket_port": "Can't connect to the Tor controller at {}:{}.",
|
||||||
@ -133,9 +137,11 @@
|
|||||||
"gui_url_label_onetime_and_persistent": "This share will not auto-stop.<br><br>Every subsequent share will reuse the address. (To use one-time addresses, turn off \"Use persistent address\" in the settings.)",
|
"gui_url_label_onetime_and_persistent": "This share will not auto-stop.<br><br>Every subsequent share will reuse the address. (To use one-time addresses, turn off \"Use persistent address\" in the settings.)",
|
||||||
"gui_status_indicator_share_stopped": "Ready to share",
|
"gui_status_indicator_share_stopped": "Ready to share",
|
||||||
"gui_status_indicator_share_working": "Starting…",
|
"gui_status_indicator_share_working": "Starting…",
|
||||||
|
"gui_status_indicator_share_scheduled": "Scheduled…",
|
||||||
"gui_status_indicator_share_started": "Sharing",
|
"gui_status_indicator_share_started": "Sharing",
|
||||||
"gui_status_indicator_receive_stopped": "Ready to receive",
|
"gui_status_indicator_receive_stopped": "Ready to receive",
|
||||||
"gui_status_indicator_receive_working": "Starting…",
|
"gui_status_indicator_receive_working": "Starting…",
|
||||||
|
"gui_status_indicator_receive_scheduled": "Scheduled…",
|
||||||
"gui_status_indicator_receive_started": "Receiving",
|
"gui_status_indicator_receive_started": "Receiving",
|
||||||
"gui_file_info": "{} files, {}",
|
"gui_file_info": "{} files, {}",
|
||||||
"gui_file_info_single": "{} file, {}",
|
"gui_file_info_single": "{} file, {}",
|
||||||
@ -180,5 +186,7 @@
|
|||||||
"gui_share_mode_no_files": "No Files Sent Yet",
|
"gui_share_mode_no_files": "No Files Sent Yet",
|
||||||
"gui_share_mode_timeout_waiting": "Waiting to finish sending",
|
"gui_share_mode_timeout_waiting": "Waiting to finish sending",
|
||||||
"gui_receive_mode_no_files": "No Files Received Yet",
|
"gui_receive_mode_no_files": "No Files Received Yet",
|
||||||
"gui_receive_mode_timeout_waiting": "Waiting to finish receiving"
|
"gui_receive_mode_timeout_waiting": "Waiting to finish receiving",
|
||||||
|
"waiting_for_startup_timer": "Waiting for the timer to run down before starting...",
|
||||||
|
"scheduled_onion_service": "Your OnionShare URL will be: {}"
|
||||||
}
|
}
|
||||||
|
@ -172,6 +172,9 @@ class GuiBaseTest(object):
|
|||||||
'''Test that the Server Status indicator shows we are Starting'''
|
'''Test that the Server Status indicator shows we are Starting'''
|
||||||
self.assertEqual(mode.server_status_label.text(), strings._('gui_status_indicator_share_working'))
|
self.assertEqual(mode.server_status_label.text(), strings._('gui_status_indicator_share_working'))
|
||||||
|
|
||||||
|
def server_status_indicator_says_scheduled(self, mode):
|
||||||
|
'''Test that the Server Status indicator shows we are Scheduled'''
|
||||||
|
self.assertEqual(mode.server_status_label.text(), strings._('gui_status_indicator_share_scheduled'))
|
||||||
|
|
||||||
def server_is_started(self, mode, startup_time=2000):
|
def server_is_started(self, mode, startup_time=2000):
|
||||||
'''Test that the server has started'''
|
'''Test that the server has started'''
|
||||||
@ -294,7 +297,6 @@ class GuiBaseTest(object):
|
|||||||
mode.server_status.shutdown_timeout.setDateTime(timer)
|
mode.server_status.shutdown_timeout.setDateTime(timer)
|
||||||
self.assertTrue(mode.server_status.shutdown_timeout.dateTime(), timer)
|
self.assertTrue(mode.server_status.shutdown_timeout.dateTime(), timer)
|
||||||
|
|
||||||
|
|
||||||
def timeout_widget_hidden(self, mode):
|
def timeout_widget_hidden(self, mode):
|
||||||
'''Test that the timeout widget is hidden when share has started'''
|
'''Test that the timeout widget is hidden when share has started'''
|
||||||
self.assertFalse(mode.server_status.shutdown_timeout_container.isVisible())
|
self.assertFalse(mode.server_status.shutdown_timeout_container.isVisible())
|
||||||
@ -306,6 +308,23 @@ class GuiBaseTest(object):
|
|||||||
# We should have timed out now
|
# We should have timed out now
|
||||||
self.assertEqual(mode.server_status.status, 0)
|
self.assertEqual(mode.server_status.status, 0)
|
||||||
|
|
||||||
|
# Startup timer tests
|
||||||
|
def set_startup_timer(self, mode, timer):
|
||||||
|
'''Test that the timer can be set'''
|
||||||
|
schedule = QtCore.QDateTime.currentDateTime().addSecs(timer)
|
||||||
|
mode.server_status.startup_timer.setDateTime(schedule)
|
||||||
|
self.assertTrue(mode.server_status.startup_timer.dateTime(), schedule)
|
||||||
|
|
||||||
|
def startup_timer_widget_hidden(self, mode):
|
||||||
|
'''Test that the startup timer widget is hidden when share has started'''
|
||||||
|
self.assertFalse(mode.server_status.startup_timer_container.isVisible())
|
||||||
|
|
||||||
|
def scheduled_service_started(self, mode, wait):
|
||||||
|
'''Test that the server has timed out after the timer ran out'''
|
||||||
|
QtTest.QTest.qWait(wait)
|
||||||
|
# We should have started now
|
||||||
|
self.assertEqual(mode.server_status.status, 2)
|
||||||
|
|
||||||
# Hack to close an Alert dialog that would otherwise block tests
|
# Hack to close an Alert dialog that would otherwise block tests
|
||||||
def accept_dialog(self):
|
def accept_dialog(self):
|
||||||
window = self.gui.qtapp.activeWindow()
|
window = self.gui.qtapp.activeWindow()
|
||||||
|
@ -195,6 +195,15 @@ class GuiShareTest(GuiBaseTest):
|
|||||||
self.server_timed_out(self.gui.share_mode, 10000)
|
self.server_timed_out(self.gui.share_mode, 10000)
|
||||||
self.web_server_is_stopped()
|
self.web_server_is_stopped()
|
||||||
|
|
||||||
|
def run_all_share_mode_startup_timer_tests(self, public_mode):
|
||||||
|
"""Auto-stop timer tests in share mode"""
|
||||||
|
self.run_all_share_mode_setup_tests()
|
||||||
|
self.set_startup_timer(self.gui.share_mode, 5)
|
||||||
|
self.server_working_on_start_button_pressed(self.gui.share_mode)
|
||||||
|
self.startup_timer_widget_hidden(self.gui.share_mode)
|
||||||
|
self.server_status_indicator_says_scheduled(self.gui.share_mode)
|
||||||
|
self.scheduled_service_started(self.gui.share_mode, 7000)
|
||||||
|
self.web_server_is_running()
|
||||||
|
|
||||||
def run_all_share_mode_unreadable_file_tests(self):
|
def run_all_share_mode_unreadable_file_tests(self):
|
||||||
'''Attempt to share an unreadable file'''
|
'''Attempt to share an unreadable file'''
|
||||||
|
26
tests/local_onionshare_share_mode_startup_timer_test.py
Normal file
26
tests/local_onionshare_share_mode_startup_timer_test.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import pytest
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from .GuiShareTest import GuiShareTest
|
||||||
|
|
||||||
|
class LocalShareModeStartupTimerTest(unittest.TestCase, GuiShareTest):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
test_settings = {
|
||||||
|
"public_mode": False,
|
||||||
|
"startup_timer": True,
|
||||||
|
}
|
||||||
|
cls.gui = GuiShareTest.set_up(test_settings)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
GuiShareTest.tear_down()
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def test_gui(self):
|
||||||
|
self.run_all_common_setup_tests()
|
||||||
|
self.run_all_share_mode_startup_timer_tests(False)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
@ -30,9 +30,10 @@ class MyOnion:
|
|||||||
self.auth_string = 'TestHidServAuth'
|
self.auth_string = 'TestHidServAuth'
|
||||||
self.private_key = ''
|
self.private_key = ''
|
||||||
self.stealth = stealth
|
self.stealth = stealth
|
||||||
|
self.scheduled_key = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def start_onion_service(_):
|
def start_onion_service(self, await_publication=True, save_scheduled_key=False):
|
||||||
return 'test_service_id.onion'
|
return 'test_service_id.onion'
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ class TestSettings:
|
|||||||
'auth_password': '',
|
'auth_password': '',
|
||||||
'close_after_first_download': True,
|
'close_after_first_download': True,
|
||||||
'shutdown_timeout': False,
|
'shutdown_timeout': False,
|
||||||
|
'startup_timer': False,
|
||||||
'use_stealth': False,
|
'use_stealth': False,
|
||||||
'use_autoupdate': True,
|
'use_autoupdate': True,
|
||||||
'autoupdate_timestamp': None,
|
'autoupdate_timestamp': None,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user