diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index ee340884..13c62552 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -25,6 +25,7 @@ from onionshare.settings import Settings from onionshare.onion import * from .alert import Alert +from .tor_dialog import TorDialog class SettingsDialog(QtWidgets.QDialog): """ @@ -284,14 +285,20 @@ class SettingsDialog(QtWidgets.QDialog): """ settings = self.settings_from_fields() - try: - onion = Onion(settings=settings) + # If using bundled Tor, first connect to Tor + if settings.get('connection_type') == 'bundled': + tor_dialog = TorDialog() + tor_dialog.start() - # 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)) + else: + try: + onion = Onion(settings=settings) - except (TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorNotSupported) as e: - Alert(e.args[0], QtWidgets.QMessageBox.Warning) + # 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)) + + except (TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorNotSupported) as e: + Alert(e.args[0], QtWidgets.QMessageBox.Warning) def save_clicked(self): """ diff --git a/onionshare_gui/tor_dialog.py b/onionshare_gui/tor_dialog.py new file mode 100644 index 00000000..defd82ae --- /dev/null +++ b/onionshare_gui/tor_dialog.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" +OnionShare | https://onionshare.org/ + +Copyright (C) 2017 Micah Lee + +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 . +""" +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 diff --git a/share/locale/en.json b/share/locale/en.json index 29b2bad3..c0db438b 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -87,5 +87,8 @@ "settings_error_unreadable_cookie_file": "Connected to Tor controller, but can't authenticate because your password may be wrong, and your user doesn't have permission to read the cookie file.", "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: {}", - "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" }