Fix conflicts arising from the reuse_private_key branch

This commit is contained in:
Miguel Jacq 2018-01-16 15:30:36 +11:00
commit 76cf0ae107
No known key found for this signature in database
GPG Key ID: EEA4341C6D97A0B6
14 changed files with 178 additions and 38 deletions

View File

@ -2,13 +2,21 @@
[![Build Status](https://travis-ci.org/micahflee/onionshare.png)](https://travis-ci.org/micahflee/onionshare) [![Build Status](https://travis-ci.org/micahflee/onionshare.png)](https://travis-ci.org/micahflee/onionshare)
OnionShare lets you securely and anonymously share files of any size. It works by starting a web server, making it accessible as a Tor onion service, and generating an unguessable URL to access and download the files. It doesn't require setting up a server on the internet somewhere or using a third party file-sharing service. You host the file on your own computer and use a Tor onion service to make it temporarily accessible over the internet. The other user just needs to use Tor Browser to download the file from you. [OnionShare](https://onionshare.org) lets you securely and anonymously share files of any size. It works by starting a web server, making it accessible as a Tor Onion Service, and generating an unguessable URL to access and download the files. It does _not_ require setting up a separate server or using a third party file-sharing service. You host the files on your own computer and use a Tor Onion Service to make it temporarily accessible over the internet. The receiving user just needs to open the URL in Tor Browser to download the file.
**To learn how OnionShare works, what its security properties are, and how to use it, check out the [wiki](https://github.com/micahflee/onionshare/wiki).** ## Documentation
**You can download OnionShare for Windows and macOS from <https://onionshare.org/>. It should be available in your package manager for Linux, and it's included by default in Tails.** To learn how OnionShare works, what its security properties are, and how to use it, check out the [wiki](https://github.com/micahflee/onionshare/wiki).
## Downloading Onionshare
You can download OnionShare for Windows and macOS from the [OnionShare website](https://onionshare.org). It should be available in your package manager for Linux, and it's included by default in [Tails](https://tails.boum.org).
## Developing OnionShare
You can set up your development environment to build OnionShare yourself by following [these instructions](/BUILD.md). You can set up your development environment to build OnionShare yourself by following [these instructions](/BUILD.md).
# Screenshots
![Server Screenshot](/screenshots/server.png) ![Server Screenshot](/screenshots/server.png)
![Client Screenshot](/screenshots/client.png) ![Client Screenshot](/screenshots/client.png)

View File

@ -28,9 +28,9 @@ import inspect, os, sys, hashlib, zipfile, io, shutil, subprocess
import urllib.request import urllib.request
def main(): def main():
dmg_url = 'https://archive.torproject.org/tor-package-archive/torbrowser/7.0.10/TorBrowser-7.0.10-osx64_en-US.dmg' dmg_url = 'https://archive.torproject.org/tor-package-archive/torbrowser/7.0.11/TorBrowser-7.0.11-osx64_en-US.dmg'
dmg_filename = 'TorBrowser-7.0.10-osx64_en-US.dmg' dmg_filename = 'TorBrowser-7.0.11-osx64_en-US.dmg'
expected_dmg_sha256 = '2de32de962d14ecb55f8008078875f3d0b0875dba4a140726714e67ce329fe9a' expected_dmg_sha256 = '5143e4a2141a69f66869be13eef4bcaac4e6c27c78383fc8a4c38b334759f3a2'
# Build paths # Build paths
root_path = os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) root_path = os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))

View File

@ -28,9 +28,9 @@ import inspect, os, sys, hashlib, zipfile, io, shutil
import urllib.request import urllib.request
def main(): def main():
zip_url = 'https://archive.torproject.org/tor-package-archive/torbrowser/7.0.10/tor-win32-0.3.1.8.zip' zip_url = 'https://archive.torproject.org/tor-package-archive/torbrowser/7.0.11/tor-win32-0.3.1.9.zip'
zip_filename = 'tor-win32-0.3.1.8.zip' zip_filename = 'tor-win32-0.3.1.9.zip'
expected_zip_sha256 = '101defd239cda42f364815e91809fad16b17f03843a169ffbeb8cb91183b6ba8' expected_zip_sha256 = 'faf28efb606455842bda66ca369287a116b6d6e5ad3720ebed9337da0717f1b4'
# Build paths # Build paths
root_path = os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) root_path = os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))

