diff options
-rw-r--r-- | changes/bug-handle-closed-soledad | 1 | ||||
-rw-r--r-- | changes/bug_cancel-login-does-not-work | 1 | ||||
-rw-r--r-- | src/leap/bitmask/backend.py | 36 | ||||
-rw-r--r-- | src/leap/bitmask/crypto/srpauth.py | 16 | ||||
-rwxr-xr-x | src/leap/bitmask/crypto/tests/fake_provider.py | 1 | ||||
-rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 72 | ||||
-rw-r--r-- | src/leap/bitmask/platform_init/locks.py | 2 | ||||
-rw-r--r-- | src/leap/bitmask/services/abstractbootstrapper.py | 7 | ||||
-rw-r--r-- | src/leap/bitmask/services/soledad/soledadbootstrapper.py | 19 |
9 files changed, 107 insertions, 48 deletions
diff --git a/changes/bug-handle-closed-soledad b/changes/bug-handle-closed-soledad new file mode 100644 index 00000000..bf0a905e --- /dev/null +++ b/changes/bug-handle-closed-soledad @@ -0,0 +1 @@ +- Handle closed Soledad database on quit, speedup exit. Closes #5130. diff --git a/changes/bug_cancel-login-does-not-work b/changes/bug_cancel-login-does-not-work new file mode 100644 index 00000000..8fbccb5a --- /dev/null +++ b/changes/bug_cancel-login-does-not-work @@ -0,0 +1 @@ +- Cancel login does not work or needs to be pressed twice. Closes #4869, #4973. 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..b46f0ea6 100644 --- a/src/leap/bitmask/crypto/srpauth.py +++ b/src/leap/bitmask/crypto/srpauth.py @@ -600,7 +600,8 @@ class SRPAuth(QtCore.QObject): QtCore.QMutexLocker(self._uuid_lock) full_uid = "%s@%s" % ( self._username, self._provider_config.get_domain()) - self._settings.set_uuid(full_uid, uuid) + if uuid is not None: # avoid removing the uuid from settings + self._settings.set_uuid(full_uid, uuid) self._uuid = uuid def get_uuid(self): @@ -655,7 +656,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 +695,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/crypto/tests/fake_provider.py b/src/leap/bitmask/crypto/tests/fake_provider.py index 54af485d..b8cdbb12 100755 --- a/src/leap/bitmask/crypto/tests/fake_provider.py +++ b/src/leap/bitmask/crypto/tests/fake_provider.py @@ -280,7 +280,6 @@ class FakeSession(Resource): if HAMK is None: print '[server] verification failed!!!' raise Exception("Authentication failed!") - #import ipdb;ipdb.set_trace() assert svr.authenticated() print "***" diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index db24a1c8..6512ffce 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -20,10 +20,12 @@ Main window for Bitmask. import logging from threading import Condition -from PySide import QtCore, QtGui from datetime import datetime -from twisted.internet import threads + +from PySide import QtCore, QtGui 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 @@ -341,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) @@ -394,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. @@ -1034,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 @@ -1042,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): """ @@ -1079,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") @@ -1167,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) ################################################################### @@ -1242,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 @@ -1813,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]) # @@ -1887,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/platform_init/locks.py b/src/leap/bitmask/platform_init/locks.py index 34f884dc..78ebf4cd 100644 --- a/src/leap/bitmask/platform_init/locks.py +++ b/src/leap/bitmask/platform_init/locks.py @@ -83,8 +83,6 @@ if platform_init.IS_UNIX: flock(self._fd, LOCK_EX | LOCK_NB) except IOError as exc: # could not get the lock - #import ipdb; ipdb.set_trace() - if exc.args[0] in (errno.EDEADLK, errno.EAGAIN): # errno 11 or 35 # Resource temporarily unavailable 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 f7217af6..b61d0d43 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -20,6 +20,7 @@ Soledad bootstrapping import logging import os import socket +import sqlite3 import sys from ssl import SSLError @@ -348,6 +349,10 @@ class SoledadBootstrapper(AbstractBootstrapper): # ubuntu folks. sync_tries -= 1 continue + except Exception as e: + logger.exception("Unhandled error while syncing " + "soledad: %r" % (e,)) + break # reached bottom, failed to sync # and there's nothing we can do... @@ -435,7 +440,9 @@ class SoledadBootstrapper(AbstractBootstrapper): except u1db_errors.InvalidGeneration as exc: logger.error("%r" % (exc,)) raise SoledadSyncError("u1db: InvalidGeneration") - + except sqlite3.ProgrammingError as e: + logger.exception("%r" % (e,)) + raise except Exception as exc: logger.exception("Unhandled error while syncing " "soledad: %r" % (exc,)) @@ -508,10 +515,10 @@ class SoledadBootstrapper(AbstractBootstrapper): if flags.OFFLINE is True: args = (address, "https://localhost", self._soledad) kwargs = { - "session_id": "", - "ca_cert_path": "", - "api_uri": "", - "api_version": "", + "session_id": "", + "ca_cert_path": "", + "api_uri": "", + "api_version": "", "uid": self._uuid, "gpgbinary": self._get_gpg_bin_path() } @@ -617,4 +624,4 @@ class SoledadBootstrapper(AbstractBootstrapper): (self._gen_key, self.gen_key) ] - self.addCallbackChain(cb_chain) + return self.addCallbackChain(cb_chain) |