summaryrefslogtreecommitdiff
path: root/src/leap/bitmask
diff options
context:
space:
mode:
authorTomás Touceda <chiiph@leap.se>2014-05-28 11:05:25 -0300
committerTomás Touceda <chiiph@leap.se>2014-05-28 11:05:25 -0300
commitc85b1d594225cb71d744f387ab82107c499da947 (patch)
treee583f8bf852665c67f91d49c5e911199cb60c03b /src/leap/bitmask
parent19140b9b1959218c1d6bbfec105a1d62125c2635 (diff)
parent027aff14f3ea9c6965100a625071505f63271ffd (diff)
Merge remote-tracking branch 'refs/remotes/ivan/feature/cleanup-backend-from-hacks' into develop
Diffstat (limited to 'src/leap/bitmask')
-rw-r--r--src/leap/bitmask/backend.py365
-rw-r--r--src/leap/bitmask/config/providerconfig.py100
-rw-r--r--src/leap/bitmask/gui/advanced_key_management.py237
-rw-r--r--src/leap/bitmask/gui/mainwindow.py217
-rw-r--r--src/leap/bitmask/gui/preferenceswindow.py114
-rw-r--r--src/leap/bitmask/gui/wizard.py73
-rw-r--r--src/leap/bitmask/services/mail/conductor.py4
-rw-r--r--src/leap/bitmask/services/soledad/soledadbootstrapper.py3
8 files changed, 703 insertions, 410 deletions
diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py
index d6d5004f..5e22a8c4 100644
--- a/src/leap/bitmask/backend.py
+++ b/src/leap/bitmask/backend.py
@@ -37,8 +37,8 @@ from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.crypto.srpauth import SRPAuth
from leap.bitmask.crypto.srpregister import SRPRegister
from leap.bitmask.platform_init import IS_LINUX
-from leap.bitmask.provider import get_provider_path
from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
+from leap.bitmask.services import get_supported
from leap.bitmask.services.eip import eipconfig
from leap.bitmask.services.eip import get_openvpn_management
from leap.bitmask.services.eip.eipbootstrapper import EIPBootstrapper
@@ -56,6 +56,9 @@ from leap.bitmask.services.soledad.soledadbootstrapper import \
from leap.common import certs as leap_certs
+from leap.keymanager import openpgp
+from leap.keymanager.errors import KeyAddressMismatch, KeyFingerprintMismatch
+
from leap.soledad.client import NoStorageSecret, PassphraseTooShort
# Frontend side
@@ -64,26 +67,6 @@ from PySide import QtCore
logger = logging.getLogger(__name__)
-def get_provider_config(config, domain):
- """
- Return the ProviderConfig object for the given domain.
- If it is already loaded in `config`, then don't reload.
-
- :param config: a ProviderConfig object
- :type conig: ProviderConfig
- :param domain: the domain which config is required.
- :type domain: unicode
-
- :returns: True if the config was loaded successfully, False otherwise.
- :rtype: bool
- """
- # TODO: see ProviderConfig.get_provider_config
- if (not config.loaded() or config.get_domain() != domain):
- config.load(get_provider_path(domain))
-
- return config.loaded()
-
-
class ILEAPComponent(zope.interface.Interface):
"""
Interface that every component for the backend should comply to
@@ -167,6 +150,7 @@ class Provider(object):
:type bypass_checks: bool
"""
self.key = "provider"
+ self._signaler = signaler
self._provider_bootstrapper = ProviderBootstrapper(signaler,
bypass_checks)
self._download_provider_defer = None
@@ -208,13 +192,9 @@ class Provider(object):
"""
d = None
- # TODO: use this commented code when we don't need the provider config
- # in the maiwindow.
- # config = ProviderConfig.get_provider_config(provider)
- # self._provider_config = config
- # if config is not None:
- config = self._provider_config
- if get_provider_config(config, provider):
+ config = ProviderConfig.get_provider_config(provider)
+ self._provider_config = config
+ if config is not None:
d = self._provider_bootstrapper.run_provider_setup_checks(
config, download_if_needed=True)
else:
@@ -228,6 +208,73 @@ class Provider(object):
d = defer.Deferred()
return d
+ def _get_services(self, domain):
+ """
+ Returns a list of services provided by the given provider.
+
+ :param domain: the provider to get the services from.
+ :type domain: str
+
+ :rtype: list of str
+ """
+ services = []
+ provider_config = ProviderConfig.get_provider_config(domain)
+ if provider_config is not None:
+ services = provider_config.get_services()
+
+ return services
+
+ def get_supported_services(self, domain):
+ """
+ Signal a list of supported services provided by the given provider.
+
+ :param domain: the provider to get the services from.
+ :type domain: str
+
+ Signals:
+ prov_get_supported_services -> list of unicode
+ """
+ services = get_supported(self._get_services(domain))
+
+ self._signaler.signal(
+ self._signaler.PROV_GET_SUPPORTED_SERVICES, services)
+
+ def get_all_services(self, providers):
+ """
+ Signal a list of services provided by all the configured providers.
+
+ :param providers: the list of providers to get the services.
+ :type providers: list
+
+ Signals:
+ prov_get_all_services -> list of unicode
+ """
+ services_all = set()
+
+ for domain in providers:
+ services = self._get_services(domain)
+ services_all = services_all.union(set(services))
+
+ self._signaler.signal(
+ self._signaler.PROV_GET_ALL_SERVICES, services_all)
+
+ def get_details(self, domain, lang=None):
+ """
+ Signal a ProviderConfigLight object with the current ProviderConfig
+ settings.
+
+ :param domain: the domain name of the provider.
+ :type domain: str
+ :param lang: the language to use for localized strings.
+ :type lang: str
+
+ Signals:
+ prov_get_details -> ProviderConfigLight
+ """
+ self._signaler.signal(
+ self._signaler.PROV_GET_DETAILS,
+ self._provider_config.get_light_config(domain, lang))
+
class Register(object):
"""
@@ -701,6 +748,125 @@ class Soledad(object):
d.addErrback(self._change_password_error)
+class Keymanager(object):
+ """
+ Interfaces with KeyManager.
+ """
+ zope.interface.implements(ILEAPComponent)
+
+ def __init__(self, keymanager_proxy, signaler=None):
+ """
+ Constructor for the Keymanager component.
+
+ :param keymanager_proxy: proxy to pass around a Keymanager object.
+ :type keymanager_proxy: zope.ProxyBase
+ :param signaler: Object in charge of handling communication
+ back to the frontend
+ :type signaler: Signaler
+ """
+ self.key = "keymanager"
+ self._keymanager_proxy = keymanager_proxy
+ self._signaler = signaler
+
+ def import_keys(self, username, filename):
+ """
+ Imports the username's key pair.
+ Those keys need to be ascii armored.
+
+ :param username: the user that will have the imported pair of keys.
+ :type username: str
+ :param filename: the name of the file where the key pair is stored.
+ :type filename: str
+ """
+ # NOTE: This feature is disabled right now since is dangerous
+ return
+
+ new_key = ''
+ signal = None
+ try:
+ with open(filename, 'r') as keys_file:
+ new_key = keys_file.read()
+ except IOError as e:
+ logger.error("IOError importing key. {0!r}".format(e))
+ signal = self._signaler.KEYMANAGER_IMPORT_IOERROR
+ self._signaler.signal(signal)
+ return
+
+ keymanager = self._keymanager_proxy
+ try:
+ public_key, private_key = keymanager.parse_openpgp_ascii_key(
+ new_key)
+ except (KeyAddressMismatch, KeyFingerprintMismatch) as e:
+ logger.error(repr(e))
+ signal = self._signaler.KEYMANAGER_IMPORT_DATAMISMATCH
+ self._signaler.signal(signal)
+ return
+
+ if public_key is None or private_key is None:
+ signal = self._signaler.KEYMANAGER_IMPORT_MISSINGKEY
+ self._signaler.signal(signal)
+ return
+
+ current_public_key = keymanager.get_key(username, openpgp.OpenPGPKey)
+ if public_key.address != current_public_key.address:
+ logger.error("The key does not match the ID")
+ signal = self._signaler.KEYMANAGER_IMPORT_ADDRESSMISMATCH
+ self._signaler.signal(signal)
+ return
+
+ keymanager.delete_key(self._key)
+ keymanager.delete_key(self._key_priv)
+ keymanager.put_key(public_key)
+ keymanager.put_key(private_key)
+ keymanager.send_key(openpgp.OpenPGPKey)
+
+ logger.debug('Import ok')
+ signal = self._signaler.KEYMANAGER_IMPORT_OK
+
+ self._signaler.signal(signal)
+
+ def export_keys(self, username, filename):
+ """
+ Export the given username's keys to a file.
+
+ :param username: the username whos keys we need to export.
+ :type username: str
+ :param filename: the name of the file where we want to save the keys.
+ :type filename: str
+ """
+ keymanager = self._keymanager_proxy
+
+ public_key = keymanager.get_key(username, openpgp.OpenPGPKey)
+ private_key = keymanager.get_key(username, openpgp.OpenPGPKey,
+ private=True)
+ try:
+ with open(filename, 'w') as keys_file:
+ keys_file.write(public_key.key_data)
+ keys_file.write(private_key.key_data)
+
+ logger.debug('Export ok')
+ self._signaler.signal(self._signaler.KEYMANAGER_EXPORT_OK)
+ except IOError as e:
+ logger.error("IOError exporting key. {0!r}".format(e))
+ self._signaler.signal(self._signaler.KEYMANAGER_EXPORT_ERROR)
+
+ def list_keys(self):
+ """
+ List all the keys stored in the local DB.
+ """
+ keys = self._keymanager_proxy.get_all_keys_in_local_db()
+ self._signaler.signal(self._signaler.KEYMANAGER_KEYS_LIST, keys)
+
+ def get_key_details(self, username):
+ """
+ List all the keys stored in the local DB.
+ """
+ public_key = self._keymanager_proxy.get_key(username,
+ openpgp.OpenPGPKey)
+ details = (public_key.key_id, public_key.fingerprint)
+ self._signaler.signal(self._signaler.KEYMANAGER_KEY_DETAILS, details)
+
+
class Mail(object):
"""
Interfaces with setup and launch of Mail.
@@ -926,6 +1092,10 @@ class Signaler(QtCore.QObject):
prov_unsupported_client = QtCore.Signal(object)
prov_unsupported_api = QtCore.Signal(object)
+ prov_get_all_services = QtCore.Signal(object)
+ prov_get_supported_services = QtCore.Signal(object)
+ prov_get_details = QtCore.Signal(object)
+
prov_cancelled_setup = QtCore.Signal(object)
# Signals for SRPRegister
@@ -999,6 +1169,19 @@ class Signaler(QtCore.QObject):
soledad_password_change_ok = QtCore.Signal(object)
soledad_password_change_error = QtCore.Signal(object)
+ # Keymanager signals
+ keymanager_export_ok = QtCore.Signal(object)
+ keymanager_export_error = QtCore.Signal(object)
+ keymanager_keys_list = QtCore.Signal(object)
+
+ keymanager_import_ioerror = QtCore.Signal(object)
+ keymanager_import_datamismatch = QtCore.Signal(object)
+ keymanager_import_missingkey = QtCore.Signal(object)
+ keymanager_import_addressmismatch = QtCore.Signal(object)
+ keymanager_import_ok = QtCore.Signal(object)
+
+ keymanager_key_details = QtCore.Signal(object)
+
# mail related signals
imap_stopped = QtCore.Signal(object)
@@ -1021,6 +1204,9 @@ class Signaler(QtCore.QObject):
PROV_UNSUPPORTED_CLIENT = "prov_unsupported_client"
PROV_UNSUPPORTED_API = "prov_unsupported_api"
PROV_CANCELLED_SETUP = "prov_cancelled_setup"
+ PROV_GET_ALL_SERVICES = "prov_get_all_services"
+ PROV_GET_SUPPORTED_SERVICES = "prov_get_supported_services"
+ PROV_GET_DETAILS = "prov_get_details"
SRP_REGISTRATION_FINISHED = "srp_registration_finished"
SRP_REGISTRATION_FAILED = "srp_registration_failed"
@@ -1084,6 +1270,17 @@ class Signaler(QtCore.QObject):
SOLEDAD_CANCELLED_BOOTSTRAP = "soledad_cancelled_bootstrap"
+ KEYMANAGER_EXPORT_OK = "keymanager_export_ok"
+ KEYMANAGER_EXPORT_ERROR = "keymanager_export_error"
+ KEYMANAGER_KEYS_LIST = "keymanager_keys_list"
+
+ KEYMANAGER_IMPORT_IOERROR = "keymanager_import_ioerror"
+ KEYMANAGER_IMPORT_DATAMISMATCH = "keymanager_import_datamismatch"
+ KEYMANAGER_IMPORT_MISSINGKEY = "keymanager_import_missingkey"
+ KEYMANAGER_IMPORT_ADDRESSMISMATCH = "keymanager_import_addressmismatch"
+ KEYMANAGER_IMPORT_OK = "keymanager_import_ok"
+ KEYMANAGER_KEY_DETAILS = "keymanager_key_details"
+
IMAP_STOPPED = "imap_stopped"
BACKEND_BAD_CALL = "backend_bad_call"
@@ -1106,6 +1303,9 @@ class Signaler(QtCore.QObject):
self.PROV_UNSUPPORTED_CLIENT,
self.PROV_UNSUPPORTED_API,
self.PROV_CANCELLED_SETUP,
+ self.PROV_GET_ALL_SERVICES,
+ self.PROV_GET_SUPPORTED_SERVICES,
+ self.PROV_GET_DETAILS,
self.SRP_REGISTRATION_FINISHED,
self.SRP_REGISTRATION_FAILED,
@@ -1169,6 +1369,17 @@ class Signaler(QtCore.QObject):
self.SOLEDAD_PASSWORD_CHANGE_OK,
self.SOLEDAD_PASSWORD_CHANGE_ERROR,
+ self.KEYMANAGER_EXPORT_OK,
+ self.KEYMANAGER_EXPORT_ERROR,
+ self.KEYMANAGER_KEYS_LIST,
+
+ self.KEYMANAGER_IMPORT_IOERROR,
+ self.KEYMANAGER_IMPORT_DATAMISMATCH,
+ self.KEYMANAGER_IMPORT_MISSINGKEY,
+ self.KEYMANAGER_IMPORT_ADDRESSMISMATCH,
+ self.KEYMANAGER_IMPORT_OK,
+ self.KEYMANAGER_KEY_DETAILS,
+
self.IMAP_STOPPED,
self.BACKEND_BAD_CALL,
@@ -1238,6 +1449,8 @@ class Backend(object):
self._register(Soledad(self._soledad_proxy,
self._keymanager_proxy,
self._signaler))
+ self._register(Keymanager(self._keymanager_proxy,
+ self._signaler))
self._register(Mail(self._soledad_proxy,
self._keymanager_proxy,
self._signaler))
@@ -1393,6 +1606,47 @@ class Backend(object):
"""
self._call_queue.put(("provider", "bootstrap", None, provider))
+ def provider_get_supported_services(self, domain):
+ """
+ Signal a list of supported services provided by the given provider.
+
+ :param domain: the provider to get the services from.
+ :type domain: str
+
+ Signals:
+ prov_get_supported_services -> list of unicode
+ """
+ self._call_queue.put(("provider", "get_supported_services", None,
+ domain))
+
+ def provider_get_all_services(self, providers):
+ """
+ Signal a list of services provided by all the configured providers.
+
+ :param providers: the list of providers to get the services.
+ :type providers: list
+
+ Signals:
+ prov_get_all_services -> list of unicode
+ """
+ self._call_queue.put(("provider", "get_all_services", None,
+ providers))
+
+ def provider_get_details(self, domain, lang):
+ """
+ Signal a ProviderConfigLight object with the current ProviderConfig
+ settings.
+
+ :param domain: the domain name of the provider.
+ :type domain: str
+ :param lang: the language to use for localized strings.
+ :type lang: str
+
+ Signals:
+ prov_get_details -> ProviderConfigLight
+ """
+ self._call_queue.put(("provider", "get_details", None, domain, lang))
+
def user_register(self, provider, username, password):
"""
Register a user using the domain and password given as parameters.
@@ -1654,6 +1908,43 @@ class Backend(object):
"""
self._call_queue.put(("soledad", "close", None))
+ def keymanager_list_keys(self):
+ """
+ Signal a list of public keys locally stored.
+
+ Signals:
+ keymanager_keys_list -> list
+ """
+ self._call_queue.put(("keymanager", "list_keys", None))
+
+ def keymanager_export_keys(self, username, filename):
+ """
+ Export the given username's keys to a file.
+
+ :param username: the username whos keys we need to export.
+ :type username: str
+ :param filename: the name of the file where we want to save the keys.
+ :type filename: str
+
+ Signals:
+ keymanager_export_ok
+ keymanager_export_error
+ """
+ self._call_queue.put(("keymanager", "export_keys", None,
+ username, filename))
+
+ def keymanager_get_key_details(self, username):
+ """
+ Signal the given username's key details.
+
+ :param username: the username whos keys we need to get details.
+ :type username: str
+
+ Signals:
+ keymanager_key_details
+ """
+ self._call_queue.put(("keymanager", "get_key_details", None, username))
+
def smtp_start_service(self, full_user_id, download_if_needed=False):
"""
Start the SMTP service.
@@ -1693,21 +1984,3 @@ class Backend(object):
imap_stopped
"""
self._call_queue.put(("mail", "stop_imap_service", None))
-
- ###########################################################################
- # XXX HACK: this section is meant to be a place to hold methods and
- # variables needed in the meantime while we migrate all to the backend.
-
- def get_provider_config(self):
- # TODO: refactor the provider config into a singleton/global loading it
- # every time from the file.
- provider_config = self._components["provider"]._provider_config
- return provider_config
-
- def get_soledad(self):
- soledad = self._components["soledad"]._soledad_bootstrapper._soledad
- return soledad
-
- def get_keymanager(self):
- km = self._components["soledad"]._soledad_bootstrapper._keymanager
- return km
diff --git a/src/leap/bitmask/config/providerconfig.py b/src/leap/bitmask/config/providerconfig.py
index 2ebe05ce..cf31b3b2 100644
--- a/src/leap/bitmask/config/providerconfig.py
+++ b/src/leap/bitmask/config/providerconfig.py
@@ -38,6 +38,35 @@ class MissingCACert(Exception):
pass
+class ProviderConfigLight(object):
+ """
+ A light config object to hold some provider settings needed by the GUI.
+ """
+ def __init__(self):
+ """
+ Define the public attributes.
+ """
+ self.domain = ""
+ self.name = ""
+ self.description = ""
+ self.enrollment_policy = ""
+ self.services = []
+
+ @property
+ def services_string(self):
+ """
+ Return a comma separated list of serices provided by this provider.
+
+ :rtype: str
+ """
+ services = []
+ for service in self.services:
+ services.append(get_service_display_name(service))
+
+ services_str = ", ".join(services)
+ return services_str
+
+
class ProviderConfig(BaseConfig):
"""
Provider configuration abstraction class
@@ -45,6 +74,29 @@ class ProviderConfig(BaseConfig):
def __init__(self):
BaseConfig.__init__(self)
+ def get_light_config(self, domain, lang=None):
+ """
+ Return a ProviderConfigLight object with the data for the loaded
+ object.
+
+ :param domain: the domain name of the provider.
+ :type domain: str
+ :param lang: the language to use for localized strings.
+ :type lang: str
+
+ :rtype: ProviderConfigLight or None if the ProviderConfig isn't loaded.
+ """
+ config = self.get_provider_config(domain)
+ details = ProviderConfigLight()
+
+ details.domain = config.get_domain()
+ details.name = config.get_name(lang=lang)
+ details.description = config.get_description(lang=lang)
+ details.enrollment_policy = config.get_enrollment_policy()
+ details.services = config.get_services()
+
+ return details
+
@classmethod
def get_provider_config(self, domain):
"""
@@ -144,18 +196,6 @@ class ProviderConfig(BaseConfig):
services = self._safe_get_value("services")
return services
- def get_services_string(self):
- """
- Returns a string with the available services in the current
- provider, ready to be shown to the user.
- """
- services = []
- for service in self.get_services():
- services.append(get_service_display_name(service))
-
- services_str = ", ".join(services)
- return services_str
-
def get_ca_cert_path(self, about_to_download=False):
"""
Returns the path to the certificate for the current provider.
@@ -199,39 +239,3 @@ class ProviderConfig(BaseConfig):
:rtype: bool
"""
return "mx" in self.get_services()
-
-
-if __name__ == "__main__":
- logger = logging.getLogger(name='leap')
- logger.setLevel(logging.DEBUG)
- console = logging.StreamHandler()
- console.setLevel(logging.DEBUG)
- formatter = logging.Formatter(
- '%(asctime)s '
- '- %(name)s - %(levelname)s - %(message)s')
- console.setFormatter(formatter)
- logger.addHandler(console)
-
- provider = ProviderConfig()
-
- try:
- provider.get_api_version()
- except Exception as e:
- assert isinstance(e, AssertionError), "Expected an assert"
- print "Safe value getting is working"
-
- # standalone minitest
- #if provider.load("provider_bad.json"):
- if provider.load("leap/providers/bitmask.net/provider.json"):
- print provider.get_api_version()
- print provider.get_ca_cert_fingerprint()
- print provider.get_ca_cert_uri()
- print provider.get_default_language()
- print provider.get_description()
- print provider.get_description(lang="asd")
- print provider.get_domain()
- print provider.get_enrollment_policy()
- print provider.get_languages()
- print provider.get_name()
- print provider.get_services()
- print provider.get_services_string()
diff --git a/src/leap/bitmask/gui/advanced_key_management.py b/src/leap/bitmask/gui/advanced_key_management.py
index 1681caca..b3a4ed8e 100644
--- a/src/leap/bitmask/gui/advanced_key_management.py
+++ b/src/leap/bitmask/gui/advanced_key_management.py
@@ -19,10 +19,8 @@ Advanced Key Management
"""
import logging
-from PySide import QtGui
+from PySide import QtCore, QtGui
-from leap.keymanager import openpgp
-from leap.keymanager.errors import KeyAddressMismatch, KeyFingerprintMismatch
from leap.bitmask.services import get_service_display_name, MX_SERVICE
from ui_advanced_key_management import Ui_AdvancedKeyManagement
@@ -33,7 +31,7 @@ class AdvancedKeyManagement(QtGui.QDialog):
"""
Advanced Key Management
"""
- def __init__(self, parent, has_mx, user, keymanager, soledad_started):
+ def __init__(self, parent, has_mx, user, backend, soledad_started):
"""
:param parent: parent object of AdvancedKeyManagement.
:parent type: QWidget
@@ -42,8 +40,8 @@ class AdvancedKeyManagement(QtGui.QDialog):
:type has_mx: bool
:param user: the current logged in user.
:type user: unicode
- :param keymanager: the existing keymanager instance
- :type keymanager: KeyManager
+ :param backend: Backend being used
+ :type backend: Backend
:param soledad_started: whether soledad has started or not
:type soledad_started: bool
"""
@@ -75,16 +73,12 @@ class AdvancedKeyManagement(QtGui.QDialog):
# "existing e-mails.")
# self.ui.lblStatus.setText(msg)
- self._keymanager = keymanager
-
- self._key = keymanager.get_key(user, openpgp.OpenPGPKey)
- self._key_priv = keymanager.get_key(
- user, openpgp.OpenPGPKey, private=True)
+ self._user = user
+ self._backend = backend
+ self._backend_connect()
# show current key information
self.ui.leUser.setText(user)
- self.ui.leKeyID.setText(self._key.key_id)
- self.ui.leFingerprint.setText(self._key.fingerprint)
# set up connections
self.ui.pbImportKeys.clicked.connect(self._import_keys)
@@ -94,7 +88,15 @@ class AdvancedKeyManagement(QtGui.QDialog):
self.ui.twPublicKeys.horizontalHeader().setResizeMode(
0, QtGui.QHeaderView.Stretch)
- self._list_keys()
+ self._backend.keymanager_get_key_details(user)
+ self._backend.keymanager_list_keys()
+
+ def _keymanager_key_details(self, details):
+ """
+ Set the current user's key details into the gui.
+ """
+ self.ui.leKeyID.setText(details[0])
+ self.ui.leFingerprint.setText(details[1])
def _disable_ui(self, msg):
"""
@@ -113,53 +115,11 @@ class AdvancedKeyManagement(QtGui.QDialog):
Imports the user's key pair.
Those keys need to be ascii armored.
"""
- fileName, filtr = QtGui.QFileDialog.getOpenFileName(
+ file_name, filtr = QtGui.QFileDialog.getOpenFileName(
self, self.tr("Open keys file"),
options=QtGui.QFileDialog.DontUseNativeDialog)
- if fileName:
- new_key = ''
- try:
- with open(fileName, 'r') as keys_file:
- new_key = keys_file.read()
- except IOError as e:
- logger.error("IOError importing key. {0!r}".format(e))
- QtGui.QMessageBox.critical(
- self, self.tr("Input/Output error"),
- self.tr("There was an error accessing the file.\n"
- "Import canceled."))
- return
-
- keymanager = self._keymanager
- try:
- public_key, private_key = keymanager.parse_openpgp_ascii_key(
- new_key)
- except (KeyAddressMismatch, KeyFingerprintMismatch) as e:
- logger.error(repr(e))
- QtGui.QMessageBox.warning(
- self, self.tr("Data mismatch"),
- self.tr("The public and private key should have the "
- "same address and fingerprint.\n"
- "Import canceled."))
- return
-
- if public_key is None or private_key is None:
- QtGui.QMessageBox.warning(
- self, self.tr("Missing key"),
- self.tr("You need to provide the public AND private "
- "key in the same file.\n"
- "Import canceled."))
- return
-
- if public_key.address != self._key.address:
- logger.error("The key does not match the ID")
- QtGui.QMessageBox.warning(
- self, self.tr("Address mismatch"),
- self.tr("The identity for the key needs to be the same "
- "as your user address.\n"
- "Import canceled."))
- return
-
+ if file_name:
question = self.tr("Are you sure that you want to replace "
"the current key pair whith the imported?")
res = QtGui.QMessageBox.question(
@@ -167,61 +127,152 @@ class AdvancedKeyManagement(QtGui.QDialog):
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
QtGui.QMessageBox.No) # default No
- if res == QtGui.QMessageBox.No:
- return
+ if res == QtGui.QMessageBox.Yes:
+ self._backend.keymanager_import_keys(self._user, file_name)
+ else:
+ logger.debug('Import canceled by the user.')
- keymanager.delete_key(self._key)
- keymanager.delete_key(self._key_priv)
- keymanager.put_key(public_key)
- keymanager.put_key(private_key)
- keymanager.send_key(openpgp.OpenPGPKey)
+ @QtCore.Slot()
+ def _keymanager_import_ok(self):
+ """
+ TRIGGERS:
+ Signaler.keymanager_import_ok
- logger.debug('Import ok')
+ Notify the user that the key import went OK.
+ """
+ QtGui.QMessageBox.information(
+ self, self.tr("Import Successful"),
+ self.tr("The key pair was imported successfully."))
- QtGui.QMessageBox.information(
- self, self.tr("Import Successful"),
- self.tr("The key pair was imported successfully."))
- else:
- logger.debug('Import canceled by the user.')
+ @QtCore.Slot()
+ def _import_ioerror(self):
+ """
+ TRIGGERS:
+ Signaler.keymanager_import_ioerror
+
+ Notify the user that the key import had an IOError problem.
+ """
+ QtGui.QMessageBox.critical(
+ self, self.tr("Input/Output error"),
+ self.tr("There was an error accessing the file.\n"
+ "Import canceled."))
+
+ @QtCore.Slot()
+ def _import_datamismatch(self):
+ """
+ TRIGGERS:
+ Signaler.keymanager_import_datamismatch
+
+ Notify the user that the key import had an data mismatch problem.
+ """
+ QtGui.QMessageBox.warning(
+ self, self.tr("Data mismatch"),
+ self.tr("The public and private key should have the "
+ "same address and fingerprint.\n"
+ "Import canceled."))
+
+ @QtCore.Slot()
+ def _import_missingkey(self):
+ """
+ TRIGGERS:
+ Signaler.keymanager_import_missingkey
+
+ Notify the user that the key import failed due a missing key.
+ """
+ QtGui.QMessageBox.warning(
+ self, self.tr("Missing key"),
+ self.tr("You need to provide the public AND private "
+ "key in the same file.\n"
+ "Import canceled."))
+
+ @QtCore.Slot()
+ def _import_addressmismatch(self):
+ """
+ TRIGGERS:
+ Signaler.keymanager_import_addressmismatch
+
+ Notify the user that the key import failed due an address mismatch.
+ """
+ QtGui.QMessageBox.warning(
+ self, self.tr("Address mismatch"),
+ self.tr("The identity for the key needs to be the same "
+ "as your user address.\n"
+ "Import canceled."))
def _export_keys(self):
"""
Exports the user's key pair.
"""
- fileName, filtr = QtGui.QFileDialog.getSaveFileName(
+ file_name, filtr = QtGui.QFileDialog.getSaveFileName(
self, self.tr("Save keys file"),
options=QtGui.QFileDialog.DontUseNativeDialog)
- if fileName:
- try:
- with open(fileName, 'w') as keys_file:
- keys_file.write(self._key.key_data)
- keys_file.write(self._key_priv.key_data)
-
- logger.debug('Export ok')
- QtGui.QMessageBox.information(
- self, self.tr("Export Successful"),
- self.tr("The key pair was exported successfully.\n"
- "Please, store your private key in a safe place."))
- except IOError as e:
- logger.error("IOError exporting key. {0!r}".format(e))
- QtGui.QMessageBox.critical(
- self, self.tr("Input/Output error"),
- self.tr("There was an error accessing the file.\n"
- "Export canceled."))
- return
+ if file_name:
+ self._backend.keymanager_export_keys(self._user, file_name)
else:
logger.debug('Export canceled by the user.')
- def _list_keys(self):
+ @QtCore.Slot()
+ def _keymanager_export_ok(self):
+ """
+ TRIGGERS:
+ Signaler.keymanager_export_ok
+
+ Notify the user that the key export went OK.
"""
- Loads all the public keys stored in the local db to the keys table.
+ QtGui.QMessageBox.information(
+ self, self.tr("Export Successful"),
+ self.tr("The key pair was exported successfully.\n"
+ "Please, store your private key in a safe place."))
+
+ @QtCore.Slot()
+ def _keymanager_export_error(self):
+ """
+ TRIGGERS:
+ Signaler.keymanager_export_error
+
+ Notify the user that the key export didn't go well.
+ """
+ QtGui.QMessageBox.critical(
+ self, self.tr("Input/Output error"),
+ self.tr("There was an error accessing the file.\n"
+ "Export canceled."))
+
+ @QtCore.Slot()
+ def _keymanager_keys_list(self, keys):
"""
- keys = self._keymanager.get_all_keys_in_local_db()
+ TRIGGERS:
+ Signaler.keymanager_keys_list
+ Load the keys given as parameter in the table.
+
+ :param keys: the list of keys to load.
+ :type keys: list
+ """
keys_table = self.ui.twPublicKeys
+
for key in keys:
row = keys_table.rowCount()
keys_table.insertRow(row)
keys_table.setItem(row, 0, QtGui.QTableWidgetItem(key.address))
keys_table.setItem(row, 1, QtGui.QTableWidgetItem(key.key_id))
+
+ def _backend_connect(self):
+ """
+ Connect to backend signals.
+ """
+ sig = self._backend.signaler
+
+ sig.keymanager_export_ok.connect(self._keymanager_export_ok)
+ sig.keymanager_export_error.connect(self._keymanager_export_error)
+ sig.keymanager_keys_list.connect(self._keymanager_keys_list)
+
+ sig.keymanager_key_details.connect(self._keymanager_key_details)
+
+ sig.keymanager_import_ok.connect(self._keymanager_import_ok)
+
+ sig.keymanager_import_ioerror.connect(self._import_ioerror)
+ sig.keymanager_import_datamismatch.connect(self._import_datamismatch)
+ sig.keymanager_import_missingkey.connect(self._import_missingkey)
+ sig.keymanager_import_addressmismatch.connect(
+ self._import_addressmismatch)
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index 201a24ec..cf6614be 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -23,14 +23,12 @@ import socket
from datetime import datetime
from PySide import QtCore, QtGui
-from zope.proxy import ProxyBase, setProxiedObject
from twisted.internet import reactor, threads
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.gui import statemachines
from leap.bitmask.gui.advanced_key_management import AdvancedKeyManagement
@@ -43,7 +41,6 @@ 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
from leap.bitmask.platform_init.initializers import init_platform
@@ -64,7 +61,6 @@ if IS_WIN:
from leap.bitmask.platform_init.locks import WindowsLock
from leap.bitmask.platform_init.locks import raise_window_ack
-from leap.common.check import leap_assert
from leap.common.events import register
from leap.common.events import events_pb2 as proto
@@ -178,15 +174,6 @@ class MainWindow(QtGui.QMainWindow):
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.get_provider_config()
-
- # Used for automatic start of EIP
- self._provisional_provider_config = ProviderConfig()
-
self._already_started_eip = False
self._soledad_started = False
@@ -247,6 +234,8 @@ class MainWindow(QtGui.QMainWindow):
self._ui_mx_visible = True
self._ui_eip_visible = True
+ self._provider_details = None
+
# last minute UI manipulations
self._center_window()
@@ -279,8 +268,6 @@ class MainWindow(QtGui.QMainWindow):
self._bypass_checks = bypass_checks
self._start_hidden = start_hidden
- self._keymanager = ProxyBase(None)
-
self._mail_conductor = mail_conductor.MailConductor(self._backend)
self._mail_conductor.connect_mail_signals(self._mail_status)
@@ -383,6 +370,9 @@ class MainWindow(QtGui.QMainWindow):
self._connect_and_track(sig.prov_cancelled_setup,
self._set_login_cancelled)
+ self._connect_and_track(sig.prov_get_details,
+ self._provider_get_details)
+
# Login signals
self._connect_and_track(sig.srp_auth_ok, self._authentication_finished)
@@ -430,9 +420,14 @@ class MainWindow(QtGui.QMainWindow):
sig.backend_bad_call.connect(self._backend_bad_call)
+ sig.prov_check_api_certificate.connect(self._get_provider_details)
+
sig.prov_unsupported_client.connect(self._needs_update)
sig.prov_unsupported_api.connect(self._incompatible_api)
+ sig.prov_get_all_services.connect(
+ self._provider_get_all_services)
+
# EIP start signals
sig.eip_openvpn_already_running.connect(
self._on_eip_openvpn_already_running)
@@ -597,13 +592,14 @@ class MainWindow(QtGui.QMainWindow):
domain = self._login_widget.get_selected_provider()
logged_user = "{0}@{1}".format(self._logged_user, domain)
- has_mx = True
- if self._logged_user is not None:
- provider_config = self._get_best_provider_config()
- has_mx = provider_config.provides_mx()
+ details = self._provider_details
+ mx_provided = False
+ if details is not None:
+ mx_provided = MX_SERVICE in details.services
- akm = AdvancedKeyManagement(self, has_mx, logged_user,
- self._keymanager, self._soledad_started)
+ # XXX: handle differently not logged in user?
+ akm = AdvancedKeyManagement(self, mx_provided, logged_user,
+ self._backend, self._soledad_started)
akm.show()
@QtCore.Slot()
@@ -618,8 +614,7 @@ class MainWindow(QtGui.QMainWindow):
user = self._login_widget.get_user()
prov = self._login_widget.get_selected_provider()
preferences = PreferencesWindow(
- self, self._backend, self._provider_config,
- self._soledad_started, user, prov)
+ self, self._backend, self._soledad_started, user, prov)
self.soledad_ready.connect(preferences.set_soledad_ready)
preferences.show()
@@ -857,16 +852,9 @@ class MainWindow(QtGui.QMainWindow):
"""
providers = self._settings.get_configured_providers()
- services = set()
-
- for prov in providers:
- provider_config = ProviderConfig()
- loaded = provider_config.load(
- provider.get_provider_path(prov))
- if loaded:
- for service in provider_config.get_services():
- services.add(service)
+ self._backend.provider_get_all_services(providers)
+ def _provider_get_all_services(self, services):
self._set_eip_visible(EIP_SERVICE in services)
self._set_mx_visible(MX_SERVICE in services)
@@ -904,14 +892,11 @@ class MainWindow(QtGui.QMainWindow):
"""
Set the login label to reflect offline status.
"""
- if self._logged_in_offline:
- provider = ""
- else:
+ provider = ""
+ if not self._logged_in_offline:
provider = self.ui.lblLoginProvider.text()
- self.ui.lblLoginProvider.setText(
- provider +
- self.tr(" (offline mode)"))
+ self.ui.lblLoginProvider.setText(provider + self.tr(" (offline mode)"))
#
# systray
@@ -1165,9 +1150,8 @@ class MainWindow(QtGui.QMainWindow):
provider configuration if it's not present, otherwise will
emit the corresponding signals inmediately
"""
- # XXX should rename this provider, name clash.
- provider = self._login_widget.get_selected_provider()
- self._backend.provider_setup(provider)
+ domain = self._login_widget.get_selected_provider()
+ self._backend.provider_setup(domain)
@QtCore.Slot(dict)
def _load_provider_config(self, data):
@@ -1175,12 +1159,11 @@ class MainWindow(QtGui.QMainWindow):
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
- part of the bootstrapping sequence
+ Once the provider config has been downloaded, start the second
+ part of the bootstrapping sequence.
:param data: result from the last stage of the
- run_provider_select_checks
+ backend.provider_setup()
:type data: dict
"""
if data[self._backend.PASSED_KEY]:
@@ -1222,7 +1205,6 @@ class MainWindow(QtGui.QMainWindow):
self._set_label_offline()
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()
@@ -1290,15 +1272,13 @@ class MainWindow(QtGui.QMainWindow):
Once the provider configuration is loaded, this starts the SRP
authentication
"""
- leap_assert(self._provider_config, "We need a provider config!")
-
if data[self._backend.PASSED_KEY]:
username = self._login_widget.get_user()
password = self._login_widget.get_password()
self._show_hide_unsupported_services()
- domain = self._provider_config.get_domain()
+ domain = self._login_widget.get_selected_provider()
self._backend.user_login(domain, username, password)
else:
logger.error(data[self._backend.ERROR_KEY])
@@ -1317,7 +1297,7 @@ class MainWindow(QtGui.QMainWindow):
self._logged_user = self._login_widget.get_user()
user = self._logged_user
- domain = self._provider_config.get_domain()
+ domain = self._login_widget.get_selected_provider()
full_user_id = make_address(user, domain)
self._mail_conductor.userid = full_user_id
self._start_eip_bootstrap()
@@ -1331,7 +1311,7 @@ class MainWindow(QtGui.QMainWindow):
sig.soledad_bootstrap_failed.connect(lambda: btn_enabled(True))
sig.soledad_bootstrap_finished.connect(lambda: btn_enabled(True))
- if not self._get_best_provider_config().provides_mx():
+ if not MX_SERVICE in self._provider_details.services:
self._set_mx_visible(False)
def _start_eip_bootstrap(self):
@@ -1341,11 +1321,10 @@ class MainWindow(QtGui.QMainWindow):
"""
self._login_widget.logged_in()
- provider = self._provider_config.get_domain()
- self.ui.lblLoginProvider.setText(provider)
+ domain = self._login_widget.get_selected_provider()
+ self.ui.lblLoginProvider.setText(domain)
- self._enabled_services = self._settings.get_enabled_services(
- self._provider_config.get_domain())
+ self._enabled_services = self._settings.get_enabled_services(domain)
# TODO separate UI from logic.
if self._provides_mx_and_enabled():
@@ -1355,6 +1334,30 @@ class MainWindow(QtGui.QMainWindow):
self._maybe_start_eip()
+ @QtCore.Slot()
+ def _get_provider_details(self):
+ """
+ TRIGGERS:
+ prov_check_api_certificate
+
+ Set the attributes to know if the EIP and MX services are supported
+ and enabled.
+ This is triggered right after the provider has been set up.
+ """
+ domain = self._login_widget.get_selected_provider()
+ lang = QtCore.QLocale.system().name()
+ self._backend.provider_get_details(domain, lang)
+
+ @QtCore.Slot()
+ def _provider_get_details(self, details):
+ """
+ Set the details for the just downloaded provider.
+
+ :param details: the details of the provider.
+ :type details: ProviderConfigLight
+ """
+ self._provider_details = details
+
def _provides_mx_and_enabled(self):
"""
Defines if the current provider provides mx and if we have it enabled.
@@ -1362,9 +1365,15 @@ class MainWindow(QtGui.QMainWindow):
: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)
+ domain = self._login_widget.get_selected_provider()
+ enabled_services = self._settings.get_enabled_services(domain)
+
+ mx_enabled = MX_SERVICE in enabled_services
+ mx_provided = False
+ if self._provider_details is not None:
+ mx_provided = MX_SERVICE in self._provider_details.services
+
+ return mx_enabled and mx_provided
def _provides_eip_and_enabled(self):
"""
@@ -1373,16 +1382,23 @@ class MainWindow(QtGui.QMainWindow):
: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)
+ domain = self._login_widget.get_selected_provider()
+ enabled_services = self._settings.get_enabled_services(domain)
+
+ eip_enabled = EIP_SERVICE in enabled_services
+ eip_provided = False
+ if self._provider_details is not None:
+ eip_provided = EIP_SERVICE in self._provider_details.services
+
+ return eip_enabled and eip_provided
def _maybe_run_soledad_setup_checks(self):
"""
Conditionally start Soledad.
"""
# TODO split.
- if not self._provides_mx_and_enabled():
+ if not self._provides_mx_and_enabled() and not flags.OFFLINE:
+ logger.debug("Does not provides and enabled MX")
return
username = self._login_widget.get_user()
@@ -1390,9 +1406,6 @@ class MainWindow(QtGui.QMainWindow):
provider_domain = self._login_widget.get_selected_provider()
if flags.OFFLINE:
- 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
@@ -1405,7 +1418,7 @@ class MainWindow(QtGui.QMainWindow):
self._backend.soledad_load_offline(full_user_id, password, uuid)
else:
if self._logged_user is not None:
- domain = self._provider_config.get_domain()
+ domain = self._login_widget.get_selected_provider()
self._backend.soledad_bootstrap(username, domain, password)
###################################################################
@@ -1421,12 +1434,7 @@ class MainWindow(QtGui.QMainWindow):
"""
logger.debug("Done bootstrapping Soledad")
- # Update the proxy objects to point to the initialized instances.
- # setProxiedObject(self._soledad, self._backend.get_soledad())
- setProxiedObject(self._keymanager, self._backend.get_keymanager())
-
self._soledad_started = True
-
self.soledad_ready.emit()
###################################################################
@@ -1457,19 +1465,8 @@ class MainWindow(QtGui.QMainWindow):
# 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:
- 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 and provides_mx:
- start_fun()
- return
-
- if self._provides_mx_and_enabled():
- start_fun()
+ if self._provides_mx_and_enabled() or flags.OFFLINE:
+ self._mail_conductor.start_imap_service()
# end service control methods (imap)
@@ -1519,9 +1516,7 @@ class MainWindow(QtGui.QMainWindow):
"""
self._eip_connection.qtsigs.connected_signal.emit()
- provider_config = self._get_best_provider_config()
- domain = provider_config.get_domain()
-
+ domain = self._login_widget.get_selected_provider()
self._eip_status.set_provider(domain)
self._settings.set_defaultprovider(domain)
self._already_started_eip = True
@@ -1582,19 +1577,8 @@ class MainWindow(QtGui.QMainWindow):
self._enabled_services = settings.get_enabled_services(
default_provider)
- loaded = self._provisional_provider_config.load(
- provider.get_provider_path(default_provider))
- 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.
+ if settings.get_autostart_eip():
self._maybe_start_eip()
- 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):
@@ -1700,11 +1684,12 @@ class MainWindow(QtGui.QMainWindow):
logger.debug('Setting autostart to: False')
self._settings.set_autostart_eip(False)
- if self._logged_user:
- self._eip_status.set_provider(
- make_address(
- self._logged_user,
- self._get_best_provider_config().get_domain()))
+ user = self._logged_user
+ if user:
+ domain = self._login_widget.get_selected_provider()
+ full_user_id = make_address(user, domain)
+ self._eip_status.set_provider(full_user_id)
+
self._eip_status.eip_stopped()
@QtCore.Slot()
@@ -1874,30 +1859,6 @@ class MainWindow(QtGui.QMainWindow):
# end of EIP methods ---------------------------------------------
- def _get_best_provider_config(self):
- """
- Returns the best ProviderConfig to use at a moment. We may
- have to use self._provider_config or
- self._provisional_provider_config depending on the start
- status.
-
- :rtype: ProviderConfig
- """
- # TODO move this out of gui.
- leap_assert(self._provider_config is not None or
- self._provisional_provider_config is not None,
- "We need a provider config")
-
- provider_config = None
- if self._provider_config.loaded():
- provider_config = self._provider_config
- elif self._provisional_provider_config.loaded():
- provider_config = self._provisional_provider_config
- else:
- leap_assert(False, "We could not find any usable ProviderConfig.")
-
- return provider_config
-
@QtCore.Slot()
def _logout(self):
"""
diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py
index 0a4c7f56..c67052f3 100644
--- a/src/leap/bitmask/gui/preferenceswindow.py
+++ b/src/leap/bitmask/gui/preferenceswindow.py
@@ -24,12 +24,9 @@ from functools import partial
from PySide import QtCore, QtGui
-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.bitmask.util.credentials import password_checks
-from leap.bitmask.services import get_supported
-from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.services import get_service_display_name, MX_SERVICE
logger = logging.getLogger(__name__)
@@ -41,15 +38,12 @@ class PreferencesWindow(QtGui.QDialog):
"""
preferences_saved = QtCore.Signal()
- def __init__(self, parent, backend, provider_config, soledad_started,
- username, domain):
+ def __init__(self, parent, backend, soledad_started, username, domain):
"""
:param parent: parent object of the PreferencesWindow.
:parent type: QWidget
:param backend: Backend being used
:type backend: Backend
- :param provider_config: ProviderConfig object.
- :type provider_config: ProviderConfig
:param soledad_started: whether soledad has started or not
:type soledad_started: bool
:param username: the user set in the login widget
@@ -62,7 +56,6 @@ class PreferencesWindow(QtGui.QDialog):
self._backend = backend
self._settings = LeapSettings()
- self._provider_config = provider_config
self._soledad_started = soledad_started
self._username = username
self._domain = domain
@@ -99,31 +92,7 @@ class PreferencesWindow(QtGui.QDialog):
Actions to perform is the user is logged in.
"""
- settings = self._settings
- pw_enabled = True
-
- # check if provider has 'mx' ...
- # TODO: we should move this to the backend.
- if self._provider_config.provides_mx():
- enabled_services = settings.get_enabled_services(self._domain)
- mx_name = get_service_display_name(MX_SERVICE)
-
- # ... and if the user have it enabled
- if MX_SERVICE not in enabled_services:
- msg = self.tr("You need to enable {0} in order to change "
- "the password.".format(mx_name))
- self._set_password_change_status(msg, error=True)
- pw_enabled = False
- else:
- # check if Soledad is bootstrapped
- if not self._soledad_started:
- msg = self.tr(
- "You need to wait until {0} is ready in "
- "order to change the password.".format(mx_name))
- self._set_password_change_status(msg)
- pw_enabled = False
-
- self.ui.gbPasswordChange.setEnabled(pw_enabled)
+ self._backend.provider_provides_mx()
@QtCore.Slot()
def _not_logged_in(self):
@@ -139,6 +108,44 @@ class PreferencesWindow(QtGui.QDialog):
self.ui.gbPasswordChange.setEnabled(False)
@QtCore.Slot()
+ def _provides_mx(self):
+ """
+ TRIGGERS:
+ Signaler.prov_provides_mx
+
+ Actions to perform if the provider provides MX.
+ """
+ pw_enabled = True
+ enabled_services = self._settings.get_enabled_services(self._domain)
+ mx_name = get_service_display_name(MX_SERVICE)
+
+ if MX_SERVICE not in enabled_services:
+ msg = self.tr("You need to enable {0} in order to change "
+ "the password.".format(mx_name))
+ self._set_password_change_status(msg, error=True)
+ pw_enabled = False
+ else:
+ # check if Soledad is bootstrapped
+ if not self._soledad_started:
+ msg = self.tr(
+ "You need to wait until {0} is ready in "
+ "order to change the password.".format(mx_name))
+ self._set_password_change_status(msg)
+ pw_enabled = False
+
+ self.ui.gbPasswordChange.setEnabled(pw_enabled)
+
+ @QtCore.Slot()
+ def _not_provides_mx(self):
+ """
+ TRIGGERS:
+ Signaler.prov_not_provides_mx
+
+ Actions to perform if the provider does not provides MX.
+ """
+ self.ui.gbPasswordChange.setEnabled(False)
+
+ @QtCore.Slot()
def set_soledad_ready(self):
"""
TRIGGERS:
@@ -339,8 +346,7 @@ class PreferencesWindow(QtGui.QDialog):
TRIGGERS:
self.ui.cbProvidersServices.currentIndexChanged[unicode]
- Loads the services that the provider provides into the UI for
- the user to enable or disable.
+ Fill the services list with the selected provider's services.
:param domain: the domain of the provider to load services from.
:type domain: str
@@ -351,10 +357,6 @@ class PreferencesWindow(QtGui.QDialog):
if not domain:
return
- provider_config = self._get_provider_config(domain)
- if provider_config is None:
- return
-
# set the proper connection for the 'save' button
try:
self.ui.pbSaveServices.clicked.disconnect()
@@ -364,7 +366,21 @@ class PreferencesWindow(QtGui.QDialog):
save_services = partial(self._save_enabled_services, domain)
self.ui.pbSaveServices.clicked.connect(save_services)
- services = get_supported(provider_config.get_services())
+ self._backend.provider_get_supported_services(domain)
+
+ @QtCore.Slot(str)
+ def _load_services(self, services):
+ """
+ TRIGGERS:
+ self.ui.cbProvidersServices.currentIndexChanged[unicode]
+
+ Loads the services that the provider provides into the UI for
+ the user to enable or disable.
+
+ :param domain: the domain of the provider to load services from.
+ :type domain: str
+ """
+ domain = self.ui.cbProvidersServices.currentText()
services_conf = self._settings.get_enabled_services(domain)
# discard changes if other provider is selected
@@ -412,27 +428,15 @@ class PreferencesWindow(QtGui.QDialog):
self._set_providers_services_status(msg, success=True)
self.preferences_saved.emit()
- def _get_provider_config(self, domain):
- """
- Helper to return a valid Provider Config from the domain name.
-
- :param domain: the domain name of the provider.
- :type domain: str
-
- :rtype: ProviderConfig or None if there is a problem loading the config
- """
- provider_config = ProviderConfig()
- if not provider_config.load(get_provider_path(domain)):
- provider_config = None
-
- return provider_config
-
def _backend_connect(self):
"""
Helper to connect to backend signals
"""
sig = self._backend.signaler
+ sig.prov_provides_mx.connect(self._provides_mx)
+ sig.prov_get_supported_services.connect(self._load_services)
+
sig.srp_status_logged_in.connect(self._is_logged_in)
sig.srp_status_not_logged_in.connect(self._not_logged_in)
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index ce45b431..4d774907 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -26,8 +26,6 @@ from PySide import QtCore, QtGui
from leap.bitmask.config import flags
from leap.bitmask.config.leapsettings import LeapSettings
-from leap.bitmask.config.providerconfig import ProviderConfig
-from leap.bitmask.provider import get_provider_path
from leap.bitmask.services import get_service_display_name, get_supported
from leap.bitmask.util.credentials import password_checks, username_checks
from leap.bitmask.util.credentials import USERNAME_REGEX
@@ -88,10 +86,9 @@ class Wizard(QtGui.QWizard):
self._backend_connect()
self._domain = None
- # HACK!! We need provider_config for the time being, it'll be
- # removed
- self._provider_config = (
- self._backend._components["provider"]._provider_config)
+
+ # this details are set when the provider download is complete.
+ self._provider_details = None
# We will store a reference to the defers for eventual use
# (eg, to cancel them) but not doing anything with them right now.
@@ -513,10 +510,12 @@ 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(get_provider_path(self._domain)):
+ if data[self._backend.PASSED_KEY]:
self._complete_task(data, self.ui.lblProviderInfo,
True, self.SELECT_PROVIDER_PAGE)
self._provider_checks_ok = True
+ lang = QtCore.QLocale.system().name()
+ self._backend.provider_get_details(self._domain, lang)
else:
new_data = {
self._backend.PASSED_KEY: False,
@@ -538,6 +537,16 @@ class Wizard(QtGui.QWizard):
else:
self.ui.cbProviders.setEnabled(True)
+ @QtCore.Slot()
+ def _provider_get_details(self, details):
+ """
+ Set the details for the just downloaded provider.
+
+ :param details: the details of the provider.
+ :type details: ProviderConfigLight
+ """
+ self._provider_details = details
+
@QtCore.Slot(dict)
def _download_ca_cert(self, data):
"""
@@ -605,11 +614,9 @@ class Wizard(QtGui.QWizard):
the user to enable or disable.
"""
self.ui.grpServices.setTitle(
- self.tr("Services by %s") %
- (self._provider_config.get_name(),))
+ self.tr("Services by {0}").format(self._provider_details.name))
- services = get_supported(
- self._provider_config.get_services())
+ services = get_supported(self._provider_details.services)
for service in services:
try:
@@ -652,38 +659,31 @@ class Wizard(QtGui.QWizard):
if not self._provider_setup_ok:
self._reset_provider_setup()
sub_title = self.tr("Gathering configuration options for {0}")
- sub_title = sub_title.format(self._provider_config.get_name())
+ sub_title = sub_title.format(self._provider_details.name)
self.page(pageId).setSubTitle(sub_title)
self.ui.lblDownloadCaCert.setPixmap(self.QUESTION_ICON)
self._provider_setup_defer = self._backend.\
provider_bootstrap(self._domain)
if pageId == self.PRESENT_PROVIDER_PAGE:
- self.page(pageId).setSubTitle(self.tr("Description of services "
- "offered by %s") %
- (self._provider_config
- .get_name(),))
-
- lang = QtCore.QLocale.system().name()
- self.ui.lblProviderName.setText(
- "<b>%s</b>" %
- (self._provider_config.get_name(lang=lang),))
- self.ui.lblProviderURL.setText(
- "https://%s" % (self._provider_config.get_domain(),))
- self.ui.lblProviderDesc.setText(
- "<i>%s</i>" %
- (self._provider_config.get_description(lang=lang),))
-
- self.ui.lblServicesOffered.setText(self._provider_config
- .get_services_string())
- self.ui.lblProviderPolicy.setText(self._provider_config
- .get_enrollment_policy())
+ sub_title = self.tr("Description of services offered by {0}")
+ sub_title = sub_title.format(self._provider_details.name)
+ self.page(pageId).setSubTitle(sub_title)
+
+ details = self._provider_details
+ name = "<b>{0}</b>".format(details.name)
+ domain = "https://{0}".format(details.domain)
+ description = "<i>{0}</i>".format(details.description)
+ self.ui.lblProviderName.setText(name)
+ self.ui.lblProviderURL.setText(domain)
+ self.ui.lblProviderDesc.setText(description)
+ self.ui.lblServicesOffered.setText(details.services_string)
+ self.ui.lblProviderPolicy.setText(details.enrollment_policy)
if pageId == self.REGISTER_USER_PAGE:
- self.page(pageId).setSubTitle(self.tr("Register a new user with "
- "%s") %
- (self._provider_config
- .get_name(),))
+ sub_title = self.tr("Register a new user with {0}")
+ sub_title = sub_title.format(self._provider_details.name)
+ self.page(pageId).setSubTitle(sub_title)
self.ui.chkRemember.setVisible(False)
if pageId == self.SERVICES_PAGE:
@@ -706,8 +706,6 @@ class Wizard(QtGui.QWizard):
if self.currentPage() == self.page(self.SELECT_PROVIDER_PAGE):
if self._use_existing_provider:
self._domain = self.ui.cbProviders.currentText()
- self._provider_config = ProviderConfig.get_provider_config(
- self._domain)
if self._show_register:
return self.REGISTER_USER_PAGE
else:
@@ -732,6 +730,7 @@ class Wizard(QtGui.QWizard):
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_get_details.connect(self._provider_get_details)
sig.prov_download_ca_cert.connect(self._download_ca_cert)
sig.prov_check_ca_fingerprint.connect(self._check_ca_fingerprint)
diff --git a/src/leap/bitmask/services/mail/conductor.py b/src/leap/bitmask/services/mail/conductor.py
index 7fc19f1c..98b40929 100644
--- a/src/leap/bitmask/services/mail/conductor.py
+++ b/src/leap/bitmask/services/mail/conductor.py
@@ -249,8 +249,8 @@ class MailConductor(IMAPControl, SMTPControl):
"""
Stop the IMAP and SMTP services.
"""
- self.imap_stop_service()
- self.smtp_stop_service()
+ self.stop_imap_service()
+ self.stop_smtp_service()
def connect_mail_signals(self, widget):
"""
diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py
index 2bdad7e2..db12fd80 100644
--- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py
+++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py
@@ -188,8 +188,9 @@ class SoledadBootstrapper(AbstractBootstrapper):
try:
self.load_and_sync_soledad(uuid, offline=True)
self._signaler.signal(self._signaler.SOLEDAD_OFFLINE_FINISHED)
- except Exception:
+ except Exception as e:
# TODO: we should handle more specific exceptions in here
+ logger.exception(e)
self._signaler.signal(self._signaler.SOLEDAD_OFFLINE_FAILED)
def _get_soledad_local_params(self, uuid, offline=False):