View File

@ -23,7 +23,7 @@ import os, sys, time, argparse, threading
from . import strings, common, web from . import strings, common, web
from .onion import * from .onion import *
from .onionshare import OnionShare from .onionshare import OnionShare
from .settings import Settings
def main(cwd=None): def main(cwd=None):
""" """
@ -68,7 +68,7 @@ def main(cwd=None):
# Validation # Validation
valid = True valid = True
for filename in filenames: for filename in filenames:
if not os.path.exists(filename): if not os.path.isfile(filename) and not os.path.isdir(filename):
print(strings._("not_a_file").format(filename)) print(strings._("not_a_file").format(filename))
valid = False valid = False
if not os.access(filename, os.R_OK): if not os.access(filename, os.R_OK):
@ -77,6 +77,10 @@ def main(cwd=None):
if not valid: if not valid:
sys.exit() sys.exit()
settings = Settings(config)
settings.load()
# Start the Onion object # Start the Onion object
onion = Onion() onion = Onion()
try: try:
@ -98,8 +102,12 @@ def main(cwd=None):
# Prepare files to share # Prepare files to share
print(strings._("preparing_files")) print(strings._("preparing_files"))
web.set_file_info(filenames) try:
app.cleanup_filenames.append(web.zip_filename) web.set_file_info(filenames)
app.cleanup_filenames.append(web.zip_filename)
except OSError as e:
print(e.strerror)
sys.exit(1)
# Warn about sending large files over Tor # Warn about sending large files over Tor
if web.zip_filesize >= 157286400: # 150mb if web.zip_filesize >= 157286400: # 150mb
@ -108,7 +116,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, app.stay_open)) t = threading.Thread(target=web.start, args=(app.port, app.stay_open, settings.get('slug')))
t.daemon = True t.daemon = True
t.start() t.start()
@ -120,6 +128,12 @@ def main(cwd=None):
if app.shutdown_timeout > 0: if app.shutdown_timeout > 0:
app.shutdown_timer.start() 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): if(stealth):
print(strings._("give_this_url_stealth")) print(strings._("give_this_url_stealth"))
print('http://{0:s}/{1:s}'.format(app.onion_host, web.slug)) print('http://{0:s}/{1:s}'.format(app.onion_host, web.slug))

View File

@ -383,7 +383,7 @@ class Onion(object):
# Do the versions of stem and tor that I'm using support stealth onion services? # Do the versions of stem and tor that I'm using support stealth onion services?
try: try:
res = self.c.create_ephemeral_hidden_service({1:1}, basic_auth={'onionshare':None}, await_publication=False) 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.c.remove_ephemeral_hidden_service(tmp_service_id)
self.supports_stealth = True self.supports_stealth = True
except: except:
@ -418,16 +418,29 @@ class Onion(object):
print(strings._('using_ephemeral')) print(strings._('using_ephemeral'))
if self.stealth: 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: else:
basic_auth = None 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: 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) 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 # 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: except ProtocolError:
raise TorErrorProtocolError(strings._('error_tor_protocol_error')) raise TorErrorProtocolError(strings._('error_tor_protocol_error'))
@ -435,10 +448,29 @@ class Onion(object):
self.service_id = res.service_id self.service_id = res.service_id
onion_host = self.service_id + '.onion' onion_host = self.service_id + '.onion'
if self.stealth: # A new private key was generated and is in the Control port response.
auth_cookie = res.content()[2][2].split('=')[1].split(':')[1] if self.settings.get('save_private_key'):
self.auth_string = 'HidServAuth {} {}'.format(onion_host, auth_cookie) 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
# 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('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()
if onion_host is not None: if onion_host is not None:
return onion_host return onion_host
else: else:

View File

@ -63,7 +63,11 @@ class Settings(object):
'autoupdate_timestamp': None, 'autoupdate_timestamp': None,
'no_bridges': True, 'no_bridges': True,
'tor_bridges_use_obfs4': False, 'tor_bridges_use_obfs4': False,
'tor_bridges_use_custom_bridges': '' 'tor_bridges_use_custom_bridges': '',
'save_private_key': False,
'private_key': '',
'slug': '',
'hidservauth_string': ''
} }
self._settings = {} self._settings = {}
self.fill_in_defaults() self.fill_in_defaults()

