From 751638b4eb8208e1eaa1beaaed284da6b412bca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 7 Mar 2013 19:05:11 -0300 Subject: Change asserts for a custom leap_assert method Also: - Make SRPAuth and the Bootstrappers be a QObject instead of a QThread so we can use them inside another more generic thread - Add a generic CheckerThread that runs checks or whatever operation as long as it returns a boolean value - Closes the whole application if the wizard is rejected at the first run - Do not fail when the config directory doesn't exist - Set the wizard pixmap logo as LEAP's logo - Improve wizard checks - Make SRPRegister play nice with the CheckerThread --- src/leap/config/baseconfig.py | 3 +- src/leap/config/pluggableconfig.py | 4 +- src/leap/config/prefixers.py | 6 +- src/leap/config/providerconfig.py | 5 +- src/leap/crypto/srpauth.py | 108 ++++++++++++-------- src/leap/crypto/srpregister.py | 17 ++-- src/leap/gui/mainwindow.py | 91 +++++++++++------ src/leap/gui/ui/wizard.ui | 5 +- src/leap/gui/wizard.py | 91 +++++++++-------- src/leap/services/eip/eipbootstrapper.py | 101 +++++-------------- src/leap/services/eip/eipconfig.py | 13 ++- src/leap/services/eip/providerbootstrapper.py | 137 ++++++++------------------ src/leap/services/eip/vpn.py | 15 ++- src/leap/services/eip/vpnlaunchers.py | 24 ++--- src/leap/util/check.py | 61 ++++++++++++ src/leap/util/checkerthread.py | 110 +++++++++++++++++++++ 16 files changed, 465 insertions(+), 326 deletions(-) create mode 100644 src/leap/util/check.py create mode 100644 src/leap/util/checkerthread.py (limited to 'src') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index d553255e..b80fd419 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -29,6 +29,7 @@ from abc import ABCMeta, abstractmethod from leap.config.prefixers import get_platform_prefixer from leap.config.pluggableconfig import PluggableConfig +from leap.util.check import leap_assert logger = logging.getLogger(__name__) @@ -58,7 +59,7 @@ class BaseConfig: @rtype: depends on the config structure, dict, str, array, int @return: returns the value for the specified key in the config """ - assert self._config_checker, "Load the config first" + leap_assert(self._config_checker, "Load the config first") return self._config_checker.config[key] def get_path_prefix(self, standalone=False): diff --git a/src/leap/config/pluggableconfig.py b/src/leap/config/pluggableconfig.py index 64aa05cc..5ed83b3f 100644 --- a/src/leap/config/pluggableconfig.py +++ b/src/leap/config/pluggableconfig.py @@ -28,6 +28,8 @@ import urlparse import jsonschema #from leap.base.util.translations import LEAPTranslatable +from leap.util.check import leap_assert + logger = logging.getLogger(__name__) @@ -378,7 +380,7 @@ class PluggableConfig(object): # XXX cleanup this! if fromfile: - assert os.path.exists(fromfile) + leap_assert(os.path.exists(fromfile)) if not format: format = self.filename2format(fromfile) diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index a33e68aa..20d59b2d 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -24,6 +24,8 @@ import platform from abc import ABCMeta, abstractmethod from xdg import BaseDirectory +from leap.util.check import leap_assert + class Prefixer: """ @@ -47,8 +49,8 @@ class Prefixer: def get_platform_prefixer(): prefixer = globals()[platform.system() + "Prefixer"] - assert prefixer, "Unimplemented platform prefixer: %s" % \ - (platform.system(),) + leap_assert(prefixer, "Unimplemented platform prefixer: %s" % + (platform.system(),)) return prefixer() diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index c3c2c298..55b33b98 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -23,6 +23,7 @@ import os from leap.config.baseconfig import BaseConfig, LocalizedKey from leap.config.provider_spec import leap_provider_spec +from leap.util.check import leap_assert logger = logging.getLogger(__name__) @@ -95,8 +96,8 @@ class ProviderConfig(BaseConfig): "cacert.pem") if not about_to_download: - assert os.path.exists(cert_path), \ - "You need to download the certificate first" + leap_assert(os.path.exists(cert_path), + "You need to download the certificate first") logger.debug("Going to verify SSL against %s" % (cert_path,)) return cert_path diff --git a/src/leap/crypto/srpauth.py b/src/leap/crypto/srpauth.py index dbcc95cb..28e4f037 100644 --- a/src/leap/crypto/srpauth.py +++ b/src/leap/crypto/srpauth.py @@ -20,9 +20,11 @@ import srp import binascii import logging -from PySide import QtCore +from PySide import QtCore, QtGui from leap.config.providerconfig import ProviderConfig +from leap.util.check import leap_assert +from leap.util.checkerthread import CheckerThread logger = logging.getLogger(__name__) @@ -34,7 +36,7 @@ class SRPAuthenticationError(Exception): pass -class SRPAuth(QtCore.QThread): +class SRPAuth(QtCore.QObject): """ SRPAuth singleton """ @@ -55,7 +57,8 @@ class SRPAuth(QtCore.QThread): @param server: Server to which we will authenticate @type server: str """ - assert provider_config, "We need a provider config to authenticate" + leap_assert(provider_config, + "We need a provider config to authenticate") self._provider_config = provider_config @@ -277,15 +280,15 @@ class SRPAuth(QtCore.QThread): @param password: password for this user @type password: str """ - assert self.get_session_id() is None, "Already logged in" + leap_assert(self.get_session_id() is None, "Already logged in") self._authentication_preprocessing(username, password) salt, B = self._start_authentication(username, password) M2 = self._process_challenge(salt, B, username) self._verify_session(M2) - assert self.get_session_id(), "Something went wrong because" + \ - " we don't have the auth cookie afterwards" + leap_assert(self.get_session_id(), "Something went wrong because" + " we don't have the auth cookie afterwards") def logout(self): """ @@ -294,7 +297,8 @@ class SRPAuth(QtCore.QThread): """ logger.debug("Starting logout...") - assert self.get_session_id(), "Cannot logout an unexisting session" + leap_assert(self.get_session_id(), + "Cannot logout an unexisting session") logout_url = "%s/%s/%s/" % (self._provider_config.get_api_uri(), self._provider_config. @@ -344,7 +348,7 @@ class SRPAuth(QtCore.QThread): """ Creates a singleton instance if needed """ - QtCore.QThread.__init__(self) + QtCore.QObject.__init__(self) # Check whether we already have an instance if SRPAuth.__instance is None: @@ -371,47 +375,47 @@ class SRPAuth(QtCore.QThread): @type password: str """ - with QtCore.QMutexLocker(self._should_login_lock): - self._should_login = self.DO_LOGIN - self._username = username - self._password = password - # Detach the start call to Qt's event loop - QtCore.QTimer.singleShot(0, self.start) - - def logout(self): - """ - Logs out the current session. - Expects a session_id to exists, might raise AssertionError - """ - QtCore.QMutexLocker(self._should_login_lock) - self._should_login = self.DO_LOGOUT - # Detach the start call to Qt's event loop - QtCore.QTimer.singleShot(0, self.start) - - def _runLogin(self, username, password): try: self.__instance.authenticate(username, password) + + logger.debug("Successful login!") self.authentication_finished.emit(True, "Succeeded") + return True except Exception as e: + logger.error("Error logging in %s" % (e,)) self.authentication_finished.emit(False, "%s" % (e,)) + return False - def _runLogout(self): + def logout(self): + """ + Logs out the current session. + Expects a session_id to exists, might raise AssertionError + """ try: self.__instance.logout() self.logout_finished.emit(True, "Succeeded") + return True except Exception as e: self.logout_finished.emit(False, "%s" % (e,)) - - def run(self): - QtCore.QMutexLocker(self._should_login_lock) - if self._should_login == self.DO_LOGIN: - self._runLogin(self._username, self._password) - elif self._should_login == self.DO_LOGOUT: - self._runLogout() - self._should_login = self.DO_NOTHING + return False if __name__ == "__main__": + import sys + from functools import partial + app = QtGui.QApplication(sys.argv) + + import signal + + def sigint_handler(*args, **kwargs): + logger.debug('SIGINT catched. shutting down...') + checker = args[0] + checker.set_should_quit() + QtGui.QApplication.quit() + + def signal_tester(d): + print d + logger = logging.getLogger(name='leap') logger.setLevel(logging.DEBUG) console = logging.StreamHandler() @@ -422,8 +426,23 @@ if __name__ == "__main__": console.setFormatter(formatter) logger.addHandler(console) - provider = ProviderConfig() + checker = CheckerThread() + + sigint = partial(sigint_handler, checker) + signal.signal(signal.SIGINT, sigint) + timer = QtCore.QTimer() + timer.start(500) + timer.timeout.connect(lambda: None) + app.connect(app, QtCore.SIGNAL("aboutToQuit()"), + checker.set_should_quit) + w = QtGui.QWidget() + w.resize(100, 100) + w.show() + + checker.start() + + provider = ProviderConfig() if provider.load("leap/providers/bitmask.net/provider.json"): # url = "%s/tickets" % (provider.get_api_uri(),) # print url @@ -431,9 +450,14 @@ if __name__ == "__main__": # print res.content # res.raise_for_status() auth = SRPAuth(provider) - auth.start() - auth.authenticate("test2", "sarasaaaa") - res = requests.session().get("%s/cert" % (provider.get_api_uri(),), - verify=provider.get_ca_cert_path()) - print res.content - auth.logout() + auth_instantiated = partial(auth.authenticate, "test2", "sarasaaaa") + + checker.add_checks([auth_instantiated, auth.logout]) + + #auth.authenticate("test2", "sarasaaaa") + #res = requests.session().get("%s/cert" % (provider.get_api_uri(),), + #verify=provider.get_ca_cert_path()) + #print res.content + #auth.logout() + + sys.exit(app.exec_()) diff --git a/src/leap/crypto/srpregister.py b/src/leap/crypto/srpregister.py index d9b2b22b..cf673e35 100644 --- a/src/leap/crypto/srpregister.py +++ b/src/leap/crypto/srpregister.py @@ -20,15 +20,17 @@ import binascii import srp import logging +from PySide import QtCore from urlparse import urlparse from leap.config.providerconfig import ProviderConfig from leap.crypto.constants import SIGNUP_TIMEOUT +from leap.util.check import leap_assert, leap_assert_type logger = logging.getLogger(__name__) -class SRPRegister(object): +class SRPRegister(QtCore.QObject): """ Registers a user to a specific provider using SRP """ @@ -37,6 +39,8 @@ class SRPRegister(object): USER_VERIFIER_KEY = 'user[password_verifier]' USER_SALT_KEY = 'user[password_salt]' + registration_finished = QtCore.Signal(bool, object) + def __init__(self, provider_config=None, register_path="users"): @@ -49,10 +53,9 @@ class SRPRegister(object): @param register_path: webapp path for registering users @type register_path; str """ - - assert provider_config, "Please provider a provider" - assert isinstance(provider_config, ProviderConfig), \ - "We need a ProviderConfig instead of %r" % (provider_config,) + QtCore.QObject.__init__(self) + leap_assert(provider_config, "Please provider a provider") + leap_assert_type(provider_config, ProviderConfig) self._provider_config = provider_config @@ -131,7 +134,9 @@ class SRPRegister(object): verify=self._provider_config. get_ca_cert_path()) - return (req.ok, req) + self.registration_finished.emit(req.ok, req) + + return req.ok if __name__ == "__main__": diff --git a/src/leap/gui/mainwindow.py b/src/leap/gui/mainwindow.py index 1821e4a6..50a03fb9 100644 --- a/src/leap/gui/mainwindow.py +++ b/src/leap/gui/mainwindow.py @@ -31,6 +31,8 @@ from leap.services.eip.providerbootstrapper import ProviderBootstrapper from leap.services.eip.eipbootstrapper import EIPBootstrapper from leap.services.eip.eipconfig import EIPConfig from leap.gui.wizard import Wizard +from leap.util.check import leap_assert +from leap.util.checkerthread import CheckerThread logger = logging.getLogger(__name__) @@ -44,6 +46,9 @@ class MainWindow(QtGui.QMainWindow): LOGIN_INDEX = 0 EIP_STATUS_INDEX = 1 + GEOMETRY_KEY = "Geometry" + WINDOWSTATE_KEY = "WindowState" + def __init__(self): QtGui.QMainWindow.__init__(self) @@ -69,6 +74,9 @@ class MainWindow(QtGui.QMainWindow): # This is created once we have a valid provider config self._srp_auth = None + self._checker_thread = CheckerThread() + self._checker_thread.start() + # This thread is always running, although it's quite # lightweight when it's done setting up provider # configuration and certificate. @@ -110,11 +118,7 @@ class MainWindow(QtGui.QMainWindow): QtCore.QCoreApplication.instance().connect( QtCore.QCoreApplication.instance(), QtCore.SIGNAL("aboutToQuit()"), - self._provider_bootstrapper.set_should_quit) - QtCore.QCoreApplication.instance().connect( - QtCore.QCoreApplication.instance(), - QtCore.SIGNAL("aboutToQuit()"), - self._eip_bootstrapper.set_should_quit) + self._checker_thread.set_should_quit) self.ui.action_sign_out.setEnabled(False) self.ui.action_sign_out.triggered.connect(self._logout) @@ -131,17 +135,26 @@ class MainWindow(QtGui.QMainWindow): self._center_window() self._wizard = None + self._wizard_firstrun = False if self._first_run(): - self._wizard = Wizard() + self._wizard_firstrun = True + self._wizard = Wizard(self._checker_thread) # Give this window time to finish init and then show the wizard QtCore.QTimer.singleShot(1, self._launch_wizard) - self._wizard.finished.connect(self._finish_init) + self._wizard.accepted.connect(self._finish_init) + self._wizard.rejected.connect(self._rejected_wizard) + else: + self._finish_init() + + def _rejected_wizard(self): + if self._wizard_firstrun: + self.quit() else: self._finish_init() def _launch_wizard(self): if self._wizard is None: - self._wizard = Wizard() + self._wizard = Wizard(self._checker_thread) self._wizard.exec_() def _finish_init(self): @@ -187,14 +200,23 @@ class MainWindow(QtGui.QMainWindow): """ Centers the mainwindow based on the desktop geometry """ - app = QtGui.QApplication.instance() - width = app.desktop().width() - height = app.desktop().height() - window_width = self.size().width() - window_height = self.size().height() - x = (width / 2.0) - (window_width / 2.0) - y = (height / 2.0) - (window_height / 2.0) - self.move(x, y) + settings = QtCore.QSettings() + geometry = settings.value(self.GEOMETRY_KEY, None) + state = settings.value(self.WINDOWSTATE_KEY, None) + if geometry is None: + app = QtGui.QApplication.instance() + width = app.desktop().width() + height = app.desktop().height() + window_width = self.size().width() + window_height = self.size().height() + x = (width / 2.0) - (window_width / 2.0) + y = (height / 2.0) - (window_height / 2.0) + self.move(x, y) + else: + self.restoreGeometry(geometry) + + if state is not None: + self.restoreState(state) def _about(self): """ @@ -236,6 +258,9 @@ class MainWindow(QtGui.QMainWindow): self._toggle_visible() e.ignore() return + settings = QtCore.QSettings() + settings.setValue(self.GEOMETRY_KEY, self.saveGeometry()) + settings.setValue(self.WINDOWSTATE_KEY, self.saveState()) QtGui.QMainWindow.closeEvent(self, e) def _configured_providers(self): @@ -244,10 +269,16 @@ class MainWindow(QtGui.QMainWindow): @rtype: list """ - providers = os.listdir( - os.path.join(self._provider_config.get_path_prefix(), - "leap", - "providers")) + providers = [] + try: + providers = os.listdir( + os.path.join(self._provider_config.get_path_prefix(), + "leap", + "providers")) + except Exception as e: + logger.debug("Error listing providers, assume there are none. %r" + % (e,)) + return providers def _first_run(self): @@ -303,8 +334,8 @@ class MainWindow(QtGui.QMainWindow): """ provider = self.ui.cmbProviders.currentText() - self._provider_bootstrapper.start() self._provider_bootstrapper.run_provider_select_checks( + self._checker_thread, provider, download_if_needed=True) @@ -329,6 +360,7 @@ class MainWindow(QtGui.QMainWindow): provider, "provider.json")): self._provider_bootstrapper.run_provider_setup_checks( + self._checker_thread, self._provider_config, download_if_needed=True) else: @@ -350,7 +382,7 @@ class MainWindow(QtGui.QMainWindow): start the SRP authentication, and as the last step bootstrapping the EIP service """ - assert self._provider_config, "We need a provider config" + leap_assert(self._provider_config, "We need a provider config") username = self.ui.lnUser.text() password = self.ui.lnPassword.text() @@ -381,9 +413,7 @@ class MainWindow(QtGui.QMainWindow): Once the provider configuration is loaded, this starts the SRP authentication """ - assert self._provider_config, "We need a provider config!" - - self._provider_bootstrapper.set_should_quit() + leap_assert(self._provider_config, "We need a provider config!") if data[self._provider_bootstrapper.PASSED_KEY]: username = self.ui.lnUser.text() @@ -431,14 +461,14 @@ class MainWindow(QtGui.QMainWindow): """ Starts the EIP bootstrapping sequence """ - assert self._eip_bootstrapper, "We need an eip bootstrapper!" - assert self._provider_config, "We need a provider config" + leap_assert(self._eip_bootstrapper, "We need an eip bootstrapper!") + leap_assert(self._provider_config, "We need a provider config") self._set_eip_status("Checking configuration, please wait...") if self._provider_config.provides_eip(): - self._eip_bootstrapper.start() self._eip_bootstrapper.run_eip_setup_checks( + self._checker_thread, self._provider_config, download_if_needed=True) else: @@ -503,10 +533,9 @@ class MainWindow(QtGui.QMainWindow): Starts the VPN thread if the eip configuration is properly loaded """ - assert self._eip_config, "We need an eip config!" - assert self._provider_config, "We need a provider config!" + leap_assert(self._eip_config, "We need an eip config!") + leap_assert(self._provider_config, "We need a provider config!") - self._eip_bootstrapper.set_should_quit() if self._eip_config.loaded() or \ self._eip_config.load(os.path.join("leap", "providers", diff --git a/src/leap/gui/ui/wizard.ui b/src/leap/gui/ui/wizard.ui index 86f8d458..2d9cb441 100644 --- a/src/leap/gui/ui/wizard.ui +++ b/src/leap/gui/ui/wizard.ui @@ -47,7 +47,7 @@ - <html><head/><body><p>New we will guide you through some configuration that is needed before you can connect for the first time.</p><p>If you ever need to modify these options again, you can find the wizard in the <span style=" font-style:italic;">'Settings'</span> menu from the main window.</p><p>Do you want to <span style=" font-weight:600;">sign up</span> for a new account, or <span style=" font-weight:600;">log in</span> with an already existing username?</p></body></html> + <html><head/><body><p>Now we will guide you through some configuration that is needed before you can connect for the first time.</p><p>If you ever need to modify these options again, you can find the wizard in the <span style=" font-style:italic;">'Settings'</span> menu from the main window.</p><p>Do you want to <span style=" font-weight:600;">sign up</span> for a new account, or <span style=" font-weight:600;">log in</span> with an already existing username?</p></body></html> Qt::RichText @@ -497,6 +497,9 @@ + + Qt::AutoText + Qt::AlignCenter diff --git a/src/leap/gui/wizard.py b/src/leap/gui/wizard.py index 7dcc8dd6..bac74d1d 100644 --- a/src/leap/gui/wizard.py +++ b/src/leap/gui/wizard.py @@ -21,7 +21,8 @@ First run wizard import os import logging -from PySide import QtCore, QtGui +from PySide import QtGui +from functools import partial from ui_wizard import Ui_Wizard from leap.config.providerconfig import ProviderConfig @@ -45,15 +46,18 @@ class Wizard(QtGui.QWizard): SETUP_EIP_PAGE = 5 FINISH_PATH = 6 - WEAK_PASSWORDS = ("1234", "12345", "123456", + WEAK_PASSWORDS = ("123456", "qweasd", "qwerty", "password") - def __init__(self): + def __init__(self, checker): QtGui.QWizard.__init__(self) self.ui = Ui_Wizard() self.ui.setupUi(self) + self.setPixmap(QtGui.QWizard.LogoPixmap, + QtGui.QPixmap(":/images/leap-color-small.png")) + self.QUESTION_ICON = QtGui.QPixmap(":/images/Emblem-question.png") self.ERROR_ICON = QtGui.QPixmap(":/images/Dialog-error.png") self.OK_ICON = QtGui.QPixmap(":/images/Dialog-accept.png") @@ -94,6 +98,9 @@ class Wizard(QtGui.QWizard): self.ui.lblPassword.setEchoMode(QtGui.QLineEdit.Password) self.ui.lblPassword2.setEchoMode(QtGui.QLineEdit.Password) + self.ui.lnProvider.textChanged.connect( + self._enable_check) + self.ui.lblUser.returnPressed.connect( self._focus_password) self.ui.lblPassword.returnPressed.connect( @@ -105,15 +112,14 @@ class Wizard(QtGui.QWizard): self._username = None - def __del__(self): - self._provider_bootstrapper.set_should_quit() - self._eip_bootstrapper.set_should_quit() - self._provider_bootstrapper.wait() - self._eip_bootstrapper.wait() + self._checker_thread = checker def get_username(self): return self._username + def _enable_check(self, text): + self.ui.btnCheck.setEnabled(len(self.ui.lnProvider.text()) != 0) + def _focus_password(self): """ Focuses at the password lineedit for the registration page @@ -151,13 +157,13 @@ class Wizard(QtGui.QWizard): if message is not None and password != password2: message = "Passwords don't match" - if message is not None and len(password) < 4: + if message is None and len(password) < 6: message = "Password too short" - if message is not None and password in self.WEAK_PASSWORDS: + if message is None and password in self.WEAK_PASSWORDS: message = "Password too easy" - if message is not None and username == password: + if message is None and username == password: message = "Password equal to username" if message is not None: @@ -172,10 +178,6 @@ class Wizard(QtGui.QWizard): Performs the registration based on the values provided in the form """ self.ui.btnRegister.setEnabled(False) - # See the disabled button - while QtGui.QApplication.instance().hasPendingEvents(): - QtGui.QApplication.instance().processEvents() - self.button(QtGui.QWizard.NextButton).setFocus() username = self.ui.lblUser.text() password = self.ui.lblPassword.text() @@ -183,27 +185,32 @@ class Wizard(QtGui.QWizard): if self._basic_password_checks(username, password, password2): register = SRPRegister(provider_config=self._provider_config) - ok, req = register.register_user(username, password) - if ok: - self._set_register_status("User registration OK") - self._username = username - self.ui.lblPassword2.clearFocus() - # Detach this call to allow UI updates briefly - QtCore.QTimer.singleShot(1, - self.page(self.REGISTER_USER_PAGE) - .set_completed) - else: - print req.content - error_msg = "Unknown error" - try: - error_msg = req.json().get("errors").get("login")[0] - except: - logger.error("Unknown error: %r" % (req.content,)) - self._set_register_status(error_msg) - self.ui.btnRegister.setEnabled(True) + register.registration_finished.connect( + self._registration_finished) + self._checker_thread.add_checks( + [partial(register.register_user, username, password)]) + self._username = username + self._set_register_status("Starting registration...") else: self.ui.btnRegister.setEnabled(True) + def _registration_finished(self, ok, req): + if ok: + self._set_register_status("" + "User registration OK") + self.ui.lblPassword2.clearFocus() + self.page(self.REGISTER_USER_PAGE).set_completed() + self.button(QtGui.QWizard.BackButton).setEnabled(False) + else: + self._username = None + error_msg = "Unknown error" + try: + error_msg = req.json().get("errors").get("login")[0] + except: + logger.error("Unknown error: %r" % (req.content,)) + self._set_register_status(error_msg) + self.ui.btnRegister.setEnabled(True) + def _set_register_status(self, status): """ Sets the status label in the registration page to status @@ -222,12 +229,16 @@ class Wizard(QtGui.QWizard): Starts the checks for a given provider """ + if len(self.ui.lnProvider.text()) == 0: + return + self.ui.grpCheckProvider.setVisible(True) self.ui.btnCheck.setEnabled(False) self._domain = self.ui.lnProvider.text() - self._provider_bootstrapper.start() - self._provider_bootstrapper.run_provider_select_checks(self._domain) + self._provider_bootstrapper.run_provider_select_checks( + self._checker_thread, + self._domain) def _complete_task(self, data, label, complete=False, complete_page=-1): """ @@ -328,7 +339,6 @@ class Wizard(QtGui.QWizard): """ self._complete_task(data, self.ui.lblCheckApiCert, True, self.SETUP_PROVIDER_PAGE) - self._provider_bootstrapper.set_should_quit() def _download_eip_config(self, data): """ @@ -351,7 +361,6 @@ class Wizard(QtGui.QWizard): """ self._complete_task(data, self.ui.lblDownloadClientCert, True, self.SETUP_EIP_PAGE) - self._eip_bootstrapper.set_should_quit() def _current_id_changed(self, pageId): """ @@ -365,14 +374,16 @@ class Wizard(QtGui.QWizard): self.ui.lblNameResolution.setPixmap(self.QUESTION_ICON) self.ui.lblHTTPS.setPixmap(self.QUESTION_ICON) self.ui.lblProviderInfo.setPixmap(self.QUESTION_ICON) + self._enable_check("") if pageId == self.SETUP_PROVIDER_PAGE: self._provider_bootstrapper.\ - run_provider_setup_checks(self._provider_config) + run_provider_setup_checks(self._checker_thread, + self._provider_config) if pageId == self.SETUP_EIP_PAGE: - self._eip_bootstrapper.start() - self._eip_bootstrapper.run_eip_setup_checks(self._provider_config) + self._eip_bootstrapper.run_eip_setup_checks(self._checker_thread, + self._provider_config) if pageId == self.PRESENT_PROVIDER_PAGE: # TODO: get the right lang for these diff --git a/src/leap/services/eip/eipbootstrapper.py b/src/leap/services/eip/eipbootstrapper.py index 77d7020a..bd6ab715 100644 --- a/src/leap/services/eip/eipbootstrapper.py +++ b/src/leap/services/eip/eipbootstrapper.py @@ -28,11 +28,13 @@ from PySide import QtGui, QtCore from leap.config.providerconfig import ProviderConfig from leap.services.eip.eipconfig import EIPConfig +from leap.util.check import leap_assert, leap_assert_type +from leap.util.checkerthread import CheckerThread logger = logging.getLogger(__name__) -class EIPBootstrapper(QtCore.QThread): +class EIPBootstrapper(QtCore.QObject): """ Sets up EIP for a provider a series of checks and emits signals after they are passed. @@ -50,13 +52,7 @@ class EIPBootstrapper(QtCore.QThread): download_client_certificate = QtCore.Signal(dict) def __init__(self): - QtCore.QThread.__init__(self) - - self._checks = [] - self._checks_lock = QtCore.QMutex() - - self._should_quit = False - self._should_quit_lock = QtCore.QMutex() + QtCore.QObject.__init__(self) # **************************************************** # # Dependency injection helpers, override this for more @@ -69,35 +65,6 @@ class EIPBootstrapper(QtCore.QThread): self._eip_config = None self._download_if_needed = False - def get_should_quit(self): - """ - Returns wether this thread should quit - - @rtype: bool - @return: True if the thread should terminate itself, Flase otherwise - """ - - QtCore.QMutexLocker(self._should_quit_lock) - return self._should_quit - - def set_should_quit(self): - """ - Sets the should_quit flag to True so that this thread - terminates the first chance it gets - """ - QtCore.QMutexLocker(self._should_quit_lock) - self._should_quit = True - self.wait() - - def start(self): - """ - Starts the thread and resets the should_quit flag - """ - with QtCore.QMutexLocker(self._should_quit_lock): - self._should_quit = False - - QtCore.QThread.start(self) - def _download_config(self): """ Downloads the EIP config for the given provider @@ -106,7 +73,8 @@ class EIPBootstrapper(QtCore.QThread): @rtype: bool """ - assert self._provider_config, "We need a provider configuration!" + leap_assert(self._provider_config, + "We need a provider configuration!") logger.debug("Downloading EIP config for %s" % (self._provider_config.get_domain(),)) @@ -162,8 +130,8 @@ class EIPBootstrapper(QtCore.QThread): @return: True if the checks passed, False otherwise @rtype: bool """ - assert self._provider_config, "We need a provider configuration!" - assert self._eip_config, "We need an eip configuration!" + leap_assert(self._provider_config, "We need a provider configuration!") + leap_assert(self._eip_config, "We need an eip configuration!") logger.debug("Downloading EIP client certificate for %s" % (self._provider_config.get_domain(),)) @@ -218,49 +186,25 @@ class EIPBootstrapper(QtCore.QThread): return download_cert[self.PASSED_KEY] - def run_eip_setup_checks(self, provider_config, download_if_needed=False): + def run_eip_setup_checks(self, checker, + provider_config, + download_if_needed=False): """ Starts the checks needed for a new eip setup @param provider_config: Provider configuration @type provider_config: ProviderConfig """ - assert provider_config, "We need a provider config!" - assert isinstance(provider_config, ProviderConfig), "Expected " + \ - "ProviderConfig type, not %r" % (type(provider_config),) + leap_assert(provider_config, "We need a provider config!") + leap_assert_type(provider_config, ProviderConfig) self._provider_config = provider_config self._download_if_needed = download_if_needed - QtCore.QMutexLocker(self._checks_lock) - self._checks = [ + checker.add_checks([ self._download_config, self._download_client_certificates - ] - - def run(self): - """ - Main run loop for this thread. Executes the checks. - """ - shouldContinue = False - while True: - if self.get_should_quit(): - logger.debug("Quitting provider bootstrap thread") - return - checkSomething = False - with QtCore.QMutexLocker(self._checks_lock): - if len(self._checks) > 0: - check = self._checks.pop(0) - shouldContinue = check() - checkSomething = True - if not shouldContinue: - logger.debug("Something went wrong with the checks, " - - "clearing...") - self._checks = [] - checkSomething = False - if not checkSomething: - self.usleep(self.IDLE_SLEEP_INTERVAL) + ]) if __name__ == "__main__": @@ -272,8 +216,8 @@ if __name__ == "__main__": def sigint_handler(*args, **kwargs): logger.debug('SIGINT catched. shutting down...') - bootstrapper_thread = args[0] - bootstrapper_thread.set_should_quit() + checker = args[0] + checker.set_should_quit() QtGui.QApplication.quit() def signal_tester(d): @@ -289,27 +233,28 @@ if __name__ == "__main__": console.setFormatter(formatter) logger.addHandler(console) - eip_thread = EIPBootstrapper() + eip_checks = EIPBootstrapper() + checker = CheckerThread() - sigint = partial(sigint_handler, eip_thread) + sigint = partial(sigint_handler, checker) signal.signal(signal.SIGINT, sigint) timer = QtCore.QTimer() timer.start(500) timer.timeout.connect(lambda: None) app.connect(app, QtCore.SIGNAL("aboutToQuit()"), - eip_thread.set_should_quit) + checker.set_should_quit) w = QtGui.QWidget() w.resize(100, 100) w.show() - eip_thread.start() + checker.start() provider_config = ProviderConfig() if provider_config.load(os.path.join("leap", "providers", "bitmask.net", "provider.json")): - eip_thread.run_eip_setup_checks(provider_config) + eip_checks.run_eip_setup_checks(checker, provider_config) sys.exit(app.exec_()) diff --git a/src/leap/services/eip/eipconfig.py b/src/leap/services/eip/eipconfig.py index ac06fef1..eab5bfd4 100644 --- a/src/leap/services/eip/eipconfig.py +++ b/src/leap/services/eip/eipconfig.py @@ -24,6 +24,7 @@ import logging from leap.config.baseconfig import BaseConfig from leap.config.providerconfig import ProviderConfig from leap.services.eip.eipspec import eipservice_config_spec +from leap.util.check import leap_assert, leap_assert_type logger = logging.getLogger(__name__) @@ -61,7 +62,7 @@ class EIPConfig(BaseConfig): def get_gateway_ip(self, index=0): gateways = self.get_gateways() - assert len(gateways) > 0, "We don't have any gateway!" + leap_assert(len(gateways) > 0, "We don't have any gateway!") if index > len(gateways): index = 0 logger.warning("Provided an unknown gateway index %s, " + @@ -75,10 +76,8 @@ class EIPConfig(BaseConfig): Returns the path to the certificate used by openvpn """ - assert providerconfig, "We need a provider" - assert isinstance(providerconfig, ProviderConfig), "The provider " + \ - "needs to be of type ProviderConfig instead of %s" % \ - (type(providerconfig),) + leap_assert(providerconfig, "We need a provider") + leap_assert_type(providerconfig, ProviderConfig) cert_path = os.path.join(self.get_path_prefix(), "leap", @@ -89,8 +88,8 @@ class EIPConfig(BaseConfig): "openvpn.pem") if not about_to_download: - assert os.path.exists(cert_path), \ - "You need to download the certificate first" + leap_assert(os.path.exists(cert_path), + "You need to download the certificate first") logger.debug("Using OpenVPN cert %s" % (cert_path,)) return cert_path diff --git a/src/leap/services/eip/providerbootstrapper.py b/src/leap/services/eip/providerbootstrapper.py index babcd47b..ecdc4e07 100644 --- a/src/leap/services/eip/providerbootstrapper.py +++ b/src/leap/services/eip/providerbootstrapper.py @@ -29,11 +29,13 @@ from OpenSSL import crypto from PySide import QtGui, QtCore from leap.config.providerconfig import ProviderConfig +from leap.util.check import leap_assert, leap_assert_type +from leap.util.checkerthread import CheckerThread logger = logging.getLogger(__name__) -class ProviderBootstrapper(QtCore.QThread): +class ProviderBootstrapper(QtCore.QObject): """ Given a provider URL performs a series of checks and emits signals after they are passed. @@ -56,13 +58,7 @@ class ProviderBootstrapper(QtCore.QThread): check_api_certificate = QtCore.Signal(dict) def __init__(self): - QtCore.QThread.__init__(self) - - self._checks = [] - self._checks_lock = QtCore.QMutex() - - self._should_quit = False - self._should_quit_lock = QtCore.QMutex() + QtCore.QObject.__init__(self) # **************************************************** # # Dependency injection helpers, override this for more @@ -75,35 +71,6 @@ class ProviderBootstrapper(QtCore.QThread): self._provider_config = None self._download_if_needed = False - def get_should_quit(self): - """ - Returns wether this thread should quit - - @rtype: bool - @return: True if the thread should terminate itself, Flase otherwise - """ - - QtCore.QMutexLocker(self._should_quit_lock) - return self._should_quit - - def set_should_quit(self): - """ - Sets the should_quit flag to True so that this thread - terminates the first chance it gets - """ - QtCore.QMutexLocker(self._should_quit_lock) - self._should_quit = True - self.wait() - - def start(self): - """ - Starts the thread and resets the should_quit flag - """ - with QtCore.QMutexLocker(self._should_quit_lock): - self._should_quit = False - - QtCore.QThread.start(self) - def _should_proceed_provider(self): """ Returns False if provider.json already exists for the given @@ -131,7 +98,7 @@ class ProviderBootstrapper(QtCore.QThread): @rtype: bool """ - assert self._domain, "Cannot check DNS without a domain" + leap_assert(self._domain, "Cannot check DNS without a domain") logger.debug("Checking name resolution for %s" % (self._domain)) @@ -162,7 +129,7 @@ class ProviderBootstrapper(QtCore.QThread): @rtype: bool """ - assert self._domain, "Cannot check HTTPS without a domain" + leap_assert(self._domain, "Cannot check HTTPS without a domain") logger.debug("Checking https for %s" % (self._domain)) @@ -193,7 +160,8 @@ class ProviderBootstrapper(QtCore.QThread): @return: True if the checks passed, False otherwise @rtype: bool """ - assert self._domain, "Cannot download provider info without a domain" + leap_assert(self._domain, + "Cannot download provider info without a domain") logger.debug("Downloading provider info for %s" % (self._domain)) @@ -230,7 +198,8 @@ class ProviderBootstrapper(QtCore.QThread): return download_data[self.PASSED_KEY] - def run_provider_select_checks(self, domain, download_if_needed=False): + def run_provider_select_checks(self, checker, + domain, download_if_needed=False): """ Populates the check queue @@ -243,17 +212,16 @@ class ProviderBootstrapper(QtCore.QThread): @return: True if the checks passed, False otherwise @rtype: bool """ - assert domain and len(domain) > 0, "We need a domain!" + leap_assert(domain and len(domain) > 0, "We need a domain!") self._domain = domain self._download_if_needed = download_if_needed - QtCore.QMutexLocker(self._checks_lock) - self._checks = [ + checker.add_checks([ self._check_name_resolution, self._check_https, self._download_provider_info - ] + ]) def _should_proceed_cert(self): """ @@ -262,7 +230,7 @@ class ProviderBootstrapper(QtCore.QThread): @rtype: bool """ - assert self._provider_config, "We need a provider config!" + leap_assert(self._provider_config, "We need a provider config!") if not self._download_if_needed: return True @@ -278,8 +246,8 @@ class ProviderBootstrapper(QtCore.QThread): @rtype: bool """ - assert self._provider_config, "Cannot download the ca cert " + \ - "without a provider config!" + leap_assert(self._provider_config, "Cannot download the ca cert " + "without a provider config!") logger.debug("Downloading ca cert for %s at %s" % (self._domain, self._provider_config.get_ca_cert_uri())) @@ -331,8 +299,8 @@ class ProviderBootstrapper(QtCore.QThread): @return: True if the checks passed, False otherwise @rtype: bool """ - assert self._provider_config, "Cannot check the ca cert " + \ - "without a provider config!" + leap_assert(self._provider_config, "Cannot check the ca cert " + "without a provider config!") logger.debug("Checking ca fingerprint for %s and cert %s" % (self._domain, @@ -350,7 +318,7 @@ class ProviderBootstrapper(QtCore.QThread): try: parts = self._provider_config.get_ca_cert_fingerprint().split(":") - assert len(parts) == 2, "Wrong fingerprint format" + leap_assert(len(parts) == 2, "Wrong fingerprint format") method = parts[0].strip() fingerprint = parts[1].strip() @@ -358,13 +326,13 @@ class ProviderBootstrapper(QtCore.QThread): with open(self._provider_config.get_ca_cert_path()) as f: cert_data = f.read() - assert len(cert_data) > 0, "Could not read certificate data" + leap_assert(len(cert_data) > 0, "Could not read certificate data") x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert_data) digest = x509.digest(method).replace(":", "").lower() - assert digest == fingerprint, \ - "Downloaded certificate has a different fingerprint!" + leap_assert(digest == fingerprint, + "Downloaded certificate has a different fingerprint!") check_ca_fingerprint_data[self.PASSED_KEY] = True except Exception as e: @@ -384,8 +352,8 @@ class ProviderBootstrapper(QtCore.QThread): @return: True if the checks passed, False otherwise @rtype: bool """ - assert self._provider_config, "Cannot check the ca cert " + \ - "without a provider config!" + leap_assert(self._provider_config, "Cannot check the ca cert " + "without a provider config!") logger.debug("Checking api certificate for %s and cert %s" % (self._provider_config.get_api_uri(), @@ -418,7 +386,8 @@ class ProviderBootstrapper(QtCore.QThread): return check_api_certificate_data[self.PASSED_KEY] - def run_provider_setup_checks(self, provider_config, + def run_provider_setup_checks(self, checker, + provider_config, download_if_needed=False): """ Starts the checks needed for a new provider setup @@ -429,43 +398,17 @@ class ProviderBootstrapper(QtCore.QThread): overwrite already downloaded data @type download_if_needed: bool """ - assert provider_config, "We need a provider config!" - assert isinstance(provider_config, ProviderConfig), "Expected " + \ - "ProviderConfig type, not %r" % (type(provider_config),) + leap_assert(provider_config, "We need a provider config!") + leap_assert_type(provider_config, ProviderConfig) self._provider_config = provider_config self._download_if_needed = download_if_needed - QtCore.QMutexLocker(self._checks_lock) - self._checks = [ + checker.add_checks([ self._download_ca_cert, self._check_ca_fingerprint, self._check_api_certificate - ] - - def run(self): - """ - Main run loop for this thread. Executes the checks. - """ - shouldContinue = False - while True: - if self.get_should_quit(): - logger.debug("Quitting provider bootstrap thread") - return - checkSomething = False - with QtCore.QMutexLocker(self._checks_lock): - if len(self._checks) > 0: - check = self._checks.pop(0) - shouldContinue = check() - checkSomething = True - if not shouldContinue: - logger.debug("Something went wrong with the checks, " - "clearing...") - self._checks = [] - checkSomething = False - if not checkSomething: - self.usleep(self.IDLE_SLEEP_INTERVAL) - + ]) if __name__ == "__main__": import sys @@ -476,8 +419,8 @@ if __name__ == "__main__": def sigint_handler(*args, **kwargs): logger.debug('SIGINT catched. shutting down...') - bootstrapper_thread = args[0] - bootstrapper_thread.set_should_quit() + bootstrapper_checks = args[0] + bootstrapper_checks.set_should_quit() QtGui.QApplication.quit() def signal_tester(d): @@ -493,28 +436,32 @@ if __name__ == "__main__": console.setFormatter(formatter) logger.addHandler(console) - bootstrapper_thread = ProviderBootstrapper() + bootstrapper_checks = ProviderBootstrapper() + + checker = CheckerThread() + checker.start() - sigint = partial(sigint_handler, bootstrapper_thread) + sigint = partial(sigint_handler, checker) signal.signal(signal.SIGINT, sigint) timer = QtCore.QTimer() timer.start(500) timer.timeout.connect(lambda: None) app.connect(app, QtCore.SIGNAL("aboutToQuit()"), - bootstrapper_thread.set_should_quit) + checker.set_should_quit) w = QtGui.QWidget() w.resize(100, 100) w.show() - bootstrapper_thread.start() - bootstrapper_thread.run_provider_select_checks("bitmask.net") + bootstrapper_checks.run_provider_select_checks(checker, + "bitmask.net") provider_config = ProviderConfig() if provider_config.load(os.path.join("leap", "providers", "bitmask.net", "provider.json")): - bootstrapper_thread.run_provider_setup_checks(provider_config) + bootstrapper_checks.run_provider_setup_checks(checker, + provider_config) sys.exit(app.exec_()) diff --git a/src/leap/services/eip/vpn.py b/src/leap/services/eip/vpn.py index f117cdbc..88692442 100644 --- a/src/leap/services/eip/vpn.py +++ b/src/leap/services/eip/vpn.py @@ -29,6 +29,7 @@ from leap.config.providerconfig import ProviderConfig from leap.services.eip.vpnlaunchers import get_platform_launcher from leap.services.eip.eipconfig import EIPConfig from leap.services.eip.udstelnet import UDSTelnet +from leap.util.check import leap_assert, leap_assert_type logger = logging.getLogger(__name__) ON_POSIX = 'posix' in sys.builtin_module_names @@ -123,13 +124,11 @@ class VPN(QtCore.QThread): socket, or port otherwise @type socket_port: str """ - assert eipconfig, "We need an eip config" - assert isinstance(eipconfig, EIPConfig), "Expected EIPConfig " + \ - "object instead of %s" % (type(eipconfig),) - assert providerconfig, "We need a provider config" - assert isinstance(providerconfig, ProviderConfig), "Expected " + \ - "ProviderConfig object instead of %s" % (type(providerconfig),) - assert not self._started, "Starting process more than once!" + leap_assert(eipconfig, "We need an eip config") + leap_assert_type(eipconfig, EIPConfig) + leap_assert(providerconfig, "We need a provider config") + leap_assert_type(providerconfig, ProviderConfig) + leap_assert(not self._started, "Starting process more than once!") logger.debug("Starting VPN...") @@ -202,7 +201,7 @@ class VPN(QtCore.QThread): @return: response read @rtype: list """ - assert self._tn, "We need a tn connection!" + leap_assert(self._tn, "We need a tn connection!") try: self._tn.write("%s\n" % (command,)) buf = self._tn.read_until(until, 2) diff --git a/src/leap/services/eip/vpnlaunchers.py b/src/leap/services/eip/vpnlaunchers.py index 68978248..00e9c966 100644 --- a/src/leap/services/eip/vpnlaunchers.py +++ b/src/leap/services/eip/vpnlaunchers.py @@ -29,6 +29,7 @@ from abc import ABCMeta, abstractmethod from leap.config.providerconfig import ProviderConfig from leap.services.eip.eipconfig import EIPConfig +from leap.util.check import leap_assert, leap_assert_type logger = logging.getLogger(__name__) @@ -65,8 +66,8 @@ class VPNLauncher: def get_platform_launcher(): launcher = globals()[platform.system() + "VPNLauncher"] - assert launcher, "Unimplemented platform launcher: %s" % \ - (platform.system(),) + leap_assert(launcher, "Unimplemented platform launcher: %s" % + (platform.system(),)) return launcher() @@ -165,17 +166,15 @@ class LinuxVPNLauncher(VPNLauncher): @return: A VPN command ready to be launched @rtype: list """ - assert eipconfig, "We need an eip config" - assert isinstance(eipconfig, EIPConfig), "Expected EIPConfig " + \ - "object instead of %s" % (type(eipconfig),) - assert providerconfig, "We need a provider config" - assert isinstance(providerconfig, ProviderConfig), "Expected " + \ - "ProviderConfig object instead of %s" % (type(providerconfig),) - assert socket_host, "We need a socket host!" - assert socket_port, "We need a socket port!" + leap_assert(eipconfig, "We need an eip config") + leap_assert_type(eipconfig, EIPConfig) + leap_assert(providerconfig, "We need a provider config") + leap_assert_type(providerconfig, ProviderConfig) + leap_assert(socket_host, "We need a socket host!") + leap_assert(socket_port, "We need a socket port!") openvpn_possibilities = which(self.OPENVPN_BIN) - assert len(openvpn_possibilities) > 0, "We couldn't find openvpn" + leap_assert(len(openvpn_possibilities) > 0, "We couldn't find openvpn") openvpn = openvpn_possibilities[0] args = [] @@ -183,7 +182,8 @@ class LinuxVPNLauncher(VPNLauncher): if _is_pkexec_in_system(): if _is_auth_agent_running(): pkexec_possibilities = which(self.PKEXEC_BIN) - assert len(pkexec_possibilities) > 0, "We couldn't find pkexec" + leap_assert(len(pkexec_possibilities) > 0, + "We couldn't find pkexec") args.append(openvpn) openvpn = pkexec_possibilities[0] else: diff --git a/src/leap/util/check.py b/src/leap/util/check.py new file mode 100644 index 00000000..9787341a --- /dev/null +++ b/src/leap/util/check.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# check.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 . + +""" +Set of functions to help checking situations +""" +import logging +import inspect +import traceback + + +logger = logging.getLogger(__name__) + + +def leap_assert(condition, message=""): + """ + Asserts the condition and displays the message if that's not + met. It also logs the error and its backtrace. + + @param condition: condition to check + @type condition: bool + @param message: message to display if the condition isn't met + @type message: str + """ + if not condition: + logger.error("Bug: %s" % (message,)) + try: + frame = inspect.currentframe() + stack_trace = traceback.format_stack(frame) + logger.error(''.join(stack_trace)) + except Exception as e: + logger.error("Bug in leap_assert: %r" % (e,)) + assert condition, message + + +def leap_assert_type(var, expectedType): + """ + Helper assert check for a variable's expected type + + @param var: variable to check + @type var: any + @param expectedType: type to check agains + @type expectedType: type + """ + leap_assert(isinstance(var, expectedType), + "Expected type %r instead of %r" % + (expectedType, type(var))) diff --git a/src/leap/util/checkerthread.py b/src/leap/util/checkerthread.py new file mode 100644 index 00000000..681c33e1 --- /dev/null +++ b/src/leap/util/checkerthread.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# providerbootstrapper.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 . + +""" +Checker thread +""" + +import logging + +from PySide import QtCore + +from leap.util.check import leap_assert_type + +logger = logging.getLogger(__name__) + + +class CheckerThread(QtCore.QThread): + """ + Generic checker thread that can perform any type of operation as + long as it returns a boolean value that identifies how the + execution went. + """ + + IDLE_SLEEP_INTERVAL = 1 + + def __init__(self): + QtCore.QThread.__init__(self) + + self._checks = [] + self._checks_lock = QtCore.QMutex() + + self._should_quit = False + self._should_quit_lock = QtCore.QMutex() + + def get_should_quit(self): + """ + Returns wether this thread should quit + + @rtype: bool + @return: True if the thread should terminate itself, Flase otherwise + """ + + QtCore.QMutexLocker(self._should_quit_lock) + return self._should_quit + + def set_should_quit(self): + """ + Sets the should_quit flag to True so that this thread + terminates the first chance it gets + """ + QtCore.QMutexLocker(self._should_quit_lock) + self._should_quit = True + self.wait() + + def start(self): + """ + Starts the thread and resets the should_quit flag + """ + with QtCore.QMutexLocker(self._should_quit_lock): + self._should_quit = False + + QtCore.QThread.start(self) + + def add_checks(self, checks): + """ + Adds a list of checks to the ones being executed + + @param checks: check functions to perform + @type checkes: list + """ + with QtCore.QMutexLocker(self._checks_lock): + self._checks += checks + + def run(self): + """ + Main run loop for this thread. Executes the checks. + """ + shouldContinue = False + while True: + if self.get_should_quit(): + logger.debug("Quitting checker thread") + return + checkSomething = False + with QtCore.QMutexLocker(self._checks_lock): + if len(self._checks) > 0: + check = self._checks.pop(0) + shouldContinue = check() + leap_assert_type(shouldContinue, bool) + checkSomething = True + if not shouldContinue: + logger.debug("Something went wrong with the checks, " + "clearing...") + self._checks = [] + checkSomething = False + if not checkSomething: + self.sleep(self.IDLE_SLEEP_INTERVAL) -- cgit v1.2.3