mirror of
https://github.com/onionshare/onionshare.git
synced 2025-02-04 17:05:31 -05:00
Fix conflicts arising from the reuse_private_key branch
This commit is contained in:
commit
76cf0ae107
14
README.md
14
README.md
@ -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)
|
||||||
|
@ -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()))))
|
||||||
|
@ -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()))))
|
||||||
|
@ -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))
|
||||||
|
@ -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:
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user