View File

@ -128,9 +128,12 @@ def add_request(request_type, path, data=None):
slug = None slug = None
def generate_slug(): def generate_slug(persistent_slug=''):
global slug global slug
slug = common.build_slug() if persistent_slug:
slug = persistent_slug
else:
slug = common.build_slug()
download_count = 0 download_count = 0
error404_count = 0 error404_count = 0
@ -383,11 +386,11 @@ def force_shutdown():
func() func()
def start(port, stay_open=False): def start(port, stay_open=False, persistent_slug=''):
""" """
Start the flask web server. Start the flask web server.
""" """
generate_slug() generate_slug(persistent_slug)
set_stay_open(stay_open) set_stay_open(stay_open)

View File

@ -91,7 +91,7 @@ def main():
if filenames: if filenames:
valid = True valid = True
for filename in filenames: for filename in filenames:
if not os.path.exists(filename): if not os.path.isfile(filename) and not os.path.isdir(filename):
Alert(strings._("not_a_file", True).format(filename)) Alert(strings._("not_a_file", True).format(filename))
valid = False valid = False
if not os.access(filename, os.R_OK): if not os.access(filename, os.R_OK):

View File

@ -69,7 +69,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.file_selection.file_list.add_file(filename) self.file_selection.file_list.add_file(filename)
# Server status # 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.file_selection.server_started)
self.server_status.server_started.connect(self.start_server) self.server_status.server_started.connect(self.start_server)
self.server_status.server_stopped.connect(self.file_selection.server_stopped) self.server_status.server_stopped.connect(self.file_selection.server_stopped)
@ -119,11 +119,17 @@ class OnionShareGui(QtWidgets.QMainWindow):
# Status bar, zip progress bar # Status bar, zip progress bar
self._zip_progress_bar = None 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 # Main layout
self.layout = QtWidgets.QVBoxLayout() self.layout = QtWidgets.QVBoxLayout()
self.layout.addLayout(self.file_selection) self.layout.addLayout(self.file_selection)
self.layout.addLayout(self.server_status) self.layout.addLayout(self.server_status)
self.layout.addWidget(self.filesize_warning) self.layout.addWidget(self.filesize_warning)
self.layout.addWidget(self.persistent_url_label)
self.layout.addWidget(self.downloads_container) self.layout.addWidget(self.downloads_container)
central_widget = QtWidgets.QWidget() central_widget = QtWidgets.QWidget()
central_widget.setLayout(self.layout) central_widget.setLayout(self.layout)
@ -272,7 +278,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.app.stay_open = not self.settings.get('close_after_first_download') self.app.stay_open = not self.settings.get('close_after_first_download')
# start onionshare http service in new thread # 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.daemon = True
t.start() t.start()
# wait for modules in thread to load, preventing a thread-related cx_Freeze crash # wait for modules in thread to load, preventing a thread-related cx_Freeze crash
@ -346,6 +352,9 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.stop_server() self.stop_server()
self.start_server_error(strings._('gui_server_started_after_timeout')) 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): def start_server_error(self, error):
""" """
If there's an error when trying to start the onion service If there's an error when trying to start the onion service
@ -377,6 +386,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
# Remove ephemeral service, but don't disconnect from Tor # Remove ephemeral service, but don't disconnect from Tor
self.onion.cleanup(stop_tor=False) self.onion.cleanup(stop_tor=False)
self.filesize_warning.hide() self.filesize_warning.hide()
self.persistent_url_label.hide()
self.stop_server_finished.emit() self.stop_server_finished.emit()
self.set_server_active(False) self.set_server_active(False)

View File

