summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/gui
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2014-04-09 16:24:12 -0500
committerKali Kaneko <kali@leap.se>2014-04-09 16:24:12 -0500
commitcf9fb29fd1098f694974ee60e5673f22e286d859 (patch)
tree5cdc4210b8c944ee4f2669fd8096998d507beda3 /src/leap/bitmask/gui
parentf0232265a2725d8129b472479dd380b9ec3ca6b2 (diff)
parent81715dc47d77934c4f67d2527a56c28f58f0345d (diff)
Merge tag '0.5.0' into deb-0.5.0
Tag leap.bitmask version 0.5.0 Conflicts: pkg/requirements.pip
Diffstat (limited to 'src/leap/bitmask/gui')
-rw-r--r--src/leap/bitmask/gui/advanced_key_management.py16
-rw-r--r--src/leap/bitmask/gui/eip_preferenceswindow.py21
-rw-r--r--src/leap/bitmask/gui/eip_status.py6
-rw-r--r--src/leap/bitmask/gui/loggerwindow.py82
-rw-r--r--src/leap/bitmask/gui/login.py52
-rw-r--r--src/leap/bitmask/gui/mail_status.py55
-rw-r--r--src/leap/bitmask/gui/mainwindow.py568
-rw-r--r--src/leap/bitmask/gui/preferenceswindow.py7
-rw-r--r--src/leap/bitmask/gui/twisted_main.py18
-rw-r--r--src/leap/bitmask/gui/ui/loggerwindow.ui15
-rw-r--r--src/leap/bitmask/gui/ui/login.ui43
-rw-r--r--src/leap/bitmask/gui/ui/mainwindow.ui16
-rw-r--r--src/leap/bitmask/gui/ui/wizard.ui90
-rw-r--r--src/leap/bitmask/gui/wizard.py182
14 files changed, 817 insertions, 354 deletions
diff --git a/src/leap/bitmask/gui/advanced_key_management.py b/src/leap/bitmask/gui/advanced_key_management.py
index 8f15719d..cbc8c3e3 100644
--- a/src/leap/bitmask/gui/advanced_key_management.py
+++ b/src/leap/bitmask/gui/advanced_key_management.py
@@ -48,6 +48,9 @@ class AdvancedKeyManagement(QtGui.QWidget):
self.ui = Ui_AdvancedKeyManagement()
self.ui.setupUi(self)
+ # XXX: Temporarily disable the key import.
+ self.ui.pbImportKeys.setVisible(False)
+
# if Soledad is not started yet
if sameProxiedObjects(soledad, None):
self.ui.gbMyKeyPair.setEnabled(False)
@@ -57,12 +60,13 @@ class AdvancedKeyManagement(QtGui.QWidget):
msg = msg.format(get_service_display_name(MX_SERVICE))
self.ui.lblStatus.setText(msg)
return
- else:
- msg = self.tr(
- "<span style='color:#ff0000;'>WARNING</span>:<br>"
- "This is an experimental feature, you can lose access to "
- "existing e-mails.")
- self.ui.lblStatus.setText(msg)
+ # XXX: since import is disabled this is no longer a dangerous feature.
+ # else:
+ # msg = self.tr(
+ # "<span style='color:#ff0000;'>WARNING</span>:<br>"
+ # "This is an experimental feature, you can lose access to "
+ # "existing e-mails.")
+ # self.ui.lblStatus.setText(msg)
self._keymanager = keymanager
self._soledad = soledad
diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py
index 504d1cf1..dcaa8b1e 100644
--- a/src/leap/bitmask/gui/eip_preferenceswindow.py
+++ b/src/leap/bitmask/gui/eip_preferenceswindow.py
@@ -22,7 +22,7 @@ import os
import logging
from functools import partial
-from PySide import QtGui
+from PySide import QtCore, QtGui
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
@@ -37,10 +37,12 @@ class EIPPreferencesWindow(QtGui.QDialog):
"""
Window that displays the EIP preferences.
"""
- def __init__(self, parent):
+ def __init__(self, parent, domain):
"""
:param parent: parent object of the EIPPreferencesWindow.
- :parent type: QWidget
+ :type parent: QWidget
+ :param domain: the selected by default domain.
+ :type domain: unicode
"""
QtGui.QDialog.__init__(self, parent)
self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")
@@ -59,7 +61,7 @@ class EIPPreferencesWindow(QtGui.QDialog):
self.ui.cbGateways.currentIndexChanged[unicode].connect(
lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False))
- self._add_configured_providers()
+ self._add_configured_providers(domain)
def _set_providers_gateway_status(self, status, success=False,
error=False):
@@ -83,9 +85,12 @@ class EIPPreferencesWindow(QtGui.QDialog):
self.ui.lblProvidersGatewayStatus.setVisible(True)
self.ui.lblProvidersGatewayStatus.setText(status)
- def _add_configured_providers(self):
+ def _add_configured_providers(self, domain=None):
"""
Add the client's configured providers to the providers combo boxes.
+
+ :param domain: the domain to be selected by default.
+ :type domain: unicode
"""
self.ui.cbProvidersGateway.clear()
providers = self._settings.get_configured_providers()
@@ -100,6 +105,12 @@ class EIPPreferencesWindow(QtGui.QDialog):
label = provider + self.tr(" (uninitialized)")
self.ui.cbProvidersGateway.addItem(label, userData=provider)
+ # Select provider by name
+ if domain is not None:
+ provider_index = self.ui.cbProvidersGateway.findText(
+ domain, QtCore.Qt.MatchStartsWith)
+ self.ui.cbProvidersGateway.setCurrentIndex(provider_index)
+
def _save_selected_gateway(self, provider):
"""
SLOT
diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py
index 92bb623e..19942d9d 100644
--- a/src/leap/bitmask/gui/eip_status.py
+++ b/src/leap/bitmask/gui/eip_status.py
@@ -248,10 +248,10 @@ class EIPStatusWidget(QtGui.QWidget):
Triggered when a default provider_config has not been found.
Disables the start button and adds instructions to the user.
"""
- logger.debug('Hiding EIP start button')
+ #logger.debug('Hiding EIP start button')
# you might be tempted to change this for a .setEnabled(False).
# it won't work. it's under the claws of the state machine.
- # probably the best thing would be to make a transitional
+ # probably the best thing would be to make a conditional
# transition there, but that's more involved.
self.eip_button.hide()
msg = self.tr("You must login to use {0}".format(self._service_name))
@@ -272,7 +272,7 @@ class EIPStatusWidget(QtGui.QWidget):
Triggered after a successful login.
Enables the start button.
"""
- logger.debug('Showing EIP start button')
+ #logger.debug('Showing EIP start button')
self.eip_button.show()
# Restore the eip action menu
diff --git a/src/leap/bitmask/gui/loggerwindow.py b/src/leap/bitmask/gui/loggerwindow.py
index 6ef58558..9f396574 100644
--- a/src/leap/bitmask/gui/loggerwindow.py
+++ b/src/leap/bitmask/gui/loggerwindow.py
@@ -22,10 +22,13 @@ import logging
import cgi
from PySide import QtGui
+from twisted.internet import threads
from ui_loggerwindow import Ui_LoggerWindow
+from leap.bitmask.util.constants import PASTEBIN_API_DEV_KEY
from leap.bitmask.util.leap_log_handler import LeapLogHandler
+from leap.bitmask.util.pastebin import PastebinAPI, PastebinError
from leap.common.check import leap_assert, leap_assert_type
logger = logging.getLogger(__name__)
@@ -42,6 +45,9 @@ class LoggerWindow(QtGui.QDialog):
:param handler: Custom handler that supports history and signal.
:type handler: LeapLogHandler.
"""
+ from twisted.internet import reactor
+ self.reactor = reactor
+
QtGui.QDialog.__init__(self)
leap_assert(handler, "We need a handler for the logger window")
leap_assert_type(handler, LeapLogHandler)
@@ -59,8 +65,10 @@ class LoggerWindow(QtGui.QDialog):
self.ui.btnCritical.toggled.connect(self._load_history)
self.ui.leFilterBy.textEdited.connect(self._filter_by)
self.ui.cbCaseInsensitive.stateChanged.connect(self._load_history)
+ self.ui.btnPastebin.clicked.connect(self._pastebin_this)
self._current_filter = ""
+ self._current_history = ""
# Load logging history and connect logger with the widget
self._logging_handler = handler
@@ -116,8 +124,13 @@ class LoggerWindow(QtGui.QDialog):
self._set_logs_to_display()
self.ui.txtLogHistory.clear()
history = self._logging_handler.log_history
+ current_history = []
for line in history:
self._add_log_line(line)
+ message = line[LeapLogHandler.MESSAGE_KEY]
+ current_history.append(message)
+
+ self._current_history = "\n".join(current_history)
def _set_logs_to_display(self):
"""
@@ -164,3 +177,72 @@ class LoggerWindow(QtGui.QDialog):
logger.error("Error saving log file: %r" % (e, ))
else:
logger.debug('Log not saved!')
+
+ def _set_pastebin_sending(self, sending):
+ """
+ Define the status of the pastebin button.
+ Change the text and enable/disable according to the current action.
+
+ :param sending: if we are sending to pastebin or not.
+ :type sending: bool
+ """
+ if sending:
+ self.ui.btnPastebin.setText(self.tr("Sending to pastebin..."))
+ self.ui.btnPastebin.setEnabled(False)
+ else:
+ self.ui.btnPastebin.setText(self.tr("Send to Pastebin.com"))
+ self.ui.btnPastebin.setEnabled(True)
+
+ def _pastebin_this(self):
+ """
+ Send the current log history to pastebin.com and gives the user a link
+ to see it.
+ """
+ def do_pastebin():
+ """
+ Send content to pastebin and return the link.
+ """
+ content = self._current_history
+ pb = PastebinAPI()
+ link = pb.paste(PASTEBIN_API_DEV_KEY, content,
+ paste_name="Bitmask log",
+ paste_expire_date='1W')
+
+ # convert to 'raw' link
+ link = "http://pastebin.com/raw.php?i=" + link.split('/')[-1]
+
+ return link
+
+ def pastebin_ok(link):
+ """
+ Callback handler for `do_pastebin`.
+
+ :param link: the recently created pastebin link.
+ :type link: str
+ """
+ msg = self.tr("Your pastebin link <a href='{0}'>{0}</a>")
+ msg = msg.format(link)
+ show_info = lambda: QtGui.QMessageBox.information(
+ self, self.tr("Pastebin OK"), msg)
+ self._set_pastebin_sending(False)
+ self.reactor.callLater(0, show_info)
+
+ def pastebin_err(failure):
+ """
+ Errback handler for `do_pastebin`.
+
+ :param failure: the failure that triggered the errback.
+ :type failure: twisted.python.failure.Failure
+ """
+ logger.error(repr(failure))
+ msg = self.tr("Sending logs to Pastebin failed!")
+ show_err = lambda: QtGui.QMessageBox.critical(
+ self, self.tr("Pastebin Error"), msg)
+ self._set_pastebin_sending(False)
+ self.reactor.callLater(0, show_err)
+ failure.trap(PastebinError)
+
+ self._set_pastebin_sending(True)
+ d = threads.deferToThread(do_pastebin)
+ d.addCallback(pastebin_ok)
+ d.addErrback(pastebin_err)
diff --git a/src/leap/bitmask/gui/login.py b/src/leap/bitmask/gui/login.py
index b21057f0..4a483c32 100644
--- a/src/leap/bitmask/gui/login.py
+++ b/src/leap/bitmask/gui/login.py
@@ -19,12 +19,13 @@ Login widget implementation
"""
import logging
-import keyring
-
from PySide import QtCore, QtGui
from ui_login import Ui_LoginWidget
+from leap.bitmask.config import flags
+from leap.bitmask.util import make_address
from leap.bitmask.util.keyring_helpers import has_keyring
+from leap.bitmask.util.keyring_helpers import get_keyring
from leap.common.check import leap_assert_type
logger = logging.getLogger(__name__)
@@ -221,6 +222,15 @@ class LoginWidget(QtGui.QWidget):
self._set_cancel(not enabled)
+ def set_logout_btn_enabled(self, enabled):
+ """
+ Enables or disables the logout button.
+
+ :param enabled: wether they should be enabled or not
+ :type enabled: bool
+ """
+ self.ui.btnLogout.setEnabled(enabled)
+
def _set_cancel(self, enabled=False):
"""
Enables or disables the cancel action in the "log in" process.
@@ -304,14 +314,15 @@ class LoginWidget(QtGui.QWidget):
if self.get_remember() and has_keyring():
# in the keyring and in the settings
# we store the value 'usename@provider'
- username_domain = (username + '@' + provider).encode("utf8")
+ full_user_id = make_address(username, provider).encode("utf8")
try:
+ keyring = get_keyring()
keyring.set_password(self.KEYRING_KEY,
- username_domain,
+ full_user_id,
password.encode("utf8"))
# Only save the username if it was saved correctly in
# the keyring
- self._settings.set_user(username_domain)
+ self._settings.set_user(full_user_id)
except Exception as e:
logger.exception("Problem saving data to keyring. %r"
% (e,))
@@ -323,15 +334,19 @@ class LoginWidget(QtGui.QWidget):
"""
self.ui.login_widget.hide()
self.ui.logged_widget.show()
- self.ui.lblUser.setText("%s@%s" % (self.get_user(),
- self.get_selected_provider()))
- self.set_login_status("")
- self.logged_in_signal.emit()
+ self.ui.lblUser.setText(make_address(
+ self.get_user(), self.get_selected_provider()))
+
+ if flags.OFFLINE is False:
+ self.logged_in_signal.emit()
def logged_out(self):
"""
Sets the widgets to the logged out state
"""
+ # TODO consider "logging out offline" too...
+ # how that would be ???
+
self.ui.login_widget.show()
self.ui.logged_widget.hide()
@@ -339,27 +354,11 @@ class LoginWidget(QtGui.QWidget):
self.set_enabled(True)
self.set_status("", error=False)
- def set_login_status(self, msg, error=False):
- """
- Sets the status label for the logged in state.
-
- :param msg: status message
- :type msg: str or unicode
- :param error: if the status is an erroneous one, then set this
- to True
- :type error: bool
- """
- leap_assert_type(error, bool)
- if error:
- msg = "<font color='red'><b>%s</b></font>" % (msg,)
- self.ui.lblLoginStatus.setText(msg)
- self.ui.lblLoginStatus.show()
-
def start_logout(self):
"""
Sets the widgets to the logging out state
"""
- self.ui.btnLogout.setText(self.tr("Loggin out..."))
+ self.ui.btnLogout.setText(self.tr("Logging out..."))
self.ui.btnLogout.setEnabled(False)
def done_logout(self):
@@ -396,6 +395,7 @@ class LoginWidget(QtGui.QWidget):
saved_password = None
try:
+ keyring = get_keyring()
saved_password = keyring.get_password(self.KEYRING_KEY,
saved_user
.encode("utf8"))
diff --git a/src/leap/bitmask/gui/mail_status.py b/src/leap/bitmask/gui/mail_status.py
index 3c933c9a..44a138e2 100644
--- a/src/leap/bitmask/gui/mail_status.py
+++ b/src/leap/bitmask/gui/mail_status.py
@@ -112,6 +112,10 @@ class MailStatusWidget(QtGui.QWidget):
callback=self._mail_handle_imap_events,
reqcbk=lambda req, resp: None)
+ register(signal=proto.SOLEDAD_INVALID_AUTH_TOKEN,
+ callback=self.set_soledad_invalid_auth_token,
+ reqcbk=lambda req, resp: None)
+
self._soledad_event.connect(
self._mail_handle_soledad_events_slot)
self._imap_event.connect(
@@ -191,6 +195,17 @@ class MailStatusWidget(QtGui.QWidget):
msg = self.tr("There was an unexpected problem with Soledad.")
self._set_mail_status(msg, ready=-1)
+ def set_soledad_invalid_auth_token(self):
+ """
+ SLOT
+ TRIGGER:
+ SoledadBootstrapper.soledad_invalid_token
+
+ This method is called when the auth token is invalid
+ """
+ msg = self.tr("Invalid auth token, try logging in again.")
+ self._set_mail_status(msg, ready=-1)
+
def _set_mail_status(self, status, ready=0):
"""
Sets the Mail status in the label and in the tray icon.
@@ -213,7 +228,7 @@ class MailStatusWidget(QtGui.QWidget):
self._service_name))
elif ready == 1:
icon = self.CONNECTING_ICON
- self._mx_status = self.tr('Starting..')
+ self._mx_status = self.tr('Starting…')
tray_status = self.tr('Mail is starting')
elif ready >= 2:
icon = self.CONNECTED_ICON
@@ -362,10 +377,19 @@ class MailStatusWidget(QtGui.QWidget):
ext_status = None
if req.event == proto.IMAP_UNREAD_MAIL:
+ # By now, the semantics of the UNREAD_MAIL event are
+ # limited to mails with the Unread flag *in the Inbox".
+ # We could make this configurable to include all unread mail
+ # or all unread mail in subscribed folders.
if self._started:
- if req.content != "0":
- self._set_mail_status(self.tr("%s Unread Emails") %
- (req.content,), ready=2)
+ count = req.content
+ if count != "0":
+ status = self.tr("{0} Unread Emails "
+ "in your Inbox").format(count)
+ if count == "1":
+ status = self.tr("1 Unread Email in your Inbox")
+
+ self._set_mail_status(status, ready=2)
else:
self._set_mail_status("", ready=2)
elif req.event == proto.IMAP_SERVICE_STARTED:
@@ -375,7 +399,7 @@ class MailStatusWidget(QtGui.QWidget):
def about_to_start(self):
"""
- Displays the correct UI for the point where mail components
+ Display the correct UI for the point where mail components
haven't really started, but they are about to in a second.
"""
self._set_mail_status(self.tr("About to start, please wait..."),
@@ -383,7 +407,7 @@ class MailStatusWidget(QtGui.QWidget):
def set_disabled(self):
"""
- Displays the correct UI for disabled mail.
+ Display the correct UI for disabled mail.
"""
self._set_mail_status(self.tr("Disabled"), -1)
@@ -394,7 +418,7 @@ class MailStatusWidget(QtGui.QWidget):
@QtCore.Slot()
def mail_state_disconnected(self):
"""
- Displays the correct UI for the disconnected state.
+ Display the correct UI for the disconnected state.
"""
# XXX this should handle the disabled state better.
self._started = False
@@ -406,7 +430,7 @@ class MailStatusWidget(QtGui.QWidget):
@QtCore.Slot()
def mail_state_connecting(self):
"""
- Displays the correct UI for the connecting state.
+ Display the correct UI for the connecting state.
"""
self._disabled = False
self._started = True
@@ -415,23 +439,32 @@ class MailStatusWidget(QtGui.QWidget):
@QtCore.Slot()
def mail_state_disconnecting(self):
"""
- Displays the correct UI for the connecting state.
+ Display the correct UI for the connecting state.
"""
self._set_mail_status(self.tr("Disconnecting..."), 1)
@QtCore.Slot()
def mail_state_connected(self):
"""
- Displays the correct UI for the connected state.
+ Display the correct UI for the connected state.
"""
self._set_mail_status(self.tr("ON"), 2)
@QtCore.Slot()
def mail_state_disabled(self):
"""
- Displays the correct UI for the disabled state.
+ Display the correct UI for the disabled state.
"""
self._disabled = True
status = self.tr("You must be logged in to use {0}.").format(
self._service_name)
self._set_mail_status(status, -1)
+
+ @QtCore.Slot()
+ def soledad_invalid_auth_token(self):
+ """
+ Display the correct UI for the invalid token state
+ """
+ self._disabled = True
+ status = self.tr("Invalid auth token, try logging in again.")
+ self._set_mail_status(status, -1)
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index 96aa8074..5abfaa67 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# mainwindow.py
-# Copyright (C) 2013 LEAP
+# Copyright (C) 2013, 2014 LEAP
#
# 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
@@ -18,16 +18,25 @@
Main window for Bitmask.
"""
import logging
+import socket
+
+from threading import Condition
+from datetime import datetime
from PySide import QtCore, QtGui
-from functools import partial
-from twisted.internet import threads
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
+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.advanced_key_management import AdvancedKeyManagement
from leap.bitmask.gui.login import LoginWidget
@@ -40,7 +49,7 @@ from leap.bitmask.gui.wizard import Wizard
from leap.bitmask.gui.systray import SysTray
from leap.bitmask import provider
-from leap.bitmask.platform_init import IS_WIN, IS_MAC
+from leap.bitmask.platform_init import IS_WIN, IS_MAC, IS_LINUX
from leap.bitmask.platform_init.initializers import init_platform
from leap.bitmask import backend
@@ -67,6 +76,7 @@ from leap.bitmask.services.eip.darwinvpnlauncher import EIPNoTunKextLoaded
from leap.bitmask.services.soledad.soledadbootstrapper import \
SoledadBootstrapper
+from leap.bitmask.util import make_address
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.leap_log_handler import LeapLogHandler
@@ -78,6 +88,8 @@ from leap.common.check import leap_assert
from leap.common.events import register
from leap.common.events import events_pb2 as proto
+from leap.mail.imap.service.imap import IMAP_PORT
+
from ui_mainwindow import Ui_MainWindow
logger = logging.getLogger(__name__)
@@ -94,6 +106,7 @@ class MainWindow(QtGui.QMainWindow):
# Signals
eip_needs_login = QtCore.Signal([])
+ offline_mode_bypass_login = QtCore.Signal([])
new_updates = QtCore.Signal(object)
raise_window = QtCore.Signal([])
soledad_ready = QtCore.Signal([])
@@ -103,6 +116,12 @@ class MainWindow(QtGui.QMainWindow):
# We use this flag to detect abnormal terminations
user_stopped_eip = False
+ # We give EIP some time to come up before starting soledad anyway
+ EIP_TIMEOUT = 60000 # in milliseconds
+
+ # 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):
@@ -133,12 +152,12 @@ class MainWindow(QtGui.QMainWindow):
# end register leap events ####################################
self._quit_callback = quit_callback
-
self._updates_content = ""
+ # setup UI
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
-
+ self.menuBar().setNativeMenuBar(not IS_LINUX)
self._backend = backend.Backend(bypass_checks)
self._backend.start()
@@ -177,6 +196,11 @@ class MainWindow(QtGui.QMainWindow):
self._eip_status.eip_connection_connected.connect(
self._on_eip_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(
@@ -193,10 +217,12 @@ class MainWindow(QtGui.QMainWindow):
self._eip_config = eipconfig.EIPConfig()
self._already_started_eip = False
+ self._already_started_soledad = False
# This is created once we have a valid provider config
self._srp_auth = None
self._logged_user = None
+ self._logged_in_offline = False
self._backend_connect()
@@ -232,8 +258,12 @@ class MainWindow(QtGui.QMainWindow):
self._soledad_intermediate_stage)
self._soledad_bootstrapper.gen_key.connect(
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(
self._mail_status.set_soledad_failed)
@@ -244,6 +274,7 @@ class MainWindow(QtGui.QMainWindow):
self.ui.action_quit.triggered.connect(self.quit)
self.ui.action_wizard.triggered.connect(self._launch_wizard)
self.ui.action_show_logs.triggered.connect(self._show_logger_window)
+ self.ui.action_help.triggered.connect(self._help)
self.ui.action_create_new_account.triggered.connect(
self._launch_wizard)
@@ -277,8 +308,9 @@ class MainWindow(QtGui.QMainWindow):
self._enabled_services = []
- self._center_window()
+ # last minute UI manipulations
+ self._center_window()
self.ui.lblNewUpdates.setVisible(False)
self.ui.btnMore.setVisible(False)
#########################################
@@ -287,6 +319,8 @@ class MainWindow(QtGui.QMainWindow):
self.ui.btnMore.resize(0, 0)
#########################################
self.ui.btnMore.clicked.connect(self._updates_details)
+ if flags.OFFLINE is True:
+ self._set_label_offline()
# Services signals/slots connection
self.new_updates.connect(self._react_to_new_updates)
@@ -316,7 +350,7 @@ class MainWindow(QtGui.QMainWindow):
self._keymanager = ProxyBase(None)
self._login_defer = None
- self._download_provider_defer = None
+ self._soledad_defer = None
self._mail_conductor = mail_conductor.MailConductor(
self._soledad, self._keymanager)
@@ -354,23 +388,22 @@ class MainWindow(QtGui.QMainWindow):
"""
Helper to connect to backend signals
"""
- self._backend.signaler.prov_name_resolution.connect(
- self._intermediate_stage)
- self._backend.signaler.prov_https_connection.connect(
- self._intermediate_stage)
- self._backend.signaler.prov_download_ca_cert.connect(
- self._intermediate_stage)
+ 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)
- self._backend.signaler.prov_download_provider_info.connect(
- self._load_provider_config)
- self._backend.signaler.prov_check_api_certificate.connect(
- self._provider_config_loaded)
+ sig.prov_download_provider_info.connect(self._load_provider_config)
+ sig.prov_check_api_certificate.connect(self._provider_config_loaded)
# Only used at login, no need to disconnect this like we do
# with the other
- self._backend.signaler.prov_problem_with_provider.connect(
- partial(self._login_widget.set_status,
- self.tr("Unable to login: Problem with provider")))
+ sig.prov_problem_with_provider.connect(self._login_problem_provider)
+
+ 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):
"""
@@ -379,17 +412,13 @@ class MainWindow(QtGui.QMainWindow):
Some signals are emitted from the wizard, and we want to
ignore those.
"""
- self._backend.signaler.prov_name_resolution.disconnect(
- self._intermediate_stage)
- self._backend.signaler.prov_https_connection.disconnect(
- self._intermediate_stage)
- self._backend.signaler.prov_download_ca_cert.disconnect(
- self._intermediate_stage)
+ 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)
- self._backend.signaler.prov_download_provider_info.disconnect(
- self._load_provider_config)
- self._backend.signaler.prov_check_api_certificate.disconnect(
- self._provider_config_loaded)
+ sig.prov_download_provider_info.disconnect(self._load_provider_config)
+ sig.prov_check_api_certificate.disconnect(self._provider_config_loaded)
def _rejected_wizard(self):
"""
@@ -412,7 +441,8 @@ class MainWindow(QtGui.QMainWindow):
# setup but does not register
self._wizard = None
self._backend_connect()
- self._finish_init()
+ if self._wizard_firstrun:
+ self._finish_init()
def _launch_wizard(self):
"""
@@ -431,7 +461,7 @@ class MainWindow(QtGui.QMainWindow):
self._wizard = Wizard(backend=self._backend,
bypass_checks=self._bypass_checks)
self._wizard.accepted.connect(self._finish_init)
- self._wizard.rejected.connect(self._wizard.close)
+ self._wizard.rejected.connect(self._rejected_wizard)
self.setVisible(False)
# Do NOT use exec_, it will use a child event loop!
@@ -564,7 +594,8 @@ class MainWindow(QtGui.QMainWindow):
Displays the EIP preferences window.
"""
- EIPPreferencesWindow(self).show()
+ domain = self._login_widget.get_selected_provider()
+ EIPPreferencesWindow(self, domain).show()
#
# updates
@@ -705,6 +736,19 @@ class MainWindow(QtGui.QMainWindow):
self.ui.eipWidget.setVisible(EIP_SERVICE in services)
self.ui.mailWidget.setVisible(MX_SERVICE in services)
+ def _set_label_offline(self):
+ """
+ Set the login label to reflect offline status.
+ """
+ if self._logged_in_offline:
+ provider = ""
+ else:
+ provider = self.ui.lblLoginProvider.text()
+
+ self.ui.lblLoginProvider.setText(
+ provider +
+ self.tr(" (offline mode)"))
+
#
# systray
#
@@ -827,10 +871,13 @@ class MainWindow(QtGui.QMainWindow):
Display the About Bitmask dialog
"""
+ today = datetime.now().date()
+ greet = ("Happy New 1984!... or not ;)<br><br>"
+ if today.month == 1 and today.day < 15 else "")
QtGui.QMessageBox.about(
self, self.tr("About Bitmask - %s") % (VERSION,),
- self.tr("Version: <b>%s</b><br>"
- "<br>"
+ self.tr("Version: <b>%s</b> (%s)<br>"
+ "<br>%s"
"Bitmask is the Desktop client application for "
"the LEAP platform, supporting encrypted internet "
"proxy, secure email, and secure chat (coming soon).<br>"
@@ -842,7 +889,58 @@ class MainWindow(QtGui.QMainWindow):
"and widely available. <br>"
"<br>"
"<a href='https://leap.se'>More about LEAP"
- "</a>") % (VERSION,))
+ "</a>") % (VERSION, VERSION_HASH[:10], greet))
+
+ def _help(self):
+ """
+ SLOT
+ TRIGGERS: self.ui.action_help.triggered
+
+ Display the Bitmask help dialog.
+ """
+ # TODO: don't hardcode!
+ smtp_port = 2013
+
+ url = ("<a href='https://addons.mozilla.org/es/thunderbird/"
+ "addon/bitmask/'>bitmask addon</a>")
+
+ msg = self.tr(
+ "<strong>Instructions to use mail:</strong><br>"
+ "If you use Thunderbird you can use the Bitmask extension helper. "
+ "Search for 'Bitmask' in the add-on manager or download it "
+ "from: {0}.<br><br>"
+ "You can configure Bitmask manually with these options:<br>"
+ "<em>"
+ " Incoming -> IMAP, port: {1}<br>"
+ " Outgoing -> SMTP, port: {2}<br>"
+ " Username -> your bitmask username.<br>"
+ " Password -> does not matter, use any text. "
+ " Just don't leave it empty and don't use your account's password."
+ "</em>").format(url, IMAP_PORT, smtp_port)
+ QtGui.QMessageBox.about(self, self.tr("Bitmask Help"), msg)
+
+ def _needs_update(self):
+ """
+ Display a warning dialog to inform the user that the app needs update.
+ """
+ url = "https://dl.bitmask.net/"
+ msg = self.tr(
+ "The current client version is not supported "
+ "by this provider.<br>"
+ "Please update to latest version.<br><br>"
+ "You can get the latest version from "
+ "<a href='{0}'>{1}</a>").format(url, url)
+ QtGui.QMessageBox.warning(self, self.tr("Update Needed"), msg)
+
+ def _incompatible_api(self):
+ """
+ Display a warning dialog to inform the user that the provider has an
+ incompatible API.
+ """
+ msg = self.tr(
+ "This provider is not compatible with the client.<br><br>"
+ "Error: API version incompatible.")
+ QtGui.QMessageBox.warning(self, self.tr("Incompatible Provider"), msg)
def changeEvent(self, e):
"""
@@ -891,7 +989,6 @@ class MainWindow(QtGui.QMainWindow):
"""
# XXX should rename this provider, name clash.
provider = self._login_widget.get_selected_provider()
-
self._backend.setup_provider(provider)
def _load_provider_config(self, data):
@@ -904,18 +1001,24 @@ class MainWindow(QtGui.QMainWindow):
part of the bootstrapping sequence
:param data: result from the last stage of the
- run_provider_select_checks
+ run_provider_select_checks
:type data: dict
"""
if data[self._backend.PASSED_KEY]:
selected_provider = self._login_widget.get_selected_provider()
self._backend.provider_bootstrap(selected_provider)
else:
- self._login_widget.set_status(
- self.tr("Unable to login: Problem with provider"))
logger.error(data[self._backend.ERROR_KEY])
self._login_widget.set_enabled(True)
+ def _login_problem_provider(self):
+ """
+ Warns the user about a problem with the provider during login.
+ """
+ self._login_widget.set_status(
+ self.tr("Unable to login: Problem with provider"))
+ self._login_widget.set_enabled(True)
+
def _login(self):
"""
SLOT
@@ -927,10 +1030,57 @@ class MainWindow(QtGui.QMainWindow):
start the SRP authentication, and as the last step
bootstrapping the EIP service
"""
- leap_assert(self._provider_config, "We need a provider config")
+ # TODO most of this could ve handled by the login widget,
+ # but we'd have to move lblLoginProvider into the widget itself,
+ # instead of having it as a top-level attribute.
+ if flags.OFFLINE is True:
+ logger.debug("OFFLINE mode! bypassing remote login")
+ # TODO reminder, we're not handling logout for offline
+ # mode.
+ self._login_widget.logged_in()
+ self._logged_in_offline = True
+ self._set_label_offline()
+ self.offline_mode_bypass_login.emit()
+ else:
+ leap_assert(self._provider_config, "We need a provider config")
+ if self._login_widget.start_login():
+ self._download_provider_config()
+
+ def _login_errback(self, failure):
+ """
+ Error handler for the srpauth.authenticate method.
+
+ :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))
- if self._login_widget.start_login():
- self._download_provider_config()
+ self._login_widget.set_status(msg)
+ self._login_widget.set_enabled(True)
def _cancel_login(self):
"""
@@ -941,16 +1091,36 @@ class MainWindow(QtGui.QMainWindow):
Stops the login sequence.
"""
logger.debug("Cancelling log in.")
+ self._cancel_ongoing_defers()
- if self._download_provider_defer:
- logger.debug("Cancelling download provider defer.")
- self._download_provider_defer.cancel()
+ def _cancel_ongoing_defers(self):
+ """
+ Cancel the running defers to avoid app blocking.
+ """
+ self._backend.cancel_setup_provider()
- if self._login_defer:
+ if self._login_defer is not None:
logger.debug("Cancelling login defer.")
self._login_defer.cancel()
+ self._login_defer = None
+
+ if self._soledad_defer is not None:
+ logger.debug("Cancelling soledad defer.")
+ self._soledad_defer.cancel()
+ self._soledad_defer = None
+
+ def _set_login_cancelled(self):
+ """
+ SLOT
+ TRIGGERS:
+ Signaler.prov_cancelled_setup fired by
+ self._backend.cancel_setup_provider()
+ This method re-enables the login widget and display a message for
+ the cancelled operation.
+ """
self._login_widget.set_status(self.tr("Log in cancelled by the user."))
+ self._login_widget.set_enabled(True)
def _provider_config_loaded(self, data):
"""
@@ -972,18 +1142,18 @@ class MainWindow(QtGui.QMainWindow):
self._srp_auth = SRPAuth(self._provider_config)
self._srp_auth.authentication_finished.connect(
self._authentication_finished)
- self._srp_auth.logout_finished.connect(
- self._done_logging_out)
+ self._srp_auth.logout_ok.connect(self._logout_ok)
+ self._srp_auth.logout_error.connect(self._logout_error)
- # TODO Add errback!
self._login_defer = self._srp_auth.authenticate(username, password)
+ self._login_defer.addErrback(self._login_errback)
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)
- def _authentication_finished(self, ok, message):
+ def _authentication_finished(self):
"""
SLOT
TRIGGER: self._srp_auth.authentication_finished
@@ -991,22 +1161,23 @@ class MainWindow(QtGui.QMainWindow):
Once the user is properly authenticated, try starting the EIP
service
"""
- # In general we want to "filter" likely complicated error
- # messages, but in this case, the messages make more sense as
- # they come. Since they are "Unknown user" or "Unknown
- # password"
- self._login_widget.set_status(message, error=not ok)
-
- if ok:
- self._logged_user = self._login_widget.get_user()
- user = self._logged_user
- domain = self._provider_config.get_domain()
- userid = "%s@%s" % (user, domain)
- self._mail_conductor.userid = userid
- self._login_defer = None
- self._start_eip_bootstrap()
- else:
- self._login_widget.set_enabled(True)
+ self._login_widget.set_status(self.tr("Succeeded"), error=False)
+
+ self._logged_user = self._login_widget.get_user()
+ user = self._logged_user
+ 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()
+
+ # if soledad/mail is enabled:
+ if MX_SERVICE in self._enabled_services:
+ btn_enabled = self._login_widget.set_logout_btn_enabled
+ btn_enabled(False)
+ self.soledad_ready.connect(lambda: btn_enabled(True))
+ self._soledad_bootstrapper.soledad_failed.connect(
+ lambda: btn_enabled(True))
def _start_eip_bootstrap(self):
"""
@@ -1015,28 +1186,83 @@ class MainWindow(QtGui.QMainWindow):
"""
self._login_widget.logged_in()
- self.ui.lblLoginProvider.setText(self._provider_config.get_domain())
+ provider = self._provider_config.get_domain()
+ self.ui.lblLoginProvider.setText(provider)
self._enabled_services = self._settings.get_enabled_services(
self._provider_config.get_domain())
# TODO separate UI from logic.
- # TODO soledad should check if we want to run only over EIP.
- if self._provider_config.provides_mx() and \
- self._enabled_services.count(MX_SERVICE) > 0:
+ if self._provides_mx_and_enabled():
self._mail_status.about_to_start()
-
- self._soledad_bootstrapper.run_soledad_setup_checks(
- self._provider_config,
- self._login_widget.get_user(),
- self._login_widget.get_password(),
- download_if_needed=True)
else:
self._mail_status.set_disabled()
- # XXX the config should be downloaded from the start_eip
- # method.
- self._download_eip_config()
+ self._maybe_start_eip()
+
+ def _provides_mx_and_enabled(self):
+ """
+ Defines if the current provider provides mx and if we have it enabled.
+
+ :returns: True if provides and is enabled, False otherwise
+ :rtype: bool
+ """
+ provider_config = self._get_best_provider_config()
+ return (provider_config.provides_mx() and
+ MX_SERVICE in self._enabled_services)
+
+ def _provides_eip_and_enabled(self):
+ """
+ Defines if the current provider provides eip and if we have it enabled.
+
+ :returns: True if provides and is enabled, False otherwise
+ :rtype: bool
+ """
+ provider_config = self._get_best_provider_config()
+ return (provider_config.provides_eip() and
+ EIP_SERVICE in self._enabled_services)
+
+ def _maybe_run_soledad_setup_checks(self):
+ """
+ Conditionally start Soledad.
+ """
+ # TODO split.
+ if self._already_started_soledad is True:
+ return
+
+ if not self._provides_mx_and_enabled():
+ return
+
+ username = self._login_widget.get_user()
+ password = unicode(self._login_widget.get_password())
+ provider_domain = self._login_widget.get_selected_provider()
+
+ sb = self._soledad_bootstrapper
+ if flags.OFFLINE is True:
+ provider_domain = self._login_widget.get_selected_provider()
+ sb._password = password
+
+ self._provisional_provider_config.load(
+ provider.get_provider_path(provider_domain))
+
+ full_user_id = make_address(username, provider_domain)
+ uuid = self._settings.get_uuid(full_user_id)
+ self._mail_conductor.userid = full_user_id
+
+ if uuid is None:
+ # We don't need more visibility at the moment,
+ # this is mostly for internal use/debug for now.
+ logger.warning("Sorry! Log-in at least one time.")
+ return
+ fun = sb.load_offline_soledad
+ fun(full_user_id, password, uuid)
+ else:
+ provider_config = self._provider_config
+
+ if self._logged_user is not None:
+ self._soledad_defer = sb.run_soledad_setup_checks(
+ provider_config, username, password,
+ download_if_needed=True)
###################################################################
# Service control methods: soledad
@@ -1069,8 +1295,9 @@ class MainWindow(QtGui.QMainWindow):
logger.debug("Retrying soledad connection.")
if self._soledad_bootstrapper.should_retry_initialization():
self._soledad_bootstrapper.increment_retries_count()
- threads.deferToThread(
- self._soledad_bootstrapper.load_and_sync_soledad)
+ # 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.")
@@ -1080,6 +1307,7 @@ class MainWindow(QtGui.QMainWindow):
SLOT
TRIGGERS:
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
@@ -1106,6 +1334,7 @@ class MainWindow(QtGui.QMainWindow):
# Ok, now soledad is ready, so we can allow other things that
# depend on soledad to start.
+ self._soledad_defer = None
# this will trigger start_imap_service
# and start_smtp_boostrapping
@@ -1121,10 +1350,13 @@ class MainWindow(QtGui.QMainWindow):
TRIGGERS:
self.soledad_ready
"""
+ if flags.OFFLINE is True:
+ 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._provider_config.provides_mx() and \
- self._enabled_services.count(MX_SERVICE) > 0:
+ if self._provides_mx_and_enabled():
self._mail_conductor.smtp_bootstrapper.run_smtp_setup_checks(
self._provider_config,
self._mail_conductor.smtp_config,
@@ -1152,9 +1384,22 @@ class MainWindow(QtGui.QMainWindow):
TRIGGERS:
self.soledad_ready
"""
- if self._provider_config.provides_mx() and \
- self._enabled_services.count(MX_SERVICE) > 0:
- self._mail_conductor.start_imap_service()
+ # TODO in the OFFLINE mode we should also modify the rules
+ # in the mail state machine so it shows that imap is active
+ # (but not smtp since it's not yet ready for offline use)
+ start_fun = self._mail_conductor.start_imap_service
+ if flags.OFFLINE is True:
+ provider_domain = self._login_widget.get_selected_provider()
+ self._provider_config.load(
+ provider.get_provider_path(provider_domain))
+ provides_mx = self._provider_config.provides_mx()
+
+ if flags.OFFLINE is True and provides_mx:
+ start_fun()
+ return
+
+ if self._provides_mx_and_enabled():
+ start_fun()
def _on_mail_client_logged_in(self, req):
"""
@@ -1179,8 +1424,13 @@ class MainWindow(QtGui.QMainWindow):
TRIGGERS:
self.logout
"""
+ cv = Condition()
+ cv.acquire()
# TODO call stop_mail_service
- self._mail_conductor.stop_imap_service()
+ threads.deferToThread(self._mail_conductor.stop_imap_service, cv)
+ # and wait for it to be stopped
+ logger.debug('Waiting for imap service to stop.')
+ cv.wait(self.SERVICE_STOP_TIMEOUT)
# end service control methods (imap)
@@ -1230,6 +1480,55 @@ 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._check_name_resolution(domain)
+
+ def _check_name_resolution(self, domain):
+ """
+ Check if we can resolve the given domain name.
+
+ :param domain: the domain to check.
+ :type domain: str
+ """
+ def do_check():
+ """
+ Try to resolve the domain name.
+ """
+ socket.gethostbyname(domain.encode('idna'))
+
+ def check_err(failure):
+ """
+ Errback handler for `do_check`.
+
+ :param failure: the failure that triggered the errback.
+ :type failure: twisted.python.failure.Failure
+ """
+ logger.error(repr(failure))
+ logger.error("Can't resolve hostname.")
+
+ msg = self.tr(
+ "The server at {0} can't be found, because the DNS lookup "
+ "failed. DNS is the network service that translates a "
+ "website's name to its Internet address. Either your computer "
+ "is having trouble connecting to the network, or you are "
+ "missing some helper files that are needed to securely use "
+ "DNS while {1} is active. To install these helper files, quit "
+ "this application and start it again."
+ ).format(domain, self._eip_name)
+
+ show_err = lambda: QtGui.QMessageBox.critical(
+ self, self.tr("Connection Error"), msg)
+ reactor.callLater(0, show_err)
+
+ # python 2.7.4 raises socket.error
+ # python 2.7.5 raises socket.gaierror
+ failure.trap(socket.gaierror, socket.error)
+
+ d = threads.deferToThread(do_check)
+ d.addErrback(check_err)
+
def _try_autostart_eip(self):
"""
Tries to autostart EIP
@@ -1250,7 +1549,7 @@ class MainWindow(QtGui.QMainWindow):
# it adds some delay.
# Maybe if it's the first run in a session,
# or we can try only if it fails.
- self._download_eip_config()
+ self._maybe_start_eip()
else:
# XXX: Display a proper message to the user
self.eip_needs_login.emit()
@@ -1384,8 +1683,9 @@ class MainWindow(QtGui.QMainWindow):
if self._logged_user:
self._eip_status.set_provider(
- "%s@%s" % (self._logged_user,
- self._get_best_provider_config().get_domain()))
+ make_address(
+ self._logged_user,
+ self._get_best_provider_config().get_domain()))
self._eip_status.eip_stopped()
@QtCore.Slot()
@@ -1482,18 +1782,16 @@ class MainWindow(QtGui.QMainWindow):
# eip boostrapping, config etc...
- def _download_eip_config(self):
+ def _maybe_start_eip(self):
"""
- Starts the EIP bootstrapping sequence
+ 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 provider_config.provides_eip() and \
- self._enabled_services.count(EIP_SERVICE) > 0 and \
- not self._already_started_eip:
-
+ 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..."))
@@ -1501,14 +1799,22 @@ class MainWindow(QtGui.QMainWindow):
provider_config,
download_if_needed=True)
self._already_started_eip = True
- elif not self._already_started_eip:
- if self._enabled_services.count(EIP_SERVICE) > 0:
- self._eip_status.set_eip_status(
- self.tr("Not supported"),
- error=True)
- else:
- self._eip_status.disable_eip_start()
- self._eip_status.set_eip_status(self.tr("Disabled"))
+ # we want to start soledad anyway after a certain timeout if eip
+ # fails to come up
+ QtCore.QTimer.singleShot(
+ self.EIP_TIMEOUT,
+ self._maybe_run_soledad_setup_checks)
+ else:
+ if not self._already_started_eip:
+ if EIP_SERVICE in self._enabled_services:
+ self._eip_status.set_eip_status(
+ self.tr("Not supported"),
+ error=True)
+ else:
+ self._eip_status.disable_eip_start()
+ self._eip_status.set_eip_status(self.tr("Disabled"))
+ # eip will not start, so we start soledad anyway
+ self._maybe_run_soledad_setup_checks()
def _finish_eip_bootstrap(self, data):
"""
@@ -1595,20 +1901,35 @@ class MainWindow(QtGui.QMainWindow):
Starts the logout sequence
"""
-
self._soledad_bootstrapper.cancel_bootstrap()
setProxiedObject(self._soledad, None)
+ self._cancel_ongoing_defers()
+
+ # reset soledad status flag
+ self._already_started_soledad = False
+
# XXX: If other defers are doing authenticated stuff, this
# might conflict with those. CHECK!
threads.deferToThread(self._srp_auth.logout)
self.logout.emit()
- def _done_logging_out(self, ok, message):
- # TODO missing params in docstring
+ def _logout_error(self):
"""
SLOT
- TRIGGER: self._srp_auth.logout_finished
+ TRIGGER: self._srp_auth.logout_error
+
+ Inform the user about a logout error.
+ """
+ self._login_widget.done_logout()
+ self.ui.lblLoginProvider.setText(self.tr("Login"))
+ self._login_widget.set_status(
+ self.tr("Something went wrong with the logout."))
+
+ def _logout_ok(self):
+ """
+ SLOT
+ TRIGGER: self._srp_auth.logout_ok
Switches the stackedWidget back to the login stage after
logging out
@@ -1616,15 +1937,9 @@ class MainWindow(QtGui.QMainWindow):
self._login_widget.done_logout()
self.ui.lblLoginProvider.setText(self.tr("Login"))
- if ok:
- self._logged_user = None
- self._login_widget.logged_out()
- self._mail_status.mail_state_disabled()
-
- else:
- self._login_widget.set_login_status(
- self.tr("Something went wrong with the logout."),
- error=True)
+ self._logged_user = None
+ self._login_widget.logged_out()
+ self._mail_status.mail_state_disabled()
def _intermediate_stage(self, data):
# TODO this method name is confusing as hell.
@@ -1642,9 +1957,9 @@ 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)
- self._login_widget.set_status(
- self.tr("Unable to connect: Problem with provider"))
logger.error(data[self._backend.ERROR_KEY])
#
@@ -1695,7 +2010,7 @@ class MainWindow(QtGui.QMainWindow):
"""
logger.debug('About to quit, doing cleanup...')
- self._mail_conductor.stop_imap_service()
+ self._stop_imap_service()
if self._srp_auth is not None:
if self._srp_auth.get_session_id() is not None or \
@@ -1712,13 +2027,7 @@ class MainWindow(QtGui.QMainWindow):
logger.debug('Terminating vpn')
self._vpn.terminate(shutdown=True)
- if self._login_defer:
- logger.debug("Cancelling login defer.")
- self._login_defer.cancel()
-
- if self._download_provider_defer:
- logger.debug("Cancelling download provider defer.")
- self._download_provider_defer.cancel()
+ self._cancel_ongoing_defers()
# TODO missing any more cancels?
@@ -1737,7 +2046,6 @@ class MainWindow(QtGui.QMainWindow):
self._backend.stop()
self._cleanup_and_quit()
-
self._really_quit = True
if self._wizard:
diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py
index 517a90c4..b2cc2236 100644
--- a/src/leap/bitmask/gui/preferenceswindow.py
+++ b/src/leap/bitmask/gui/preferenceswindow.py
@@ -18,7 +18,6 @@
"""
Preferences window
"""
-import os
import logging
from functools import partial
@@ -26,6 +25,7 @@ from functools import partial
from PySide import QtCore, QtGui
from zope.proxy import sameProxiedObjects
+from leap.bitmask.provider import get_provider_path
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.gui.ui_preferences import Ui_Preferences
from leap.soledad.client import NoStorageSecret
@@ -383,10 +383,7 @@ class PreferencesWindow(QtGui.QDialog):
:rtype: ProviderConfig or None if there is a problem loading the config
"""
provider_config = ProviderConfig()
- provider_config_path = os.path.join(
- "leap", "providers", domain, "provider.json")
-
- if not provider_config.load(provider_config_path):
+ if not provider_config.load(get_provider_path(domain)):
provider_config = None
return provider_config
diff --git a/src/leap/bitmask/gui/twisted_main.py b/src/leap/bitmask/gui/twisted_main.py
index e11af7bd..1e876c57 100644
--- a/src/leap/bitmask/gui/twisted_main.py
+++ b/src/leap/bitmask/gui/twisted_main.py
@@ -27,24 +27,6 @@ from twisted.internet import error
logger = logging.getLogger(__name__)
-def start(app):
- """
- Start the mainloop.
-
- :param app: the main qt QApplication instance.
- :type app: QtCore.QApplication
- """
- from twisted.internet import reactor
- logger.debug('starting twisted reactor')
-
- # this seems to be troublesome under some
- # unidentified settings.
- #reactor.run()
-
- reactor.runReturn()
- app.exec_()
-
-
def quit(app):
"""
Stop the mainloop.
diff --git a/src/leap/bitmask/gui/ui/loggerwindow.ui b/src/leap/bitmask/gui/ui/loggerwindow.ui
index 3de786f7..b19ed91a 100644
--- a/src/leap/bitmask/gui/ui/loggerwindow.ui
+++ b/src/leap/bitmask/gui/ui/loggerwindow.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>648</width>
- <height>469</height>
+ <width>769</width>
+ <height>464</height>
</rect>
</property>
<property name="windowTitle">
@@ -154,6 +154,17 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QPushButton" name="btnPastebin">
+ <property name="text">
+ <string>Send to Pastebin.com</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../../../../data/resources/loggerwindow.qrc">
+ <normaloff>:/images/pastebin.png</normaloff>:/images/pastebin.png</iconset>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
</layout>
diff --git a/src/leap/bitmask/gui/ui/login.ui b/src/leap/bitmask/gui/ui/login.ui
index 7e8f9daf..f5725d5a 100644
--- a/src/leap/bitmask/gui/ui/login.ui
+++ b/src/leap/bitmask/gui/ui/login.ui
@@ -217,26 +217,6 @@
<property name="bottomMargin">
<number>0</number>
</property>
- <item row="1" column="1">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="0">
- <widget class="QPushButton" name="btnLogout">
- <property name="text">
- <string>Logout</string>
- </property>
- </widget>
- </item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="lblUser">
<property name="font">
@@ -251,17 +231,26 @@
</property>
</widget>
</item>
- <item row="2" column="0" colspan="2">
- <widget class="QLabel" name="lblLoginStatus">
- <property name="styleSheet">
- <string notr="true">color: rgb(132, 132, 132);
-font: 75 12pt;</string>
- </property>
+ <item row="1" column="0">
+ <widget class="QPushButton" name="btnLogout">
<property name="text">
- <string/>
+ <string>Logout</string>
</property>
</widget>
</item>
+ <item row="1" column="1">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui
index ce05f8f3..d755115a 100644
--- a/src/leap/bitmask/gui/ui/mainwindow.ui
+++ b/src/leap/bitmask/gui/ui/mainwindow.ui
@@ -75,7 +75,7 @@
<x>0</x>
<y>0</y>
<width>524</width>
- <height>651</height>
+ <height>667</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -85,8 +85,6 @@
<property name="margin">
<number>0</number>
</property>
-
- <!-- LOGIN -->
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
@@ -134,7 +132,6 @@
</property>
</layout>
</item>
-
<item>
<widget class="Line" name="lineUnderLogin">
<property name="orientation">
@@ -142,8 +139,6 @@
</property>
</widget>
</item>
-
- <!-- EIP -->
<item>
<widget class="QWidget" name="eipWidget" native="true">
<layout class="QVBoxLayout" name="eipVerticalLayout">
@@ -172,7 +167,6 @@
</layout>
</widget>
</item>
-
<item>
<widget class="Line" name="lineUnderEIP">
<property name="orientation">
@@ -180,8 +174,6 @@
</property>
</widget>
</item>
-
- <!-- EMAIL -->
<item>
<widget class="QWidget" name="mailWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_3">
@@ -204,7 +196,6 @@
</layout>
</widget>
</item>
-
<item>
<widget class="Line" name="lineUnderEmail">
<property name="orientation">
@@ -212,7 +203,6 @@
</property>
</widget>
</item>
-
<item>
<spacer name="verticalSpacer">
<property name="orientation">
@@ -307,7 +297,7 @@
<x>0</x>
<y>0</y>
<width>524</width>
- <height>21</height>
+ <height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -324,7 +314,7 @@
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
- <string>Help</string>
+ <string>&amp;Help</string>
</property>
<addaction name="action_help"/>
<addaction name="action_show_logs"/>
diff --git a/src/leap/bitmask/gui/ui/wizard.ui b/src/leap/bitmask/gui/ui/wizard.ui
index cf591470..6c592522 100644
--- a/src/leap/bitmask/gui/ui/wizard.ui
+++ b/src/leap/bitmask/gui/ui/wizard.ui
@@ -59,7 +59,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Now we will guide you through some configuration that is needed before you can connect for the first time.&lt;/p&gt;&lt;p&gt;If you ever need to modify these options again, you can find the wizard in the &lt;span style=&quot; font-style:italic;&quot;&gt;'Settings'&lt;/span&gt; menu from the main window.&lt;/p&gt;&lt;p&gt;Do you want to &lt;span style=&quot; font-weight:600;&quot;&gt;sign up&lt;/span&gt; for a new account, or &lt;span style=&quot; font-weight:600;&quot;&gt;log in&lt;/span&gt; with an already existing username?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Now we will guide you through some configuration that is needed before you can connect for the first time.&lt;/p&gt;&lt;p&gt;If you ever need to modify these options again, you can find the wizard in the &lt;span style=&quot; font-style:italic;&quot;&gt;'Bitmask -&amp;gt; Create new account...'&lt;/span&gt; menu from the main window.&lt;/p&gt;&lt;p&gt;Do you want to &lt;span style=&quot; font-weight:600;&quot;&gt;sign up&lt;/span&gt; for a new account, or &lt;span style=&quot; font-weight:600;&quot;&gt;log in&lt;/span&gt; with an already existing username?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
@@ -269,7 +269,7 @@
<string>Configure or select a provider</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
- <item row="0" column="0">
+ <item row="1" column="0">
<widget class="QRadioButton" name="rbNewProvider">
<property name="text">
<string>Configure new provider:</string>
@@ -279,14 +279,14 @@
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QRadioButton" name="rbExistingProvider">
- <property name="text">
- <string>Use existing one:</string>
+ <item row="0" column="2">
+ <widget class="QComboBox" name="cbProviders">
+ <property name="enabled">
+ <bool>false</bool>
</property>
</widget>
</item>
- <item row="1" column="0">
+ <item row="1" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>https://</string>
@@ -296,17 +296,20 @@
</property>
</widget>
</item>
- <item row="1" column="1">
+ <item row="1" column="2">
<widget class="QLineEdit" name="lnProvider"/>
</item>
- <item row="1" column="2">
- <widget class="QPushButton" name="btnCheck">
+ <item row="0" column="0">
+ <widget class="QRadioButton" name="rbExistingProvider">
<property name="text">
- <string>Check</string>
+ <string>Use existing one:</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
</property>
</widget>
</item>
- <item row="3" column="0">
+ <item row="0" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string>https://</string>
@@ -316,12 +319,29 @@
</property>
</widget>
</item>
- <item row="3" column="1">
- <widget class="QComboBox" name="cbProviders">
- <property name="enabled">
- <bool>false</bool>
- </property>
- </widget>
+ <item row="2" column="2">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnCheck">
+ <property name="text">
+ <string>Check</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</widget>
@@ -820,8 +840,8 @@
<slot>setFocus()</slot>
<hints>
<hint type="sourcelabel">
- <x>167</x>
- <y>192</y>
+ <x>174</x>
+ <y>174</y>
</hint>
<hint type="destinationlabel">
<x>265</x>
@@ -836,12 +856,12 @@
<slot>setFocus()</slot>
<hints>
<hint type="sourcelabel">
- <x>171</x>
- <y>164</y>
+ <x>174</x>
+ <y>227</y>
</hint>
<hint type="destinationlabel">
- <x>246</x>
- <y>164</y>
+ <x>425</x>
+ <y>254</y>
</hint>
</hints>
</connection>
@@ -852,12 +872,12 @@
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
- <x>169</x>
- <y>196</y>
+ <x>174</x>
+ <y>174</y>
</hint>
<hint type="destinationlabel">
- <x>327</x>
- <y>163</y>
+ <x>450</x>
+ <y>266</y>
</hint>
</hints>
</connection>
@@ -868,8 +888,8 @@
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
- <x>169</x>
- <y>162</y>
+ <x>174</x>
+ <y>227</y>
</hint>
<hint type="destinationlabel">
<x>269</x>
@@ -881,15 +901,15 @@
<sender>rbExistingProvider</sender>
<signal>toggled(bool)</signal>
<receiver>btnCheck</receiver>
- <slot>setDisabled(bool)</slot>
+ <slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
- <x>154</x>
- <y>193</y>
+ <x>169</x>
+ <y>174</y>
</hint>
<hint type="destinationlabel">
- <x>498</x>
- <y>170</y>
+ <x>520</x>
+ <y>255</y>
</hint>
</hints>
</connection>
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index ec007110..e2c1a16e 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -17,21 +17,17 @@
"""
First run wizard
"""
-import os
import logging
-import json
import random
from functools import partial
from PySide import QtCore, QtGui
-from twisted.internet import threads
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
-from leap.bitmask.crypto.srpregister import SRPRegister
+from leap.bitmask.provider import get_provider_path
from leap.bitmask.services import get_service_display_name, get_supported
-from leap.bitmask.util.request_helpers import get_content
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.password import basic_password_checks
@@ -70,8 +66,6 @@ class Wizard(QtGui.QWizard):
self.ui = Ui_Wizard()
self.ui.setupUi(self)
- self._backend = backend
-
self.setPixmap(QtGui.QWizard.LogoPixmap,
QtGui.QPixmap(":/images/mask-icon.png"))
@@ -90,19 +84,8 @@ class Wizard(QtGui.QWizard):
self.ui.btnCheck.clicked.connect(self._check_provider)
self.ui.lnProvider.returnPressed.connect(self._check_provider)
- self._backend.signaler.prov_name_resolution.connect(
- self._name_resolution)
- self._backend.signaler.prov_https_connection.connect(
- self._https_connection)
- self._backend.signaler.prov_download_provider_info.connect(
- self._download_provider_info)
-
- self._backend.signaler.prov_download_ca_cert.connect(
- self._download_ca_cert)
- self._backend.signaler.prov_check_ca_fingerprint.connect(
- self._check_ca_fingerprint)
- self._backend.signaler.prov_check_api_certificate.connect(
- self._check_api_certificate)
+ self._backend = backend
+ self._backend_connect()
self._domain = None
# HACK!! We need provider_config for the time being, it'll be
@@ -120,6 +103,8 @@ class Wizard(QtGui.QWizard):
self.ui.lnProvider.textChanged.connect(self._enable_check)
self.ui.rbNewProvider.toggled.connect(
lambda x: self._enable_check())
+ self.ui.cbProviders.currentIndexChanged[int].connect(
+ self._reset_provider_check)
self.ui.lblUser.returnPressed.connect(
self._focus_password)
@@ -172,6 +157,7 @@ class Wizard(QtGui.QWizard):
self._provider_setup_ok = False
self.ui.lnProvider.setText('')
self.ui.grpCheckProvider.setVisible(False)
+ self._backend_disconnect()
def _load_configured_providers(self):
"""
@@ -205,6 +191,10 @@ class Wizard(QtGui.QWizard):
random.shuffle(pinned) # don't prioritize alphabetically
self.ui.cbProviders.addItems(pinned)
+ # We have configured providers, so by default we select the
+ # 'Use existing provider' option.
+ self.ui.rbExistingProvider.setChecked(True)
+
def get_domain(self):
return self._domain
@@ -231,7 +221,7 @@ class Wizard(QtGui.QWizard):
depending on the lnProvider content.
"""
enabled = len(self.ui.lnProvider.text()) != 0
- enabled = enabled and self.ui.rbNewProvider.isChecked()
+ enabled = enabled or self.ui.rbExistingProvider.isChecked()
self.ui.btnCheck.setEnabled(enabled)
if reset:
@@ -261,16 +251,11 @@ class Wizard(QtGui.QWizard):
ok, msg = basic_password_checks(username, password, password2)
if ok:
- register = SRPRegister(provider_config=self._provider_config)
- register.registration_finished.connect(
- self._registration_finished)
-
- threads.deferToThread(
- partial(register.register_user, username, password))
+ self._set_register_status(self.tr("Starting registration..."))
+ self._backend.register_user(self._domain, username, password)
self._username = username
self._password = password
- self._set_register_status(self.tr("Starting registration..."))
else:
self._set_register_status(msg, error=True)
self._focus_password()
@@ -297,42 +282,59 @@ class Wizard(QtGui.QWizard):
# register button
self.ui.btnRegister.setVisible(visible)
- def _registration_finished(self, ok, req):
- if ok:
- user_domain = self._username + "@" + self._domain
- message = "<font color='green'><h3>"
- message += self.tr("User %s successfully registered.") % (
- user_domain, )
- message += "</h3></font>"
- self._set_register_status(message)
-
- self.ui.lblPassword2.clearFocus()
- self._set_registration_fields_visibility(False)
-
- # Allow the user to remember his password
- if has_keyring():
- self.ui.chkRemember.setVisible(True)
- self.ui.chkRemember.setEnabled(True)
-
- self.page(self.REGISTER_USER_PAGE).set_completed()
- self.button(QtGui.QWizard.BackButton).setEnabled(False)
- else:
- old_username = self._username
- self._username = None
- self._password = None
- error_msg = self.tr("Something has gone wrong. "
- "Please try again.")
- try:
- content, _ = get_content(req)
- json_content = json.loads(content)
- error_msg = json_content.get("errors").get("login")[0]
- if not error_msg.istitle():
- error_msg = "%s %s" % (old_username, error_msg)
- except Exception as e:
- logger.error("Unknown error: %r" % (e,))
-
- self._set_register_status(error_msg, error=True)
- self.ui.btnRegister.setEnabled(True)
+ def _registration_finished(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._backend.signaler.srp_registration_finished
+
+ The registration has finished successfully, so we do some final steps.
+ """
+ user_domain = self._username + "@" + self._domain
+ message = "<font color='green'><h3>"
+ message += self.tr("User %s successfully registered.") % (
+ user_domain, )
+ message += "</h3></font>"
+ self._set_register_status(message)
+
+ self.ui.lblPassword2.clearFocus()
+ self._set_registration_fields_visibility(False)
+
+ # Allow the user to remember his password
+ if has_keyring():
+ self.ui.chkRemember.setVisible(True)
+ self.ui.chkRemember.setEnabled(True)
+
+ self.page(self.REGISTER_USER_PAGE).set_completed()
+ self.button(QtGui.QWizard.BackButton).setEnabled(False)
+
+ def _registration_failed(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._backend.signaler.srp_registration_failed
+
+ The registration has failed, so we report the problem.
+ """
+ self._username = self._password = None
+
+ error_msg = self.tr("Something has gone wrong. Please try again.")
+ self._set_register_status(error_msg, error=True)
+ self.ui.btnRegister.setEnabled(True)
+
+ def _registration_taken(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._backend.signaler.srp_registration_taken
+
+ The requested username is taken, warn the user about that.
+ """
+ self._username = self._password = None
+
+ error_msg = self.tr("The requested username is taken, choose another.")
+ self._set_register_status(error_msg, error=True)
+ self.ui.btnRegister.setEnabled(True)
def _set_register_status(self, status, error=False):
"""
@@ -375,8 +377,10 @@ class Wizard(QtGui.QWizard):
Starts the checks for a given provider
"""
- if len(self.ui.lnProvider.text()) == 0:
- return
+ if self.ui.rbNewProvider.isChecked():
+ self._domain = self.ui.lnProvider.text()
+ else:
+ self._domain = self.ui.cbProviders.currentText()
self._provider_checks_ok = False
@@ -388,7 +392,6 @@ class Wizard(QtGui.QWizard):
self.ui.btnCheck.setEnabled(False)
self.ui.lnProvider.setEnabled(False)
self.button(QtGui.QWizard.BackButton).clearFocus()
- self._domain = self.ui.lnProvider.text()
self.ui.lblNameResolution.setPixmap(self.QUESTION_ICON)
self._provider_select_defer = self._backend.\
@@ -409,8 +412,6 @@ class Wizard(QtGui.QWizard):
if skip:
self._reset_provider_check()
- self.page(self.SELECT_PROVIDER_PAGE).set_completed(skip)
- self.button(QtGui.QWizard.NextButton).setEnabled(skip)
self._use_existing_provider = skip
def _complete_task(self, data, label, complete=False, complete_page=-1):
@@ -487,10 +488,7 @@ class Wizard(QtGui.QWizard):
check. Since this check is the last of this set, it also
completes the page if passed
"""
- if self._provider_config.load(os.path.join("leap",
- "providers",
- self._domain,
- "provider.json")):
+ if self._provider_config.load(get_provider_path(self._domain)):
self._complete_task(data, self.ui.lblProviderInfo,
True, self.SELECT_PROVIDER_PAGE)
self._provider_checks_ok = True
@@ -686,3 +684,41 @@ class Wizard(QtGui.QWizard):
self.ui.lblUser.setText("")
self.ui.lblPassword.setText("")
self.ui.lblPassword2.setText("")
+
+ def _backend_connect(self):
+ """
+ Connects all the backend signals with the wizard.
+ """
+ sig = self._backend.signaler
+ sig.prov_name_resolution.connect(self._name_resolution)
+ sig.prov_https_connection.connect(self._https_connection)
+ sig.prov_download_provider_info.connect(self._download_provider_info)
+
+ sig.prov_download_ca_cert.connect(self._download_ca_cert)
+ sig.prov_check_ca_fingerprint.connect(self._check_ca_fingerprint)
+ sig.prov_check_api_certificate.connect(self._check_api_certificate)
+
+ sig.srp_registration_finished.connect(self._registration_finished)
+ sig.srp_registration_failed.connect(self._registration_failed)
+ sig.srp_registration_taken.connect(self._registration_taken)
+
+ def _backend_disconnect(self):
+ """
+ This method is called when the wizard dialog is closed.
+ We disconnect all the backend signals in here.
+ """
+ sig = self._backend.signaler
+ try:
+ # disconnect backend signals
+ sig.prov_name_resolution.disconnect(self._name_resolution)
+ sig.prov_https_connection.disconnect(self._https_connection)
+ sig.prov_download_provider_info.disconnect(
+ self._download_provider_info)
+
+ sig.prov_download_ca_cert.disconnect(self._download_ca_cert)
+ sig.prov_check_ca_fingerprint.disconnect(
+ self._check_ca_fingerprint)
+ sig.prov_check_api_certificate.disconnect(
+ self._check_api_certificate)
+ except RuntimeError:
+ pass # Signal was not connected