diff --git a/onionshare/onion.py b/onionshare/onion.py index 10181536..2bcb79b6 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -98,8 +98,16 @@ class Onion(object): Onion is an abstraction layer for connecting to the Tor control port and creating onion services. OnionShare supports creating onion services by 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.service_id = None @@ -172,6 +180,11 @@ class Onion(object): # "\033[K" clears the rest of the line 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': print("") break diff --git a/onionshare_gui/__init__.py b/onionshare_gui/__init__.py index 3b7f43f7..a1a7ea26 100644 --- a/onionshare_gui/__init__.py +++ b/onionshare_gui/__init__.py @@ -72,7 +72,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.setWindowIcon(window_icon) # the menu bar - self.setMenuBar(Menu()) + self.setMenuBar(Menu(self.qtapp)) def send_files(self, filenames=None): """ diff --git a/onionshare_gui/menu.py b/onionshare_gui/menu.py index eb9c948e..771fad67 100644 --- a/onionshare_gui/menu.py +++ b/onionshare_gui/menu.py @@ -26,8 +26,9 @@ class Menu(QtWidgets.QMenuBar): """ OnionShare's menu bar. """ - def __init__(self, parent=None): - super(Menu, self).__init__(parent) + def __init__(self, qtapp): + super(Menu, self).__init__() + self.qtapp = qtapp file_menu = self.addMenu(strings._('gui_menu_file_menu', True)) @@ -40,7 +41,7 @@ class Menu(QtWidgets.QMenuBar): """ Settings action triggered. """ - SettingsDialog() + SettingsDialog(self.qtapp) def quit(self): """ diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 6cb6cdff..749fed48 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -30,8 +30,9 @@ class SettingsDialog(QtWidgets.QDialog): """ Settings dialog. """ - def __init__(self, parent=None): - super(SettingsDialog, self).__init__(parent) + def __init__(self, qtapp): + super(SettingsDialog, self).__init__() + self.qtapp = qtapp self.setModal(True) self.setWindowTitle(strings._('gui_settings_window_title', True)) @@ -154,16 +155,21 @@ class SettingsDialog(QtWidgets.QDialog): # Buttons - test_button = QtWidgets.QPushButton(strings._('gui_settings_button_test', True)) - test_button.clicked.connect(self.test_clicked) - save_button = QtWidgets.QPushButton(strings._('gui_settings_button_save', True)) - save_button.clicked.connect(self.save_clicked) - cancel_button = QtWidgets.QPushButton(strings._('gui_settings_button_cancel', True)) - cancel_button.clicked.connect(self.cancel_clicked) + self.test_button = QtWidgets.QPushButton(strings._('gui_settings_button_test', True)) + self.test_button.clicked.connect(self.test_clicked) + self.save_button = QtWidgets.QPushButton(strings._('gui_settings_button_save', True)) + self.save_button.clicked.connect(self.save_clicked) + self.cancel_button = QtWidgets.QPushButton(strings._('gui_settings_button_cancel', True)) + self.cancel_button.clicked.connect(self.cancel_clicked) buttons_layout = QtWidgets.QHBoxLayout() - buttons_layout.addWidget(test_button) - buttons_layout.addWidget(save_button) - buttons_layout.addWidget(cancel_button) + buttons_layout.addWidget(self.test_button) + buttons_layout.addWidget(self.save_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 = QtWidgets.QVBoxLayout() @@ -173,6 +179,7 @@ class SettingsDialog(QtWidgets.QDialog): layout.addWidget(self.authenticate_group) layout.addStretch() layout.addLayout(buttons_layout) + layout.addWidget(self.tor_status) self.setLayout(layout) @@ -279,7 +286,24 @@ class SettingsDialog(QtWidgets.QDialog): settings = self.settings_from_fields() 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('{}
{}'.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 Alert(strings._('settings_test_success', True).format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth)) diff --git a/onionshare_gui/tor_dialog.py b/onionshare_gui/tor_dialog.py deleted file mode 100644 index defd82ae..00000000 --- a/onionshare_gui/tor_dialog.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- 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 e50f93e6..d4743cf4 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -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_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.", - "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" }