@ -21,7 +21,7 @@ import platform
from .alert import Alert from .alert import Alert
from PyQt5 import QtCore, QtWidgets, QtGui from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings, common from onionshare import strings, common, settings
class ServerStatus(QtWidgets.QVBoxLayout): class ServerStatus(QtWidgets.QVBoxLayout):
""" """
@ -36,7 +36,7 @@ class ServerStatus(QtWidgets.QVBoxLayout):
STATUS_WORKING = 1 STATUS_WORKING = 1
STATUS_STARTED = 2 STATUS_STARTED = 2
def __init__(self, qtapp, app, web, file_selection): def __init__(self, qtapp, app, web, file_selection, settings):
super(ServerStatus, self).__init__() super(ServerStatus, self).__init__()
self.status = self.STATUS_STOPPED self.status = self.STATUS_STOPPED
@ -45,6 +45,8 @@ class ServerStatus(QtWidgets.QVBoxLayout):
self.web = web self.web = web
self.file_selection = file_selection self.file_selection = file_selection
self.settings = settings
# Helper boolean as this is used in a few places # Helper boolean as this is used in a few places
self.timer_enabled = False self.timer_enabled = False
# Shutdown timeout layout # Shutdown timeout layout
@ -141,6 +143,11 @@ class ServerStatus(QtWidgets.QVBoxLayout):
self.url_label.show() self.url_label.show()
self.copy_url_button.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: if self.app.stealth:
self.copy_hidservauth_button.show() self.copy_hidservauth_button.show()
else: else:

View File

