In the settings dialog, show Tor connection status when the connection type is bundled

This commit is contained in:
Micah Lee 2017-04-13 22:56:47 -07:00
parent 169be518eb
commit 80d475f65b
No known key found for this signature in database
GPG Key ID: 403C2657CD994F73
6 changed files with 55 additions and 120 deletions

View File

@ -98,8 +98,16 @@ class Onion(object):
Onion is an abstraction layer for connecting to the Tor control port and Onion is an abstraction layer for connecting to the Tor control port and
creating onion services. OnionShare supports creating onion services by creating onion services. OnionShare supports creating onion services by
connecting to the Tor controller and using ADD_ONION, DEL_ONION. connecting to the Tor controller and using ADD_ONION, DEL_ONION.
stealth: Should the onion service be stealth?
settings: A Settings object. If it's not passed in, load from disk.
bundled_connection_func: If the tor connection type is bundled, optionally
call this function and pass in a status string while connecting to tor. This
is necessary for status updates to reach the GUI.
""" """
def __init__(self, stealth=False, settings=False): def __init__(self, stealth=False, settings=False, bundled_tor_func=None):
self.stealth = stealth self.stealth = stealth
self.service_id = None self.service_id = None
@ -172,6 +180,11 @@ class Onion(object):
# "\033[K" clears the rest of the line # "\033[K" clears the rest of the line
print("{}: {}% - {}{}".format(strings._('connecting_to_tor'), progress, summary, "\033[K"), end="\r") print("{}: {}% - {}{}".format(strings._('connecting_to_tor'), progress, summary, "\033[K"), end="\r")
if callable(bundled_tor_func):
status_string = "{}% - {}".format(progress, summary)
bundled_tor_func(status_string)
if summary == 'Done': if summary == 'Done':
print("") print("")
break break

View File

@ -72,7 +72,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.setWindowIcon(window_icon) self.setWindowIcon(window_icon)
# the menu bar # the menu bar
self.setMenuBar(Menu()) self.setMenuBar(Menu(self.qtapp))
def send_files(self, filenames=None): def send_files(self, filenames=None):
""" """

View File

@ -26,8 +26,9 @@ class Menu(QtWidgets.QMenuBar):
""" """
OnionShare's menu bar. OnionShare's menu bar.
""" """
def __init__(self, parent=None): def __init__(self, qtapp):
super(Menu, self).__init__(parent) super(Menu, self).__init__()
self.qtapp = qtapp
file_menu = self.addMenu(strings._('gui_menu_file_menu', True)) file_menu = self.addMenu(strings._('gui_menu_file_menu', True))
@ -40,7 +41,7 @@ class Menu(QtWidgets.QMenuBar):
""" """
Settings action triggered. Settings action triggered.
""" """
SettingsDialog() SettingsDialog(self.qtapp)
def quit(self): def quit(self):
""" """

View File

@ -30,8 +30,9 @@ class SettingsDialog(QtWidgets.QDialog):
""" """
Settings dialog. Settings dialog.
""" """
def __init__(self, parent=None): def __init__(self, qtapp):
super(SettingsDialog, self).__init__(parent) super(SettingsDialog, self).__init__()
self.qtapp = qtapp
self.setModal(True) self.setModal(True)
self.setWindowTitle(strings._('gui_settings_window_title', True)) self.setWindowTitle(strings._('gui_settings_window_title', True))
@ -154,16 +155,21 @@ class SettingsDialog(QtWidgets.QDialog):
# Buttons # Buttons
test_button = QtWidgets.QPushButton(strings._('gui_settings_button_test', True)) self.test_button = QtWidgets.QPushButton(strings._('gui_settings_button_test', True))
test_button.clicked.connect(self.test_clicked) self.test_button.clicked.connect(self.test_clicked)
save_button = QtWidgets.QPushButton(strings._('gui_settings_button_save', True)) self.save_button = QtWidgets.QPushButton(strings._('gui_settings_button_save', True))
save_button.clicked.connect(self.save_clicked) self.save_button.clicked.connect(self.save_clicked)
cancel_button = QtWidgets.QPushButton(strings._('gui_settings_button_cancel', True)) self.cancel_button = QtWidgets.QPushButton(strings._('gui_settings_button_cancel', True))
cancel_button.clicked.connect(self.cancel_clicked) self.cancel_button.clicked.connect(self.cancel_clicked)
buttons_layout = QtWidgets.QHBoxLayout() buttons_layout = QtWidgets.QHBoxLayout()
buttons_layout.addWidget(test_button) buttons_layout.addWidget(self.test_button)
buttons_layout.addWidget(save_button) buttons_layout.addWidget(self.save_button)
buttons_layout.addWidget(cancel_button) buttons_layout.addWidget(self.cancel_button)
# Tor networkconnection status
self.tor_status = QtWidgets.QLabel()
self.tor_status.setStyleSheet('color: #ff0000; padding-top: 10px')
self.tor_status.hide()
# Layout # Layout
layout = QtWidgets.QVBoxLayout() layout = QtWidgets.QVBoxLayout()
@ -173,6 +179,7 @@ class SettingsDialog(QtWidgets.QDialog):
layout.addWidget(self.authenticate_group) layout.addWidget(self.authenticate_group)
layout.addStretch() layout.addStretch()
layout.addLayout(buttons_layout) layout.addLayout(buttons_layout)
layout.addWidget(self.tor_status)
self.setLayout(layout) self.setLayout(layout)
@ -279,7 +286,24 @@ class SettingsDialog(QtWidgets.QDialog):
settings = self.settings_from_fields() settings = self.settings_from_fields()
try: try:
onion = Onion(settings=settings) # Create dialog for showing Tor connection status
if settings.get('connection_type') == 'bundled':
self.tor_status.show()
self.test_button.setEnabled(False)
self.save_button.setEnabled(False)
self.cancel_button.setEnabled(False)
def bundled_tor_func(message):
self.tor_status.setText('<strong>{}</strong><br>{}'.format(strings._('connecting_to_tor'), message))
self.qtapp.processEvents()
if 'Done' in message:
self.tor_status.hide()
self.test_button.setEnabled(True)
self.save_button.setEnabled(True)
self.cancel_button.setEnabled(True)
else:
bundled_tor_func = None
onion = Onion(settings=settings, bundled_tor_func=bundled_tor_func)
# If an exception hasn't been raised yet, the Tor settings work # If an exception hasn't been raised yet, the Tor settings work
Alert(strings._('settings_test_success', True).format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth)) Alert(strings._('settings_test_success', True).format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth))

