summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/gui/mainwindow.py
diff options
context:
space:
mode:
authorTomás Touceda <chiiph@leap.se>2014-05-16 16:18:59 -0300
committerTomás Touceda <chiiph@leap.se>2014-05-16 16:18:59 -0300
commit4c550c558dcb554b3ea1bc0246492e39e8532886 (patch)
tree3a18999e62eb18091d002e6c1c7222846a92947b /src/leap/bitmask/gui/mainwindow.py
parent81715dc47d77934c4f67d2527a56c28f58f0345d (diff)
parentd52daa106a97562e1cc67c5a2f242f2ef9884508 (diff)
Merge branch 'release-0.5.1'0.5.1
Diffstat (limited to 'src/leap/bitmask/gui/mainwindow.py')
-rw-r--r--src/leap/bitmask/gui/mainwindow.py910
1 files changed, 507 insertions, 403 deletions
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index 5abfaa67..e3848c46 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -19,6 +19,7 @@ Main window for Bitmask.
"""
import logging
import socket
+import time
from threading import Condition
from datetime import datetime
@@ -26,7 +27,6 @@ from datetime import datetime
from PySide import QtCore, QtGui
from zope.proxy import ProxyBase, setProxiedObject
from twisted.internet import reactor, threads
-from twisted.internet.defer import CancelledError
from leap.bitmask import __version__ as VERSION
from leap.bitmask import __version_hash__ as VERSION_HASH
@@ -34,19 +34,16 @@ from leap.bitmask.config import flags
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
-from leap.bitmask.crypto import srpauth
-from leap.bitmask.crypto.srpauth import SRPAuth
-
-from leap.bitmask.gui.loggerwindow import LoggerWindow
+from leap.bitmask.gui import statemachines
from leap.bitmask.gui.advanced_key_management import AdvancedKeyManagement
-from leap.bitmask.gui.login import LoginWidget
-from leap.bitmask.gui.preferenceswindow import PreferencesWindow
from leap.bitmask.gui.eip_preferenceswindow import EIPPreferencesWindow
-from leap.bitmask.gui import statemachines
from leap.bitmask.gui.eip_status import EIPStatusWidget
+from leap.bitmask.gui.loggerwindow import LoggerWindow
+from leap.bitmask.gui.login import LoginWidget
from leap.bitmask.gui.mail_status import MailStatusWidget
-from leap.bitmask.gui.wizard import Wizard
+from leap.bitmask.gui.preferenceswindow import PreferencesWindow
from leap.bitmask.gui.systray import SysTray
+from leap.bitmask.gui.wizard import Wizard
from leap.bitmask import provider
from leap.bitmask.platform_init import IS_WIN, IS_MAC, IS_LINUX
@@ -59,20 +56,7 @@ from leap.bitmask.services import get_service_display_name
from leap.bitmask.services.mail import conductor as mail_conductor
from leap.bitmask.services import EIP_SERVICE, MX_SERVICE
-from leap.bitmask.services.eip import eipconfig
-from leap.bitmask.services.eip import get_openvpn_management
-from leap.bitmask.services.eip.eipbootstrapper import EIPBootstrapper
from leap.bitmask.services.eip.connection import EIPConnection
-from leap.bitmask.services.eip.vpnprocess import VPN
-from leap.bitmask.services.eip.vpnprocess import OpenVPNAlreadyRunning
-from leap.bitmask.services.eip.vpnprocess import AlienOpenVPNAlreadyRunning
-
-from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
-from leap.bitmask.services.eip.vpnlauncher import OpenVPNNotFoundException
-from leap.bitmask.services.eip.linuxvpnlauncher import EIPNoPkexecAvailable
-from leap.bitmask.services.eip.linuxvpnlauncher import \
- EIPNoPolkitAuthAgentAvailable
-from leap.bitmask.services.eip.darwinvpnlauncher import EIPNoTunKextLoaded
from leap.bitmask.services.soledad.soledadbootstrapper import \
SoledadBootstrapper
@@ -99,11 +83,6 @@ class MainWindow(QtGui.QMainWindow):
"""
Main window for login and presenting status updates to the user
"""
-
- # StackedWidget indexes
- LOGIN_INDEX = 0
- EIP_STATUS_INDEX = 1
-
# Signals
eip_needs_login = QtCore.Signal([])
offline_mode_bypass_login = QtCore.Signal([])
@@ -122,9 +101,7 @@ class MainWindow(QtGui.QMainWindow):
# We give each service some time to come to a halt before forcing quit
SERVICE_STOP_TIMEOUT = 20
- def __init__(self, quit_callback,
- openvpn_verb=1,
- bypass_checks=False):
+ def __init__(self, quit_callback, bypass_checks=False, start_hidden=False):
"""
Constructor for the client main window
@@ -132,10 +109,12 @@ class MainWindow(QtGui.QMainWindow):
the application.
:type quit_callback: callable
- :param bypass_checks: Set to true if the app should bypass
- first round of checks for CA
- certificates at bootstrap
+ :param bypass_checks: Set to true if the app should bypass first round
+ of checks for CA certificates at bootstrap
:type bypass_checks: bool
+ :param start_hidden: Set to true if the app should not show the window
+ but just the tray.
+ :type start_hidden: bool
"""
QtGui.QMainWindow.__init__(self)
@@ -190,31 +169,30 @@ class MainWindow(QtGui.QMainWindow):
# XXX this should be handled by EIP Conductor
self._eip_connection.qtsigs.connecting_signal.connect(
- self._start_eip)
+ self._start_EIP)
self._eip_connection.qtsigs.disconnecting_signal.connect(
self._stop_eip)
self._eip_status.eip_connection_connected.connect(
- self._on_eip_connected)
+ self._on_eip_connection_connected)
self._eip_status.eip_connection_connected.connect(
self._maybe_run_soledad_setup_checks)
self.offline_mode_bypass_login.connect(
self._maybe_run_soledad_setup_checks)
- self.eip_needs_login.connect(
- self._eip_status.disable_eip_start)
- self.eip_needs_login.connect(
- self._disable_eip_start_action)
+ self.eip_needs_login.connect(self._eip_status.disable_eip_start)
+ self.eip_needs_login.connect(self._disable_eip_start_action)
+
+ self._trying_to_start_eip = False
# This is loaded only once, there's a bug when doing that more
# than once
# XXX HACK!! But we need it as long as we are using
# provider_config in here
- self._provider_config = (
- self._backend._components["provider"]._provider_config)
+ self._provider_config = self._backend.get_provider_config()
+
# Used for automatic start of EIP
self._provisional_provider_config = ProviderConfig()
- self._eip_config = eipconfig.EIPConfig()
self._already_started_eip = False
self._already_started_soledad = False
@@ -224,35 +202,9 @@ class MainWindow(QtGui.QMainWindow):
self._logged_user = None
self._logged_in_offline = False
+ self._backend_connected_signals = {}
self._backend_connect()
- # This thread is similar to the provider bootstrapper
- self._eip_bootstrapper = EIPBootstrapper()
-
- # EIP signals ---- move to eip conductor.
- # TODO change the name of "download_config" signal to
- # something less confusing (config_ready maybe)
- self._eip_bootstrapper.download_config.connect(
- self._eip_intermediate_stage)
- self._eip_bootstrapper.download_client_certificate.connect(
- self._finish_eip_bootstrap)
-
- self._vpn = VPN(openvpn_verb=openvpn_verb)
-
- # connect vpn process signals
- self._vpn.qtsigs.state_changed.connect(
- self._eip_status.update_vpn_state)
- self._vpn.qtsigs.status_changed.connect(
- self._eip_status.update_vpn_status)
- self._vpn.qtsigs.process_finished.connect(
- self._eip_finished)
- self._vpn.qtsigs.network_unreachable.connect(
- self._on_eip_network_unreachable)
- self._vpn.qtsigs.process_restart_tls.connect(
- self._do_eip_restart)
- self._vpn.qtsigs.process_restart_ping.connect(
- self._do_eip_restart)
-
self._soledad_bootstrapper = SoledadBootstrapper()
self._soledad_bootstrapper.download_config.connect(
self._soledad_intermediate_stage)
@@ -260,8 +212,6 @@ class MainWindow(QtGui.QMainWindow):
self._soledad_bootstrapped_stage)
self._soledad_bootstrapper.local_only_ready.connect(
self._soledad_bootstrapped_stage)
- self._soledad_bootstrapper.soledad_timeout.connect(
- self._retry_soledad_connection)
self._soledad_bootstrapper.soledad_invalid_auth_token.connect(
self._mail_status.set_soledad_invalid_auth_token)
self._soledad_bootstrapper.soledad_failed.connect(
@@ -307,6 +257,8 @@ class MainWindow(QtGui.QMainWindow):
# self.ui.btnEIPPreferences.clicked.connect(self._show_eip_preferences)
self._enabled_services = []
+ self._ui_mx_visible = True
+ self._ui_eip_visible = True
# last minute UI manipulations
@@ -342,6 +294,7 @@ class MainWindow(QtGui.QMainWindow):
self._logger_window = None
self._bypass_checks = bypass_checks
+ self._start_hidden = start_hidden
# We initialize Soledad and Keymanager instances as
# transparent proxies, so we can pass the reference freely
@@ -349,7 +302,6 @@ class MainWindow(QtGui.QMainWindow):
self._soledad = ProxyBase(None)
self._keymanager = ProxyBase(None)
- self._login_defer = None
self._soledad_defer = None
self._mail_conductor = mail_conductor.MailConductor(
@@ -372,7 +324,7 @@ class MainWindow(QtGui.QMainWindow):
if self._first_run():
self._wizard_firstrun = True
- self._backend_disconnect()
+ self._disconnect_and_untrack()
self._wizard = Wizard(backend=self._backend,
bypass_checks=bypass_checks)
# Give this window time to finish init and then show the wizard
@@ -384,46 +336,151 @@ class MainWindow(QtGui.QMainWindow):
# so this has to be done after eip_machine is started
self._finish_init()
+ def _not_logged_in_error(self):
+ """
+ Handle the 'not logged in' backend error if we try to do an operation
+ that requires to be logged in.
+ """
+ logger.critical("You are trying to do an operation that requires "
+ "log in first.")
+ QtGui.QMessageBox.critical(
+ self, self.tr("Application error"),
+ self.tr("You are trying to do an operation "
+ "that requires logging in first."))
+
+ def _connect_and_track(self, signal, method):
+ """
+ Helper to connect signals and keep track of them.
+
+ :param signal: the signal to connect to.
+ :type signal: QtCore.Signal
+ :param method: the method to call when the signal is triggered.
+ :type method: callable, Slot or Signal
+ """
+ self._backend_connected_signals[signal] = method
+ signal.connect(method)
+
+ def _backend_bad_call(self, data):
+ """
+ Callback for debugging bad backend calls
+
+ :param data: data from the backend about the problem
+ :type data: str
+ """
+ logger.error("Bad call to the backend:")
+ logger.error(data)
+
def _backend_connect(self):
"""
Helper to connect to backend signals
"""
sig = self._backend.signaler
- sig.prov_name_resolution.connect(self._intermediate_stage)
- sig.prov_https_connection.connect(self._intermediate_stage)
- sig.prov_download_ca_cert.connect(self._intermediate_stage)
- sig.prov_download_provider_info.connect(self._load_provider_config)
- sig.prov_check_api_certificate.connect(self._provider_config_loaded)
+ sig.backend_bad_call.connect(self._backend_bad_call)
+
+ self._connect_and_track(sig.prov_name_resolution,
+ self._intermediate_stage)
+ self._connect_and_track(sig.prov_https_connection,
+ self._intermediate_stage)
+ self._connect_and_track(sig.prov_download_ca_cert,
+ self._intermediate_stage)
+
+ self._connect_and_track(sig.prov_download_provider_info,
+ self._load_provider_config)
+ self._connect_and_track(sig.prov_check_api_certificate,
+ self._provider_config_loaded)
+
+ self._connect_and_track(sig.prov_problem_with_provider,
+ self._login_problem_provider)
+
+ self._connect_and_track(sig.prov_cancelled_setup,
+ self._set_login_cancelled)
+
+ # Login signals
+ self._connect_and_track(sig.srp_auth_ok, self._authentication_finished)
+
+ auth_error = (
+ lambda: self._authentication_error(self.tr("Unknown error.")))
+ self._connect_and_track(sig.srp_auth_error, auth_error)
- # Only used at login, no need to disconnect this like we do
- # with the other
- sig.prov_problem_with_provider.connect(self._login_problem_provider)
+ auth_server_error = (
+ lambda: self._authentication_error(
+ self.tr("There was a server problem with authentication.")))
+ self._connect_and_track(sig.srp_auth_server_error, auth_server_error)
+ auth_connection_error = (
+ lambda: self._authentication_error(
+ self.tr("Could not establish a connection.")))
+ self._connect_and_track(sig.srp_auth_connection_error,
+ auth_connection_error)
+
+ auth_bad_user_or_password = (
+ lambda: self._authentication_error(
+ self.tr("Invalid username or password.")))
+ self._connect_and_track(sig.srp_auth_bad_user_or_password,
+ auth_bad_user_or_password)
+
+ # Logout signals
+ self._connect_and_track(sig.srp_logout_ok, self._logout_ok)
+ self._connect_and_track(sig.srp_logout_error, self._logout_error)
+
+ self._connect_and_track(sig.srp_not_logged_in_error,
+ self._not_logged_in_error)
+
+ # EIP bootstrap signals
+ self._connect_and_track(sig.eip_config_ready,
+ self._eip_intermediate_stage)
+ self._connect_and_track(sig.eip_client_certificate_ready,
+ self._finish_eip_bootstrap)
+
+ # We don't want to disconnect some signals so don't track them:
sig.prov_unsupported_client.connect(self._needs_update)
sig.prov_unsupported_api.connect(self._incompatible_api)
- sig.prov_cancelled_setup.connect(self._set_login_cancelled)
-
- def _backend_disconnect(self):
- """
- Helper to disconnect from backend signals.
+ # EIP start signals
+ sig.eip_openvpn_already_running.connect(
+ self._on_eip_openvpn_already_running)
+ sig.eip_alien_openvpn_already_running.connect(
+ self._on_eip_alien_openvpn_already_running)
+ sig.eip_openvpn_not_found_error.connect(
+ self._on_eip_openvpn_not_found_error)
+ sig.eip_vpn_launcher_exception.connect(
+ self._on_eip_vpn_launcher_exception)
+ sig.eip_no_polkit_agent_error.connect(
+ self._on_eip_no_polkit_agent_error)
+ sig.eip_no_pkexec_error.connect(self._on_eip_no_pkexec_error)
+ sig.eip_no_tun_kext_error.connect(self._on_eip_no_tun_kext_error)
+
+ sig.eip_state_changed.connect(self._eip_status.update_vpn_state)
+ sig.eip_status_changed.connect(self._eip_status.update_vpn_status)
+ sig.eip_process_finished.connect(self._eip_finished)
+ sig.eip_network_unreachable.connect(self._on_eip_network_unreachable)
+ sig.eip_process_restart_tls.connect(self._do_eip_restart)
+ sig.eip_process_restart_ping.connect(self._do_eip_restart)
+
+ sig.eip_can_start.connect(self._backend_can_start_eip)
+ sig.eip_cannot_start.connect(self._backend_cannot_start_eip)
+
+ def _disconnect_and_untrack(self):
+ """
+ Helper to disconnect the tracked signals.
Some signals are emitted from the wizard, and we want to
ignore those.
"""
- sig = self._backend.signaler
- sig.prov_name_resolution.disconnect(self._intermediate_stage)
- sig.prov_https_connection.disconnect(self._intermediate_stage)
- sig.prov_download_ca_cert.disconnect(self._intermediate_stage)
+ for signal, method in self._backend_connected_signals.items():
+ try:
+ signal.disconnect(method)
+ except RuntimeError:
+ pass # Signal was not connected
- sig.prov_download_provider_info.disconnect(self._load_provider_config)
- sig.prov_check_api_certificate.disconnect(self._provider_config_loaded)
+ self._backend_connected_signals = {}
+ @QtCore.Slot()
def _rejected_wizard(self):
"""
- SLOT
- TRIGGERS: self._wizard.rejected
+ TRIGGERS:
+ self._wizard.rejected
Called if the wizard has been cancelled or closed before
finishing.
@@ -444,12 +501,12 @@ class MainWindow(QtGui.QMainWindow):
if self._wizard_firstrun:
self._finish_init()
+ @QtCore.Slot()
def _launch_wizard(self):
"""
- SLOT
TRIGGERS:
- self._login_widget.show_wizard
- self.ui.action_wizard.triggered
+ self._login_widget.show_wizard
+ self.ui.action_wizard.triggered
Also called in first run.
@@ -457,7 +514,7 @@ class MainWindow(QtGui.QMainWindow):
there.
"""
if self._wizard is None:
- self._backend_disconnect()
+ self._disconnect_and_untrack()
self._wizard = Wizard(backend=self._backend,
bypass_checks=self._bypass_checks)
self._wizard.accepted.connect(self._finish_init)
@@ -472,11 +529,11 @@ class MainWindow(QtGui.QMainWindow):
self._wizard.finished.connect(self._wizard_finished)
self._settings.set_skip_first_run(True)
+ @QtCore.Slot()
def _wizard_finished(self):
"""
- SLOT
- TRIGGERS
- self._wizard.finished
+ TRIGGERS:
+ self._wizard.finished
Called when the wizard has finished.
"""
@@ -497,11 +554,11 @@ class MainWindow(QtGui.QMainWindow):
return h
return None
+ @QtCore.Slot()
def _show_logger_window(self):
"""
- SLOT
TRIGGERS:
- self.ui.action_show_logs.triggered
+ self.ui.action_show_logs.triggered
Displays the window with the history of messages logged until now
and displays the new ones on arrival.
@@ -518,9 +575,9 @@ class MainWindow(QtGui.QMainWindow):
else:
self._logger_window.setVisible(not self._logger_window.isVisible())
+ @QtCore.Slot()
def _show_AKM(self):
"""
- SLOT
TRIGGERS:
self.ui.action_advanced_key_management.triggered
@@ -528,30 +585,38 @@ class MainWindow(QtGui.QMainWindow):
"""
domain = self._login_widget.get_selected_provider()
logged_user = "{0}@{1}".format(self._logged_user, domain)
- self._akm = AdvancedKeyManagement(
- logged_user, self._keymanager, self._soledad)
- self._akm.show()
+ has_mx = True
+ if self._logged_user is not None:
+ provider_config = self._get_best_provider_config()
+ has_mx = provider_config.provides_mx()
+
+ akm = AdvancedKeyManagement(
+ self, has_mx, logged_user, self._keymanager, self._soledad)
+ akm.show()
+
+ @QtCore.Slot()
def _show_preferences(self):
"""
- SLOT
TRIGGERS:
- self.ui.btnPreferences.clicked (disabled for now)
- self.ui.action_preferences
+ self.ui.btnPreferences.clicked (disabled for now)
+ self.ui.action_preferences
Displays the preferences window.
"""
+ user = self._login_widget.get_user()
+ prov = self._login_widget.get_selected_provider()
preferences = PreferencesWindow(
- self, self._srp_auth, self._provider_config, self._soledad,
- self._login_widget.get_selected_provider())
+ self, self._backend, self._provider_config, self._soledad,
+ user, prov)
self.soledad_ready.connect(preferences.set_soledad_ready)
preferences.show()
preferences.preferences_saved.connect(self._update_eip_enabled_status)
+ @QtCore.Slot()
def _update_eip_enabled_status(self):
"""
- SLOT
TRIGGER:
PreferencesWindow.preferences_saved
@@ -563,17 +628,43 @@ class MainWindow(QtGui.QMainWindow):
"""
settings = self._settings
default_provider = settings.get_defaultprovider()
+
+ if default_provider is None:
+ logger.warning("Trying toupdate eip enabled status but there's no"
+ " default provider. Disabling EIP for the time"
+ " being...")
+ self._backend_cannot_start_eip()
+ return
+
+ self._trying_to_start_eip = settings.get_autostart_eip()
+ self._backend.eip_can_start(default_provider)
+
+ # If we don't want to start eip, we leave everything
+ # initialized to quickly start it
+ if not self._trying_to_start_eip:
+ self._backend.setup_eip(default_provider, skip_network=True)
+
+ def _backend_can_start_eip(self):
+ """
+ TRIGGER:
+ self._backend.signaler.eip_can_start
+
+ If EIP can be started right away, and the client is configured
+ to do so, start it. Otherwise it leaves everything in place
+ for the user to click Turn ON.
+ """
+ settings = self._settings
+ default_provider = settings.get_defaultprovider()
enabled_services = []
if default_provider is not None:
enabled_services = settings.get_enabled_services(default_provider)
eip_enabled = False
if EIP_SERVICE in enabled_services:
- should_autostart = settings.get_autostart_eip()
- if should_autostart and default_provider is not None:
+ eip_enabled = True
+ if default_provider is not None:
self._eip_status.enable_eip_start()
self._eip_status.set_eip_status("")
- eip_enabled = True
else:
# we don't have an usable provider
# so the user needs to log in first
@@ -583,19 +674,44 @@ class MainWindow(QtGui.QMainWindow):
self._eip_status.disable_eip_start()
self._eip_status.set_eip_status(self.tr("Disabled"))
- return eip_enabled
+ if eip_enabled and self._trying_to_start_eip:
+ self._trying_to_start_eip = False
+ self._try_autostart_eip()
+
+ def _backend_cannot_start_eip(self):
+ """
+ TRIGGER:
+ self._backend.signaler.eip_cannot_start
+ If EIP can't be started right away, get the UI to what it
+ needs to look like and waits for a proper login/eip bootstrap.
+ """
+ settings = self._settings
+ default_provider = settings.get_defaultprovider()
+ enabled_services = []
+ if default_provider is not None:
+ enabled_services = settings.get_enabled_services(default_provider)
+
+ if EIP_SERVICE in enabled_services:
+ # we don't have a usable provider
+ # so the user needs to log in first
+ self._eip_status.disable_eip_start()
+ else:
+ self._stop_eip()
+ self._eip_status.disable_eip_start()
+ self._eip_status.set_eip_status(self.tr("Disabled"))
+
+ @QtCore.Slot()
def _show_eip_preferences(self):
"""
- SLOT
TRIGGERS:
- self.ui.btnEIPPreferences.clicked
- self.ui.action_eip_preferences (disabled for now)
+ self.ui.btnEIPPreferences.clicked
+ self.ui.action_eip_preferences (disabled for now)
Displays the EIP preferences window.
"""
domain = self._login_widget.get_selected_provider()
- EIPPreferencesWindow(self, domain).show()
+ EIPPreferencesWindow(self, domain, self._backend).show()
#
# updates
@@ -610,22 +726,27 @@ class MainWindow(QtGui.QMainWindow):
"""
self.new_updates.emit(req)
+ @QtCore.Slot(object)
def _react_to_new_updates(self, req):
"""
- SLOT
- TRIGGER: self._new_updates_available
+ TRIGGERS:
+ self.new_updates
Displays the new updates label and sets the updates_content
+
+ :param req: Request type
+ :type req: leap.common.events.events_pb2.SignalRequest
"""
self.moveToThread(QtCore.QCoreApplication.instance().thread())
self.ui.lblNewUpdates.setVisible(True)
self.ui.btnMore.setVisible(True)
self._updates_content = req.content
+ @QtCore.Slot()
def _updates_details(self):
"""
- SLOT
- TRIGGER: self.ui.btnMore.clicked
+ TRIGGERS:
+ self.ui.btnMore.clicked
Parses and displays the updates details
"""
@@ -649,11 +770,11 @@ class MainWindow(QtGui.QMainWindow):
self.tr("Updates available"),
msg)
+ @QtCore.Slot()
def _finish_init(self):
"""
- SLOT
TRIGGERS:
- self._wizard.accepted
+ self._wizard.accepted
Also called at the end of the constructor if not first run.
@@ -666,11 +787,13 @@ class MainWindow(QtGui.QMainWindow):
providers = self._settings.get_configured_providers()
self._login_widget.set_providers(providers)
self._show_systray()
- self.show()
- if IS_MAC:
- self.raise_()
- self._hide_unsupported_services()
+ if not self._start_hidden:
+ self.show()
+ if IS_MAC:
+ self.raise_()
+
+ self._show_hide_unsupported_services()
if self._wizard:
possible_username = self._wizard.get_username()
@@ -696,7 +819,7 @@ class MainWindow(QtGui.QMainWindow):
self._wizard = None
self._backend_connect()
else:
- self._try_autostart_eip()
+ self._update_eip_enabled_status()
domain = self._settings.get_provider()
if domain is not None:
@@ -712,7 +835,7 @@ class MainWindow(QtGui.QMainWindow):
if self._login_widget.load_user_from_keyring(saved_user):
self._login()
- def _hide_unsupported_services(self):
+ def _show_hide_unsupported_services(self):
"""
Given a set of configured providers, it creates a set of
available services among all of them and displays the service
@@ -733,8 +856,38 @@ class MainWindow(QtGui.QMainWindow):
for service in provider_config.get_services():
services.add(service)
- self.ui.eipWidget.setVisible(EIP_SERVICE in services)
- self.ui.mailWidget.setVisible(MX_SERVICE in services)
+ self._set_eip_visible(EIP_SERVICE in services)
+ self._set_mx_visible(MX_SERVICE in services)
+
+ def _set_mx_visible(self, visible):
+ """
+ Change the visibility of MX_SERVICE related UI components.
+
+ :param visible: whether the components should be visible or not.
+ :type visible: bool
+ """
+ # only update visibility if it is something to change
+ if self._ui_mx_visible ^ visible:
+ self.ui.mailWidget.setVisible(visible)
+ self.ui.lineUnderEmail.setVisible(visible)
+ self._action_mail_status.setVisible(visible)
+ self._ui_mx_visible = visible
+
+ def _set_eip_visible(self, visible):
+ """
+ Change the visibility of EIP_SERVICE related UI components.
+
+ :param visible: whether the components should be visible or not.
+ :type visible: bool
+ """
+ # NOTE: we use xor to avoid the code being run if the visibility hasn't
+ # changed. This is meant to avoid the eip menu being displayed floating
+ # around at start because the systray isn't rendered yet.
+ if self._ui_eip_visible ^ visible:
+ self.ui.eipWidget.setVisible(visible)
+ self.ui.lineUnderEIP.setVisible(visible)
+ self._eip_menu.setVisible(visible)
+ self._ui_eip_visible = visible
def _set_label_offline(self):
"""
@@ -771,7 +924,7 @@ class MainWindow(QtGui.QMainWindow):
systrayMenu.addSeparator()
eip_status_label = "{0}: {1}".format(self._eip_name, self.tr("OFF"))
- eip_menu = systrayMenu.addMenu(eip_status_label)
+ self._eip_menu = eip_menu = systrayMenu.addMenu(eip_status_label)
eip_menu.addAction(self._action_eip_startstop)
self._eip_status.set_eip_status_menu(eip_menu)
systrayMenu.addSeparator()
@@ -787,10 +940,21 @@ class MainWindow(QtGui.QMainWindow):
self._mail_status.set_systray(self._systray)
self._eip_status.set_systray(self._systray)
+ if self._start_hidden:
+ hello = lambda: self._systray.showMessage(
+ self.tr('Hello!'),
+ self.tr('Bitmask has started in the tray.'))
+ # we wait for the systray to be ready
+ reactor.callLater(1, hello)
+
+ @QtCore.Slot(int)
def _tray_activated(self, reason=None):
"""
- SLOT
- TRIGGER: self._systray.activated
+ TRIGGERS:
+ self._systray.activated
+
+ :param reason: the reason why the tray got activated.
+ :type reason: int
Displays the context menu from the tray icon
"""
@@ -817,10 +981,11 @@ class MainWindow(QtGui.QMainWindow):
visible = self.isVisible() and self.isActiveWindow()
self._action_visible.setText(get_action(visible))
+ @QtCore.Slot()
def _toggle_visible(self):
"""
- SLOT
- TRIGGER: self._action_visible.triggered
+ TRIGGERS:
+ self._action_visible.triggered
Toggles the window visibility
"""
@@ -864,10 +1029,11 @@ class MainWindow(QtGui.QMainWindow):
if state is not None:
self.restoreState(state)
+ @QtCore.Slot()
def _about(self):
"""
- SLOT
- TRIGGERS: self.ui.action_about_leap.triggered
+ TRIGGERS:
+ self.ui.action_about_leap.triggered
Display the About Bitmask dialog
"""
@@ -891,10 +1057,11 @@ class MainWindow(QtGui.QMainWindow):
"<a href='https://leap.se'>More about LEAP"
"</a>") % (VERSION, VERSION_HASH[:10], greet))
+ @QtCore.Slot()
def _help(self):
"""
- SLOT
- TRIGGERS: self.ui.action_help.triggered
+ TRIGGERS:
+ self.ui.action_help.triggered
Display the Bitmask help dialog.
"""
@@ -991,10 +1158,11 @@ class MainWindow(QtGui.QMainWindow):
provider = self._login_widget.get_selected_provider()
self._backend.setup_provider(provider)
+ @QtCore.Slot(dict)
def _load_provider_config(self, data):
"""
- SLOT
- TRIGGER: self._backend.signaler.prov_download_provider_info
+ TRIGGERS:
+ self._backend.signaler.prov_download_provider_info
Once the provider config has been downloaded, this loads the
self._provider_config instance with it and starts the second
@@ -1009,8 +1177,9 @@ class MainWindow(QtGui.QMainWindow):
self._backend.provider_bootstrap(selected_provider)
else:
logger.error(data[self._backend.ERROR_KEY])
- self._login_widget.set_enabled(True)
+ self._login_problem_provider()
+ @QtCore.Slot()
def _login_problem_provider(self):
"""
Warns the user about a problem with the provider during login.
@@ -1019,11 +1188,11 @@ class MainWindow(QtGui.QMainWindow):
self.tr("Unable to login: Problem with provider"))
self._login_widget.set_enabled(True)
+ @QtCore.Slot()
def _login(self):
"""
- SLOT
TRIGGERS:
- self._login_widget.login
+ self._login_widget.login
Starts the login sequence. Which involves bootstrapping the
selected provider if the selection is valid (not empty), then
@@ -1043,50 +1212,33 @@ class MainWindow(QtGui.QMainWindow):
self.offline_mode_bypass_login.emit()
else:
leap_assert(self._provider_config, "We need a provider config")
+ self.ui.action_create_new_account.setEnabled(False)
if self._login_widget.start_login():
self._download_provider_config()
- def _login_errback(self, failure):
+ @QtCore.Slot(unicode)
+ def _authentication_error(self, msg):
"""
- Error handler for the srpauth.authenticate method.
+ TRIGGERS:
+ Signaler.srp_auth_error
+ Signaler.srp_auth_server_error
+ Signaler.srp_auth_connection_error
+ Signaler.srp_auth_bad_user_or_password
- :param failure: failure object that Twisted generates
- :type failure: twisted.python.failure.Failure
- """
- # NOTE: this behavior needs to be managed through the signaler,
- # as we are doing with the prov_cancelled_setup signal.
- # After we move srpauth to the backend, we need to update this.
- logger.error("Error logging in, {0!r}".format(failure))
-
- if failure.check(CancelledError):
- logger.debug("Defer cancelled.")
- failure.trap(Exception)
- self._set_login_cancelled()
- return
- elif failure.check(srpauth.SRPAuthBadUserOrPassword):
- msg = self.tr("Invalid username or password.")
- elif failure.check(srpauth.SRPAuthBadStatusCode,
- srpauth.SRPAuthenticationError,
- srpauth.SRPAuthVerificationFailed,
- srpauth.SRPAuthNoSessionId,
- srpauth.SRPAuthNoSalt, srpauth.SRPAuthNoB,
- srpauth.SRPAuthBadDataFromServer,
- srpauth.SRPAuthJSONDecodeError):
- msg = self.tr("There was a server problem with authentication.")
- elif failure.check(srpauth.SRPAuthConnectionError):
- msg = self.tr("Could not establish a connection.")
- else:
- # this shouldn't happen, but just in case.
- msg = self.tr("Unknown error: {0!r}".format(failure.value))
+ Handle the authentication errors.
+ :param msg: the message to show to the user.
+ :type msg: unicode
+ """
self._login_widget.set_status(msg)
self._login_widget.set_enabled(True)
+ self.ui.action_create_new_account.setEnabled(True)
+ @QtCore.Slot()
def _cancel_login(self):
"""
- SLOT
TRIGGERS:
- self._login_widget.cancel_login
+ self._login_widget.cancel_login
Stops the login sequence.
"""
@@ -1097,21 +1249,18 @@ class MainWindow(QtGui.QMainWindow):
"""
Cancel the running defers to avoid app blocking.
"""
+ # XXX: Should we stop all the backend defers?
self._backend.cancel_setup_provider()
-
- if self._login_defer is not None:
- logger.debug("Cancelling login defer.")
- self._login_defer.cancel()
- self._login_defer = None
+ self._backend.cancel_login()
if self._soledad_defer is not None:
logger.debug("Cancelling soledad defer.")
self._soledad_defer.cancel()
self._soledad_defer = None
+ @QtCore.Slot()
def _set_login_cancelled(self):
"""
- SLOT
TRIGGERS:
Signaler.prov_cancelled_setup fired by
self._backend.cancel_setup_provider()
@@ -1122,10 +1271,11 @@ class MainWindow(QtGui.QMainWindow):
self._login_widget.set_status(self.tr("Log in cancelled by the user."))
self._login_widget.set_enabled(True)
+ @QtCore.Slot(dict)
def _provider_config_loaded(self, data):
"""
- SLOT
- TRIGGER: self._backend.signaler.prov_check_api_certificate
+ TRIGGERS:
+ self._backend.signaler.prov_check_api_certificate
Once the provider configuration is loaded, this starts the SRP
authentication
@@ -1136,27 +1286,19 @@ class MainWindow(QtGui.QMainWindow):
username = self._login_widget.get_user()
password = self._login_widget.get_password()
- self._hide_unsupported_services()
+ self._show_hide_unsupported_services()
- if self._srp_auth is None:
- self._srp_auth = SRPAuth(self._provider_config)
- self._srp_auth.authentication_finished.connect(
- self._authentication_finished)
- self._srp_auth.logout_ok.connect(self._logout_ok)
- self._srp_auth.logout_error.connect(self._logout_error)
-
- self._login_defer = self._srp_auth.authenticate(username, password)
- self._login_defer.addErrback(self._login_errback)
+ domain = self._provider_config.get_domain()
+ self._backend.login(domain, username, password)
else:
- self._login_widget.set_status(
- "Unable to login: Problem with provider")
logger.error(data[self._backend.ERROR_KEY])
- self._login_widget.set_enabled(True)
+ self._login_problem_provider()
+ @QtCore.Slot()
def _authentication_finished(self):
"""
- SLOT
- TRIGGER: self._srp_auth.authentication_finished
+ TRIGGERS:
+ self._srp_auth.authentication_finished
Once the user is properly authenticated, try starting the EIP
service
@@ -1168,8 +1310,8 @@ class MainWindow(QtGui.QMainWindow):
domain = self._provider_config.get_domain()
full_user_id = make_address(user, domain)
self._mail_conductor.userid = full_user_id
- self._login_defer = None
self._start_eip_bootstrap()
+ self.ui.action_create_new_account.setEnabled(True)
# if soledad/mail is enabled:
if MX_SERVICE in self._enabled_services:
@@ -1179,6 +1321,9 @@ class MainWindow(QtGui.QMainWindow):
self._soledad_bootstrapper.soledad_failed.connect(
lambda: btn_enabled(True))
+ if not self._get_best_provider_config().provides_mx():
+ self._set_mx_visible(False)
+
def _start_eip_bootstrap(self):
"""
Changes the stackedWidget index to the EIP status one and
@@ -1267,12 +1412,12 @@ class MainWindow(QtGui.QMainWindow):
###################################################################
# Service control methods: soledad
+ @QtCore.Slot(dict)
def _soledad_intermediate_stage(self, data):
# TODO missing param docstring
"""
- SLOT
TRIGGERS:
- self._soledad_bootstrapper.download_config
+ self._soledad_bootstrapper.download_config
If there was a problem, displays it, otherwise it does nothing.
This is used for intermediate bootstrapping stages, in case
@@ -1285,29 +1430,13 @@ class MainWindow(QtGui.QMainWindow):
# that sets the global status
logger.error("Soledad failed to start: %s" %
(data[self._soledad_bootstrapper.ERROR_KEY],))
- self._retry_soledad_connection()
-
- def _retry_soledad_connection(self):
- """
- Retries soledad connection.
- """
- # XXX should move logic to soledad boostrapper itself
- logger.debug("Retrying soledad connection.")
- if self._soledad_bootstrapper.should_retry_initialization():
- self._soledad_bootstrapper.increment_retries_count()
- # XXX should cancel the existing socket --- this
- # is avoiding a clean termination.
- self._maybe_run_soledad_setup_checks()
- else:
- logger.warning("Max number of soledad initialization "
- "retries reached.")
+ @QtCore.Slot(dict)
def _soledad_bootstrapped_stage(self, data):
"""
- SLOT
TRIGGERS:
- self._soledad_bootstrapper.gen_key
- self._soledad_bootstrapper.local_only_ready
+ self._soledad_bootstrapper.gen_key
+ self._soledad_bootstrapper.local_only_ready
If there was a problem, displays it, otherwise it does nothing.
This is used for intermediate bootstrapping stages, in case
@@ -1346,7 +1475,6 @@ class MainWindow(QtGui.QMainWindow):
@QtCore.Slot()
def _start_smtp_bootstrapping(self):
"""
- SLOT
TRIGGERS:
self.soledad_ready
"""
@@ -1354,20 +1482,15 @@ class MainWindow(QtGui.QMainWindow):
logger.debug("not starting smtp in offline mode")
return
- # TODO for simmetry, this should be called start_smtp_service
- # (and delegate all the checks to the conductor)
if self._provides_mx_and_enabled():
- self._mail_conductor.smtp_bootstrapper.run_smtp_setup_checks(
- self._provider_config,
- self._mail_conductor.smtp_config,
- download_if_needed=True)
+ self._mail_conductor.start_smtp_service(self._provider_config,
+ download_if_needed=True)
# XXX --- should remove from here, and connecte directly to the state
# machine.
@QtCore.Slot()
def _stop_smtp_service(self):
"""
- SLOT
TRIGGERS:
self.logout
"""
@@ -1380,7 +1503,6 @@ class MainWindow(QtGui.QMainWindow):
@QtCore.Slot()
def _start_imap_service(self):
"""
- SLOT
TRIGGERS:
self.soledad_ready
"""
@@ -1410,7 +1532,6 @@ class MainWindow(QtGui.QMainWindow):
@QtCore.Slot()
def _fetch_incoming_mail(self):
"""
- SLOT
TRIGGERS:
self.mail_client_logged_in
"""
@@ -1420,7 +1541,6 @@ class MainWindow(QtGui.QMainWindow):
@QtCore.Slot()
def _stop_imap_service(self):
"""
- SLOT
TRIGGERS:
self.logout
"""
@@ -1467,11 +1587,11 @@ class MainWindow(QtGui.QMainWindow):
self._action_eip_startstop.setEnabled(True)
@QtCore.Slot()
- def _on_eip_connected(self):
+ def _on_eip_connection_connected(self):
"""
- SLOT
TRIGGERS:
self._eip_status.eip_connection_connected
+
Emits the EIPConnection.qtsigs.connected_signal
This is a little workaround for connecting the vpn-connected
@@ -1480,9 +1600,14 @@ class MainWindow(QtGui.QMainWindow):
"""
self._eip_connection.qtsigs.connected_signal.emit()
- # check for connectivity
provider_config = self._get_best_provider_config()
domain = provider_config.get_domain()
+
+ self._eip_status.set_provider(domain)
+ self._settings.set_defaultprovider(domain)
+ self._already_started_eip = True
+
+ # check for connectivity
self._check_name_resolution(domain)
def _check_name_resolution(self, domain):
@@ -1534,137 +1659,112 @@ class MainWindow(QtGui.QMainWindow):
Tries to autostart EIP
"""
settings = self._settings
-
- if not self._update_eip_enabled_status():
- return
-
default_provider = settings.get_defaultprovider()
self._enabled_services = settings.get_enabled_services(
default_provider)
loaded = self._provisional_provider_config.load(
provider.get_provider_path(default_provider))
- if loaded:
+ if loaded and settings.get_autostart_eip():
# XXX I think we should not try to re-download config every time,
# it adds some delay.
# Maybe if it's the first run in a session,
# or we can try only if it fails.
self._maybe_start_eip()
- else:
+ elif settings.get_autostart_eip():
# XXX: Display a proper message to the user
self.eip_needs_login.emit()
logger.error("Unable to load %s config, cannot autostart." %
(default_provider,))
@QtCore.Slot()
- def _start_eip(self):
+ def _start_EIP(self):
"""
- SLOT
- TRIGGERS:
- self._eip_connection.qtsigs.do_connect_signal
- (via state machine)
- or called from _finish_eip_bootstrap
-
Starts EIP
"""
- provider_config = self._get_best_provider_config()
- provider = provider_config.get_domain()
self._eip_status.eip_pre_up()
self.user_stopped_eip = False
- # until we set an option in the preferences window,
- # we'll assume that by default we try to autostart.
- # If we switch it off manually, it won't try the next
- # time.
+ # Until we set an option in the preferences window, we'll assume that
+ # by default we try to autostart. If we switch it off manually, it
+ # won't try the next time.
self._settings.set_autostart_eip(True)
- loaded = eipconfig.load_eipconfig_if_needed(
- provider_config, self._eip_config, provider)
+ self._backend.start_eip()
- if not loaded:
- eip_status_label = self.tr("Could not load {0} configuration.")
- eip_status_label = eip_status_label.format(self._eip_name)
- self._eip_status.set_eip_status(eip_status_label, error=True)
- # signal connection aborted to state machine
- qtsigs = self._eip_connection.qtsigs
- qtsigs.connection_aborted_signal.emit()
- logger.error("Tried to start EIP but cannot find any "
- "available provider!")
- return
+ @QtCore.Slot()
+ def _on_eip_connection_aborted(self):
+ """
+ TRIGGERS:
+ Signaler.eip_connection_aborted
+ """
+ logger.error("Tried to start EIP but cannot find any "
+ "available provider!")
- try:
- # XXX move this to EIPConductor
- host, port = get_openvpn_management()
- self._vpn.start(eipconfig=self._eip_config,
- providerconfig=provider_config,
- socket_host=host,
- socket_port=port)
- self._settings.set_defaultprovider(provider)
-
- # XXX move to the state machine too
- self._eip_status.set_provider(provider)
-
- # TODO refactor exceptions so they provide translatable
- # usef-facing messages.
- except EIPNoPolkitAuthAgentAvailable:
- self._eip_status.set_eip_status(
- # XXX this should change to polkit-kde where
- # applicable.
- self.tr("We could not find any "
- "authentication "
- "agent in your system.<br/>"
- "Make sure you have "
- "<b>polkit-gnome-authentication-"
- "agent-1</b> "
- "running and try again."),
- error=True)
- self._set_eipstatus_off()
- except EIPNoTunKextLoaded:
- self._eip_status.set_eip_status(
- self.tr("{0} cannot be started because "
- "the tuntap extension is not installed properly "
- "in your system.").format(self._eip_name))
- self._set_eipstatus_off()
- except EIPNoPkexecAvailable:
- self._eip_status.set_eip_status(
- self.tr("We could not find <b>pkexec</b> "
- "in your system."),
- error=True)
- self._set_eipstatus_off()
- except OpenVPNNotFoundException:
- self._eip_status.set_eip_status(
- self.tr("We could not find openvpn binary."),
- error=True)
- self._set_eipstatus_off()
- except OpenVPNAlreadyRunning as e:
- self._eip_status.set_eip_status(
- self.tr("Another openvpn instance is already running, and "
- "could not be stopped."),
- error=True)
- self._set_eipstatus_off()
- except AlienOpenVPNAlreadyRunning as e:
- self._eip_status.set_eip_status(
- self.tr("Another openvpn instance is already running, and "
- "could not be stopped because it was not launched by "
- "Bitmask. Please stop it and try again."),
- error=True)
- self._set_eipstatus_off()
- except VPNLauncherException as e:
- # XXX We should implement again translatable exceptions so
- # we can pass a translatable string to the panel (usermessage attr)
- self._eip_status.set_eip_status("%s" % (e,), error=True)
- self._set_eipstatus_off()
- else:
- self._already_started_eip = True
+ eip_status_label = self.tr("Could not load {0} configuration.")
+ eip_status_label = eip_status_label.format(self._eip_name)
+ self._eip_status.set_eip_status(eip_status_label, error=True)
+
+ # signal connection_aborted to state machine:
+ qtsigs = self._eip_connection.qtsigs
+ qtsigs.connection_aborted_signal.emit()
+
+ def _on_eip_openvpn_already_running(self):
+ self._eip_status.set_eip_status(
+ self.tr("Another openvpn instance is already running, and "
+ "could not be stopped."),
+ error=True)
+ self._set_eipstatus_off()
+
+ def _on_eip_alien_openvpn_already_running(self):
+ self._eip_status.set_eip_status(
+ self.tr("Another openvpn instance is already running, and "
+ "could not be stopped because it was not launched by "
+ "Bitmask. Please stop it and try again."),
+ error=True)
+ self._set_eipstatus_off()
+
+ def _on_eip_openvpn_not_found_error(self):
+ self._eip_status.set_eip_status(
+ self.tr("We could not find openvpn binary."),
+ error=True)
+ self._set_eipstatus_off()
+
+ def _on_eip_vpn_launcher_exception(self):
+ # XXX We should implement again translatable exceptions so
+ # we can pass a translatable string to the panel (usermessage attr)
+ self._eip_status.set_eip_status("VPN Launcher error.", error=True)
+ self._set_eipstatus_off()
+
+ def _on_eip_no_polkit_agent_error(self):
+ self._eip_status.set_eip_status(
+ # XXX this should change to polkit-kde where
+ # applicable.
+ self.tr("We could not find any authentication agent in your "
+ "system.<br/>Make sure you have"
+ "<b>polkit-gnome-authentication-agent-1</b> running and"
+ "try again."),
+ error=True)
+ self._set_eipstatus_off()
+
+ def _on_eip_no_pkexec_error(self):
+ self._eip_status.set_eip_status(
+ self.tr("We could not find <b>pkexec</b> in your system."),
+ error=True)
+ self._set_eipstatus_off()
+
+ def _on_eip_no_tun_kext_error(self):
+ self._eip_status.set_eip_status(
+ self.tr("{0} cannot be started because the tuntap extension is "
+ "not installed properly in your "
+ "system.").format(self._eip_name))
+ self._set_eipstatus_off()
@QtCore.Slot()
def _stop_eip(self):
"""
- SLOT
TRIGGERS:
- self._eip_connection.qtsigs.do_disconnect_signal
- (via state machine)
- or called from _eip_finished
+ self._eip_connection.qtsigs.do_disconnect_signal (via state machine)
Stops vpn process and makes gui adjustments to reflect
the change of state.
@@ -1673,7 +1773,7 @@ class MainWindow(QtGui.QMainWindow):
:type abnormal: bool
"""
self.user_stopped_eip = True
- self._vpn.terminate()
+ self._backend.stop_eip()
self._set_eipstatus_off(False)
self._already_started_eip = False
@@ -1692,7 +1792,6 @@ class MainWindow(QtGui.QMainWindow):
def _on_eip_network_unreachable(self):
# XXX Should move to EIP Conductor
"""
- SLOT
TRIGGERS:
self._eip_connection.qtsigs.network_unreachable
@@ -1706,7 +1805,7 @@ class MainWindow(QtGui.QMainWindow):
def _do_eip_restart(self):
# XXX Should move to EIP Conductor
"""
- SLOT
+ TRIGGERS:
self._eip_connection.qtsigs.process_restart
Restart the connection.
@@ -1714,7 +1813,7 @@ class MainWindow(QtGui.QMainWindow):
# for some reason, emitting the do_disconnect/do_connect
# signals hangs the UI.
self._stop_eip()
- QtCore.QTimer.singleShot(2000, self._start_eip)
+ QtCore.QTimer.singleShot(2000, self._start_EIP)
def _set_eipstatus_off(self, error=True):
"""
@@ -1724,11 +1823,11 @@ class MainWindow(QtGui.QMainWindow):
self._eip_status.set_eip_status("", error=error)
self._eip_status.set_eip_status_icon("error")
+ @QtCore.Slot(int)
def _eip_finished(self, exitCode):
"""
- SLOT
TRIGGERS:
- self._vpn.process_finished
+ Signaler.eip_process_finished
Triggered when the EIP/VPN process finishes to set the UI
accordingly.
@@ -1764,12 +1863,14 @@ class MainWindow(QtGui.QMainWindow):
"because you did not authenticate properly.")
eip_status_label = eip_status_label.format(self._eip_name)
self._eip_status.set_eip_status(eip_status_label, error=True)
- self._vpn.killit()
signal = qtsigs.connection_aborted_signal
+ self._backend.terminate_eip()
elif exitCode != 0 or not self.user_stopped_eip:
eip_status_label = self.tr("{0} finished in an unexpected manner!")
eip_status_label = eip_status_label.format(self._eip_name)
+ self._eip_status.eip_stopped()
+ self._eip_status.set_eip_status_icon("error")
self._eip_status.set_eip_status(eip_status_label, error=True)
signal = qtsigs.connection_died_signal
@@ -1787,17 +1888,14 @@ class MainWindow(QtGui.QMainWindow):
Start the EIP bootstrapping sequence if the client is configured to
do so.
"""
- leap_assert(self._eip_bootstrapper, "We need an eip bootstrapper!")
-
- provider_config = self._get_best_provider_config()
-
if self._provides_eip_and_enabled() and not self._already_started_eip:
# XXX this should be handled by the state machine.
self._eip_status.set_eip_status(
self.tr("Starting..."))
- self._eip_bootstrapper.run_eip_setup_checks(
- provider_config,
- download_if_needed=True)
+
+ domain = self._login_widget.get_selected_provider()
+ self._backend.setup_eip(domain)
+
self._already_started_eip = True
# we want to start soledad anyway after a certain timeout if eip
# fails to come up
@@ -1816,45 +1914,33 @@ class MainWindow(QtGui.QMainWindow):
# eip will not start, so we start soledad anyway
self._maybe_run_soledad_setup_checks()
+ @QtCore.Slot(dict)
def _finish_eip_bootstrap(self, data):
"""
- SLOT
- TRIGGER: self._eip_bootstrapper.download_client_certificate
+ TRIGGERS:
+ self._backend.signaler.eip_client_certificate_ready
Starts the VPN thread if the eip configuration is properly
loaded
"""
- leap_assert(self._eip_config, "We need an eip config!")
- passed = data[self._eip_bootstrapper.PASSED_KEY]
+ passed = data[self._backend.PASSED_KEY]
if not passed:
error_msg = self.tr("There was a problem with the provider")
self._eip_status.set_eip_status(error_msg, error=True)
- logger.error(data[self._eip_bootstrapper.ERROR_KEY])
+ logger.error(data[self._backend.ERROR_KEY])
self._already_started_eip = False
return
- provider_config = self._get_best_provider_config()
- domain = provider_config.get_domain()
-
- # XXX move check to _start_eip ?
- loaded = eipconfig.load_eipconfig_if_needed(
- provider_config, self._eip_config, domain)
-
- if loaded:
- # DO START EIP Connection!
- self._eip_connection.qtsigs.do_connect_signal.emit()
- else:
- eip_status_label = self.tr("Could not load {0} configuration.")
- eip_status_label = eip_status_label.format(self._eip_name)
- self._eip_status.set_eip_status(eip_status_label, error=True)
+ # DO START EIP Connection!
+ self._eip_connection.qtsigs.do_connect_signal.emit()
+ @QtCore.Slot(dict)
def _eip_intermediate_stage(self, data):
# TODO missing param
"""
- SLOT
TRIGGERS:
- self._eip_bootstrapper.download_config
+ self._backend.signaler.eip_config_ready
If there was a problem, displays it, otherwise it does nothing.
This is used for intermediate bootstrapping stages, in case
@@ -1896,12 +1982,11 @@ class MainWindow(QtGui.QMainWindow):
@QtCore.Slot()
def _logout(self):
"""
- SLOT
- TRIGGER: self._login_widget.logout
+ TRIGGERS:
+ self._login_widget.logout
Starts the logout sequence
"""
- self._soledad_bootstrapper.cancel_bootstrap()
setProxiedObject(self._soledad, None)
self._cancel_ongoing_defers()
@@ -1911,13 +1996,14 @@ class MainWindow(QtGui.QMainWindow):
# XXX: If other defers are doing authenticated stuff, this
# might conflict with those. CHECK!
- threads.deferToThread(self._srp_auth.logout)
+ self._backend.logout()
self.logout.emit()
+ @QtCore.Slot()
def _logout_error(self):
"""
- SLOT
- TRIGGER: self._srp_auth.logout_error
+ TRIGGER:
+ self._srp_auth.logout_error
Inform the user about a logout error.
"""
@@ -1926,10 +2012,11 @@ class MainWindow(QtGui.QMainWindow):
self._login_widget.set_status(
self.tr("Something went wrong with the logout."))
+ @QtCore.Slot()
def _logout_ok(self):
"""
- SLOT
- TRIGGER: self._srp_auth.logout_ok
+ TRIGGER:
+ self._srp_auth.logout_ok
Switches the stackedWidget back to the login stage after
logging out
@@ -1941,15 +2028,16 @@ class MainWindow(QtGui.QMainWindow):
self._login_widget.logged_out()
self._mail_status.mail_state_disabled()
+ self._show_hide_unsupported_services()
+
+ @QtCore.Slot(dict)
def _intermediate_stage(self, data):
# TODO this method name is confusing as hell.
"""
- SLOT
TRIGGERS:
- self._backend.signaler.prov_name_resolution
- self._backend.signaler.prov_https_connection
- self._backend.signaler.prov_download_ca_cert
- self._eip_bootstrapper.download_config
+ self._backend.signaler.prov_name_resolution
+ self._backend.signaler.prov_https_connection
+ self._backend.signaler.prov_download_ca_cert
If there was a problem, displays it, otherwise it does nothing.
This is used for intermediate bootstrapping stages, in case
@@ -1957,10 +2045,8 @@ class MainWindow(QtGui.QMainWindow):
"""
passed = data[self._backend.PASSED_KEY]
if not passed:
- msg = self.tr("Unable to connect: Problem with provider")
- self._login_widget.set_status(msg)
- self._login_widget.set_enabled(True)
logger.error(data[self._backend.ERROR_KEY])
+ self._login_problem_provider()
#
# window handling methods
@@ -1974,9 +2060,9 @@ class MainWindow(QtGui.QMainWindow):
raise_window_ack()
self.raise_window.emit()
+ @QtCore.Slot()
def _do_raise_mainwindow(self):
"""
- SLOT
TRIGGERS:
self._on_raise_window_event
@@ -2012,11 +2098,8 @@ class MainWindow(QtGui.QMainWindow):
self._stop_imap_service()
- if self._srp_auth is not None:
- if self._srp_auth.get_session_id() is not None or \
- self._srp_auth.get_token() is not None:
- # XXX this can timeout after loong time: See #3368
- self._srp_auth.logout()
+ if self._logged_user is not None:
+ self._backend.logout()
if self._soledad_bootstrapper.soledad is not None:
logger.debug("Closing soledad...")
@@ -2025,14 +2108,27 @@ class MainWindow(QtGui.QMainWindow):
logger.error("No instance of soledad was found.")
logger.debug('Terminating vpn')
- self._vpn.terminate(shutdown=True)
+ self._backend.stop_eip(shutdown=True)
+
+ # We need to give some time to the ongoing signals for shutdown
+ # to come into action. This needs to be solved using
+ # back-communication from backend.
+ QtCore.QTimer.singleShot(3000, self._shutdown)
+ def _shutdown(self):
+ """
+ Actually shutdown.
+ """
self._cancel_ongoing_defers()
# TODO missing any more cancels?
logger.debug('Cleaning pidfiles')
self._cleanup_pidfiles()
+ if self._quit_callback:
+ self._quit_callback()
+
+ logger.debug('Bye.')
def quit(self):
"""
@@ -2041,11 +2137,24 @@ class MainWindow(QtGui.QMainWindow):
# TODO separate the shutting down of services from the
# UI stuff.
+ # first thing to do quitting, hide the mainwindow and show tooltip.
+ self.hide()
+ if self._systray is not None:
+ self._systray.showMessage(
+ self.tr('Quitting...'),
+ self.tr('The app is quitting, please wait.'))
+
+ # explicitly process events to display tooltip immediately
+ QtCore.QCoreApplication.processEvents()
+
# Set this in case that the app is hidden
QtGui.QApplication.setQuitOnLastWindowClosed(True)
- self._backend.stop()
self._cleanup_and_quit()
+
+ # We queue the call to stop since we need to wait until EIP is stopped.
+ # Otherwise we may exit leaving an unmanaged openvpn process.
+ reactor.callLater(0, self._backend.stop)
self._really_quit = True
if self._wizard:
@@ -2055,8 +2164,3 @@ class MainWindow(QtGui.QMainWindow):
self._logger_window.close()
self.close()
-
- if self._quit_callback:
- self._quit_callback()
-
- logger.debug('Bye.')