summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/bug-handle-closed-soledad1
-rw-r--r--changes/bug_cancel-login-does-not-work1
-rw-r--r--src/leap/bitmask/backend.py36
-rw-r--r--src/leap/bitmask/crypto/srpauth.py16
-rwxr-xr-xsrc/leap/bitmask/crypto/tests/fake_provider.py1
-rw-r--r--src/leap/bitmask/gui/mainwindow.py72
-rw-r--r--src/leap/bitmask/platform_init/locks.py2
-rw-r--r--src/leap/bitmask/services/abstractbootstrapper.py7
-rw-r--r--src/leap/bitmask/services/soledad/soledadbootstrapper.py19
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)