From 29902330067f564185d7b57a864be2099f8ea2e8 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 10 Feb 2014 14:29:09 -0300 Subject: Properly handle defer cancelling. - Fix issues related to "Cancel login does not work". - Move srpauth errback to mainwindow. - Add signal for provider setup cancel. - Add support to cancel the soledad defer. [Closes #4869] [Closes #4973] --- src/leap/bitmask/backend.py | 36 ++++++++--- src/leap/bitmask/crypto/srpauth.py | 13 ---- src/leap/bitmask/gui/mainwindow.py | 69 +++++++++++++++++----- src/leap/bitmask/services/abstractbootstrapper.py | 7 +++ .../services/soledad/soledadbootstrapper.py | 2 +- 5 files changed, 89 insertions(+), 38 deletions(-) (limited to 'src/leap') diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index df79381c..34457e34 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -141,6 +141,14 @@ class Provider(object): self._download_provider_defer = d return d + def cancel_setup_provider(self): + """ + Cancel the ongoing setup provider defer (if any). + """ + d = self._download_provider_defer + if d is not None: + d.cancel() + def bootstrap(self, provider): """ Second stage of bootstrapping for a provider. @@ -195,6 +203,8 @@ class Signaler(QtCore.QObject): prov_unsupported_client = QtCore.Signal(object) prov_unsupported_api = QtCore.Signal(object) + prov_cancelled_setup = QtCore.Signal(object) + # These will exist both in the backend and the front end. # The frontend might choose to not "interpret" all the signals # from the backend, but the backend needs to have all the signals @@ -208,6 +218,7 @@ class Signaler(QtCore.QObject): PROV_PROBLEM_WITH_PROVIDER_KEY = "prov_problem_with_provider" PROV_UNSUPPORTED_CLIENT = "prov_unsupported_client" PROV_UNSUPPORTED_API = "prov_unsupported_api" + PROV_CANCELLED_SETUP = "prov_cancelled_setup" def __init__(self): """ @@ -225,7 +236,8 @@ class Signaler(QtCore.QObject): self.PROV_CHECK_API_CERTIFICATE_KEY, self.PROV_PROBLEM_WITH_PROVIDER_KEY, self.PROV_UNSUPPORTED_CLIENT, - self.PROV_UNSUPPORTED_API + self.PROV_UNSUPPORTED_API, + self.PROV_CANCELLED_SETUP, ] for sig in signals: @@ -355,17 +367,20 @@ class Backend(object): # cmd is: component, method, signalback, *args func = getattr(self._components[cmd[0]], cmd[1]) d = func(*cmd[3:]) - # A call might not have a callback signal, but if it does, - # we add it to the chain - if cmd[2] is not None: - d.addCallbacks(self._signal_back, log.err, cmd[2]) - d.addCallbacks(self._done_action, log.err, - callbackKeywords={"d": d}) - d.addErrback(log.err) - self._ongoing_defers.append(d) + if d is not None: # d may be None if a defer chain is cancelled. + # A call might not have a callback signal, but if it does, + # we add it to the chain + if cmd[2] is not None: + d.addCallbacks(self._signal_back, log.err, cmd[2]) + d.addCallbacks(self._done_action, log.err, + callbackKeywords={"d": d}) + d.addErrback(log.err) + self._ongoing_defers.append(d) except Empty: # If it's just empty we don't have anything to do. pass + except defer.CancelledError: + logger.debug("defer cancelled somewhere (CancelledError).") except Exception: # But we log the rest log.err() @@ -387,5 +402,8 @@ class Backend(object): def setup_provider(self, provider): self._call_queue.put(("provider", "setup_provider", None, provider)) + def cancel_setup_provider(self): + self._call_queue.put(("provider", "cancel_setup_provider", None)) + def provider_bootstrap(self, provider): self._call_queue.put(("provider", "bootstrap", None, provider)) diff --git a/src/leap/bitmask/crypto/srpauth.py b/src/leap/bitmask/crypto/srpauth.py index bdd38db2..1fb7b774 100644 --- a/src/leap/bitmask/crypto/srpauth.py +++ b/src/leap/bitmask/crypto/srpauth.py @@ -655,7 +655,6 @@ class SRPAuth(QtCore.QObject): username = username.lower() d = self.__instance.authenticate(username, password) d.addCallback(self._gui_notify) - d.addErrback(self._errback) return d def change_password(self, current_password, new_password): @@ -695,18 +694,6 @@ class SRPAuth(QtCore.QObject): logger.debug("Successful login!") self.authentication_finished.emit(True, self.tr("Succeeded")) - def _errback(self, failure): - """ - General errback for the whole login process. Will notify the - UI with the proper signal. - - :param failure: Failure object captured from a callback. - :type failure: twisted.python.failure.Failure - """ - logger.error("Error logging in %s" % (failure,)) - self.authentication_finished.emit(False, "%s" % (failure.value,)) - failure.trap(Exception) - def get_session_id(self): return self.__instance.get_session_id() diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index c078e3f4..6512ffce 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -23,8 +23,9 @@ from threading import Condition from datetime import datetime from PySide import QtCore, QtGui -from twisted.internet import threads from zope.proxy import ProxyBase, setProxiedObject +from twisted.internet import threads +from twisted.internet.defer import CancelledError from leap.bitmask import __version__ as VERSION from leap.bitmask import __version_hash__ as VERSION_HASH @@ -342,7 +343,7 @@ class MainWindow(QtGui.QMainWindow): self._keymanager = ProxyBase(None) self._login_defer = None - self._download_provider_defer = None + self._soledad_defer = None self._mail_conductor = mail_conductor.MailConductor( self._soledad, self._keymanager) @@ -395,6 +396,8 @@ class MainWindow(QtGui.QMainWindow): sig.prov_unsupported_client.connect(self._needs_update) sig.prov_unsupported_api.connect(self._incompatible_api) + sig.prov_cancelled_setup.connect(self._set_login_cancelled) + def _backend_disconnect(self): """ Helper to disconnect from backend signals. @@ -1035,6 +1038,25 @@ class MainWindow(QtGui.QMainWindow): if self._login_widget.start_login(): self._download_provider_config() + def _login_errback(self, failure): + """ + Error handler for the srpauth.authenticate method. + + :param failure: failure object that Twisted generates + :type failure: twisted.python.failure.Failure + """ + # NOTE: this behavior needs to be managed through the signaler, + # as we are doing with the prov_cancelled_setup signal. + # After we move srpauth to the backend, we need to update this. + logger.error("Error logging in, {0!r}".format(failure)) + if failure.check(CancelledError): + logger.debug("Defer cancelled.") + failure.trap(Exception) + self._set_login_cancelled() + else: + self._login_widget.set_status(str(failure.value)) + self._login_widget.set_enabled(True) + def _cancel_login(self): """ SLOT @@ -1043,17 +1065,29 @@ class MainWindow(QtGui.QMainWindow): Stops the login sequence. """ - logger.debug("Cancelling log in.") + logger.debug("Cancelling setup provider defer.") + self._backend.cancel_setup_provider() - if self._download_provider_defer: - logger.debug("Cancelling download provider defer.") - self._download_provider_defer.cancel() - - if self._login_defer: + if self._login_defer is not None: logger.debug("Cancelling login defer.") self._login_defer.cancel() + if self._soledad_defer is not None: + logger.debug("Cancelling soledad defer.") + self._soledad_defer.cancel() + + def _set_login_cancelled(self): + """ + SLOT + TRIGGERS: + Signaler.prov_cancelled_setup fired by + self._backend.cancel_setup_provider() + + This method re-enables the login widget and display a message for + the cancelled operation. + """ self._login_widget.set_status(self.tr("Log in cancelled by the user.")) + self._login_widget.set_enabled(True) def _provider_config_loaded(self, data): """ @@ -1080,6 +1114,7 @@ class MainWindow(QtGui.QMainWindow): # TODO Add errback! self._login_defer = self._srp_auth.authenticate(username, password) + self._login_defer.addErrback(self._login_errback) else: self._login_widget.set_status( "Unable to login: Problem with provider") @@ -1168,8 +1203,8 @@ class MainWindow(QtGui.QMainWindow): provider_config = self._provider_config if self._logged_user is not None: - fun = sb.run_soledad_setup_checks - fun(provider_config, username, password, + self._soledad_defer = sb.run_soledad_setup_checks( + provider_config, username, password, download_if_needed=True) ################################################################### @@ -1243,6 +1278,7 @@ class MainWindow(QtGui.QMainWindow): # Ok, now soledad is ready, so we can allow other things that # depend on soledad to start. + self._soledad_defer = None # this will trigger start_imap_service # and start_smtp_boostrapping @@ -1814,9 +1850,9 @@ class MainWindow(QtGui.QMainWindow): """ passed = data[self._backend.PASSED_KEY] if not passed: + msg = self.tr("Unable to connect: Problem with provider") + self._login_widget.set_status(msg) self._login_widget.set_enabled(True) - self._login_widget.set_status( - self.tr("Unable to connect: Problem with provider")) logger.error(data[self._backend.ERROR_KEY]) # @@ -1888,9 +1924,12 @@ class MainWindow(QtGui.QMainWindow): logger.debug("Cancelling login defer.") self._login_defer.cancel() - if self._download_provider_defer: - logger.debug("Cancelling download provider defer.") - self._download_provider_defer.cancel() + logger.debug("Cancelling setup provider defer.") + self._backend.cancel_setup_provider() + + if self._soledad_defer is not None: + logger.debug("Cancelling soledad defer.") + self._soledad_defer.cancel() # TODO missing any more cancels? diff --git a/src/leap/bitmask/services/abstractbootstrapper.py b/src/leap/bitmask/services/abstractbootstrapper.py index 3bee8e01..fc6bd3e9 100644 --- a/src/leap/bitmask/services/abstractbootstrapper.py +++ b/src/leap/bitmask/services/abstractbootstrapper.py @@ -28,6 +28,7 @@ from PySide import QtCore from twisted.python import log from twisted.internet import threads +from twisted.internet.defer import CancelledError from leap.common.check import leap_assert, leap_assert_type @@ -91,6 +92,12 @@ class AbstractBootstrapper(QtCore.QObject): :param failure: failure object that Twisted generates :type failure: twisted.python.failure.Failure """ + if failure.check(CancelledError): + logger.debug("Defer cancelled.") + failure.trap(Exception) + self._signaler.signal(self._signaler.PROV_CANCELLED_SETUP) + return + if self._signal_to_emit: err_msg = self._err_msg \ if self._err_msg is not None \ diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index 9a292b18..53846b31 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -617,4 +617,4 @@ class SoledadBootstrapper(AbstractBootstrapper): (self._gen_key, self.gen_key) ] - self.addCallbackChain(cb_chain) + return self.addCallbackChain(cb_chain) -- cgit v1.2.3