View File

@ -1,100 +0,0 @@
# -*- coding: utf-8 -*-
"""
OnionShare | https://onionshare.org/
Copyright (C) 2017 Micah Lee <micah@micahflee.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from PyQt5 import QtCore, QtWidgets, QtGui
import platform, subprocess, sys, os
from onionshare import strings
class TorSubprocess(QtCore.QThread):
log = QtCore.pyqtSignal(str)
def __init__(self):
super(TorSubprocess, self).__init__()
# Find the path to the Tor binary
s = platform.system()
if s == 'Windows':
root_path = os.path.dirname(os.path.abspath(sys.argv[0]))
self.tor_path = os.path.join(os.path.join(os.path.join(root_path, 'tor'), 'Tor'), 'tor.exe')
elif s == 'Darwin':
# TODO: not implemented yet
pass
def run(self):
# Open tor in a subprocess and monitor its output
p = subprocess.Popen([self.tor_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while True:
line = p.stdout.readline()
if line != '':
self.log.emit(line.decode())
else:
break
class TorDialog(QtWidgets.QDialog):
"""
Tor dialog.
"""
def __init__(self, parent=None):
super(TorDialog, self).__init__(parent)
self.setModal(False)
self.setWindowTitle(strings._('gui_tor_window_title', True))
# Tor log
self.log = QtWidgets.QTextEdit()
self.log.setReadOnly(True)
# Buttons
close_button = QtWidgets.QPushButton(strings._('gui_tor_button_close', True))
close_button.clicked.connect(self.close_clicked)
restart_button = QtWidgets.QPushButton(strings._('gui_tor_button_restart', True))
restart_button.clicked.connect(self.restart_clicked)
buttons_layout = QtWidgets.QHBoxLayout()
buttons_layout.addWidget(close_button)
buttons_layout.addWidget(restart_button)
# Layout
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.log)
layout.addLayout(buttons_layout)
self.setLayout(layout)
def start(self):
# Start the Tor subprocess
self.t = TorSubprocess()
self.t.log.connect(self.log_line)
self.t.start()
# Show the dialog
self.exec_()
def log_line(self, line):
self.log.moveCursor(QtGui.QTextCursor.End)
self.log.insertPlainText(line)
self.log.moveCursor(QtGui.QTextCursor.End)
def close_clicked(self):
"""
Hide the Tor dialog.
"""
self.hide()
def restart_clicked(self):
pass

View File

@ -88,8 +88,5 @@
"settings_error_bundled_tor_not_supported": "Bundled Tor is only supported in Windows and macOS. It's not supported when not using developer mode or the command line interface.", "settings_error_bundled_tor_not_supported": "Bundled Tor is only supported in Windows and macOS. It's not supported when not using developer mode or the command line interface.",
"settings_test_success": "Congratulations, OnionShare can connect to the Tor controller.\n\nTor version: {}\nSupports ephemeral onion services: {}\nSupports stealth onion services: {}", "settings_test_success": "Congratulations, OnionShare can connect to the Tor controller.\n\nTor version: {}\nSupports ephemeral onion services: {}\nSupports stealth onion services: {}",
"error_tor_protocol_error": "Error talking to the Tor controller.\nIf you're using Whonix, check out https://www.whonix.org/wiki/onionshare to make OnionShare work.", "error_tor_protocol_error": "Error talking to the Tor controller.\nIf you're using Whonix, check out https://www.whonix.org/wiki/onionshare to make OnionShare work.",
"gui_tor_window_title": "Tor Connection",
"gui_tor_button_close": "Close",
"gui_tor_button_restart": "Restart Tor",
"connecting_to_tor": "Connecting to the Tor network" "connecting_to_tor": "Connecting to the Tor network"
} }