diff options
author | Tomás Touceda <chiiph@leap.se> | 2014-05-28 11:05:25 -0300 |
---|---|---|
committer | Tomás Touceda <chiiph@leap.se> | 2014-05-28 11:05:25 -0300 |
commit | c85b1d594225cb71d744f387ab82107c499da947 (patch) | |
tree | e583f8bf852665c67f91d49c5e911199cb60c03b | |
parent | 19140b9b1959218c1d6bbfec105a1d62125c2635 (diff) | |
parent | 027aff14f3ea9c6965100a625071505f63271ffd (diff) |
Merge remote-tracking branch 'refs/remotes/ivan/feature/cleanup-backend-from-hacks' into develop
-rw-r--r-- | changes/refactor-and-cleanup | 3 | ||||
-rw-r--r-- | src/leap/bitmask/backend.py | 365 | ||||
-rw-r--r-- | src/leap/bitmask/config/providerconfig.py | 100 | ||||
-rw-r--r-- | src/leap/bitmask/gui/advanced_key_management.py | 237 | ||||
-rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 217 | ||||
-rw-r--r-- | src/leap/bitmask/gui/preferenceswindow.py | 114 | ||||
-rw-r--r-- | src/leap/bitmask/gui/wizard.py | 73 | ||||
-rw-r--r-- | src/leap/bitmask/services/mail/conductor.py | 4 | ||||
-rw-r--r-- | src/leap/bitmask/services/soledad/soledadbootstrapper.py | 3 |
9 files changed, 706 insertions, 410 deletions
diff --git a/changes/refactor-and-cleanup b/changes/refactor-and-cleanup new file mode 100644 index 00000000..18331ed4 --- /dev/null +++ b/changes/refactor-and-cleanup @@ -0,0 +1,3 @@ +- Refactor Keymanager to backend. Closes #5711. +- Cleanup backend from hacks. Closes #5698. + 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): |