@ -60,10 +60,16 @@ class SettingsDialog(QtWidgets.QDialog):
self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Checked) self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Checked)
self.systray_notifications_checkbox.setText(strings._("gui_settings_systray_notifications", True)) 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 options layout
sharing_group_layout = QtWidgets.QVBoxLayout() sharing_group_layout = QtWidgets.QVBoxLayout()
sharing_group_layout.addWidget(self.close_after_first_download_checkbox) sharing_group_layout.addWidget(self.close_after_first_download_checkbox)
sharing_group_layout.addWidget(self.systray_notifications_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 = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label", True))
sharing_group.setLayout(sharing_group_layout) sharing_group.setLayout(sharing_group_layout)
@ -78,10 +84,20 @@ class SettingsDialog(QtWidgets.QDialog):
self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked)
self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) 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 options layout
stealth_group_layout = QtWidgets.QVBoxLayout() stealth_group_layout = QtWidgets.QVBoxLayout()
stealth_group_layout.addWidget(stealth_details) stealth_group_layout.addWidget(stealth_details)
stealth_group_layout.addWidget(self.stealth_checkbox) 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 = QtWidgets.QGroupBox(strings._("gui_settings_stealth_label", True))
stealth_group.setLayout(stealth_group_layout) stealth_group.setLayout(stealth_group_layout)
@ -98,6 +114,9 @@ class SettingsDialog(QtWidgets.QDialog):
# Check for updates button # Check for updates button
self.check_for_updates_button = QtWidgets.QPushButton(strings._('gui_settings_autoupdate_check_button', True)) self.check_for_updates_button = QtWidgets.QPushButton(strings._('gui_settings_autoupdate_check_button', True))
self.check_for_updates_button.clicked.connect(self.check_for_updates) self.check_for_updates_button.clicked.connect(self.check_for_updates)
# We can't check for updates if not connected to Tor
if not self.onion.connected_to_tor:
self.check_for_updates_button.setEnabled(False)
# Autoupdate options layout # Autoupdate options layout
autoupdate_group_layout = QtWidgets.QVBoxLayout() autoupdate_group_layout = QtWidgets.QVBoxLayout()
@ -329,9 +348,18 @@ class SettingsDialog(QtWidgets.QDialog):
else: else:
self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Unchecked) self.systray_notifications_checkbox.setCheckState(QtCore.Qt.Unchecked)
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)
use_stealth = self.old_settings.get('use_stealth') use_stealth = self.old_settings.get('use_stealth')
if use_stealth: if use_stealth:
self.stealth_checkbox.setCheckState(QtCore.Qt.Checked) self.stealth_checkbox.setCheckState(QtCore.Qt.Checked)
if save_private_key:
hidservauth_details.show()
self.hidservauth_copy_button.show()
else: else:
self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked)
@ -475,6 +503,15 @@ class SettingsDialog(QtWidgets.QDialog):
else: else:
self.authenticate_password_extras.hide() 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): def test_tor_clicked(self):
""" """
Test Tor Settings button clicked. With the given settings, see if we can Test Tor Settings button clicked. With the given settings, see if we can
@ -631,7 +668,21 @@ class SettingsDialog(QtWidgets.QDialog):
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('systray_notifications', self.systray_notifications_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('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()) 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(): if self.connection_type_bundled_radio.isChecked():
settings.set('connection_type', 'bundled') settings.set('connection_type', 'bundled')
@ -727,8 +778,11 @@ class SettingsDialog(QtWidgets.QDialog):
def _enable_buttons(self): def _enable_buttons(self):
common.log('SettingsDialog', '_enable_buttons') common.log('SettingsDialog', '_enable_buttons')
# We can't check for updates if we're still not connected to Tor
self.check_for_updates_button.setEnabled(True) if not self.onion.connected_to_tor:
self.check_for_updates_button.setEnabled(False)
else:
self.check_for_updates_button.setEnabled(True)
self.connection_type_test_button.setEnabled(True) self.connection_type_test_button.setEnabled(True)
self.save_button.setEnabled(True) self.save_button.setEnabled(True)
self.cancel_button.setEnabled(True) self.cancel_button.setEnabled(True)

View File

@ -8,7 +8,7 @@
"give_this_url": "Give this URL to the person you're sending the file to:", "give_this_url": "Give this URL to the person you're sending the file to:",
"give_this_url_stealth": "Give this URL and HidServAuth line to the person you're sending the file to:", "give_this_url_stealth": "Give this URL and HidServAuth line to the person you're sending the file to:",
"ctrlc_to_stop": "Press Ctrl-C to stop server", "ctrlc_to_stop": "Press Ctrl-C to stop server",
"not_a_file": "{0:s} is not a file.", "not_a_file": "{0:s} is not a valid file.",
"not_a_readable_file": "{0:s} is not a readable file.", "not_a_readable_file": "{0:s} is not a readable file.",
"no_available_port": "Could not start the Onion service as there was no available port.", "no_available_port": "Could not start the Onion service as there was no available port.",
"download_page_loaded": "Download page loaded", "download_page_loaded": "Download page loaded",
@ -67,6 +67,7 @@
"gui_settings_stealth_label": "Stealth (advanced)", "gui_settings_stealth_label": "Stealth (advanced)",
"gui_settings_stealth_option": "Create stealth onion services", "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.<br><a href=\"https://github.com/micahflee/onionshare/wiki/Stealth-Onion-Services\">More information</a>.", "gui_settings_stealth_option_details": "This makes OnionShare more secure, but also more difficult for the recipient to connect to it.<br><a href=\"https://github.com/micahflee/onionshare/wiki/Stealth-Onion-Services\">More information</a>.",
"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_label": "Check for updates",
"gui_settings_autoupdate_option": "Notify me when updates are available", "gui_settings_autoupdate_option": "Notify me when updates are available",
"gui_settings_autoupdate_timestamp": "Last checked: {}", "gui_settings_autoupdate_timestamp": "Last checked: {}",
@ -128,5 +129,7 @@
"gui_tor_connection_lost": "Disconnected from Tor.", "gui_tor_connection_lost": "Disconnected from Tor.",
"gui_server_started_after_timeout": "The server started after your chosen auto-timeout.\nPlease start a new share.", "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.", "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_checkbox": "Use a persistent URL\n(unchecking will delete any saved URL)",
"persistent_url_in_use": "This share is using a persistent URL"
} }

View File

@ -27,6 +27,7 @@ from onionshare import OnionShare
class MyOnion: class MyOnion:
def __init__(self, stealth=False): def __init__(self, stealth=False):
self.auth_string = 'TestHidServAuth' self.auth_string = 'TestHidServAuth'
self.private_key = ''
self.stealth = stealth self.stealth = stealth
@staticmethod @staticmethod

View File

@ -60,7 +60,11 @@ class TestSettings:
'autoupdate_timestamp': None, 'autoupdate_timestamp': None,
'no_bridges': True, 'no_bridges': True,
'tor_bridges_use_obfs4': False, 'tor_bridges_use_obfs4': False,
'tor_bridges_use_custom_bridges': '' 'tor_bridges_use_custom_bridges': '',
'save_private_key': False,
'private_key': '',
'slug': '',
'hidservauth_string': ''
} }
def test_fill_in_defaults(self, settings_obj): def test_fill_in_defaults(self, settings_obj):