From 3a056803a9bcd3cbd396c17f144f9722f7ac281a Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 7 Dec 2017 12:45:29 +1100 Subject: [PATCH 01/14] Optionally save the private key of a running share to settings for reuse --- onionshare/onion.py | 13 ++++++++++--- onionshare/onionshare.py | 2 +- onionshare_gui/onionshare_gui.py | 2 +- onionshare_gui/server_status.py | 21 ++++++++++++++++++++- onionshare_gui/settings_dialog.py | 17 +++++++++++++++++ share/locale/en.json | 4 +++- 6 files changed, 52 insertions(+), 7 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 2f79719b..b95d975d 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -396,24 +396,31 @@ class Onion(object): else: basic_auth = None + if self.settings.get('private_key'): + key_type = "RSA1024" + key_content = self.settings.get('private_key') + else: + key_type = "NEW" + key_content = "RSA1024" try: if basic_auth != None : - res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, basic_auth=basic_auth) + res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, basic_auth=basic_auth, key_type = key_type, key_content=key_content) else : # if the stem interface is older than 1.5.0, basic_auth isn't a valid keyword arg - res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True) + res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, key_type = key_type, key_content=key_content) except ProtocolError: raise TorErrorProtocolError(strings._('error_tor_protocol_error')) self.service_id = res.content()[0][2].split('=')[1] onion_host = self.service_id + '.onion' + private_key = res.private_key if self.stealth: auth_cookie = res.content()[2][2].split('=')[1].split(':')[1] self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) - return onion_host + return (onion_host, private_key) def cleanup(self): """ diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index 85bfaf22..128e71af 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -76,7 +76,7 @@ class OnionShare(object): if self.shutdown_timeout > 0: self.shutdown_timer = common.close_after_seconds(self.shutdown_timeout) - self.onion_host = self.onion.start_onion_service(self.port) + self.onion_host, self.private_key = self.onion.start_onion_service(self.port) if self.stealth: self.auth_string = self.onion.auth_string diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 3ed30db7..dd5c00bf 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -69,7 +69,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.file_selection.file_list.add_file(filename) # Server status - self.server_status = ServerStatus(self.qtapp, self.app, web, self.file_selection) + 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_stopped.connect(self.file_selection.server_stopped) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 48bffdca..c2cb8f47 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -22,6 +22,7 @@ from .alert import Alert from PyQt5 import QtCore, QtWidgets, QtGui from onionshare import strings, common +from onionshare.settings import Settings class ServerStatus(QtWidgets.QVBoxLayout): """ @@ -31,12 +32,13 @@ class ServerStatus(QtWidgets.QVBoxLayout): server_stopped = QtCore.pyqtSignal() url_copied = QtCore.pyqtSignal() hidservauth_copied = QtCore.pyqtSignal() + private_key_saved = QtCore.pyqtSignal() STATUS_STOPPED = 0 STATUS_WORKING = 1 STATUS_STARTED = 2 - def __init__(self, qtapp, app, web, file_selection): + def __init__(self, qtapp, app, web, file_selection, settings): super(ServerStatus, self).__init__() self.status = self.STATUS_STOPPED @@ -44,6 +46,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.app = app self.web = web self.file_selection = file_selection + self.settings = settings # Helper boolean as this is used in a few places self.timer_enabled = False @@ -87,10 +90,13 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.copy_url_button.clicked.connect(self.copy_url) self.copy_hidservauth_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True)) self.copy_hidservauth_button.clicked.connect(self.copy_hidservauth) + self.save_private_key_button = QtWidgets.QPushButton(strings._('gui_save_private_key', True)) + self.save_private_key_button.clicked.connect(self.save_private_key) url_layout = QtWidgets.QHBoxLayout() url_layout.addWidget(self.url_label) url_layout.addWidget(self.copy_url_button) url_layout.addWidget(self.copy_hidservauth_button) + url_layout.addWidget(self.save_private_key_button) # add the widgets self.addLayout(shutdown_timeout_layout_group) @@ -140,6 +146,8 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.url_label.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) self.url_label.show() self.copy_url_button.show() + if not self.settings.get('private_key'): + self.save_private_key_button.show() if self.app.stealth: self.copy_hidservauth_button.show() @@ -153,6 +161,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.url_label.hide() self.copy_url_button.hide() self.copy_hidservauth_button.hide() + self.save_private_key_button.hide() # button if self.file_selection.get_num_files() == 0: @@ -249,3 +258,13 @@ class ServerStatus(QtWidgets.QVBoxLayout): clipboard.setText(self.app.auth_string) self.hidservauth_copied.emit() + + def save_private_key(self): + """ + Save the Onion private key to settings, so the Onion URL can be re-used. + """ + self.save_private_key_button.setEnabled(False) + self.settings.set('private_key', self.app.private_key) + self.settings.save() + + self.private_key_saved.emit() diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index df806a06..3421255d 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -60,10 +60,16 @@ class SettingsDialog(QtWidgets.QDialog): self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Checked) self.systray_notifications_checkbox.setText(strings._("gui_settings_systray_notifications", True)) + # Whether or not to save the Onion private key for reuse + self.save_private_key_checkbox = QtWidgets.QCheckBox() + self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) + self.save_private_key_checkbox.setText(strings._("gui_save_private_key_checkbox", True)) + # Sharing options layout sharing_group_layout = QtWidgets.QVBoxLayout() sharing_group_layout.addWidget(self.close_after_first_download_checkbox) sharing_group_layout.addWidget(self.systray_notifications_checkbox) + sharing_group_layout.addWidget(self.save_private_key_checkbox) sharing_group = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label", True)) sharing_group.setLayout(sharing_group_layout) @@ -275,6 +281,13 @@ class SettingsDialog(QtWidgets.QDialog): else: self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Unchecked) + save_private_key = self.old_settings.get('private_key') + if save_private_key: + self.save_private_key_checkbox.setCheckState(QtCore.Qt.Checked) + else: + self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) + self.save_private_key_checkbox.hide() + use_stealth = self.old_settings.get('use_stealth') if use_stealth: self.stealth_checkbox.setCheckState(QtCore.Qt.Checked) @@ -529,6 +542,10 @@ class SettingsDialog(QtWidgets.QDialog): settings.set('close_after_first_download', self.close_after_first_download_checkbox.isChecked()) settings.set('systray_notifications', self.systray_notifications_checkbox.isChecked()) + if self.save_private_key_checkbox.isChecked(): + settings.set('private_key', settings.get('private_key')) + else: + settings.set('private_key', '') settings.set('use_stealth', self.stealth_checkbox.isChecked()) if self.connection_type_bundled_radio.isChecked(): diff --git a/share/locale/en.json b/share/locale/en.json index 0756843e..5ec68229 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -121,5 +121,7 @@ "gui_tor_connection_canceled": "OnionShare cannot connect to Tor.\n\nMake sure you're connected to the internet, then re-open OnionShare to configure the Tor connection.", "gui_server_started_after_timeout": "The server started after your chosen auto-timeout.\nPlease start a new share.", "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", - "share_via_onionshare": "Share via OnionShare" + "share_via_onionshare": "Share via OnionShare", + "gui_save_private_key": "Save private key?", + "gui_save_private_key_checkbox": "Should the private key be saved for re-use?\nThis makes the Onion share URL persistent.\nUnchecking will remove the private key from settings." } From d69e93a5ba951f86de97f34e759d5d62e8f422b0 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 7 Dec 2017 13:02:56 +1100 Subject: [PATCH 02/14] emit to the status bar when the private key is saved to disk --- onionshare_gui/onionshare_gui.py | 8 ++++++++ share/locale/en.json | 1 + 2 files changed, 9 insertions(+) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index dd5c00bf..a5fb0edb 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -80,6 +80,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.file_selection.file_list.files_updated.connect(self.server_status.update) self.server_status.url_copied.connect(self.copy_url) self.server_status.hidservauth_copied.connect(self.copy_hidservauth) + self.server_status.private_key_saved.connect(self.private_key_saved) 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) @@ -470,6 +471,13 @@ class OnionShareGui(QtWidgets.QMainWindow): common.log('OnionShareGui', 'copy_hidservauth') self.status_bar.showMessage(strings._('gui_copied_hidservauth', True), 2000) + def private_key_saved(self): + """ + When the private key gets saved, display this in the status bar. + """ + common.log('OnionShareGui', 'private_key_saved') + self.status_bar.showMessage(strings._('gui_private_key_saved', True), 2000) + def clear_message(self): """ Clear messages from the status bar. diff --git a/share/locale/en.json b/share/locale/en.json index 5ec68229..b239433e 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -46,6 +46,7 @@ "gui_canceled": "Canceled", "gui_copied_url": "Copied URL to clipboard", "gui_copied_hidservauth": "Copied HidServAuth line to clipboard", + "gui_private_key_saved": "Private key saved to settings", "gui_starting_server1": "Starting Tor onion service...", "gui_starting_server2": "Crunching files...", "gui_please_wait": "Please wait...", From 796a4b3b5d491c19ec878e1b5a992b688322b6db Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 7 Dec 2017 15:33:12 +1100 Subject: [PATCH 03/14] try and fix tests --- onionshare/onion.py | 4 ++-- onionshare/onionshare.py | 4 +++- test/test_onionshare.py | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index b95d975d..218dafd5 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -414,13 +414,13 @@ class Onion(object): self.service_id = res.content()[0][2].split('=')[1] onion_host = self.service_id + '.onion' - private_key = res.private_key + self.private_key = res.private_key if self.stealth: auth_cookie = res.content()[2][2].split('=')[1].split(':')[1] self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) - return (onion_host, private_key) + return onion_host def cleanup(self): """ diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index 128e71af..7466a163 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -76,7 +76,9 @@ class OnionShare(object): if self.shutdown_timeout > 0: self.shutdown_timer = common.close_after_seconds(self.shutdown_timeout) - self.onion_host, self.private_key = self.onion.start_onion_service(self.port) + self.onion_host = self.onion.start_onion_service(self.port) + + self.private_key = self.onion.private_key if self.stealth: self.auth_string = self.onion.auth_string diff --git a/test/test_onionshare.py b/test/test_onionshare.py index f3e35dfc..76e471bd 100644 --- a/test/test_onionshare.py +++ b/test/test_onionshare.py @@ -27,6 +27,7 @@ from onionshare import OnionShare class MyOnion: def __init__(self, stealth=False): self.auth_string = 'TestHidServAuth' + self.private_key = '' self.stealth = stealth @staticmethod From e7dd78dba7e085863bef7518e59cac895bbd482c Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 7 Dec 2017 16:08:03 +1100 Subject: [PATCH 04/14] cleanup the ephemeral hidden service when GUI server is stopped, but don't disconnect from Tor --- onionshare/onion.py | 39 +++++++++++++++++--------------- onionshare_gui/onionshare_gui.py | 2 ++ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 218dafd5..00b7dd51 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -422,7 +422,7 @@ class Onion(object): return onion_host - def cleanup(self): + def cleanup(self, stop_tor=True): """ Stop onion services that were created earlier. If there's a tor subprocess running, kill it. """ @@ -436,25 +436,28 @@ class Onion(object): pass self.service_id = None - # Stop tor process - if self.tor_proc: - self.tor_proc.terminate() - time.sleep(0.2) - if not self.tor_proc.poll(): - self.tor_proc.kill() - self.tor_proc = None + if stop_tor: + # Stop tor process + if self.tor_proc: + self.tor_proc.terminate() + time.sleep(0.2) + if not self.tor_proc.poll(): + try: + self.tor_proc.kill() + except: + pass + self.tor_proc = None - # Reset other Onion settings - self.connected_to_tor = False - self.stealth = False - self.service_id = None + # Reset other Onion settings + self.connected_to_tor = False + self.stealth = False - try: - # Delete the temporary tor data directory - self.tor_data_directory.cleanup() - except AttributeError: - # Skip if cleanup was somehow run before connect - pass + try: + # Delete the temporary tor data directory + self.tor_data_directory.cleanup() + except AttributeError: + # Skip if cleanup was somehow run before connect + pass def get_tor_socks_port(self): """ diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index a5fb0edb..17b4cfc1 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -355,6 +355,8 @@ class OnionShareGui(QtWidgets.QMainWindow): # 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.stop_server_finished.emit() From bd5ee8b082b673be248d9fc987ee53bf3bb5b338 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Fri, 8 Dec 2017 11:15:12 +1100 Subject: [PATCH 05/14] remove question mark from QPushButton --- share/locale/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/locale/en.json b/share/locale/en.json index b239433e..88113fba 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -123,6 +123,6 @@ "gui_server_started_after_timeout": "The server started after your chosen auto-timeout.\nPlease start a new share.", "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", "share_via_onionshare": "Share via OnionShare", - "gui_save_private_key": "Save private key?", + "gui_save_private_key": "Save private key", "gui_save_private_key_checkbox": "Should the private key be saved for re-use?\nThis makes the Onion share URL persistent.\nUnchecking will remove the private key from settings." } From 08c2e106f81fa5f432a10e11f4c0a492e451a76f Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sat, 9 Dec 2017 06:49:34 +1100 Subject: [PATCH 06/14] Save the HidServAuth string to settings when private key is also saved. Allow to copy it to clipboard from the SettingsDialog too. --- onionshare/onion.py | 32 ++++++++++++++++++++++++------- onionshare_gui/server_status.py | 3 +++ onionshare_gui/settings_dialog.py | 27 ++++++++++++++++++++++++++ share/locale/en.json | 1 + 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 00b7dd51..409c9ad8 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -368,7 +368,7 @@ class Onion(object): # Do the versions of stem and tor that I'm using support stealth onion services? try: res = self.c.create_ephemeral_hidden_service({1:1}, basic_auth={'onionshare':None}, await_publication=False) - tmp_service_id = res.content()[0][2].split('=')[1] + tmp_service_id = res.service_id self.c.remove_ephemeral_hidden_service(tmp_service_id) self.supports_stealth = True except: @@ -392,20 +392,27 @@ class Onion(object): print(strings._('using_ephemeral')) if self.stealth: - basic_auth = {'onionshare':None} + if self.settings.get('hidservauth_string'): + hidservauth_string = self.settings.get('hidservauth_string').split()[2] + basic_auth = {'onionshare':hidservauth_string} + else: + basic_auth = {'onionshare':None} else: basic_auth = None if self.settings.get('private_key'): key_type = "RSA1024" key_content = self.settings.get('private_key') + common.log('Onion', 'Starting a hidden service with a saved private key') else: key_type = "NEW" key_content = "RSA1024" + common.log('Onion', 'Starting a hidden service with a new private key') + try: - if basic_auth != None : + if basic_auth != None: res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, basic_auth=basic_auth, key_type = key_type, key_content=key_content) - else : + else: # if the stem interface is older than 1.5.0, basic_auth isn't a valid keyword arg res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, key_type = key_type, key_content=key_content) @@ -414,11 +421,22 @@ class Onion(object): self.service_id = res.content()[0][2].split('=')[1] onion_host = self.service_id + '.onion' - self.private_key = res.private_key + + # A new private key was generated and is in the Control port response. + if not self.settings.get('private_key'): + self.private_key = res.private_key if self.stealth: - auth_cookie = res.content()[2][2].split('=')[1].split(':')[1] - self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) + # 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 first place. + # If we sent the basic_auth (due to a saved hidservauth_string in the settings), + # there is no response here, so use the saved value from settings. + if self.settings.get('hidservauth_string'): + self.auth_string = self.settings.get('hidservauth_string') + else: + auth_cookie = list(res.client_auth.values())[0] + self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) return onion_host diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index c2cb8f47..77964677 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -265,6 +265,9 @@ class ServerStatus(QtWidgets.QVBoxLayout): """ self.save_private_key_button.setEnabled(False) self.settings.set('private_key', self.app.private_key) + if self.app.stealth: + self.settings.set('hidservauth_string', self.app.auth_string) self.settings.save() + self.settings.load() self.private_key_saved.emit() diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 3421255d..22b24fc5 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -82,10 +82,20 @@ class SettingsDialog(QtWidgets.QDialog): self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) + hidservauth_details = QtWidgets.QLabel(strings._('gui_settings_stealth_hidservauth_string', True)) + hidservauth_details.setWordWrap(True) + hidservauth_details.hide() + + self.hidservauth_copy_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True)) + self.hidservauth_copy_button.clicked.connect(self.hidservauth_copy_button_clicked) + self.hidservauth_copy_button.hide() + # Stealth options layout stealth_group_layout = QtWidgets.QVBoxLayout() stealth_group_layout.addWidget(stealth_details) stealth_group_layout.addWidget(self.stealth_checkbox) + stealth_group_layout.addWidget(hidservauth_details) + stealth_group_layout.addWidget(self.hidservauth_copy_button) stealth_group = QtWidgets.QGroupBox(strings._("gui_settings_stealth_label", True)) stealth_group.setLayout(stealth_group_layout) @@ -291,6 +301,9 @@ class SettingsDialog(QtWidgets.QDialog): use_stealth = self.old_settings.get('use_stealth') if use_stealth: self.stealth_checkbox.setCheckState(QtCore.Qt.Checked) + if save_private_key: + hidservauth_details.show() + self.hidservauth_copy_button.show() else: self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) @@ -390,6 +403,15 @@ class SettingsDialog(QtWidgets.QDialog): else: self.authenticate_password_extras.hide() + def hidservauth_copy_button_clicked(self): + """ + Toggle the 'Copy HidServAuth' button + to copy the saved HidServAuth to clipboard. + """ + common.log('SettingsDialog', 'hidservauth_copy_button_clicked', 'HidServAuth was copied to clipboard') + clipboard = self.qtapp.clipboard() + clipboard.setText(self.old_settings.get('hidservauth_string')) + def test_tor_clicked(self): """ Test Tor Settings button clicked. With the given settings, see if we can @@ -546,7 +568,12 @@ class SettingsDialog(QtWidgets.QDialog): settings.set('private_key', settings.get('private_key')) else: settings.set('private_key', '') + # Also unset the HidServAuth if we are removing our reusable private key + settings.set('hidservauth_string', '') settings.set('use_stealth', self.stealth_checkbox.isChecked()) + # Always unset the HidServAuth if Stealth mode is unset + if not self.stealth_checkbox.isChecked(): + settings.set('hidservauth_string', '') if self.connection_type_bundled_radio.isChecked(): settings.set('connection_type', 'bundled') diff --git a/share/locale/en.json b/share/locale/en.json index 88113fba..2ec21580 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -68,6 +68,7 @@ "gui_settings_stealth_label": "Stealth (advanced)", "gui_settings_stealth_option": "Create stealth onion services", "gui_settings_stealth_option_details": "This makes OnionShare more secure, but also more difficult for the recipient to connect to it.
More information.", + "gui_settings_stealth_hidservauth_string": "You have saved the private key for reuse, so your HidServAuth string is also reused.\nClick below to copy the HidServAuth.", "gui_settings_autoupdate_label": "Check for updates", "gui_settings_autoupdate_option": "Notify me when updates are available", "gui_settings_autoupdate_timestamp": "Last checked: {}", From 7edd6930718df48e34b728e241bef4016b847d34 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sat, 9 Dec 2017 08:11:04 +1100 Subject: [PATCH 07/14] properly set saved private key/hidservauth string in settings_from_fields, so those values are populated when the SettingsDialog is saved --- onionshare_gui/settings_dialog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 22b24fc5..3de3ef85 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -565,7 +565,8 @@ class SettingsDialog(QtWidgets.QDialog): settings.set('close_after_first_download', self.close_after_first_download_checkbox.isChecked()) settings.set('systray_notifications', self.systray_notifications_checkbox.isChecked()) if self.save_private_key_checkbox.isChecked(): - settings.set('private_key', settings.get('private_key')) + settings.set('private_key', self.old_settings.get('private_key')) + settings.set('hidservauth_string', self.old_settings.get('hidservauth_string')) else: settings.set('private_key', '') # Also unset the HidServAuth if we are removing our reusable private key From bec03bb10ed49c79ef97906c0ea3356d3d3ac7db Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sat, 9 Dec 2017 08:18:06 +1100 Subject: [PATCH 08/14] only set self.private_key after the Onion service has started, if we received a new one --- onionshare/onion.py | 2 ++ onionshare/onionshare.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 409c9ad8..862266e7 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -425,6 +425,8 @@ class Onion(object): # A new private key was generated and is in the Control port response. if not self.settings.get('private_key'): self.private_key = res.private_key + else: + self.private_key = '' if self.stealth: # Similar to the PrivateKey, the Control port only returns the ClientAuth diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index 7466a163..ff5f33fb 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -78,7 +78,8 @@ class OnionShare(object): self.onion_host = self.onion.start_onion_service(self.port) - self.private_key = self.onion.private_key + if self.onion.private_key: + self.private_key = self.onion.private_key if self.stealth: self.auth_string = self.onion.auth_string From 8e89a65a22ea960ee601937d2e6028e9e95fda97 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sat, 9 Dec 2017 08:51:30 +1100 Subject: [PATCH 09/14] set the 'Save Private Key' button to enabled when showing it, but when it's not already been pressed (in case private key is removed later via SettingsDialog) --- onionshare_gui/server_status.py | 1 + 1 file changed, 1 insertion(+) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 77964677..1a5d0807 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -148,6 +148,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.copy_url_button.show() if not self.settings.get('private_key'): self.save_private_key_button.show() + self.save_private_key_button.setEnabled(True) if self.app.stealth: self.copy_hidservauth_button.show() From e90908c5b072a0b945a89aba62615b4a1033b406 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sat, 13 Jan 2018 20:58:24 +1100 Subject: [PATCH 10/14] Move the saving of private key into the SettingsDialog and Onion objects entirely (no QPushButton to save it) --- onionshare/onion.py | 17 +++++++++++------ onionshare/onionshare.py | 3 --- onionshare/settings.py | 5 ++++- onionshare_gui/onionshare_gui.py | 8 -------- onionshare_gui/server_status.py | 23 ++--------------------- onionshare_gui/settings_dialog.py | 5 +++-- share/locale/en.json | 4 +--- 7 files changed, 21 insertions(+), 44 deletions(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 862266e7..3e30761a 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -423,10 +423,9 @@ class Onion(object): onion_host = self.service_id + '.onion' # A new private key was generated and is in the Control port response. - if not self.settings.get('private_key'): - self.private_key = res.private_key - else: - self.private_key = '' + if self.settings.get('save_private_key'): + if not self.settings.get('private_key'): + self.settings.set('private_key', res.private_key) if self.stealth: # Similar to the PrivateKey, the Control port only returns the ClientAuth @@ -434,12 +433,18 @@ class Onion(object): # in the first place. # If we sent the basic_auth (due to a saved hidservauth_string in the settings), # there is no response here, so use the saved value from settings. - if self.settings.get('hidservauth_string'): - self.auth_string = self.settings.get('hidservauth_string') + if self.settings.get('save_private_key'): + if self.settings.get('hidservauth_string'): + self.auth_string = self.settings.get('hidservauth_string') + else: + auth_cookie = list(res.client_auth.values())[0] + self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) + self.settings.set('hidservauth_string', self.auth_string) else: auth_cookie = list(res.client_auth.values())[0] self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) + self.settings.save() return onion_host def cleanup(self, stop_tor=True): diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index ff5f33fb..85bfaf22 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -78,9 +78,6 @@ class OnionShare(object): self.onion_host = self.onion.start_onion_service(self.port) - if self.onion.private_key: - self.private_key = self.onion.private_key - if self.stealth: self.auth_string = self.onion.auth_string diff --git a/onionshare/settings.py b/onionshare/settings.py index 408c8bdc..e87638f1 100644 --- a/onionshare/settings.py +++ b/onionshare/settings.py @@ -60,7 +60,10 @@ class Settings(object): 'systray_notifications': True, 'use_stealth': False, 'use_autoupdate': True, - 'autoupdate_timestamp': None + 'autoupdate_timestamp': None, + 'save_private_key': False, + 'private_key': '', + 'hidservauth_string': '' } self._settings = {} self.fill_in_defaults() diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 17b4cfc1..18c0f642 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -80,7 +80,6 @@ class OnionShareGui(QtWidgets.QMainWindow): self.file_selection.file_list.files_updated.connect(self.server_status.update) self.server_status.url_copied.connect(self.copy_url) self.server_status.hidservauth_copied.connect(self.copy_hidservauth) - self.server_status.private_key_saved.connect(self.private_key_saved) 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) @@ -473,13 +472,6 @@ class OnionShareGui(QtWidgets.QMainWindow): common.log('OnionShareGui', 'copy_hidservauth') self.status_bar.showMessage(strings._('gui_copied_hidservauth', True), 2000) - def private_key_saved(self): - """ - When the private key gets saved, display this in the status bar. - """ - common.log('OnionShareGui', 'private_key_saved') - self.status_bar.showMessage(strings._('gui_private_key_saved', True), 2000) - def clear_message(self): """ Clear messages from the status bar. diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 1a5d0807..7e8706b9 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -32,7 +32,6 @@ class ServerStatus(QtWidgets.QVBoxLayout): server_stopped = QtCore.pyqtSignal() url_copied = QtCore.pyqtSignal() hidservauth_copied = QtCore.pyqtSignal() - private_key_saved = QtCore.pyqtSignal() STATUS_STOPPED = 0 STATUS_WORKING = 1 @@ -90,13 +89,10 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.copy_url_button.clicked.connect(self.copy_url) self.copy_hidservauth_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True)) self.copy_hidservauth_button.clicked.connect(self.copy_hidservauth) - self.save_private_key_button = QtWidgets.QPushButton(strings._('gui_save_private_key', True)) - self.save_private_key_button.clicked.connect(self.save_private_key) url_layout = QtWidgets.QHBoxLayout() url_layout.addWidget(self.url_label) url_layout.addWidget(self.copy_url_button) url_layout.addWidget(self.copy_hidservauth_button) - url_layout.addWidget(self.save_private_key_button) # add the widgets self.addLayout(shutdown_timeout_layout_group) @@ -146,9 +142,8 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.url_label.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) self.url_label.show() self.copy_url_button.show() - if not self.settings.get('private_key'): - self.save_private_key_button.show() - self.save_private_key_button.setEnabled(True) + if self.settings.get('save_private_key'): + common.log('ServerStatus', 'update', '@TODO need to show a "persistent URL in use" label') if self.app.stealth: self.copy_hidservauth_button.show() @@ -162,7 +157,6 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.url_label.hide() self.copy_url_button.hide() self.copy_hidservauth_button.hide() - self.save_private_key_button.hide() # button if self.file_selection.get_num_files() == 0: @@ -259,16 +253,3 @@ class ServerStatus(QtWidgets.QVBoxLayout): clipboard.setText(self.app.auth_string) self.hidservauth_copied.emit() - - def save_private_key(self): - """ - Save the Onion private key to settings, so the Onion URL can be re-used. - """ - self.save_private_key_button.setEnabled(False) - self.settings.set('private_key', self.app.private_key) - if self.app.stealth: - self.settings.set('hidservauth_string', self.app.auth_string) - self.settings.save() - self.settings.load() - - self.private_key_saved.emit() diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 3de3ef85..2aaaab47 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -291,12 +291,11 @@ class SettingsDialog(QtWidgets.QDialog): else: self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Unchecked) - save_private_key = self.old_settings.get('private_key') + save_private_key = self.old_settings.get('save_private_key') if save_private_key: self.save_private_key_checkbox.setCheckState(QtCore.Qt.Checked) else: self.save_private_key_checkbox.setCheckState(QtCore.Qt.Unchecked) - self.save_private_key_checkbox.hide() use_stealth = self.old_settings.get('use_stealth') if use_stealth: @@ -565,9 +564,11 @@ class SettingsDialog(QtWidgets.QDialog): settings.set('close_after_first_download', self.close_after_first_download_checkbox.isChecked()) settings.set('systray_notifications', self.systray_notifications_checkbox.isChecked()) if self.save_private_key_checkbox.isChecked(): + settings.set('save_private_key', True) settings.set('private_key', self.old_settings.get('private_key')) settings.set('hidservauth_string', self.old_settings.get('hidservauth_string')) else: + settings.set('save_private_key', False) settings.set('private_key', '') # Also unset the HidServAuth if we are removing our reusable private key settings.set('hidservauth_string', '') diff --git a/share/locale/en.json b/share/locale/en.json index 2ec21580..ae9da805 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -46,7 +46,6 @@ "gui_canceled": "Canceled", "gui_copied_url": "Copied URL to clipboard", "gui_copied_hidservauth": "Copied HidServAuth line to clipboard", - "gui_private_key_saved": "Private key saved to settings", "gui_starting_server1": "Starting Tor onion service...", "gui_starting_server2": "Crunching files...", "gui_please_wait": "Please wait...", @@ -124,6 +123,5 @@ "gui_server_started_after_timeout": "The server started after your chosen auto-timeout.\nPlease start a new share.", "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", "share_via_onionshare": "Share via OnionShare", - "gui_save_private_key": "Save private key", - "gui_save_private_key_checkbox": "Should the private key be saved for re-use?\nThis makes the Onion share URL persistent.\nUnchecking will remove the private key from settings." + "gui_save_private_key_checkbox": "Use a persistent URL\n(unchecking will delete any saved URL)" } From 488d88886d79496934ee88b5247db1963e9da8d9 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sat, 13 Jan 2018 21:03:44 +1100 Subject: [PATCH 11/14] Attempt to fix tests --- test/test_onionshare_settings.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/test_onionshare_settings.py b/test/test_onionshare_settings.py index 1489c348..0e03080f 100644 --- a/test/test_onionshare_settings.py +++ b/test/test_onionshare_settings.py @@ -57,7 +57,10 @@ class TestSettings: 'systray_notifications': True, 'use_stealth': False, 'use_autoupdate': True, - 'autoupdate_timestamp': None + 'autoupdate_timestamp': None, + 'save_private_key': False, + 'private_key': '', + 'hidservauth_string': '' } def test_fill_in_defaults(self, settings_obj): From 9aef3cdbb3ccfdb6f796467ee97e7d4eabbea430 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 14 Jan 2018 18:41:54 +1100 Subject: [PATCH 12/14] Add a 'persistent URL' label when re-using a private key --- onionshare_gui/onionshare_gui.py | 12 +++++++++++- onionshare_gui/server_status.py | 6 +----- share/locale/en.json | 3 ++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 18c0f642..ca332f74 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -69,7 +69,7 @@ class OnionShareGui(QtWidgets.QMainWindow): 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 = ServerStatus(self.qtapp, self.app, web, self.file_selection) self.server_status.server_started.connect(self.file_selection.server_started) self.server_status.server_started.connect(self.start_server) self.server_status.server_stopped.connect(self.file_selection.server_stopped) @@ -119,11 +119,17 @@ class OnionShareGui(QtWidgets.QMainWindow): # Status bar, zip progress bar self._zip_progress_bar = None + # Persistent URL notification + self.persistent_url_label = QtWidgets.QLabel(strings._('persistent_url_in_use', True)) + self.persistent_url_label.setStyleSheet('padding: 10px 0; font-weight: bold; color: #333333;') + self.persistent_url_label.hide() + # Main layout self.layout = QtWidgets.QVBoxLayout() self.layout.addLayout(self.file_selection) self.layout.addLayout(self.server_status) self.layout.addWidget(self.filesize_warning) + self.layout.addWidget(self.persistent_url_label) self.layout.addWidget(self.downloads_container) central_widget = QtWidgets.QWidget() central_widget.setLayout(self.layout) @@ -329,6 +335,9 @@ class OnionShareGui(QtWidgets.QMainWindow): self.stop_server() self.start_server_error(strings._('gui_server_started_after_timeout')) + if self.settings.get('save_private_key'): + self.persistent_url_label.show() + def start_server_error(self, error): """ If there's an error when trying to start the onion service @@ -357,6 +366,7 @@ class OnionShareGui(QtWidgets.QMainWindow): # Remove ephemeral service, but don't disconnect from Tor self.onion.cleanup(stop_tor=False) self.filesize_warning.hide() + self.persistent_url_label.hide() self.stop_server_finished.emit() self.set_server_active(False) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 7e8706b9..48bffdca 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -22,7 +22,6 @@ from .alert import Alert from PyQt5 import QtCore, QtWidgets, QtGui from onionshare import strings, common -from onionshare.settings import Settings class ServerStatus(QtWidgets.QVBoxLayout): """ @@ -37,7 +36,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): STATUS_WORKING = 1 STATUS_STARTED = 2 - def __init__(self, qtapp, app, web, file_selection, settings): + def __init__(self, qtapp, app, web, file_selection): super(ServerStatus, self).__init__() self.status = self.STATUS_STOPPED @@ -45,7 +44,6 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.app = app self.web = web self.file_selection = file_selection - self.settings = settings # Helper boolean as this is used in a few places self.timer_enabled = False @@ -142,8 +140,6 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.url_label.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) self.url_label.show() self.copy_url_button.show() - if self.settings.get('save_private_key'): - common.log('ServerStatus', 'update', '@TODO need to show a "persistent URL in use" label') if self.app.stealth: self.copy_hidservauth_button.show() diff --git a/share/locale/en.json b/share/locale/en.json index ae9da805..d52e0434 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -123,5 +123,6 @@ "gui_server_started_after_timeout": "The server started after your chosen auto-timeout.\nPlease start a new share.", "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.", "share_via_onionshare": "Share via OnionShare", - "gui_save_private_key_checkbox": "Use a persistent URL\n(unchecking will delete any saved URL)" + "gui_save_private_key_checkbox": "Use a persistent URL\n(unchecking will delete any saved URL)", + "persistent_url_in_use": "This share is using a persistent URL" } From 4da5ddf2c942e0896cb420f8a28005d7bf767c42 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 14 Jan 2018 18:45:10 +1100 Subject: [PATCH 13/14] Resolve conflict with upstream's onion.py --- onionshare/onion.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/onionshare/onion.py b/onionshare/onion.py index 3e30761a..cf761bb9 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -445,7 +445,10 @@ class Onion(object): self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) self.settings.save() - return onion_host + if onion_host is not None: + return onion_host + else: + raise TorErrorProtocolError(strings._('error_tor_protocol_error')) def cleanup(self, stop_tor=True): """ From 3e7d4c64ffff73b03779860d1d3423e126852690 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 15 Jan 2018 10:01:34 +1100 Subject: [PATCH 14/14] Save the slug when using a persistent private key --- onionshare/__init__.py | 14 ++++++++++++-- onionshare/settings.py | 1 + onionshare/web.py | 11 +++++++---- onionshare_gui/onionshare_gui.py | 4 ++-- onionshare_gui/server_status.py | 11 +++++++++-- onionshare_gui/settings_dialog.py | 2 ++ test/test_onionshare_settings.py | 1 + 7 files changed, 34 insertions(+), 10 deletions(-) diff --git a/onionshare/__init__.py b/onionshare/__init__.py index 99beb0e0..a3474da9 100644 --- a/onionshare/__init__.py +++ b/onionshare/__init__.py @@ -23,7 +23,7 @@ import os, sys, time, argparse, threading from . import strings, common, web from .onion import * from .onionshare import OnionShare - +from .settings import Settings def main(cwd=None): """ @@ -77,6 +77,10 @@ def main(cwd=None): if not valid: sys.exit() + + settings = Settings(config) + settings.load() + # Start the Onion object onion = Onion() try: @@ -108,7 +112,7 @@ def main(cwd=None): print('') # Start OnionShare http service in new thread - t = threading.Thread(target=web.start, args=(app.port, app.stay_open)) + t = threading.Thread(target=web.start, args=(app.port, app.stay_open, settings.get('slug'))) t.daemon = True t.start() @@ -120,6 +124,12 @@ def main(cwd=None): if app.shutdown_timeout > 0: app.shutdown_timer.start() + # Save the web slug if we are using a persistent private key + if settings.get('save_private_key'): + if not settings.get('slug'): + settings.set('slug', web.slug) + settings.save() + if(stealth): print(strings._("give_this_url_stealth")) print('http://{0:s}/{1:s}'.format(app.onion_host, web.slug)) diff --git a/onionshare/settings.py b/onionshare/settings.py index e87638f1..e8103757 100644 --- a/onionshare/settings.py +++ b/onionshare/settings.py @@ -63,6 +63,7 @@ class Settings(object): 'autoupdate_timestamp': None, 'save_private_key': False, 'private_key': '', + 'slug': '', 'hidservauth_string': '' } self._settings = {} diff --git a/onionshare/web.py b/onionshare/web.py index 03da8fd7..3eef67c7 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -128,9 +128,12 @@ def add_request(request_type, path, data=None): slug = None -def generate_slug(): +def generate_slug(persistent_slug=''): global slug - slug = common.build_slug() + if persistent_slug: + slug = persistent_slug + else: + slug = common.build_slug() download_count = 0 error404_count = 0 @@ -383,11 +386,11 @@ def force_shutdown(): func() -def start(port, stay_open=False): +def start(port, stay_open=False, persistent_slug=''): """ Start the flask web server. """ - generate_slug() + generate_slug(persistent_slug) set_stay_open(stay_open) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 452e58db..582ebdb3 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -69,7 +69,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.file_selection.file_list.add_file(filename) # Server status - self.server_status = ServerStatus(self.qtapp, self.app, web, self.file_selection) + 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_stopped.connect(self.file_selection.server_stopped) @@ -278,7 +278,7 @@ class OnionShareGui(QtWidgets.QMainWindow): 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)) + 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 diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 48bffdca..442ae440 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -21,7 +21,7 @@ import platform from .alert import Alert from PyQt5 import QtCore, QtWidgets, QtGui -from onionshare import strings, common +from onionshare import strings, common, settings class ServerStatus(QtWidgets.QVBoxLayout): """ @@ -36,7 +36,7 @@ class ServerStatus(QtWidgets.QVBoxLayout): STATUS_WORKING = 1 STATUS_STARTED = 2 - def __init__(self, qtapp, app, web, file_selection): + def __init__(self, qtapp, app, web, file_selection, settings): super(ServerStatus, self).__init__() self.status = self.STATUS_STOPPED @@ -45,6 +45,8 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.web = web self.file_selection = file_selection + self.settings = settings + # Helper boolean as this is used in a few places self.timer_enabled = False # Shutdown timeout layout @@ -141,6 +143,11 @@ class ServerStatus(QtWidgets.QVBoxLayout): self.url_label.show() self.copy_url_button.show() + if self.settings.get('save_private_key'): + if not self.settings.get('slug'): + self.settings.set('slug', self.web.slug) + self.settings.save() + if self.app.stealth: self.copy_hidservauth_button.show() else: diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index a978c403..ca2cc4c7 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -570,10 +570,12 @@ class SettingsDialog(QtWidgets.QDialog): if self.save_private_key_checkbox.isChecked(): settings.set('save_private_key', True) settings.set('private_key', self.old_settings.get('private_key')) + settings.set('slug', self.old_settings.get('slug')) settings.set('hidservauth_string', self.old_settings.get('hidservauth_string')) else: settings.set('save_private_key', False) settings.set('private_key', '') + settings.set('slug', '') # Also unset the HidServAuth if we are removing our reusable private key settings.set('hidservauth_string', '') settings.set('use_stealth', self.stealth_checkbox.isChecked()) diff --git a/test/test_onionshare_settings.py b/test/test_onionshare_settings.py index 0e03080f..ba45ef5e 100644 --- a/test/test_onionshare_settings.py +++ b/test/test_onionshare_settings.py @@ -60,6 +60,7 @@ class TestSettings: 'autoupdate_timestamp': None, 'save_private_key': False, 'private_key': '', + 'slug': '', 'hidservauth_string': '' }