summaryrefslogtreecommitdiff
path: root/src/leap/services/abstractbootstrapper.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/services/abstractbootstrapper.py')
-rw-r--r--src/leap/services/abstractbootstrapper.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/leap/services/abstractbootstrapper.py b/src/leap/services/abstractbootstrapper.py
new file mode 100644
index 00000000..633d818d
--- /dev/null
+++ b/src/leap/services/abstractbootstrapper.py
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+# abstractbootstrapper.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Abstract bootstrapper implementation
+"""
+import logging
+
+import requests
+
+from functools import partial
+
+from PySide import QtCore
+from twisted.internet import threads
+from leap.common.check import leap_assert, leap_assert_type
+
+logger = logging.getLogger(__name__)
+
+
+class AbstractBootstrapper(QtCore.QObject):
+ """
+ Abstract Bootstrapper that implements the needed deferred callbacks
+ """
+
+ PASSED_KEY = "passed"
+ ERROR_KEY = "error"
+
+ def __init__(self, bypass_checks=False):
+ """
+ Constructor for the abstract bootstrapper
+
+ :param bypass_checks: Set to true if the app should bypass
+ first round of checks for CA
+ certificates at bootstrap
+ :type bypass_checks: bool
+ """
+ QtCore.QObject.__init__(self)
+
+ leap_assert(self._gui_errback.im_func ==
+ AbstractBootstrapper._gui_errback.im_func,
+ "Cannot redefine _gui_errback")
+ leap_assert(self._errback.im_func ==
+ AbstractBootstrapper._errback.im_func,
+ "Cannot redefine _errback")
+ leap_assert(self._gui_notify.im_func ==
+ AbstractBootstrapper._gui_notify.im_func,
+ "Cannot redefine _gui_notify")
+
+ # **************************************************** #
+ # Dependency injection helpers, override this for more
+ # granular testing
+ self._fetcher = requests
+ # **************************************************** #
+
+ self._session = self._fetcher.session()
+ self._bypass_checks = bypass_checks
+ self._signal_to_emit = None
+ self._err_msg = None
+
+ def _gui_errback(self, failure):
+ """
+ Errback used to notify the GUI of a problem, it should be used
+ as the last errback of the whole chain.
+
+ Traps all exceptions if a signal is defined, otherwise it just
+ lets it continue.
+
+ NOTE: This method is final, it should not be redefined.
+
+ :param failure: failure object that Twisted generates
+ :type failure: twisted.python.failure.Failure
+ """
+ if self._signal_to_emit:
+ err_msg = self._err_msg \
+ if self._err_msg is not None \
+ else str(failure.value)
+ self._signal_to_emit.emit({
+ self.PASSED_KEY: False,
+ self.ERROR_KEY: err_msg
+ })
+ failure.trap(Exception)
+
+ def _errback(self, failure, signal=None):
+ """
+ Regular errback used for the middle of the chain. If it's
+ executed, the first one will set the signal to emit as
+ failure.
+
+ NOTE: This method is final, it should not be redefined.
+
+ :param failure: failure object that Twisted generates
+ :type failure: twisted.python.failure.Failure
+ :param signal: Signal to emit if it fails here first
+ :type signal: QtCore.SignalInstance
+
+ :returns: failure object that Twisted generates
+ :rtype: twisted.python.failure.Failure
+ """
+ if self._signal_to_emit is None:
+ self._signal_to_emit = signal
+ return failure
+
+ def _gui_notify(self, _, signal=None):
+ """
+ Callback used to notify the GUI of a success. Will emit signal
+ if specified
+
+ NOTE: This method is final, it should not be redefined.
+
+ :param _: IGNORED. Returned from the previous callback
+ :type _: IGNORED
+ :param signal: Signal to emit if it fails here first
+ :type signal: QtCore.SignalInstance
+ """
+ if signal:
+ logger.debug("Emitting %s" % (signal,))
+ signal.emit({self.PASSED_KEY: True, self.ERROR_KEY: ""})
+
+ def _callback_threader(self, cb, res, *args, **kwargs):
+ return threads.deferToThread(cb, res, *args, **kwargs)
+
+ def addCallbackChain(self, callbacks):
+ """
+ Creates a callback/errback chain on another thread using
+ deferToThread and adds the _gui_errback to the end to notify
+ the GUI on an error.
+
+ :param callbacks: List of tuples of callbacks and the signal
+ associated to that callback
+ :type callbacks: list(tuple(func, func))
+ """
+ leap_assert_type(callbacks, list)
+
+ self._signal_to_emit = None
+ self._err_msg = None
+
+ d = None
+ for cb, sig in callbacks:
+ if d is None:
+ d = threads.deferToThread(cb)
+ else:
+ d.addCallback(partial(self._callback_threader, cb))
+ d.addErrback(self._errback, signal=sig)
+ d.addCallback(self._gui_notify, signal=sig)
+ d.addErrback(self._gui_errback)
+ return d