From f1b517e08bffbd3b625192e0c23d3bc0bf89d12b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 2 Jul 2014 09:53:44 -0500 Subject: fix set syntax for 2.6 compat --- src/leap/bitmask/gui/mainwindow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 53a7d95a..8a5b8275 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -1708,7 +1708,7 @@ class MainWindow(QtGui.QMainWindow): self._cancel_ongoing_defers() - self._services_being_stopped = {'imap', 'eip'} + self._services_being_stopped = set(('imap', 'eip')) imap_stopped = lambda: self._remove_service('imap') self._backend.signaler.imap_stopped.connect(imap_stopped) -- cgit v1.2.3 From 076cbbef6edf9d6dc8145f31d0803941d5ad6605 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 7 Jul 2014 18:13:22 -0300 Subject: Add support for fingerprint-gui's polkit agent. Closes #5880. --- changes/feature-5880_add-fingerprint-gui-polkit-support | 1 + src/leap/bitmask/services/eip/linuxvpnlauncher.py | 1 + 2 files changed, 2 insertions(+) create mode 100644 changes/feature-5880_add-fingerprint-gui-polkit-support diff --git a/changes/feature-5880_add-fingerprint-gui-polkit-support b/changes/feature-5880_add-fingerprint-gui-polkit-support new file mode 100644 index 00000000..c71093d5 --- /dev/null +++ b/changes/feature-5880_add-fingerprint-gui-polkit-support @@ -0,0 +1 @@ +- Add support for fingerprint-gui's polkit agent. Closes #5880. diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py index 8ec0c050..1409d504 100644 --- a/src/leap/bitmask/services/eip/linuxvpnlauncher.py +++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py @@ -74,6 +74,7 @@ def _is_auth_agent_running(): 'ps aux | grep "polkit-[m]ate-authentication-agent-1"', 'ps aux | grep "[l]xpolkit"', 'ps aux | grep "[g]nome-shell"', + 'ps aux | grep "[f]ingerprint-polkit-agent"', ] is_running = [commands.getoutput(cmd) for cmd in polkit_options] -- cgit v1.2.3 From 65a3e16ae1d240974535935e3cf898c45cc20870 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 8 Jul 2014 10:35:56 -0500 Subject: defer encryption --- src/leap/bitmask/services/mail/plumber.py | 3 ++- src/leap/bitmask/services/soledad/soledadbootstrapper.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/leap/bitmask/services/mail/plumber.py b/src/leap/bitmask/services/mail/plumber.py index c16a1fed..1ef0543e 100644 --- a/src/leap/bitmask/services/mail/plumber.py +++ b/src/leap/bitmask/services/mail/plumber.py @@ -83,7 +83,8 @@ def initialize_soledad(uuid, email, passwd, secrets, localdb, server_url, - cert_file) + cert_file, + defer_encryption=True) return soledad diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index db12fd80..aeced001 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -423,7 +423,8 @@ class SoledadBootstrapper(AbstractBootstrapper): local_db_path=local_db_path.encode(encoding), server_url=server_url, cert_file=cert_file.encode(encoding), - auth_token=auth_token) + auth_token=auth_token, + defer_encryption=True) # XXX All these errors should be handled by soledad itself, # and return a subclass of SoledadInitializationFailed @@ -448,7 +449,7 @@ class SoledadBootstrapper(AbstractBootstrapper): Raises SoledadSyncError if not successful. """ try: - self._soledad.sync() + self._soledad.sync(defer_decryption=True) except SSLError as exc: logger.error("%r" % (exc,)) raise SoledadSyncError("Failed to sync soledad") -- cgit v1.2.3 From 3fa1ec572a4c8ca752412e70af9ad0b9744933e0 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 17 Mar 2014 10:45:02 -0400 Subject: wait for decrypt during bootstrap --- changes/feature_decrypt-inline-bootstrap | 1 + .../services/soledad/soledadbootstrapper.py | 23 ++++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 changes/feature_decrypt-inline-bootstrap diff --git a/changes/feature_decrypt-inline-bootstrap b/changes/feature_decrypt-inline-bootstrap new file mode 100644 index 00000000..092d98ea --- /dev/null +++ b/changes/feature_decrypt-inline-bootstrap @@ -0,0 +1 @@ +- Use inline decrypting for initial soledad syncrhonization, to wait for secrets. diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index aeced001..b9243add 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -21,6 +21,7 @@ import logging import os import socket import sys +import time from ssl import SSLError from sqlite3 import ProgrammingError as sqlite_ProgrammingError @@ -132,6 +133,9 @@ class SoledadBootstrapper(AbstractBootstrapper): MAX_INIT_RETRIES = 10 MAX_SYNC_RETRIES = 10 + WAIT_MAX_SECONDS = 600 + #WAIT_STEP_SECONDS = 1 + WAIT_STEP_SECONDS = 5 def __init__(self, signaler=None): AbstractBootstrapper.__init__(self, signaler) @@ -181,7 +185,6 @@ class SoledadBootstrapper(AbstractBootstrapper): :param uuid: the user uuid :type uuid: str or unicode """ - print "UUID ", uuid self._address = username self._password = password self._uuid = uuid @@ -356,12 +359,20 @@ class SoledadBootstrapper(AbstractBootstrapper): Do several retries to get an initial soledad sync. """ # and now, let's sync - sync_tries = 1 - while sync_tries <= self.MAX_SYNC_RETRIES: + sync_tries = self.MAX_SYNC_RETRIES + step = self.WAIT_STEP_SECONDS + max_wait = self.WAIT_MAX_SECONDS + while sync_tries > 0: + wait = 0 try: logger.debug("Trying to sync soledad....") self._try_soledad_sync() - logger.debug("Soledad has been synced.") + while self.soledad.syncing: + time.sleep(step) + wait += step + if wait >= max_wait: + raise SoledadSyncError("timeout!") + logger.debug("Soledad has been synced!") # so long, and thanks for all the fish return except SoledadSyncError: @@ -382,6 +393,7 @@ class SoledadBootstrapper(AbstractBootstrapper): self._signaler.SOLEDAD_INVALID_AUTH_TOKEN) raise except Exception as e: + # XXX release syncing lock logger.exception("Unhandled error while syncing " "soledad: %r" % (e,)) break @@ -449,6 +461,9 @@ class SoledadBootstrapper(AbstractBootstrapper): Raises SoledadSyncError if not successful. """ try: + logger.debug("BOOTSTRAPPER: trying to sync Soledad....") + # pass defer_decryption=False to get inline decryption + # for debugging. self._soledad.sync(defer_decryption=True) except SSLError as exc: logger.error("%r" % (exc,)) -- cgit v1.2.3 From 951a14224924e38c9a54e6047c88ad10666666c0 Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 9 Jul 2014 16:30:25 -0700 Subject: firewall stop: try to be much more robust in stopping the firewall whenever we can --- pkg/linux/bitmask-root | 142 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 103 insertions(+), 39 deletions(-) diff --git a/pkg/linux/bitmask-root b/pkg/linux/bitmask-root index 5367a31c..3ffd0eee 100755 --- a/pkg/linux/bitmask-root +++ b/pkg/linux/bitmask-root @@ -150,7 +150,7 @@ def is_valid_address(value): socket.inet_aton(value) return True except Exception: - print("%s: ERROR: MALFORMED IP: %s!" % (SCRIPT, value)) + log("%s: ERROR: MALFORMED IP: %s!" % (SCRIPT, value)) return False @@ -348,7 +348,7 @@ class Daemon(object): if os.path.exists(self.pidfile): os.remove(self.pidfile) else: - print(str(err)) + log(str(err)) sys.exit(1) def restart(self): @@ -376,23 +376,24 @@ def run(command, *args, **options): `detach`: If True, run in detached process. `input`: If True, open command for writing stream to, returning the Popen object. + `throw`: If True, raise an exception if there is an error instead of bailing. """ parts = [command] parts.extend(args) - if TEST or DEBUG: - print("%s run: %s " (SCRIPT, " ".join(parts))) + debug("%s run: %s " % (SCRIPT, " ".join(parts))) _check = options.get("check", True) _detach = options.get("detach", False) _input = options.get("input", False) _exitcode = options.get("exitcode", False) + _throw = options.get("throw", False) - if not _check or _detach or _input: + if not (_check or _throw) or _detach or _input: if _input: return subprocess.Popen(parts, stdin=subprocess.PIPE) else: - # XXX ok with return None ?? subprocess.Popen(parts) + return None else: try: devnull = open('/dev/null', 'w') @@ -402,24 +403,49 @@ def run(command, *args, **options): if DEBUG: logger.exception(exc) if _exitcode: + debug("ERROR: Could not run %s: %s" % (exc.cmd, exc.output), + exception=exc) return exc.returncode + elif _throw: + raise exc else: bail("ERROR: Could not run %s: %s" % (exc.cmd, exc.output), exception=exc) -def bail(msg=None, exception=None): +def log(msg=None, exception=None, priority=syslog.LOG_INFO): """ - Abnormal exit. + print and log warning message or exception. :param msg: optional error message. :type msg: str + :param msg: optional exception. + :type msg: Exception + :param msg: syslog level + :type msg: one of LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, + LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG """ if msg is not None: print("%s: %s" % (SCRIPT, msg)) - syslog.syslog(syslog.LOG_ERR, msg) + syslog.syslog(priority, msg) if exception is not None: - traceback.print_exc() + if TEST or DEBUG: + traceback.print_exc() + syslog.syslog(priority, traceback.format_exc()) + +def debug(msg=None, exception=None): + """ + Just like log, but is skipped unless DEBUG. Use syslog.LOG_INFO + even for debug messages (we don't want to miss them). + """ + if TEST or DEBUG: + log(msg, exception) + +def bail(msg=None, exception=None): + """ + abnormal exit. like log(), but exits with error status code. + """ + log(msg, exception) exit(1) ## @@ -457,21 +483,21 @@ def parse_openvpn_flags(args): if required_params: flag_params = flag[1:] if len(flag_params) != len(required_params): - print("%s: ERROR: not enough params for %s" % + log("%s: ERROR: not enough params for %s" % (SCRIPT, flag_name)) return None for param, param_type in zip(flag_params, required_params): if PARAM_FORMATS[param_type](param): result.append(param) else: - print("%s: ERROR: Bad argument %s" % + log("%s: ERROR: Bad argument %s" % (SCRIPT, param)) return None else: - print("WARNING: unrecognized openvpn flag %s" % flag_name) + log("WARNING: unrecognized openvpn flag %s" % flag_name) return result except Exception as exc: - print("%s: ERROR PARSING FLAGS: %s" % (SCRIPT, exc)) + log("%s: ERROR PARSING FLAGS: %s" % (SCRIPT, exc)) if DEBUG: logger.exception(exc) return None @@ -490,8 +516,8 @@ def openvpn_start(args): OPENVPN = get_openvpn_bin() flags = [OPENVPN] + FIXED_FLAGS + openvpn_flags if DEBUG: - print("%s: running openvpn with flags:" % (SCRIPT,)) - print(flags) + log("%s: running openvpn with flags:" % (SCRIPT,)) + log(flags) # note: first argument to command is ignored, but customarily set to # the command. os.execv(OPENVPN, flags) @@ -584,7 +610,7 @@ class NameserverRestorer(Daemon): if os.path.isfile(RESOLVCONF): run(RESOLVCONF, "-d", "bitmask") else: - print("%s: ERROR: package openresolv " + log("%s: ERROR: package openresolv " "or resolvconf not installed." % (SCRIPT,)) @@ -703,29 +729,49 @@ def ip6tables(*args, **options): """ run_iptable_with_check(IP6TABLES, *args, **options) +# +# NOTE: these tests to see if a chain exists might incorrectly return false. +# This happens when there is an error in calling `iptables --list bitmask`. +# +# For this reason, when stopping the firewall, we do not trust the +# output of ipvx_chain_exists() but instead always attempt to delete +# the chain. +# -def ipv4_chain_exists(table): +def ipv4_chain_exists(chain): """ Check if a given chain exists. - :param table: the table to check against - :type table: str + :param chain: the chain to check against + :type chain: str :rtype: bool """ - code = run(IPTABLES, "--list", table, "--numeric", exitcode=True) - return code == 0 + code = run(IPTABLES, "--list", chain, "--numeric", exitcode=True) + if code == 0: + return True + elif code == 1: + return False + else: + log("ERROR: Could not determine state of iptable chain") + return False -def ipv6_chain_exists(table): +def ipv6_chain_exists(chain): """ Check if a given chain exists. - :param table: the table to check against - :type table: str + :param chain: the chain to check against + :type chain: str :rtype: bool """ - code = run(IP6TABLES, "--list", table, "--numeric", exitcode=True) - return code == 0 + code = run(IP6TABLES, "--list", chain, "--numeric", exitcode=True) + if code == 0: + return True + elif code == 1: + return False + else: + log("ERROR: Could not determine state of iptable chain") + return False def firewall_start(args): @@ -812,15 +858,34 @@ def firewall_start(args): def firewall_stop(): """ - Stop the firewall. + Stop the firewall. Because we really really always want the firewall to be + stopped if at all possible, this function is cautious and contains a lot of + trys and excepts. + + If there were any problems, we raise an exception at the end. This allows the calling code + to retry stopping the firewall. Stopping the firewall can fail if iptables is being run by + another process (only one iptables command can be run at a time). """ - iptables("--delete", "OUTPUT", "--jump", BITMASK_CHAIN) - if ipv4_chain_exists(BITMASK_CHAIN): - ip4tables("--flush", BITMASK_CHAIN) - ip4tables("--delete-chain", BITMASK_CHAIN) - if ipv6_chain_exists(BITMASK_CHAIN): - ip6tables("--flush", BITMASK_CHAIN) - ip6tables("--delete-chain", BITMASK_CHAIN) + ok = True + try: + iptables("--wait", "--delete", "OUTPUT", "--jump", BITMASK_CHAIN, throw=True) + except subprocess.CalledProcessError as exc: + log("INFO: not able to remove bitmask firewall from OUTPUT chain (maybe it is already removed?)", exc) + ok = False + try: + ip4tables("--flush", BITMASK_CHAIN, throw=True) + ip4tables("--delete-chain", BITMASK_CHAIN, throw=True) + except subprocess.CalledProcessError as exc: + log("INFO: not able to flush and delete bitmask ipv4 firewall chain (maybe it is already destroyed?)", exc) + ok = False + try: + ip6tables("--flush", BITMASK_CHAIN, throw=True) + ip6tables("--delete-chain", BITMASK_CHAIN, throw=True) + except subprocess.CalledProcessError as exc: + log("INFO: not able to flush and delete bitmask ipv6 firewall chain (maybe it is already destroyed?)", exc) + ok = False + if not ok: + raise Exception("firewall might still be left up. Please try `firewall stop` again.") ## ## MAIN @@ -874,7 +939,7 @@ def main(): elif command == "firewall_isup": if ipv4_chain_exists(BITMASK_CHAIN): - print("%s: INFO: bitmask firewall is up" % (SCRIPT,)) + log("%s: INFO: bitmask firewall is up" % (SCRIPT,)) else: bail("INFO: bitmask firewall is down") @@ -884,8 +949,7 @@ def main(): bail("ERROR: No such command") if __name__ == "__main__": - if DEBUG: - logger.debug(" ".join(sys.argv)) + debug(" ".join(sys.argv)) main() - print("%s: done" % (SCRIPT,)) + log("%s: done" % (SCRIPT,)) exit(0) -- cgit v1.2.3 From 11c95ac4c8281b6f51aa0e61a4bf13159f664855 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 10 Jul 2014 11:10:08 -0300 Subject: Use preferred provider on first run. Closes #5813. --- src/leap/bitmask/gui/wizard.py | 48 +++++++++++++++++++++++++++++++------ src/leap/bitmask/provider/pinned.py | 10 ++++++-- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index f66c553d..cb745c08 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -184,21 +184,55 @@ class Wizard(QtGui.QWizard): :param pinned: list of pinned providers :type pinned: list of str + + + How the combobox items are arranged: + ----------------------------------- + + First run: + + demo.bitmask.net + -- + pinned2.org + pinned1.org + pinned3.org + + After some usage: + + added-by-user.org + pinned-but-then-used.org + --- + demo.bitmask.net + pinned1.org + pinned3.org + pinned2.org + + In other words: + * There are two sections. + * Section one consists of all the providers that the user has used. + If this is empty, than use demo.bitmask.net for this section. + This list is sorted alphabetically. + * Section two consists of all the pinned or 'pre seeded' providers, + minus any providers that are now in section one. This last list + is in random order. """ ls = LeapSettings() - providers = ls.get_configured_providers() - if not providers and not pinned: + user_added = ls.get_configured_providers() + if not user_added and not pinned: self.ui.rbExistingProvider.setEnabled(False) self.ui.label_8.setEnabled(False) # 'https://' label self.ui.cbProviders.setEnabled(False) return - user_added = [] + user_added.sort() + + if not user_added: + user_added = [pinned.pop(0)] - # separate pinned providers from user added ones - for p in providers: - if p not in pinned: - user_added.append(p) + # separate unused pinned providers from user added ones + for p in user_added: + if p in pinned: + pinned.remove(p) if user_added: self.ui.cbProviders.addItems(user_added) diff --git a/src/leap/bitmask/provider/pinned.py b/src/leap/bitmask/provider/pinned.py index 38851621..6fd2fa70 100644 --- a/src/leap/bitmask/provider/pinned.py +++ b/src/leap/bitmask/provider/pinned.py @@ -32,6 +32,7 @@ class PinnedProviders(object): CONFIG_KEY = "config" CACERT_KEY = "cacert" + PREFERRED_PROVIDER = pinned_demobitmask.DOMAIN PROVIDERS = { pinned_demobitmask.DOMAIN: { @@ -50,11 +51,16 @@ class PinnedProviders(object): @classmethod def domains(self): """ - Return the domains that are pinned in here + Return the domains that are pinned in here. + The first domain in the list is the preferred one. :rtype: list of str """ - return self.PROVIDERS.keys() + domains = self.PROVIDERS.keys() + domains.remove(self.PREFERRED_PROVIDER) + domains.insert(0, self.PREFERRED_PROVIDER) + + return domains @classmethod def save_hardcoded(self, domain, provider_path, cacert_path): -- cgit v1.2.3 From 4da9d2fa3848fb2c6a6ea9982425c09b62fabb4b Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 10 Jul 2014 11:13:50 -0300 Subject: Code style fixes. --- src/leap/bitmask/gui/wizard.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index cb745c08..4f67958f 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -83,7 +83,8 @@ class Wizard(QtGui.QWizard): self.ui.grpCheckProvider.setVisible(False) self._connect_and_track(self.ui.btnCheck.clicked, self._check_provider) - self._connect_and_track(self.ui.lnProvider.returnPressed, self._check_provider) + self._connect_and_track(self.ui.lnProvider.returnPressed, + self._check_provider) self._backend = backend self._backend_connect() @@ -98,22 +99,24 @@ class Wizard(QtGui.QWizard): self._provider_select_defer = None self._provider_setup_defer = None - self._connect_and_track(self.currentIdChanged, self._current_id_changed) + self._connect_and_track(self.currentIdChanged, + self._current_id_changed) - self._connect_and_track(self.ui.lnProvider.textChanged, self._enable_check) + self._connect_and_track(self.ui.lnProvider.textChanged, + self._enable_check) self._connect_and_track(self.ui.rbNewProvider.toggled, - lambda x: self._enable_check()) + lambda x: self._enable_check()) self._connect_and_track(self.ui.cbProviders.currentIndexChanged[int], - self._reset_provider_check) + self._reset_provider_check) self._connect_and_track(self.ui.lblUser.returnPressed, - self._focus_password) + self._focus_password) self._connect_and_track(self.ui.lblPassword.returnPressed, - self._focus_second_password) + self._focus_second_password) self._connect_and_track(self.ui.lblPassword2.returnPressed, - self._register) + self._register) self._connect_and_track(self.ui.btnRegister.clicked, - self._register) + self._register) self._connect_and_track(self.ui.rbExistingProvider.toggled, self._skip_provider_checks) -- cgit v1.2.3 From 9a0760848f038dc0271b8c9adabf8aefe4db594e Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 10 Jul 2014 11:15:36 -0300 Subject: Add changes file for #5813. --- changes/feature-5813_wizard-providers-order | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/feature-5813_wizard-providers-order diff --git a/changes/feature-5813_wizard-providers-order b/changes/feature-5813_wizard-providers-order new file mode 100644 index 00000000..f4033d26 --- /dev/null +++ b/changes/feature-5813_wizard-providers-order @@ -0,0 +1 @@ +- Use preferred provider on first run. Closes #5813. -- cgit v1.2.3 From 37172578152f6f3d6ef169d79062c1d28ab0e98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Tue, 8 Jul 2014 15:41:20 -0300 Subject: Add packaging for osx with py2app --- changes/feature_package_osx | 1 + pkg/osx/Info.plist | 34 ++++++++++++++++++---------------- setup.py | 29 ++++++++++++++++++++++++++++- src/leap/bitmask/__init__.py | 6 ++++++ src/leap/bitmask/gui/__init__.py | 6 ++++-- 5 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 changes/feature_package_osx diff --git a/changes/feature_package_osx b/changes/feature_package_osx new file mode 100644 index 00000000..cf5823bd --- /dev/null +++ b/changes/feature_package_osx @@ -0,0 +1 @@ +- Add the ability to create an osx bundle with py2app. Closes #5845. \ No newline at end of file diff --git a/pkg/osx/Info.plist b/pkg/osx/Info.plist index e90d920a..dc427c4a 100644 --- a/pkg/osx/Info.plist +++ b/pkg/osx/Info.plist @@ -2,21 +2,23 @@ - CFBundleDisplayName - leap-client - CFBundleExecutable - MacOS/app - CFBundleIconFile - icon-windowed.icns - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - leap-client - CFBundlePackageType - APPL - CFBundleShortVersionString - 1 - LSBackgroundOnly - + CFBundleDisplayName + Bitmask + CFBundleExecutable + app + CFBundleIconFile + bitmask.icns + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Bitmask + CFBundlePackageType + APPL + CFBundleShortVersionString + 1 + LSBackgroundOnly + + CFBundleIdentifier + se.leap.bitmask diff --git a/setup.py b/setup.py index bb1937cc..7cd48799 100755 --- a/setup.py +++ b/setup.py @@ -253,7 +253,8 @@ cmdclass["sdist"] = cmd_sdist import platform _system = platform.system() -IS_LINUX = True if _system == "Linux" else False +IS_LINUX = _system == "Linux" +IS_MAC = _system == "Darwin" data_files = [] @@ -267,6 +268,31 @@ if IS_LINUX: ["pkg/linux/bitmask-root"]), ] +extra_options = {} + +if IS_MAC: + extra_options["app"] = ['src/leap/bitmask/app.py'] + OPTIONS = { + 'argv_emulation': True, + 'plist': 'pkg/osx/Info.plist', + 'iconfile': 'pkg/osx/bitmask.icns', + } + extra_options["options"] = {'py2app': OPTIONS} + extra_options["setup_requires"] = ['py2app'] + + class jsonschema_recipe(object): + def check(self, dist, mf): + m = mf.findNode('jsonschema') + if m is None: + return None + + # Don't put jsonschema in the site-packages.zip file + return dict( + packages=['jsonschema'] + ) + + import py2app.recipes + py2app.recipes.jsonschema = jsonschema_recipe() setup( name="leap.bitmask", @@ -305,4 +331,5 @@ setup( entry_points={ 'console_scripts': [leap_launcher] }, + **extra_options ) diff --git a/src/leap/bitmask/__init__.py b/src/leap/bitmask/__init__.py index 0f733f26..03da1e2f 100644 --- a/src/leap/bitmask/__init__.py +++ b/src/leap/bitmask/__init__.py @@ -25,6 +25,12 @@ from pkg_resources import parse_version from leap.bitmask.util import first +# HACK: This is a hack so that py2app copies _scrypt.so to the right +# place, it can't be technically imported, but that doesn't matter +# because the import is never executed +if False: + import _scrypt + def _is_release_version(version): """ diff --git a/src/leap/bitmask/gui/__init__.py b/src/leap/bitmask/gui/__init__.py index 4b289442..94bf1fd5 100644 --- a/src/leap/bitmask/gui/__init__.py +++ b/src/leap/bitmask/gui/__init__.py @@ -17,5 +17,7 @@ """ init file for leap.gui """ -app = __import__("app", globals(), locals(), [], 2) -__all__ = [app] +# This was added for coverage and testing, but when doing the osx +# bundle with py2app it fails because of this, so commenting for now +# app = __import__("app", globals(), locals(), [], 2) +# __all__ = [app] -- cgit v1.2.3 From 2d7fcd6cbb65945c64fe9fa949a6d176f5dd0d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 10 Jul 2014 11:18:52 -0300 Subject: Add bitmask.icns file --- pkg/osx/bitmask.icns | Bin 0 -> 47303 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pkg/osx/bitmask.icns diff --git a/pkg/osx/bitmask.icns b/pkg/osx/bitmask.icns new file mode 100644 index 00000000..7cc3e752 Binary files /dev/null and b/pkg/osx/bitmask.icns differ -- cgit v1.2.3 From 1162895e124191996cc448816ad5b26bad266cfa Mon Sep 17 00:00:00 2001 From: elijah Date: Thu, 10 Jul 2014 11:59:42 -0700 Subject: bitmask-root: rip out all resolvconf code and simply rewrite all DNS packets to use the VPN nameserver. --- changes/feature-reroute_dns_packets | 1 + docs/dev/environment.rst | 14 +- docs/dev/quickstart.rst | 2 - docs/testers/howto.rst | 10 +- pkg/linux/bitmask-root | 332 ++++------------------ pkg/linux/resolv-update | 90 ------ pkg/linux/update-resolv-conf | 58 ---- src/leap/bitmask/platform_init/initializers.py | 44 +-- src/leap/bitmask/services/eip/linuxvpnlauncher.py | 6 - 9 files changed, 64 insertions(+), 493 deletions(-) create mode 100644 changes/feature-reroute_dns_packets delete mode 100755 pkg/linux/resolv-update delete mode 100755 pkg/linux/update-resolv-conf diff --git a/changes/feature-reroute_dns_packets b/changes/feature-reroute_dns_packets new file mode 100644 index 00000000..beef3a1f --- /dev/null +++ b/changes/feature-reroute_dns_packets @@ -0,0 +1 @@ +- reroute DNS packets instead of blocking them, eliminating need to muck around with resolv.conf. Closes #4633, #5655, #5738, #4823 \ No newline at end of file diff --git a/docs/dev/environment.rst b/docs/dev/environment.rst index 0f6366ef..a3184b01 100644 --- a/docs/dev/environment.rst +++ b/docs/dev/environment.rst @@ -54,7 +54,7 @@ It is a tool to create isolated Python environments. The basic problem being addressed is one of dependencies and versions, and indirectly permissions. Imagine you have an application that needs version 1 of LibFoo, but another application requires version 2. How can you use both these applications? If you install everything into /usr/lib/python2.7/site-packages (or whatever your platform's standard location is), it's easy to end up in a situation where you unintentionally upgrade an application that shouldn't be upgraded. -Read more about it in the `project documentation page `_. +Read more about it in the `project documentation page `_. .. note:: this section could be completed with useful options that can be passed to the virtualenv command (e.g., to make portable paths, site-packages, ...). We also should document how to use virtualenvwrapper. @@ -73,7 +73,7 @@ You first create a virtualenv in any directory that you like:: Note the change in the prompt. -.. TODO use virtualenvwrapper + isis non-sudo recipe here +.. TODO use virtualenvwrapper + isis non-sudo recipe here .. _pysidevirtualenv: @@ -143,16 +143,6 @@ You need to repeat this step each time you change a ``.ui`` file. .. TODO need to make translations too? -.. _copyscriptfiles: - -Copy script files ------------------ - -The openvpn invocation expects some files to be in place. If you have not installed `bitmask` from a debian package, you must copy these files manually by now:: - - $ sudo mkdir -p /etc/leap - $ sudo cp pkg/linux/resolv-update /etc/leap - .. _policykit: Running openvpn without root privileges diff --git a/docs/dev/quickstart.rst b/docs/dev/quickstart.rst index a8be955e..d9c04a69 100644 --- a/docs/dev/quickstart.rst +++ b/docs/dev/quickstart.rst @@ -65,8 +65,6 @@ Compile the resource files:: Copy necessary files into system folders, with root privileges:: - (bitmask)$ sudo mkdir -p /etc/leap - (bitmask)$ sudo cp pkg/linux/resolv-update /etc/leap (bitmask)$ sudo cp pkg/linux/polkit/net.openvpn.gui.leap.policy /usr/share/polkit-1/actions/ diff --git a/docs/testers/howto.rst b/docs/testers/howto.rst index d9536632..a7a72b7f 100644 --- a/docs/testers/howto.rst +++ b/docs/testers/howto.rst @@ -67,7 +67,7 @@ Launch Bitmask in debug mode. Logs are way more verbose that way:: Get your hand on the logs. You can achieve that either by clicking on the "Show log" button, and saving to file, or directly by specifying the path to the logfile in the command line invocation:: - + bitmask --debug --logfile /tmp/bitmask.log Attach the logfile to your bug report. @@ -99,7 +99,7 @@ painlessly fetch the latest development code. We have put together a script to allow rapid testing in different platforms for the brave souls like you. It more or less does all the steps covered in the :ref:`Setting up a Work Enviroment ` section, only that in a more -compact way suitable (ahem) also for non developers. +compact way suitable (ahem) also for non developers. .. note:: @@ -127,7 +127,7 @@ Download and source the following script in the parent folder where you want you .. code-block:: bash - cd /tmp + cd /tmp wget https://raw.github.com/leapcode/bitmask_client/develop/pkg/scripts/bitmask_bootstrap.sh source bitmask_bootstrap.sh @@ -142,7 +142,7 @@ Activating the virtualenv The above bootstrap script has fetched latest code inside a virtualenv, which is an isolated, *virtual* python local environment that avoids messing with your global paths. You will notice you are *inside* a virtualenv because you will see -a modified prompt reminding it to you (*bitmask-testbuild* in this case). +a modified prompt reminding it to you (*bitmask-testbuild* in this case). Thus, if you forget to *activate your virtualenv*, bitmask will not run from the local path, and it will be looking for something else in your global path. So, @@ -162,8 +162,6 @@ Copying config files If you have never installed ``bitmask`` globally, **you need to copy some files to its proper path before running it for the first time** (you only need to do this once). This, unless the virtualenv-based operations, will need root permissions. See :ref:`copy script files ` and :ref:`running openvpn without root privileges ` sections for more info on this. In short:: $ sudo cp pkg/linux/polkit/net.openvpn.gui.leap.policy /usr/share/polkit-1/actions/ - $ sudo mkdir -p /etc/leap - $ sudo cp pkg/linux/resolv-update /etc/leap Local config files ^^^^^^^^^^^^^^^^^^^ diff --git a/pkg/linux/bitmask-root b/pkg/linux/bitmask-root index 3ffd0eee..4463dbaa 100755 --- a/pkg/linux/bitmask-root +++ b/pkg/linux/bitmask-root @@ -60,24 +60,12 @@ IP = "/bin/ip" IPTABLES = "/sbin/iptables" IP6TABLES = "/sbin/ip6tables" -RESOLVCONF_SYSTEM_BIN = "/sbin/resolvconf" -RESOLVCONF_LEAP_BIN = "/usr/local/sbin/leap-resolvconf" - OPENVPN_USER = "nobody" OPENVPN_GROUP = "nogroup" LEAPOPENVPN = "LEAPOPENVPN" OPENVPN_SYSTEM_BIN = "/usr/sbin/openvpn" # Debian location OPENVPN_LEAP_BIN = "/usr/local/sbin/leap-openvpn" # installed by bundle - -""" -The path to the script to update resolv.conf -""" -# XXX We have to check if we have a valid resolvconf, and use -# the old resolv-update if not. -LEAP_UPDATE_RESOLVCONF_FILE = "/etc/leap/update-resolv-conf" -LEAP_RESOLV_UPDATE = "/etc/leap/resolv-update" - FIXED_FLAGS = [ "--setenv", "LEAPOPENVPN", "1", "--nobind", @@ -137,7 +125,6 @@ syslog.openlog(SCRIPT) ## UTILITY ## - def is_valid_address(value): """ Validate that the passed ip is a valid IP address. @@ -154,33 +141,6 @@ def is_valid_address(value): return False -def has_system_resolvconf(): - """ - Return True if resolvconf is found in the system. - - :rtype: bool - """ - return os.path.isfile(RESOLVCONF) - - -def has_valid_update_resolvconf(): - """ - Return True if a valid update-resolv-conf script is found in the system. - - :rtype: bool - """ - return os.path.isfile(LEAP_UPDATE_RESOLVCONF_FILE) - - -def has_valid_leap_resolv_update(): - """ - Return True if a valid resolv-update script is found in the system. - - :rtype: bool - """ - return os.path.isfile(LEAP_RESOLV_UPDATE) - - def split_list(_list, regex): """ Split a list based on a regex: @@ -235,135 +195,6 @@ def get_process_list(): return filter(None, res) -class Daemon(object): - """ - A generic daemon class. - """ - def __init__(self, pidfile, stdin='/dev/null', - stdout='/dev/null', stderr='/dev/null'): - self.stdin = stdin - self.stdout = stdout - self.stderr = stderr - self.pidfile = pidfile - - def daemonize(self): - """ - Do the UNIX double-fork magic, see Stevens' "Advanced - Programming in the UNIX Environment" for details (ISBN 0201563177) - http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 - """ - try: - pid = os.fork() - if pid > 0: - # exit first parent - sys.exit(0) - except OSError, e: - sys.stderr.write( - "fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) - sys.exit(1) - - # decouple from parent environment - os.chdir("/") - os.setsid() - os.umask(0) - - # do second fork - try: - pid = os.fork() - if pid > 0: - # exit from second parent - sys.exit(0) - except OSError, e: - sys.stderr.write( - "fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) - sys.exit(1) - - # redirect standard file descriptors - sys.stdout.flush() - sys.stderr.flush() - si = file(self.stdin, 'r') - so = file(self.stdout, 'a+') - se = file(self.stderr, 'a+', 0) - os.dup2(si.fileno(), sys.stdin.fileno()) - os.dup2(so.fileno(), sys.stdout.fileno()) - os.dup2(se.fileno(), sys.stderr.fileno()) - - # write pidfile - atexit.register(self.delpid) - pid = str(os.getpid()) - file(self.pidfile, 'w+').write("%s\n" % pid) - - def delpid(self): - """ - Delete the pidfile. - """ - os.remove(self.pidfile) - - def start(self, *args): - """ - Start the daemon. - """ - # Check for a pidfile to see if the daemon already runs - try: - pf = file(self.pidfile, 'r') - pid = int(pf.read().strip()) - pf.close() - except IOError: - pid = None - - if pid: - message = "pidfile %s already exist. Daemon already running?\n" - sys.stderr.write(message % self.pidfile) - sys.exit(1) - - # Start the daemon - self.daemonize() - self.run(args) - - def stop(self): - """ - Stop the daemon. - """ - # Get the pid from the pidfile - try: - pf = file(self.pidfile, 'r') - pid = int(pf.read().strip()) - pf.close() - except IOError: - pid = None - - if not pid: - message = "pidfile %s does not exist. Daemon not running?\n" - sys.stderr.write(message % self.pidfile) - return # not an error in a restart - - # Try killing the daemon process - try: - while 1: - os.kill(pid, signal.SIGTERM) - time.sleep(0.1) - except OSError, err: - err = str(err) - if err.find("No such process") > 0: - if os.path.exists(self.pidfile): - os.remove(self.pidfile) - else: - log(str(err)) - sys.exit(1) - - def restart(self): - """ - Restart the daemon. - """ - self.stop() - self.start() - - def run(self): - """ - This should be overridden by derived classes. - """ - - def run(command, *args, **options): """ Run an external command. @@ -400,11 +231,11 @@ def run(command, *args, **options): subprocess.check_call(parts, stdout=devnull, stderr=devnull) return 0 except subprocess.CalledProcessError as exc: - if DEBUG: - logger.exception(exc) if _exitcode: - debug("ERROR: Could not run %s: %s" % (exc.cmd, exc.output), - exception=exc) + if exc.returncode != 1: + # 0 or 1 is to be expected, but anything else should be logged. + debug("ERROR: Could not run %s: %s" % (exc.cmd, exc.output), + exception=exc) return exc.returncode elif _throw: raise exc @@ -452,7 +283,6 @@ def bail(msg=None, exception=None): ## OPENVPN ## - def get_openvpn_bin(): """ Return the path for either the system openvpn or the one the @@ -542,86 +372,10 @@ def openvpn_stop(args): pid = found_leap_openvpn[0][0] os.kill(int(pid), signal.SIGTERM) -## -## DNS -## - - -def get_resolvconf_bin(): - """ - Return the path for either the system resolvconf or the one the - bundle has put there. - """ - if os.path.isfile(RESOLVCONF_SYSTEM_BIN): - return RESOLVCONF_SYSTEM_BIN - - # the bundle option should be removed from the debian package. - if os.path.isfile(RESOLVCONF_LEAP_BIN): - return RESOLVCONF_LEAP_BIN - -RESOLVCONF = get_resolvconf_bin() - - -class NameserverSetter(Daemon): - """ - A daemon that will add leap nameserver inside the tunnel - to the system `resolv.conf` - """ - - def run(self, *args): - """ - Run when daemonized. - """ - if args: - ip_address = args[0] - self.set_dns_nameserver(ip_address) - - def set_dns_nameserver(self, ip_address): - """ - Add the tunnel DNS server to `resolv.conf` - - :param ip_address: the ip to add to `resolv.conf` - :type ip_address: str - """ - if os.path.isfile(RESOLVCONF): - process = run(RESOLVCONF, "-a", "bitmask", input=True) - process.communicate("nameserver %s\n" % ip_address) - else: - bail("ERROR: package openresolv or resolvconf not installed.") - -nameserver_setter = NameserverSetter('/tmp/leap-dns-up.pid') - - -class NameserverRestorer(Daemon): - """ - A daemon that will restore the previous nameservers. - """ - - def run(self, *args): - """ - Run when daemonized. - """ - self.restore_dns_nameserver() - - def restore_dns_nameserver(self): - """ - Remove tunnel DNS server from `resolv.conf` - """ - if os.path.isfile(RESOLVCONF): - run(RESOLVCONF, "-d", "bitmask") - else: - log("%s: ERROR: package openresolv " - "or resolvconf not installed." % - (SCRIPT,)) - -nameserver_restorer = NameserverRestorer('/tmp/leap-dns-down.pid') - - ## ## FIREWALL ## - def get_gateways(gateways): """ Filter a passed sequence of gateways, returning only the valid ones. @@ -738,15 +492,19 @@ def ip6tables(*args, **options): # the chain. # -def ipv4_chain_exists(chain): +def ipv4_chain_exists(chain, table=None): """ - Check if a given chain exists. + Check if a given chain exists. Only returns true if it actually exists, + but might return false if it exists and iptables failed to run. :param chain: the chain to check against :type chain: str :rtype: bool """ - code = run(IPTABLES, "--list", chain, "--numeric", exitcode=True) + if table: + code = run(IPTABLES, "-t", table, "--list", chain, "--numeric", exitcode=True) + else: + code = run(IPTABLES, "--list", chain, "--numeric", exitcode=True) if code == 0: return True elif code == 1: @@ -758,7 +516,7 @@ def ipv4_chain_exists(chain): def ipv6_chain_exists(chain): """ - Check if a given chain exists. + see ipv4_chain_exists() :param chain: the chain to check against :type chain: str @@ -773,6 +531,13 @@ def ipv6_chain_exists(chain): log("ERROR: Could not determine state of iptable chain") return False +def enable_ip_forwarding(): + """ + ip_fowarding must be enabled for the firewall to work. + """ + file = open('/proc/sys/net/ipv4/ip_forward', 'w') + file.write('1\n') + file.close def firewall_start(args): """ @@ -786,23 +551,29 @@ def firewall_start(args): local_network_ipv6 = get_local_network_ipv6(default_device) gateways = get_gateways(args) - # add custom chain "bitmask" to front of OUTPUT chain + # add custom chain "bitmask" to front of OUTPUT chain for both + # the 'filter' and the 'nat' tables. if not ipv4_chain_exists(BITMASK_CHAIN): ip4tables("--new-chain", BITMASK_CHAIN) + if not ipv4_chain_exists(BITMASK_CHAIN, 'nat'): + ip4tables("--table", "nat", "--new-chain", BITMASK_CHAIN) if not ipv6_chain_exists(BITMASK_CHAIN): ip6tables("--new-chain", BITMASK_CHAIN) + ip4tables("--table", "nat", "--insert", "OUTPUT", "--jump", BITMASK_CHAIN) iptables("--insert", "OUTPUT", "--jump", BITMASK_CHAIN) - # allow DNS over VPN - for allowed_dns in [NAMESERVER, "127.0.0.1", "127.0.1.1"]: - ip4tables("--append", BITMASK_CHAIN, "--protocol", "udp", - "--dport", "53", "--destination", allowed_dns, - "--jump", "ACCEPT") - - # block DNS requests to anyone but the service provider or localhost - # (when we actually route ipv6, we will need DNS rules for it too) - ip4tables("--append", BITMASK_CHAIN, "--protocol", "udp", "--dport", "53", - "--jump", "REJECT") + # route all ipv4 DNS over VPN (note: NAT does not work with ipv6 until kernel 3.7) + enable_ip_forwarding() + # allow dns to localhost + ip4tables("-t", "nat", "--append", BITMASK_CHAIN, "--protocol", "udp", + "--dest", "127.0.1.1,127.0.0.1", "--dport", "53", + "--jump", "ACCEPT") + # rewrite all outgoing packets to use VPN DNS server + # (DNS does sometimes use TCP!) + ip4tables("-t", "nat", "--append", BITMASK_CHAIN, "--protocol", "udp", + "--dport", "53", "--jump", "DNAT", "--to", NAMESERVER+":53") + ip4tables("-t", "nat", "--append", BITMASK_CHAIN, "--protocol", "tcp", + "--dport", "53", "--jump", "DNAT", "--to", NAMESERVER+":53") # allow traffic to IPs on local network if local_network_ipv4: @@ -846,8 +617,7 @@ def firewall_start(args): "--log-level", "7") # for now, ensure all other ipv6 packets get rejected (regardless of - # device) - # (not sure why, but "-p any" doesn't work) + # device). not sure why, but "-p any" doesn't work. ip6tables("--append", BITMASK_CHAIN, "-p", "tcp", "--jump", "REJECT") ip6tables("--append", BITMASK_CHAIN, "-p", "udp", "--jump", "REJECT") @@ -868,30 +638,41 @@ def firewall_stop(): """ ok = True try: - iptables("--wait", "--delete", "OUTPUT", "--jump", BITMASK_CHAIN, throw=True) + iptables("--delete", "OUTPUT", "--jump", BITMASK_CHAIN, throw=True) + except subprocess.CalledProcessError as exc: + debug("INFO: not able to remove bitmask firewall from OUTPUT chain (maybe it is already removed?)", exc) + ok = False + try: + ip4tables("-t", "nat", "--delete", "OUTPUT", "--jump", BITMASK_CHAIN, throw=True) except subprocess.CalledProcessError as exc: - log("INFO: not able to remove bitmask firewall from OUTPUT chain (maybe it is already removed?)", exc) + debug("INFO: not able to remove bitmask firewall from OUTPUT chain in 'nat' table (maybe it is already removed?)", exc) ok = False try: ip4tables("--flush", BITMASK_CHAIN, throw=True) ip4tables("--delete-chain", BITMASK_CHAIN, throw=True) except subprocess.CalledProcessError as exc: - log("INFO: not able to flush and delete bitmask ipv4 firewall chain (maybe it is already destroyed?)", exc) + debug("INFO: not able to flush and delete bitmask ipv4 firewall chain (maybe it is already destroyed?)", exc) + ok = False + try: + ip4tables("-t", "nat", "--flush", BITMASK_CHAIN, throw=True) + ip4tables("-t", "nat", "--delete-chain", BITMASK_CHAIN, throw=True) + except subprocess.CalledProcessError as exc: + debug("INFO: not able to flush and delete bitmask ipv4 firewall chain in 'nat' table (maybe it is already destroyed?)", exc) ok = False try: ip6tables("--flush", BITMASK_CHAIN, throw=True) ip6tables("--delete-chain", BITMASK_CHAIN, throw=True) except subprocess.CalledProcessError as exc: - log("INFO: not able to flush and delete bitmask ipv6 firewall chain (maybe it is already destroyed?)", exc) + debug("INFO: not able to flush and delete bitmask ipv6 firewall chain (maybe it is already destroyed?)", exc) ok = False - if not ok: + if not (ok or ipv4_chain_exists or ipv6_chain_exists): raise Exception("firewall might still be left up. Please try `firewall stop` again.") + ## ## MAIN ## - def main(): """ Entry point for cmdline execution. @@ -923,17 +704,14 @@ def main(): elif command == "firewall_start": try: firewall_start(args) - nameserver_setter.start(NAMESERVER) except Exception as ex: if not is_restart: - nameserver_restorer.start() firewall_stop() bail("ERROR: could not start firewall", ex) elif command == "firewall_stop": try: firewall_stop() - nameserver_restorer.start() except Exception as ex: bail("ERROR: could not stop firewall", ex) @@ -951,5 +729,5 @@ def main(): if __name__ == "__main__": debug(" ".join(sys.argv)) main() - log("%s: done" % (SCRIPT,)) + log("done") exit(0) diff --git a/pkg/linux/resolv-update b/pkg/linux/resolv-update deleted file mode 100755 index c308b788..00000000 --- a/pkg/linux/resolv-update +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash -# -# Parses options from openvpn to update resolv.conf -# -# The only way to enforce that a linux system will not leak DNS -# queries is to replace /etc/resolv.conf with a file that only -# has the DNS resolver specified by the VPN. -# -# That is what this script does. This is what resolvconf is for, -# but sadly it does not always work. -# -# Example envs set from openvpn: -# foreign_option_1='dhcp-option DNS 193.43.27.132' -# foreign_option_2='dhcp-option DNS 193.43.27.133' -# foreign_option_3='dhcp-option DOMAIN be.bnc.ch' -# - -function up() { - - comment=$( -cat < /etc/resolv.conf -} - -function down() { - if [ -f /etc/resolv.conf.bak ] ; then - cat /etc/resolv.conf.bak > /etc/resolv.conf - rm /etc/resolv.conf.bak - fi -} - -case $script_type in - up) up ;; - down) down ;; -esac diff --git a/pkg/linux/update-resolv-conf b/pkg/linux/update-resolv-conf deleted file mode 100755 index 76c69413..00000000 --- a/pkg/linux/update-resolv-conf +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -# -# Parses DHCP options from openvpn to update resolv.conf -# To use set as 'up' and 'down' script in your openvpn *.conf: -# up /etc/leap/update-resolv-conf -# down /etc/leap/update-resolv-conf -# -# Used snippets of resolvconf script by Thomas Hood and Chris Hanson. -# Licensed under the GNU GPL. See /usr/share/common-licenses/GPL. -# -# Example envs set from openvpn: -# -# foreign_option_1='dhcp-option DNS 193.43.27.132' -# foreign_option_2='dhcp-option DNS 193.43.27.133' -# foreign_option_3='dhcp-option DOMAIN be.bnc.ch' -# - -[ -x /sbin/resolvconf ] || exit 0 -[ "$script_type" ] || exit 0 -[ "$dev" ] || exit 0 - -split_into_parts() -{ - part1="$1" - part2="$2" - part3="$3" -} - -case "$script_type" in - up) - NMSRVRS="" - SRCHS="" - for optionvarname in ${!foreign_option_*} ; do - option="${!optionvarname}" - echo "$option" - split_into_parts $option - if [ "$part1" = "dhcp-option" ] ; then - if [ "$part2" = "DNS" ] ; then - NMSRVRS="${NMSRVRS:+$NMSRVRS }$part3" - elif [ "$part2" = "DOMAIN" ] ; then - SRCHS="${SRCHS:+$SRCHS }$part3" - fi - fi - done - R="" - [ "$SRCHS" ] && R="search $SRCHS -" - for NS in $NMSRVRS ; do - R="${R}nameserver $NS -" - done - echo -n "$R" | /sbin/resolvconf -a "${dev}.openvpn" - ;; - down) - /sbin/resolvconf -d "${dev}.openvpn" - ;; -esac - diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py index 384e1ec1..2d800703 100644 --- a/src/leap/bitmask/platform_init/initializers.py +++ b/src/leap/bitmask/platform_init/initializers.py @@ -373,30 +373,6 @@ def DarwinInitializer(): # Linux initializers # -def _get_missing_resolvconf_dialog(): - """ - Create a dialog for notifying about missing openresolv. - - :rtype: QtGui.QMessageBox instance - """ - msgstr = QtCore.QObject() - msgstr.NO_RESOLVCONF = msgstr.tr( - "Could not find resolvconf installed in your system.\n" - "Do you want to quit Bitmask now?") - - msgstr.EXPLAIN = msgstr.tr( - "Encrypted Internet needs resolvconf installed to work properly.\n" - "Please use your package manager to install it.\n") - - msg = QtGui.QMessageBox() - msg.setWindowTitle(msg.tr("Missing resolvconf framework")) - msg.setText(msgstr.NO_RESOLVCONF) - # but maybe the user really deserve to know more - msg.setInformativeText(msgstr.EXPLAIN) - msg.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) - msg.setDefaultButton(QtGui.QMessageBox.Yes) - return msg - def _get_missing_complain_dialog(stuff): """ @@ -445,21 +421,6 @@ def _get_missing_complain_dialog(stuff): return msg -def _linux_check_resolvconf(): - """ - Raise a dialog warning about the lack of the resolvconf framework. - """ - RESOLVCONF_PATH = "/sbin/resolvconf" - missing = not os.path.isfile(RESOLVCONF_PATH) - - if missing: - msg = _get_missing_resolvconf_dialog() - ret = msg.exec_() - - if ret == QtGui.QMessageBox.Yes: - sys.exit() - - def _linux_install_missing_scripts(badexec, notfound): """ Try to install the missing helper files. @@ -509,9 +470,8 @@ def LinuxInitializer(): """ Raise a dialog if needed files are missing. - Missing files can be either system-wide resolvconf, bitmask-root, or - policykit file. The dialog will also be raised if some of those files are + Missing files can be either bitmask-root policykit file. + The dialog will also be raised if some of those files are found to have incorrect permissions. """ - _linux_check_resolvconf() check_missing() diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py index 1409d504..b6e47f25 100644 --- a/src/leap/bitmask/services/eip/linuxvpnlauncher.py +++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py @@ -127,12 +127,6 @@ class LinuxVPNLauncher(VPNLauncher): # LinuxPolicyChecker will give us the right path if standalone. return LinuxPolicyChecker.get_polkit_path() - class RESOLVCONF_BIN_PATH(object): - def __call__(self): - return ("/usr/local/sbin/leap-resolvconf" if flags.STANDALONE else - "/sbin/resolvconf") - # this only will work with debian/ubuntu distros. - OTHER_FILES = (POLKIT_PATH, BITMASK_ROOT, OPENVPN_BIN_PATH) @classmethod -- cgit v1.2.3 From 6246056799898218422075994b8f657a6b208bca Mon Sep 17 00:00:00 2001 From: elijah Date: Thu, 10 Jul 2014 12:00:14 -0700 Subject: bitmask-root: pep8'ed --- pkg/linux/bitmask-root | 102 +++++++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/pkg/linux/bitmask-root b/pkg/linux/bitmask-root index 4463dbaa..6fbafff9 100755 --- a/pkg/linux/bitmask-root +++ b/pkg/linux/bitmask-root @@ -47,9 +47,9 @@ import traceback cmdcheck = subprocess.check_output -## -## CONSTANTS -## +# +# CONSTANTS +# VERSION = "1" SCRIPT = "bitmask-root" @@ -121,9 +121,10 @@ if DEBUG: syslog.openlog(SCRIPT) -## -## UTILITY -## +# +# UTILITY +# + def is_valid_address(value): """ @@ -207,7 +208,8 @@ def run(command, *args, **options): `detach`: If True, run in detached process. `input`: If True, open command for writing stream to, returning the Popen object. - `throw`: If True, raise an exception if there is an error instead of bailing. + `throw`: If True, raise an exception if there is an error instead + of bailing. """ parts = [command] parts.extend(args) @@ -233,9 +235,10 @@ def run(command, *args, **options): except subprocess.CalledProcessError as exc: if _exitcode: if exc.returncode != 1: - # 0 or 1 is to be expected, but anything else should be logged. - debug("ERROR: Could not run %s: %s" % (exc.cmd, exc.output), - exception=exc) + # 0 or 1 is to be expected, but anything else + # should be logged. + debug("ERROR: Could not run %s: %s" % + (exc.cmd, exc.output), exception=exc) return exc.returncode elif _throw: raise exc @@ -264,6 +267,7 @@ def log(msg=None, exception=None, priority=syslog.LOG_INFO): traceback.print_exc() syslog.syslog(priority, traceback.format_exc()) + def debug(msg=None, exception=None): """ Just like log, but is skipped unless DEBUG. Use syslog.LOG_INFO @@ -272,6 +276,7 @@ def debug(msg=None, exception=None): if TEST or DEBUG: log(msg, exception) + def bail(msg=None, exception=None): """ abnormal exit. like log(), but exits with error status code. @@ -279,9 +284,10 @@ def bail(msg=None, exception=None): log(msg, exception) exit(1) -## -## OPENVPN -## +# +# OPENVPN +# + def get_openvpn_bin(): """ @@ -314,14 +320,14 @@ def parse_openvpn_flags(args): flag_params = flag[1:] if len(flag_params) != len(required_params): log("%s: ERROR: not enough params for %s" % - (SCRIPT, flag_name)) + (SCRIPT, flag_name)) return None for param, param_type in zip(flag_params, required_params): if PARAM_FORMATS[param_type](param): result.append(param) else: log("%s: ERROR: Bad argument %s" % - (SCRIPT, param)) + (SCRIPT, param)) return None else: log("WARNING: unrecognized openvpn flag %s" % flag_name) @@ -372,9 +378,10 @@ def openvpn_stop(args): pid = found_leap_openvpn[0][0] os.kill(int(pid), signal.SIGTERM) -## -## FIREWALL -## +# +# FIREWALL +# + def get_gateways(gateways): """ @@ -492,6 +499,7 @@ def ip6tables(*args, **options): # the chain. # + def ipv4_chain_exists(chain, table=None): """ Check if a given chain exists. Only returns true if it actually exists, @@ -501,10 +509,11 @@ def ipv4_chain_exists(chain, table=None): :type chain: str :rtype: bool """ - if table: - code = run(IPTABLES, "-t", table, "--list", chain, "--numeric", exitcode=True) + if table is not None: + code = run(IPTABLES, "-t", table, + "--list", chain, "--numeric", exitcode=True) else: - code = run(IPTABLES, "--list", chain, "--numeric", exitcode=True) + code = run(IPTABLES, "--list", chain, "--numeric", exitcode=True) if code == 0: return True elif code == 1: @@ -531,13 +540,14 @@ def ipv6_chain_exists(chain): log("ERROR: Could not determine state of iptable chain") return False + def enable_ip_forwarding(): """ ip_fowarding must be enabled for the firewall to work. """ - file = open('/proc/sys/net/ipv4/ip_forward', 'w') - file.write('1\n') - file.close + with open('/proc/sys/net/ipv4/ip_forward', 'w') as f: + f.write('1\n') + def firewall_start(args): """ @@ -562,7 +572,8 @@ def firewall_start(args): ip4tables("--table", "nat", "--insert", "OUTPUT", "--jump", BITMASK_CHAIN) iptables("--insert", "OUTPUT", "--jump", BITMASK_CHAIN) - # route all ipv4 DNS over VPN (note: NAT does not work with ipv6 until kernel 3.7) + # route all ipv4 DNS over VPN + # (note: NAT does not work with ipv6 until kernel 3.7) enable_ip_forwarding() # allow dns to localhost ip4tables("-t", "nat", "--append", BITMASK_CHAIN, "--protocol", "udp", @@ -628,50 +639,59 @@ def firewall_start(args): def firewall_stop(): """ - Stop the firewall. Because we really really always want the firewall to be - stopped if at all possible, this function is cautious and contains a lot of - trys and excepts. + Stop the firewall. Because we really really always want the firewall to + be stopped if at all possible, this function is cautious and contains a + lot of trys and excepts. - If there were any problems, we raise an exception at the end. This allows the calling code - to retry stopping the firewall. Stopping the firewall can fail if iptables is being run by - another process (only one iptables command can be run at a time). + If there were any problems, we raise an exception at the end. This allows + the calling code to retry stopping the firewall. Stopping the firewall + can fail if iptables is being run by another process (only one iptables + command can be run at a time). """ ok = True try: iptables("--delete", "OUTPUT", "--jump", BITMASK_CHAIN, throw=True) except subprocess.CalledProcessError as exc: - debug("INFO: not able to remove bitmask firewall from OUTPUT chain (maybe it is already removed?)", exc) + debug("INFO: not able to remove bitmask firewall from OUTPUT chain " + "(maybe it is already removed?)", exc) ok = False try: - ip4tables("-t", "nat", "--delete", "OUTPUT", "--jump", BITMASK_CHAIN, throw=True) + ip4tables("-t", "nat", "--delete", "OUTPUT", + "--jump", BITMASK_CHAIN, throw=True) except subprocess.CalledProcessError as exc: - debug("INFO: not able to remove bitmask firewall from OUTPUT chain in 'nat' table (maybe it is already removed?)", exc) + debug("INFO: not able to remove bitmask firewall from OUTPUT chain " + "in 'nat' table (maybe it is already removed?)", exc) ok = False try: ip4tables("--flush", BITMASK_CHAIN, throw=True) ip4tables("--delete-chain", BITMASK_CHAIN, throw=True) except subprocess.CalledProcessError as exc: - debug("INFO: not able to flush and delete bitmask ipv4 firewall chain (maybe it is already destroyed?)", exc) + debug("INFO: not able to flush and delete bitmask ipv4 firewall " + "chain (maybe it is already destroyed?)", exc) ok = False try: ip4tables("-t", "nat", "--flush", BITMASK_CHAIN, throw=True) ip4tables("-t", "nat", "--delete-chain", BITMASK_CHAIN, throw=True) except subprocess.CalledProcessError as exc: - debug("INFO: not able to flush and delete bitmask ipv4 firewall chain in 'nat' table (maybe it is already destroyed?)", exc) + debug("INFO: not able to flush and delete bitmask ipv4 firewall " + "chain in 'nat' table (maybe it is already destroyed?)", exc) ok = False try: ip6tables("--flush", BITMASK_CHAIN, throw=True) ip6tables("--delete-chain", BITMASK_CHAIN, throw=True) except subprocess.CalledProcessError as exc: - debug("INFO: not able to flush and delete bitmask ipv6 firewall chain (maybe it is already destroyed?)", exc) + debug("INFO: not able to flush and delete bitmask ipv6 firewall " + "chain (maybe it is already destroyed?)", exc) ok = False if not (ok or ipv4_chain_exists or ipv6_chain_exists): - raise Exception("firewall might still be left up. Please try `firewall stop` again.") + raise Exception("firewall might still be left up. " + "Please try `firewall stop` again.") + +# +# MAIN +# -## -## MAIN -## def main(): """ -- cgit v1.2.3 From 2bf9a1c9768e13a148d4bc92687f8cae9fbc2198 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 11 Jul 2014 15:06:54 -0300 Subject: Use better message for the initial sync. Closes #5875. --- changes/bug-5875_better-initial-sync-message | 1 + src/leap/bitmask/gui/mail_status.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changes/bug-5875_better-initial-sync-message diff --git a/changes/bug-5875_better-initial-sync-message b/changes/bug-5875_better-initial-sync-message new file mode 100644 index 00000000..473093ed --- /dev/null +++ b/changes/bug-5875_better-initial-sync-message @@ -0,0 +1 @@ +- Initial sync message is confusing. Closes #5875. diff --git a/src/leap/bitmask/gui/mail_status.py b/src/leap/bitmask/gui/mail_status.py index 5caef745..bb755b5c 100644 --- a/src/leap/bitmask/gui/mail_status.py +++ b/src/leap/bitmask/gui/mail_status.py @@ -304,7 +304,7 @@ class MailStatusWidget(QtGui.QWidget): ext_status = "" if req.event == proto.KEYMANAGER_LOOKING_FOR_KEY: - ext_status = self.tr("Looking for key for this user") + ext_status = self.tr("Initial sync in progress, please wait...") elif req.event == proto.KEYMANAGER_KEY_FOUND: ext_status = self.tr("Found key! Starting mail...") # elif req.event == proto.KEYMANAGER_KEY_NOT_FOUND: -- cgit v1.2.3 From 543296a62235d5303662720d3f341e1dc10d478e Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 9 Jul 2014 00:39:36 -0500 Subject: invoke mainwindow.quit only from parent pid because the fork happens after the registration of the signal handler, all children processes were inheriting the handler (and being passed a reference to the mainwindow object, ugh...) --- src/leap/bitmask/app.py | 67 +++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 6a7d6ff1..d0906b7c 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -49,7 +49,6 @@ from PySide import QtCore, QtGui from leap.bitmask import __version__ as VERSION from leap.bitmask.config import flags -from leap.bitmask.gui import locale_rc # noqa - silence pylint from leap.bitmask.gui.mainwindow import MainWindow from leap.bitmask.logs.utils import create_logger from leap.bitmask.platform_init.locks import we_are_the_one_and_only @@ -67,16 +66,39 @@ import codecs codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None) +import psutil + + +def kill_the_children(): + """ + Make sure no lingering subprocesses are left in case of a bad termination. + """ + me = os.getpid() + parent = psutil.Process(me) + print "Killing all the children processes..." + for child in parent.get_children(recursive=True): + try: + child.terminate() + except Exception as exc: + print exc + +# XXX This is currently broken, but we need to fix it to avoid +# orphaned processes in case of a crash. +#atexit.register(kill_the_children) + def sigint_handler(*args, **kwargs): """ Signal handler for SIGINT """ logger = kwargs.get('logger', None) - if logger: - logger.debug("SIGINT catched. shutting down...") - mainwindow = args[0] - mainwindow.quit() + parentpid = kwargs.get('parentpid', None) + pid = os.getpid() + if parentpid == pid: + if logger: + logger.debug("SIGINT catched. shutting down...") + mainwindow = args[0] + mainwindow.quit() def sigterm_handler(*args, **kwargs): @@ -85,10 +107,13 @@ def sigterm_handler(*args, **kwargs): This handler is actually passed to twisted reactor """ logger = kwargs.get('logger', None) - if logger: - logger.debug("SIGTERM catched. shutting down...") - mainwindow = args[0] - mainwindow.quit() + parentpid = kwargs.get('parentpid', None) + pid = os.getpid() + if parentpid == pid: + if logger: + logger.debug("SIGTERM catched. shutting down...") + mainwindow = args[0] + mainwindow.quit() def do_display_version(opts): @@ -203,32 +228,26 @@ def main(): app.setApplicationName("leap") app.setOrganizationDomain("leap.se") - # XXX --------------------------------------------------------- - # In quarantine, looks like we don't need it anymore. - # This dummy timer ensures that control is given to the outside - # loop, so we can hook our sigint handler. - #timer = QtCore.QTimer() - #timer.start(500) - #timer.timeout.connect(lambda: None) - # XXX --------------------------------------------------------- - window = MainWindow(bypass_checks=bypass_checks, start_hidden=start_hidden) - sigint_window = partial(sigint_handler, window, logger=logger) + mainpid = os.getpid() + sigint_window = partial(sigint_handler, window, + logger=logger, parentpid=mainpid) signal.signal(signal.SIGINT, sigint_window) # callable used in addSystemEventTrigger to handle SIGTERM - sigterm_window = partial(sigterm_handler, window, logger=logger) - - l = LoopingCall(QtCore.QCoreApplication.processEvents, 0, 10) - l.start(0.01) - + sigterm_window = partial(sigterm_handler, window, + logger=logger, parentpid=mainpid) # SIGTERM can't be handled the same way SIGINT is, since it's # caught by twisted. See _handleSignals method in # twisted/internet/base.py#L1150. So, addSystemEventTrigger # reactor's method is used. reactor.addSystemEventTrigger('before', 'shutdown', sigterm_window) + + l = LoopingCall(QtCore.QCoreApplication.processEvents, 0, 10) + l.start(0.01) + reactor.run() if __name__ == "__main__": -- cgit v1.2.3 From 5adf4d457ed964b7cc00def5d2be0dabd66fa04c Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 9 Jul 2014 00:40:14 -0500 Subject: call soledad close --- src/leap/bitmask/gui/mainwindow.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 8a5b8275..e53ab7f3 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -1786,7 +1786,6 @@ class MainWindow(QtGui.QMainWindow): Final steps to quit the app, starting from here we don't care about running services or user interaction, just quitting. """ - logger.debug('Final quit...') # We can reach here because all the services are stopped or because a # timeout was triggered. Since we want to run this only once, we exit @@ -1796,6 +1795,10 @@ class MainWindow(QtGui.QMainWindow): self._finally_quitting = True + logger.debug('Closing soledad...') + self._backend.soledad_close() + logger.debug('Final quit...') + # Remove lockfiles on a clean shutdown. logger.debug('Cleaning pidfiles') if IS_WIN: -- cgit v1.2.3 From 4f2da1b684fde6c0b452f79883b87ca057313cc7 Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Fri, 11 Jul 2014 13:02:26 -0500 Subject: Tools for TUF repository management --- changes/feature-5864_create_TUF_release_tool | 1 + pkg/tuf/init.py | 102 ++++++++++++++++++++++++ pkg/tuf/release.py | 114 +++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 changes/feature-5864_create_TUF_release_tool create mode 100755 pkg/tuf/init.py create mode 100755 pkg/tuf/release.py diff --git a/changes/feature-5864_create_TUF_release_tool b/changes/feature-5864_create_TUF_release_tool new file mode 100644 index 00000000..b9a4a3d5 --- /dev/null +++ b/changes/feature-5864_create_TUF_release_tool @@ -0,0 +1 @@ +- Add TUF init repository and release tools. Closes #5864. diff --git a/pkg/tuf/init.py b/pkg/tuf/init.py new file mode 100755 index 00000000..7300da0a --- /dev/null +++ b/pkg/tuf/init.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# init.py +# Copyright (C) 2014 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 . + +""" +Tool to initialize a TUF repo. + +The keys can be generated with: + openssl genrsa -des3 -out private.pem 4096 +The public key can be exported with: + openssl rsa -in private.pem -outform PEM -pubout -out public.pem +""" + +import sys + +from tuf.repository_tool import create_new_repository +from tuf.repository_tool import import_rsa_privatekey_from_file +from tuf.repository_tool import import_rsa_publickey_from_file + + +def usage(): + print ("Usage: %s repo root_private_key root_pub_key targets_pub_key" + " timestamp_pub_key") % (sys.argv[0],) + + +def main(): + if len(sys.argv) < 6: + usage() + return + + repo_path = sys.argv[1] + root_priv_path = sys.argv[2] + root_pub_path = sys.argv[3] + targets_pub_path = sys.argv[4] + timestamp_pub_path = sys.argv[5] + repo = Repo(repo_path, root_priv_path) + repo.build(root_pub_path, targets_pub_path, timestamp_pub_path) + + print "%s/metadata.staged/root.json is ready" % (repo_path,) + + +class Repo(object): + """ + Repository builder class + """ + + def __init__(self, repo_path, key_path): + """ + Constructor + + :param repo_path: path where the repo lives + :type repo_path: str + :param key_path: path where the private root key lives + :type key_path: str + """ + self._repo_path = repo_path + self._key = import_rsa_privatekey_from_file(key_path) + + def build(self, root_pub_path, targets_pub_path, timestamp_pub_path): + """ + Create a new repo + + :param root_pub_path: path where the public root key lives + :type root_pub_path: str + :param targets_pub_path: path where the public targets key lives + :type targets_pub_path: str + :param timestamp_pub_path: path where the public timestamp key lives + :type timestamp_pub_path: str + """ + repository = create_new_repository(self._repo_path) + + pub_root_key = import_rsa_publickey_from_file(root_pub_path) + repository.root.add_verification_key(pub_root_key) + repository.root.load_signing_key(self._key) + + pub_target_key = import_rsa_publickey_from_file(targets_pub_path) + repository.targets.add_verification_key(pub_target_key) + repository.snapshot.add_verification_key(pub_target_key) + repository.targets.compressions = ["gz"] + repository.snapshot.compressions = ["gz"] + + pub_timestamp_key = import_rsa_publickey_from_file(timestamp_pub_path) + repository.timestamp.add_verification_key(pub_timestamp_key) + + repository.write_partial() + + +if __name__ == "__main__": + main() diff --git a/pkg/tuf/release.py b/pkg/tuf/release.py new file mode 100755 index 00000000..c4abcd0d --- /dev/null +++ b/pkg/tuf/release.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# release.py +# Copyright (C) 2014 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 . + +""" +Tool to generate TUF related files after a release + +The 'repo' folder should contain two folders: + - 'metadata.staged' with all the jsons from the previows release + - 'targets' where the release targets are +""" + +import datetime +import os.path +import sys + +from tuf.repository_tool import load_repository +from tuf.repository_tool import import_rsa_privatekey_from_file +from tuf.repository_tool import import_rsa_publickey_from_file + +""" +Days until the expiration of targets.json and snapshot.json. After this ammount +of days the TUF client won't accept this files. +""" +EXPIRATION_DAYS = 90 + + +def usage(): + print "Usage: %s repo key" % (sys.argv[0],) + + +def main(): + if len(sys.argv) < 3: + usage() + return + + repo_path = sys.argv[1] + key_path = sys.argv[2] + targets = Targets(repo_path, key_path) + targets.build() + + print "%s/metadata.staged/(targets|snapshot).json[.gz] are ready" % \ + (repo_path,) + + +class Targets(object): + """ + Targets builder class + """ + + def __init__(self, repo_path, key_path): + """ + Constructor + + :param repo_path: path where the repo lives + :type repo_path: str + :param key_path: path where the private targets key lives + :type key_path: str + """ + self._repo_path = repo_path + self._key = import_rsa_privatekey_from_file(key_path) + + def build(self): + """ + Generate snapshot.json[.gz] and targets.json[.gz] + """ + self._repo = load_repository(self._repo_path) + self._load_targets() + + self._repo.targets.load_signing_key(self._key) + self._repo.snapshot.load_signing_key(self._key) + self._repo.targets.compressions = ["gz"] + self._repo.snapshot.compressions = ["gz"] + self._repo.snapshot.expiration = ( + datetime.datetime.now() + + datetime.timedelta(days=EXPIRATION_DAYS)) + self._repo.targets.expiration = ( + datetime.datetime.now() + + datetime.timedelta(days=EXPIRATION_DAYS)) + self._repo.write_partial() + + def _load_targets(self): + """ + Load a list of targets + """ + targets_path = os.path.join(self._repo_path, 'targets') + target_list = self._repo.get_filepaths_in_directory( + targets_path, + recursive_walk=True, + followlinks=True) + + for target in target_list: + octal_file_permissions = oct(os.stat(target).st_mode)[3:] + custom_file_permissions = { + 'file_permissions': octal_file_permissions + } + self._repo.targets.add_target(target, custom_file_permissions) + + +if __name__ == "__main__": + main() -- cgit v1.2.3 From 3795dedd26fc239e143ca2a29b7e16d433f964ba Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 10 Jun 2014 17:05:06 -0300 Subject: Separate app.py and frontend_app.py logics. This prepares the scenario to run the frontend and the backend in different processes. --- src/leap/bitmask/app.py | 106 ++++++--------------------------------- src/leap/bitmask/frontend_app.py | 103 +++++++++++++++++++++++++++++++++++++ src/leap/bitmask/gui/__init__.py | 1 + 3 files changed, 119 insertions(+), 91 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index d0906b7c..f1d87d18 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -39,17 +39,13 @@ # M:::::::::::~NMMM7???7MMMM:::::::::::::::::::::::NMMMI??I7MMMM:::::::::::::M # M::::::::::::::7MMMMMMM+:::::::::::::::::::::::::::?MMMMMMMZ:::::::::::::::M # (thanks to: http://www.glassgiant.com/ascii/) -import signal -import sys +import multiprocessing import os - -from functools import partial - -from PySide import QtCore, QtGui +import sys from leap.bitmask import __version__ as VERSION from leap.bitmask.config import flags -from leap.bitmask.gui.mainwindow import MainWindow +from leap.bitmask.frontend_app import run_frontend from leap.bitmask.logs.utils import create_logger from leap.bitmask.platform_init.locks import we_are_the_one_and_only from leap.bitmask.services.mail import plumber @@ -59,9 +55,6 @@ from leap.bitmask.util.requirement_checker import check_requirements from leap.common.events import server as event_server from leap.mail import __version__ as MAIL_VERSION -from twisted.internet import reactor -from twisted.internet.task import LoopingCall - import codecs codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None) @@ -84,36 +77,7 @@ def kill_the_children(): # XXX This is currently broken, but we need to fix it to avoid # orphaned processes in case of a crash. -#atexit.register(kill_the_children) - - -def sigint_handler(*args, **kwargs): - """ - Signal handler for SIGINT - """ - logger = kwargs.get('logger', None) - parentpid = kwargs.get('parentpid', None) - pid = os.getpid() - if parentpid == pid: - if logger: - logger.debug("SIGINT catched. shutting down...") - mainwindow = args[0] - mainwindow.quit() - - -def sigterm_handler(*args, **kwargs): - """ - Signal handler for SIGTERM. - This handler is actually passed to twisted reactor - """ - logger = kwargs.get('logger', None) - parentpid = kwargs.get('parentpid', None) - pid = os.getpid() - if parentpid == pid: - if logger: - logger.debug("SIGTERM catched. shutting down...") - mainwindow = args[0] - mainwindow.quit() +# atexit.register(kill_the_children) def do_display_version(opts): @@ -141,7 +105,7 @@ def do_mail_plumbing(opts): # XXX catch when import is used w/o acct -def main(): +def start_app(): """ Starts the main event loop and launches the main window. """ @@ -149,8 +113,12 @@ def main(): opts = leap_argparse.get_options() do_display_version(opts) - bypass_checks = opts.danger - start_hidden = opts.start_hidden + options = { + 'bypass_checks': opts.danger, + 'start_hidden': opts.start_hidden, + 'debug': opts.debug, + 'log_file': opts.log_file, + } flags.STANDALONE = opts.standalone flags.OFFLINE = opts.offline @@ -202,53 +170,9 @@ def main(): logger.info('Starting app') - # We force the style if on KDE so that it doesn't load all the kde - # libs, which causes a compatibility issue in some systems. - # For more info, see issue #3194 - if flags.STANDALONE and os.environ.get("KDE_SESSION_UID") is not None: - sys.argv.append("-style") - sys.argv.append("Cleanlooks") - - app = QtGui.QApplication(sys.argv) - - # To test: - # $ LANG=es ./app.py - locale = QtCore.QLocale.system().name() - qtTranslator = QtCore.QTranslator() - if qtTranslator.load("qt_%s" % locale, ":/translations"): - app.installTranslator(qtTranslator) - appTranslator = QtCore.QTranslator() - if appTranslator.load("%s.qm" % locale[:2], ":/translations"): - app.installTranslator(appTranslator) - - # Needed for initializing qsettings it will write - # .config/leap/leap.conf top level app settings in a platform - # independent way - app.setOrganizationName("leap") - app.setApplicationName("leap") - app.setOrganizationDomain("leap.se") - - window = MainWindow(bypass_checks=bypass_checks, - start_hidden=start_hidden) - - mainpid = os.getpid() - sigint_window = partial(sigint_handler, window, - logger=logger, parentpid=mainpid) - signal.signal(signal.SIGINT, sigint_window) - - # callable used in addSystemEventTrigger to handle SIGTERM - sigterm_window = partial(sigterm_handler, window, - logger=logger, parentpid=mainpid) - # SIGTERM can't be handled the same way SIGINT is, since it's - # caught by twisted. See _handleSignals method in - # twisted/internet/base.py#L1150. So, addSystemEventTrigger - # reactor's method is used. - reactor.addSystemEventTrigger('before', 'shutdown', sigterm_window) - - l = LoopingCall(QtCore.QCoreApplication.processEvents, 0, 10) - l.start(0.01) - - reactor.run() + frontend = multiprocessing.Process(target=run_frontend, args=(options, )) + frontend.start() + if __name__ == "__main__": - main() + start_app() diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index e69de29b..ed67a77a 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# frontend_app.py +# Copyright (C) 2013, 2014 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 . +import signal +import sys +import os + +from functools import partial + +from PySide import QtCore, QtGui + +from leap.bitmask.config import flags +from leap.bitmask.gui import locale_rc # noqa - silence pylint +from leap.bitmask.gui.mainwindow import MainWindow +# from leap.bitmask.logs.utils import create_logger + +import logging +logger = logging.getLogger(__name__) + + +def sigint_handler(*args, **kwargs): + """ + Signal handler for SIGINT + """ + logger = kwargs.get('logger', None) + if logger: + logger.debug("SIGINT catched. shutting down...") + mainwindow = args[0] + mainwindow.quit() + + +def sigterm_handler(*args, **kwargs): + """ + Signal handler for SIGTERM. + This handler is actually passed to twisted reactor + """ + logger = kwargs.get('logger', None) + if logger: + logger.debug("SIGTERM catched. shutting down...") + mainwindow = args[0] + mainwindow.quit() + + +def run_frontend(options): + """ + Run the GUI for the application. + + :param options: a dict of options parsed from the command line. + :type options: dict + """ + bypass_checks = options["bypass_checks"] + start_hidden = options["start_hidden"] + + # We force the style if on KDE so that it doesn't load all the kde + # libs, which causes a compatibility issue in some systems. + # For more info, see issue #3194 + if flags.STANDALONE and os.environ.get("KDE_SESSION_UID") is not None: + sys.argv.append("-style") + sys.argv.append("Cleanlooks") + + qApp = QtGui.QApplication(sys.argv) + + # To test: + # $ LANG=es ./app.py + locale = QtCore.QLocale.system().name() + qtTranslator = QtCore.QTranslator() + if qtTranslator.load("qt_%s" % locale, ":/translations"): + qApp.installTranslator(qtTranslator) + appTranslator = QtCore.QTranslator() + if appTranslator.load("%s.qm" % locale[:2], ":/translations"): + qApp.installTranslator(appTranslator) + + # Needed for initializing qsettings it will write + # .config/leap/leap.conf top level app settings in a platform + # independent way + qApp.setOrganizationName("leap") + qApp.setApplicationName("leap") + qApp.setOrganizationDomain("leap.se") + + window = MainWindow(bypass_checks=bypass_checks, + start_hidden=start_hidden) + + sigint_window = partial(sigint_handler, window, logger=logger) + signal.signal(signal.SIGINT, sigint_window) + + sys.exit(qApp.exec_()) + + +if __name__ == '__main__': + run_frontend() diff --git a/src/leap/bitmask/gui/__init__.py b/src/leap/bitmask/gui/__init__.py index 94bf1fd5..bba02061 100644 --- a/src/leap/bitmask/gui/__init__.py +++ b/src/leap/bitmask/gui/__init__.py @@ -19,5 +19,6 @@ init file for leap.gui """ # This was added for coverage and testing, but when doing the osx # bundle with py2app it fails because of this, so commenting for now +# Also creates conflicts with the new frontend/backend separation. # app = __import__("app", globals(), locals(), [], 2) # __all__ = [app] -- cgit v1.2.3 From 1eae3af1906f95d9d2d76c205b61799d248f0e71 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 8 Jul 2014 16:45:43 -0300 Subject: Add base communication framework. --- src/leap/bitmask/backend/backend.py | 196 ++++++++++++++++++++++++++++++ src/leap/bitmask/backend/backend_proxy.py | 142 ++++++++++++++++++++++ src/leap/bitmask/backend/signaler.py | 140 +++++++++++++++++++++ src/leap/bitmask/backend/signaler_qt.py | 105 ++++++++++++++++ src/leap/bitmask/backend/utils.py | 43 +++++++ 5 files changed, 626 insertions(+) create mode 100644 src/leap/bitmask/backend/backend.py create mode 100644 src/leap/bitmask/backend/backend_proxy.py create mode 100644 src/leap/bitmask/backend/signaler.py create mode 100644 src/leap/bitmask/backend/signaler_qt.py create mode 100644 src/leap/bitmask/backend/utils.py diff --git a/src/leap/bitmask/backend/backend.py b/src/leap/bitmask/backend/backend.py new file mode 100644 index 00000000..26c547b6 --- /dev/null +++ b/src/leap/bitmask/backend/backend.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# encoding: utf-8 +import json +import threading +import time + +from twisted.internet import defer, reactor, threads + +import zmq +from zmq.auth.thread import ThreadAuthenticator + +from leap.bitmask.backend.api import API +from leap.bitmask.backend.utils import get_backend_certificates +from leap.bitmask.backend.signaler import Signaler + +import logging +logger = logging.getLogger(__name__) + + +class Backend(object): + """ + Backend server. + Receives signals from backend_proxy and emit signals if needed. + """ + PORT = '5556' + BIND_ADDR = "tcp://127.0.0.1:%s" % PORT + + def __init__(self): + """ + Backend constructor, create needed instances. + """ + self._signaler = Signaler() + + self._do_work = threading.Event() # used to stop the worker thread. + self._zmq_socket = None + + self._ongoing_defers = [] + self._init_zmq() + + def _init_zmq(self): + """ + Configure the zmq components and connection. + """ + context = zmq.Context() + socket = context.socket(zmq.REP) + + # Start an authenticator for this context. + auth = ThreadAuthenticator(context) + auth.start() + auth.allow('127.0.0.1') + + # Tell authenticator to use the certificate in a directory + auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY) + public, secret = get_backend_certificates() + socket.curve_publickey = public + socket.curve_secretkey = secret + socket.curve_server = True # must come before bind + + socket.bind(self.BIND_ADDR) + + self._zmq_socket = socket + + def _worker(self): + """ + Receive requests and send it to process. + + Note: we use a simple while since is less resource consuming than a + Twisted's LoopingCall. + """ + while self._do_work.is_set(): + # Wait for next request from client + try: + request = self._zmq_socket.recv(zmq.NOBLOCK) + self._zmq_socket.send("OK") + logger.debug("Received request: '{0}'".format(request)) + self._process_request(request) + except zmq.ZMQError as e: + if e.errno != zmq.EAGAIN: + raise + time.sleep(0.01) + + def _stop_reactor(self): + """ + Stop the Twisted reactor, but first wait a little for some threads to + complete their work. + + Note: this method needs to be run in a different thread so the + time.sleep() does not block and other threads can finish. + i.e.: + use threads.deferToThread(this_method) instead of this_method() + """ + wait_max = 5 # seconds + wait_step = 0.5 + wait = 0 + while self._ongoing_defers and wait < wait_max: + time.sleep(wait_step) + wait += wait_step + msg = "Waiting for running threads to finish... {0}/{1}" + msg = msg.format(wait, wait_max) + logger.debug(msg) + + # after a timeout we shut down the existing threads. + for d in self._ongoing_defers: + d.cancel() + + reactor.stop() + logger.debug("Twisted reactor stopped.") + + def run(self): + """ + Start the ZMQ server and run the loop to handle requests. + """ + self._signaler.start() + self._do_work.set() + threads.deferToThread(self._worker) + reactor.run() + + def stop(self): + """ + Stop the server and the zmq request parse loop. + """ + logger.debug("STOP received.") + self._signaler.stop() + self._do_work.clear() + threads.deferToThread(self._stop_reactor) + + def _process_request(self, request_json): + """ + Process a request and call the according method with the given + parameters. + + :param request_json: a json specification of a request. + :type request_json: str + """ + try: + # request = zmq.utils.jsonapi.loads(request_json) + # We use stdlib's json to ensure that we get unicode strings + request = json.loads(request_json) + api_method = request['api_method'] + kwargs = request['arguments'] or None + except Exception as e: + msg = "Malformed JSON data in Backend request '{0}'. Exc: {1!r}" + msg = msg.format(request_json, e) + msg = msg.format(request_json) + logger.critical(msg) + raise + + if api_method not in API: + logger.error("Invalid API call '{0}'".format(api_method)) + return + + self._run_in_thread(api_method, kwargs) + + def _run_in_thread(self, api_method, kwargs): + """ + Run the method name in a thread with the given arguments. + + :param api_method: the callable name to run in a thread. + :type api_method: str + :param kwargs: the arguments dict that will be sent to the callable. + :type kwargs: tuple + """ + func = getattr(self, api_method) + + method = func + if kwargs is not None: + method = lambda: func(**kwargs) + + logger.debug("Running method: '{0}' " + "with args: '{1}' in a thread".format(api_method, kwargs)) + + # run the action in a thread and keep track of it + d = threads.deferToThread(method) + d.addCallback(self._done_action, d) + d.addErrback(self._done_action, d) + self._ongoing_defers.append(d) + + def _done_action(self, failure, d): + """ + Remove the defer from the ongoing list. + + :param failure: the failure that triggered the errback. + None if no error. + :type failure: twisted.python.failure.Failure + :param d: defer to remove + :type d: twisted.internet.defer.Deferred + """ + if failure is not None: + if failure.check(defer.CancelledError): + logger.debug("A defer was cancelled.") + else: + logger.error("There was a failure - {0!r}".format(failure)) + logger.error(failure.getTraceback()) + + if d in self._ongoing_defers: + self._ongoing_defers.remove(d) diff --git a/src/leap/bitmask/backend/backend_proxy.py b/src/leap/bitmask/backend/backend_proxy.py new file mode 100644 index 00000000..ae9cf5b1 --- /dev/null +++ b/src/leap/bitmask/backend/backend_proxy.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# encoding: utf-8 +import functools +import Queue +import threading +import time + +import zmq + +from leap.bitmask.backend.api import API, STOP_REQUEST +from leap.bitmask.backend.utils import get_backend_certificates + +import logging +logger = logging.getLogger(__name__) + + +class BackendProxy(object): + """ + The BackendProxy handles calls from the GUI and forwards (through ZMQ) + to the backend. + """ + PORT = '5556' + SERVER = "tcp://localhost:%s" % PORT + + def __init__(self): + self._socket = None + + # initialize ZMQ stuff: + context = zmq.Context() + logger.debug("Connecting to server...") + socket = context.socket(zmq.REQ) + + # public, secret = zmq.curve_keypair() + client_keys = zmq.curve_keypair() + socket.curve_publickey = client_keys[0] + socket.curve_secretkey = client_keys[1] + + # The client must know the server's public key to make a CURVE + # connection. + public, _ = get_backend_certificates() + socket.curve_serverkey = public + + socket.setsockopt(zmq.RCVTIMEO, 1000) + socket.connect(self.SERVER) + self._socket = socket + + self._call_queue = Queue.Queue() + self._worker_caller = threading.Thread(target=self._worker) + self._worker_caller.start() + + def _worker(self): + """ + Worker loop that processes the Queue of pending requests to do. + """ + while True: + try: + request = self._call_queue.get(block=False) + # break the loop after sending the 'stop' action to the + # backend. + if request == STOP_REQUEST: + break + + self._send_request(request) + except Queue.Empty: + pass + time.sleep(0.01) + + logger.debug("BackendProxy worker stopped.") + + def _api_call(self, *args, **kwargs): + """ + Call the `api_method` method in backend (through zmq). + + :param kwargs: named arguments to forward to the backend api method. + :type kwargs: dict + + Note: is mandatory to have the kwarg 'api_method' defined. + """ + if args: + # Use a custom message to be more clear about using kwargs *only* + raise Exception("All arguments need to be kwargs!") + + api_method = kwargs.pop('api_method', None) + if api_method is None: + raise Exception("Missing argument, no method name specified.") + + request = { + 'api_method': api_method, + 'arguments': kwargs, + } + + try: + request_json = zmq.utils.jsonapi.dumps(request) + except Exception as e: + msg = ("Error serializing request into JSON.\n" + "Exception: {0} Data: {1}") + msg = msg.format(e, request) + logger.critical(msg) + raise + + # queue the call in order to handle the request in a thread safe way. + self._call_queue.put(request_json) + + if api_method == STOP_REQUEST: + self._call_queue.put(STOP_REQUEST) + + def _send_request(self, request): + """ + Send the given request to the server. + This is used from a thread safe loop in order to avoid sending a + request without receiving a response from a previous one. + + :param request: the request to send. + :type request: str + """ + logger.debug("Sending request to backend: {0}".format(request)) + self._socket.send(request) + + try: + # Get the reply. + response = self._socket.recv() + msg = "Received reply for '{0}' -> '{1}'".format(request, response) + logger.debug(msg) + except zmq.error.Again as e: + msg = "Timeout error contacting backend. {0!r}".format(e) + logger.critical(msg) + + def __getattribute__(self, name): + """ + This allows the user to do: + bp = BackendProxy() + bp.some_method() + + Just by having defined 'some_method' in the API + + :param name: the attribute name that is requested. + :type name: str + """ + if name in API: + return functools.partial(self._api_call, api_method=name) + else: + return object.__getattribute__(self, name) diff --git a/src/leap/bitmask/backend/signaler.py b/src/leap/bitmask/backend/signaler.py new file mode 100644 index 00000000..d96015f4 --- /dev/null +++ b/src/leap/bitmask/backend/signaler.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# encoding: utf-8 +import Queue +import threading +import time + +import zmq + +from leap.bitmask.backend.api import SIGNALS +from leap.bitmask.backend.utils import get_frontend_certificates + +import logging +logger = logging.getLogger(__name__) + + +class Signaler(object): + """ + Signaler client. + Receives signals from the backend and sends to the signaling server. + """ + PORT = "5667" + SERVER = "tcp://localhost:%s" % PORT + + def __init__(self): + """ + Initialize the ZMQ socket to talk to the signaling server. + """ + context = zmq.Context() + logger.debug("Connecting to signaling server...") + socket = context.socket(zmq.REQ) + + # public, secret = zmq.curve_keypair() + client_keys = zmq.curve_keypair() + socket.curve_publickey = client_keys[0] + socket.curve_secretkey = client_keys[1] + + # The client must know the server's public key to make a CURVE + # connection. + public, _ = get_frontend_certificates() + socket.curve_serverkey = public + + socket.setsockopt(zmq.RCVTIMEO, 1000) + socket.connect(self.SERVER) + self._socket = socket + + self._signal_queue = Queue.Queue() + + self._do_work = threading.Event() # used to stop the worker thread. + self._worker_signaler = threading.Thread(target=self._worker) + + def __getattribute__(self, name): + """ + This allows the user to do: + S = Signaler() + S.SOME_SIGNAL + + Just by having defined 'some_signal' in _SIGNALS + + :param name: the attribute name that is requested. + :type name: str + """ + if name in SIGNALS: + return name + else: + return object.__getattribute__(self, name) + + def signal(self, signal, data=None): + """ + Sends a signal to the signaling server. + + :param signal: the signal to send. + :type signal: str + """ + if signal not in SIGNALS: + raise Exception("Unknown signal: '{0}'".format(signal)) + + request = { + 'signal': signal, + 'data': data, + } + + try: + request_json = zmq.utils.jsonapi.dumps(request) + except Exception as e: + msg = ("Error serializing request into JSON.\n" + "Exception: {0} Data: {1}") + msg = msg.format(e, request) + logger.critical(msg) + raise + + # queue the call in order to handle the request in a thread safe way. + self._signal_queue.put(request_json) + + def _worker(self): + """ + Worker loop that processes the Queue of pending requests to do. + """ + while self._do_work.is_set(): + try: + request = self._signal_queue.get(block=False) + self._send_request(request) + except Queue.Empty: + pass + time.sleep(0.01) + + logger.debug("Signaler thread stopped.") + + def start(self): + """ + Start the Signaler worker. + """ + self._do_work.set() + self._worker_signaler.start() + + def stop(self): + """ + Stop the Signaler worker. + """ + self._do_work.clear() + + def _send_request(self, request): + """ + Send the given request to the server. + This is used from a thread safe loop in order to avoid sending a + request without receiving a response from a previous one. + + :param request: the request to send. + :type request: str + """ + logger.debug("Signaling '{0}'".format(request)) + self._socket.send(request) + + # Get the reply. + try: + response = self._socket.recv() + msg = "Received reply for '{0}' -> '{1}'".format(request, response) + logger.debug(msg) + except zmq.error.Again as e: + msg = "Timeout error contacting signaler. {0!r}".format(e) + logger.critical(msg) diff --git a/src/leap/bitmask/backend/signaler_qt.py b/src/leap/bitmask/backend/signaler_qt.py new file mode 100644 index 00000000..cf49df91 --- /dev/null +++ b/src/leap/bitmask/backend/signaler_qt.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# encoding: utf-8 +import threading +import time + +from PySide import QtCore + +import zmq +from zmq.auth.thread import ThreadAuthenticator + +from leap.bitmask.backend.api import SIGNALS +from leap.bitmask.backend.utils import get_frontend_certificates + +import logging +logger = logging.getLogger(__name__) + + +class SignalerQt(QtCore.QThread): + """ + Signaling server. + Receives signals from the signaling client and emit Qt signals for the GUI. + """ + PORT = "5667" + BIND_ADDR = "tcp://127.0.0.1:%s" % PORT + + def __init__(self): + QtCore.QThread.__init__(self) + self._do_work = threading.Event() + self._do_work.set() + + def run(self): + """ + Start a loop to process the ZMQ requests from the signaler client. + """ + logger.debug("Running SignalerQt loop") + context = zmq.Context() + socket = context.socket(zmq.REP) + + # Start an authenticator for this context. + auth = ThreadAuthenticator(context) + auth.start() + auth.allow('127.0.0.1') + + # Tell authenticator to use the certificate in a directory + auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY) + public, secret = get_frontend_certificates() + socket.curve_publickey = public + socket.curve_secretkey = secret + socket.curve_server = True # must come before bind + + socket.bind(self.BIND_ADDR) + + while self._do_work.is_set(): + # Wait for next request from client + try: + request = socket.recv(zmq.NOBLOCK) + logger.debug("Received request: '{0}'".format(request)) + socket.send("OK") + self._process_request(request) + except zmq.ZMQError as e: + if e.errno != zmq.EAGAIN: + raise + time.sleep(0.01) + + logger.debug("SignalerQt thread stopped.") + + def stop(self): + """ + Stop the SignalerQt blocking loop. + """ + self._do_work.clear() + + def _process_request(self, request_json): + """ + Process a request and call the according method with the given + parameters. + + :param request_json: a json specification of a request. + :type request_json: str + """ + try: + request = zmq.utils.jsonapi.loads(request_json) + signal = request['signal'] + data = request['data'] + except Exception as e: + msg = "Malformed JSON data in Signaler request '{0}'. Exc: {1!r}" + msg = msg.format(request_json, e) + logger.critical(msg) + raise + + if signal not in SIGNALS: + logger.error("Unknown signal received, '{0}'".format(signal)) + return + + try: + qt_signal = getattr(self, signal) + except Exception: + logger.warning("Signal not implemented, '{0}'".format(signal)) + return + + logger.debug("Emitting '{0}'".format(signal)) + if data is None: + qt_signal.emit() + else: + qt_signal.emit(data) diff --git a/src/leap/bitmask/backend/utils.py b/src/leap/bitmask/backend/utils.py new file mode 100644 index 00000000..5fe59a62 --- /dev/null +++ b/src/leap/bitmask/backend/utils.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# encoding: utf-8 +import os +import shutil + +import zmq.auth + +from leap.bitmask.util import get_path_prefix + +KEYS_DIR = os.path.join(get_path_prefix(), 'leap', 'zmq_certificates') + + +def generate_certificates(): + """ + Generate client and server CURVE certificate files. + """ + # Create directory for certificates, remove old content if necessary + if os.path.exists(KEYS_DIR): + shutil.rmtree(KEYS_DIR) + os.mkdir(KEYS_DIR) + + # create new keys in certificates dir + # public_file, secret_file = create_certificates(...) + zmq.auth.create_certificates(KEYS_DIR, "frontend") + zmq.auth.create_certificates(KEYS_DIR, "backend") + + +def get_frontend_certificates(): + """ + Return the frontend's public and secret certificates. + """ + frontend_secret_file = os.path.join(KEYS_DIR, "frontend.key_secret") + public, secret = zmq.auth.load_certificate(frontend_secret_file) + return public, secret + + +def get_backend_certificates(base_dir='.'): + """ + Return the backend's public and secret certificates. + """ + backend_secret_file = os.path.join(KEYS_DIR, "backend.key_secret") + public, secret = zmq.auth.load_certificate(backend_secret_file) + return public, secret -- cgit v1.2.3 From 787c799077cd19f5f87b3c2f26c66df7e8b48023 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 25 Jun 2014 12:19:00 -0300 Subject: Add API/SIGNALs definition. --- src/leap/bitmask/backend/api.py | 130 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/leap/bitmask/backend/api.py diff --git a/src/leap/bitmask/backend/api.py b/src/leap/bitmask/backend/api.py new file mode 100644 index 00000000..9e0f9ab8 --- /dev/null +++ b/src/leap/bitmask/backend/api.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +Backend available API and SIGNALS definition. +""" +STOP_REQUEST = "stop" + +API = ( + STOP_REQUEST, # this method needs to be defined in order to support the + # backend stop action + + "eip_can_start", + "eip_cancel_setup", + "eip_check_dns", + "eip_get_gateways_list", + "eip_get_initialized_providers", + "eip_setup", + "eip_start", + "eip_stop", + "eip_terminate", + "imap_start_service", + "imap_stop_service", + "keymanager_export_keys", + "keymanager_get_key_details", + "keymanager_list_keys", + "provider_bootstrap", + "provider_cancel_setup", + "provider_get_all_services", + "provider_get_details", + "provider_get_pinned_providers", + "provider_get_supported_services", + "provider_setup", + "smtp_start_service", + "smtp_stop_service", + "soledad_bootstrap", + "soledad_cancel_bootstrap", + "soledad_change_password", + "soledad_close", + "soledad_load_offline", + "tear_fw_down", + "user_cancel_login", + "user_change_password", + "user_get_logged_in_status", + "user_login", + "user_logout", + "user_register", +) + + +SIGNALS = ( + "backend_bad_call", + "eip_alien_openvpn_already_running", + "eip_can_start", + "eip_cancelled_setup", + "eip_cannot_start", + "eip_client_certificate_ready", + "eip_config_ready", + "eip_connected", + "eip_connection_aborted", + "eip_connection_died", + "eip_disconnected", + "eip_dns_error", + "eip_dns_ok", + "eip_get_gateways_list", + "eip_get_gateways_list_error", + "eip_get_initialized_providers", + "eip_network_unreachable", + "eip_no_pkexec_error", + "eip_no_polkit_agent_error", + "eip_no_tun_kext_error", + "eip_openvpn_already_running", + "eip_openvpn_not_found_error", + "eip_process_finished", + "eip_process_restart_ping", + "eip_process_restart_tls", + "eip_state_changed", + "eip_status_changed", + "eip_stopped", + "eip_tear_fw_down", + "eip_uninitialized_provider", + "eip_vpn_launcher_exception", + "imap_stopped", + "keymanager_export_error", + "keymanager_export_ok", + "keymanager_import_addressmismatch", + "keymanager_import_datamismatch", + "keymanager_import_ioerror", + "keymanager_import_missingkey", + "keymanager_import_ok", + "keymanager_key_details", + "keymanager_keys_list", + "prov_cancelled_setup", + "prov_check_api_certificate", + "prov_check_ca_fingerprint", + "prov_download_ca_cert", + "prov_download_provider_info", + "prov_get_all_services", + "prov_get_details", + "prov_get_pinned_providers", + "prov_get_supported_services", + "prov_https_connection", + "prov_name_resolution", + "prov_problem_with_provider", + "prov_unsupported_api", + "prov_unsupported_client", + "soledad_bootstrap_failed", + "soledad_bootstrap_finished", + "soledad_cancelled_bootstrap", + "soledad_invalid_auth_token", + "soledad_offline_failed", + "soledad_offline_finished", + "soledad_password_change_error", + "soledad_password_change_ok", + "srp_auth_bad_user_or_password", + "srp_auth_connection_error", + "srp_auth_error", + "srp_auth_ok", + "srp_auth_server_error", + "srp_logout_error", + "srp_logout_ok", + "srp_not_logged_in_error", + "srp_password_change_badpw", + "srp_password_change_error", + "srp_password_change_ok", + "srp_registration_failed", + "srp_registration_finished", + "srp_registration_taken", + "srp_status_logged_in", + "srp_status_not_logged_in", +) -- cgit v1.2.3 From 4d5e56dacf33465df5d195ada2855c86ee23a21f Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 19 Jun 2014 16:54:34 -0300 Subject: Add license headers. --- src/leap/bitmask/backend/api.py | 18 ++++++++++++++++-- src/leap/bitmask/backend/backend.py | 18 ++++++++++++++++-- src/leap/bitmask/backend/backend_proxy.py | 18 ++++++++++++++++-- src/leap/bitmask/backend/signaler.py | 18 ++++++++++++++++-- src/leap/bitmask/backend/signaler_qt.py | 18 ++++++++++++++++-- src/leap/bitmask/backend/utils.py | 18 ++++++++++++++++-- 6 files changed, 96 insertions(+), 12 deletions(-) diff --git a/src/leap/bitmask/backend/api.py b/src/leap/bitmask/backend/api.py index 9e0f9ab8..012b3cbc 100644 --- a/src/leap/bitmask/backend/api.py +++ b/src/leap/bitmask/backend/api.py @@ -1,5 +1,19 @@ -#!/usr/bin/env python -# encoding: utf-8 +# -*- coding: utf-8 -*- +# api.py +# Copyright (C) 2013, 2014 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 . """ Backend available API and SIGNALS definition. """ diff --git a/src/leap/bitmask/backend/backend.py b/src/leap/bitmask/backend/backend.py index 26c547b6..5f696f75 100644 --- a/src/leap/bitmask/backend/backend.py +++ b/src/leap/bitmask/backend/backend.py @@ -1,5 +1,19 @@ -#!/usr/bin/env python -# encoding: utf-8 +# -*- coding: utf-8 -*- +# backend.py +# Copyright (C) 2013, 2014 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 . import json import threading import time diff --git a/src/leap/bitmask/backend/backend_proxy.py b/src/leap/bitmask/backend/backend_proxy.py index ae9cf5b1..3c8e5d0f 100644 --- a/src/leap/bitmask/backend/backend_proxy.py +++ b/src/leap/bitmask/backend/backend_proxy.py @@ -1,5 +1,19 @@ -#!/usr/bin/env python -# encoding: utf-8 +# -*- coding: utf-8 -*- +# backend_proxy.py +# Copyright (C) 2013, 2014 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 . import functools import Queue import threading diff --git a/src/leap/bitmask/backend/signaler.py b/src/leap/bitmask/backend/signaler.py index d96015f4..f8753771 100644 --- a/src/leap/bitmask/backend/signaler.py +++ b/src/leap/bitmask/backend/signaler.py @@ -1,5 +1,19 @@ -#!/usr/bin/env python -# encoding: utf-8 +# -*- coding: utf-8 -*- +# signaler.py +# Copyright (C) 2013, 2014 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 . import Queue import threading import time diff --git a/src/leap/bitmask/backend/signaler_qt.py b/src/leap/bitmask/backend/signaler_qt.py index cf49df91..dabd6662 100644 --- a/src/leap/bitmask/backend/signaler_qt.py +++ b/src/leap/bitmask/backend/signaler_qt.py @@ -1,5 +1,19 @@ -#!/usr/bin/env python -# encoding: utf-8 +# -*- coding: utf-8 -*- +# signaler_qt.py +# Copyright (C) 2013, 2014 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 . import threading import time diff --git a/src/leap/bitmask/backend/utils.py b/src/leap/bitmask/backend/utils.py index 5fe59a62..5d600689 100644 --- a/src/leap/bitmask/backend/utils.py +++ b/src/leap/bitmask/backend/utils.py @@ -1,5 +1,19 @@ -#!/usr/bin/env python -# encoding: utf-8 +# -*- coding: utf-8 -*- +# utils.py +# Copyright (C) 2013, 2014 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 . import os import shutil -- cgit v1.2.3 From 8d5ca2dda0f014cc036768a5eef343e568bb292d Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 19 Jun 2014 14:58:43 -0300 Subject: Use new frontend/backend structure in LEAP implementation. --- src/leap/bitmask/backend/leapbackend.py | 278 ++++++-------------- src/leap/bitmask/backend/leapsignaler.py | 433 ++++++------------------------- 2 files changed, 155 insertions(+), 556 deletions(-) diff --git a/src/leap/bitmask/backend/leapbackend.py b/src/leap/bitmask/backend/leapbackend.py index 3c5222f4..cc7227ad 100644 --- a/src/leap/bitmask/backend/leapbackend.py +++ b/src/leap/bitmask/backend/leapbackend.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # leapbackend.py -# Copyright (C) 2013 LEAP +# Copyright (C) 2013, 2014 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 @@ -15,178 +15,60 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ -Backend for GUI/Logic communication. +Backend for everything """ import logging - -from Queue import Queue, Empty - -from twisted.internet import reactor -from twisted.internet import threads, defer -from twisted.internet.task import LoopingCall +import signal import zope.interface import zope.proxy -from leap.bitmask.backend.leapsignaler import Signaler from leap.bitmask.backend import components +from leap.bitmask.backend.backend import Backend logger = logging.getLogger(__name__) -class Backend(object): +class LeapBackend(Backend): """ - Backend for everything, the UI should only use this class. + Backend server subclass, used to implement the API methods. """ - - PASSED_KEY = "passed" - ERROR_KEY = "error" - def __init__(self, bypass_checks=False): """ Constructor for the backend. """ - # Components map for the commands received - self._components = {} - - # Ongoing defers that will be cancelled at stop time - self._ongoing_defers = [] - - # Signaler object to translate commands into Qt signals - self._signaler = Signaler() + Backend.__init__(self) # Objects needed by several components, so we make a proxy and pass # them around self._soledad_proxy = zope.proxy.ProxyBase(None) self._keymanager_proxy = zope.proxy.ProxyBase(None) - # Component registration - self._register(components.Provider(self._signaler, bypass_checks)) - self._register(components.Register(self._signaler)) - self._register(components.Authenticate(self._signaler)) - self._register(components.EIP(self._signaler)) - self._register(components.Soledad(self._soledad_proxy, - self._keymanager_proxy, - self._signaler)) - self._register(components.Keymanager(self._keymanager_proxy, - self._signaler)) - self._register(components.Mail(self._soledad_proxy, - self._keymanager_proxy, - self._signaler)) - - # We have a looping call on a thread executing all the - # commands in queue. Right now this queue is an actual Queue - # object, but it'll become the zmq recv_multipart queue - self._lc = LoopingCall(threads.deferToThread, self._worker) - - # Temporal call_queue for worker, will be replaced with - # recv_multipart os something equivalent in the loopingcall - self._call_queue = Queue() - - @property - def signaler(self): - """ - Public signaler access to let the UI connect to its signals. - """ - return self._signaler - - def start(self): - """ - Starts the looping call - """ - logger.debug("Starting worker...") - self._lc.start(0.01) - - def stop(self): - """ - Stops the looping call and tries to cancel all the defers. - """ - reactor.callLater(2, self._stop) - - def _stop(self): - """ - Delayed stopping of worker. Called from `stop`. - """ - logger.debug("Stopping worker...") - if self._lc.running: - self._lc.stop() - else: - logger.warning("Looping call is not running, cannot stop") - - logger.debug("Cancelling ongoing defers...") - while len(self._ongoing_defers) > 0: - d = self._ongoing_defers.pop() - d.cancel() - logger.debug("Defers cancelled.") - - def _register(self, component): - """ - Registers a component in this backend - - :param component: Component to register - :type component: any object that implements ILEAPComponent - """ - # TODO: assert that the component implements the interfaces - # expected - try: - self._components[component.key] = component - except Exception: - logger.error("There was a problem registering %s" % (component,)) - - def _signal_back(self, _, signal): - """ - Helper method to signal back (callback like behavior) to the - UI that an operation finished. + # Component instances creation + self._provider = components.Provider(self._signaler, bypass_checks) + self._register = components.Register(self._signaler) + self._authenticate = components.Authenticate(self._signaler) + self._eip = components.EIP(self._signaler) + self._soledad = components.Soledad(self._soledad_proxy, + self._keymanager_proxy, + self._signaler) + self._keymanager = components.Keymanager(self._keymanager_proxy, + self._signaler) + self._mail = components.Mail(self._soledad_proxy, + self._keymanager_proxy, + self._signaler) - :param signal: signal name - :type signal: str + def _check_type(self, obj, expected_type): """ - self._signaler.signal(signal) + Check the type of a parameter. - def _worker(self): + :param obj: object to check its type. + :type obj: any type + :param expected_type: the expected type of the object. + :type expected_type: type """ - Worker method, called from a different thread and as a part of - a looping call - """ - try: - # this'll become recv_multipart - cmd = self._call_queue.get(block=False) - - # cmd is: component, method, signalback, *args - func = getattr(self._components[cmd[0]], cmd[1]) - d = func(*cmd[3:]) - 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, logger.error, cmd[2]) - d.addCallbacks(self._done_action, logger.error, - callbackKeywords={"d": d}) - d.addErrback(logger.error) - 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 as e: - # But we log the rest - logger.exception("Unexpected exception: {0!r}".format(e)) - - def _done_action(self, _, d): - """ - Remover of the defer once it's done - - :param d: defer to remove - :type d: twisted.internet.defer.Deferred - """ - if d in self._ongoing_defers: - self._ongoing_defers.remove(d) - - # XXX: Temporal interface until we migrate to zmq - # We simulate the calls to zmq.send_multipart. Once we separate - # this in two processes, the methods bellow can be changed to - # send_multipart and this backend class will be really simple. + if not isinstance(obj, expected_type): + raise TypeError("The parameter type is incorrect.") def provider_setup(self, provider): """ @@ -202,13 +84,13 @@ class Backend(object): prov_https_connection -> { PASSED_KEY: bool, ERROR_KEY: str } prov_download_provider_info -> { PASSED_KEY: bool, ERROR_KEY: str } """ - self._call_queue.put(("provider", "setup_provider", None, provider)) + self._provider.setup_provider(provider) def provider_cancel_setup(self): """ Cancel the ongoing setup provider (if any). """ - self._call_queue.put(("provider", "cancel_setup_provider", None)) + self._provider.cancel_setup_provider() def provider_bootstrap(self, provider): """ @@ -223,7 +105,7 @@ class Backend(object): prov_check_ca_fingerprint -> {PASSED_KEY: bool, ERROR_KEY: str} prov_check_api_certificate -> {PASSED_KEY: bool, ERROR_KEY: str} """ - self._call_queue.put(("provider", "bootstrap", None, provider)) + self._provider.bootstrap(provider) def provider_get_supported_services(self, domain): """ @@ -235,8 +117,7 @@ class Backend(object): Signals: prov_get_supported_services -> list of unicode """ - self._call_queue.put(("provider", "get_supported_services", None, - domain)) + self._provider.get_supported_services(domain) def provider_get_all_services(self, providers): """ @@ -248,13 +129,11 @@ class Backend(object): Signals: prov_get_all_services -> list of unicode """ - self._call_queue.put(("provider", "get_all_services", None, - providers)) + self._provider.get_all_services(providers) def provider_get_details(self, domain, lang): """ - Signal a ProviderConfigLight object with the current ProviderConfig - settings. + Signal a dict with the current ProviderConfig settings. :param domain: the domain name of the provider. :type domain: str @@ -262,9 +141,9 @@ class Backend(object): :type lang: str Signals: - prov_get_details -> ProviderConfigLight + prov_get_details -> dict """ - self._call_queue.put(("provider", "get_details", None, domain, lang)) + self._provider.get_details(domain, lang) def provider_get_pinned_providers(self): """ @@ -273,7 +152,7 @@ class Backend(object): Signals: prov_get_pinned_providers -> list of provider domains """ - self._call_queue.put(("provider", "get_pinned_providers", None)) + self._provider.get_pinned_providers() def user_register(self, provider, username, password): """ @@ -291,8 +170,7 @@ class Backend(object): srp_registration_taken srp_registration_failed """ - self._call_queue.put(("register", "register_user", None, provider, - username, password)) + self._register.register_user(provider, username, password) def eip_setup(self, provider, skip_network=False): """ @@ -309,14 +187,13 @@ class Backend(object): eip_client_certificate_ready -> {PASSED_KEY: bool, ERROR_KEY: str} eip_cancelled_setup """ - self._call_queue.put(("eip", "setup_eip", None, provider, - skip_network)) + self._eip.setup_eip(provider, skip_network) def eip_cancel_setup(self): """ Cancel the ongoing setup EIP (if any). """ - self._call_queue.put(("eip", "cancel_setup_eip", None)) + self._eip.cancel_setup_eip() def eip_start(self, restart=False): """ @@ -343,7 +220,7 @@ class Backend(object): :param restart: whether is is a restart. :type restart: bool """ - self._call_queue.put(("eip", "start", None, restart)) + self._eip.start(restart) def eip_stop(self, shutdown=False, restart=False, failed=False): """ @@ -355,13 +232,13 @@ class Backend(object): :param restart: whether this is part of a restart. :type restart: bool """ - self._call_queue.put(("eip", "stop", None, shutdown, restart)) + self._eip.stop(shutdown, restart) def eip_terminate(self): """ Terminate the EIP service, not necessarily in a nice way. """ - self._call_queue.put(("eip", "terminate", None)) + self._eip.terminate() def eip_get_gateways_list(self, domain): """ @@ -370,16 +247,12 @@ class Backend(object): :param domain: the domain to get the gateways. :type domain: str - # TODO discuss how to document the expected result object received of - # the signal - :signal type: list of str - Signals: eip_get_gateways_list -> list of unicode eip_get_gateways_list_error eip_uninitialized_provider """ - self._call_queue.put(("eip", "get_gateways_list", None, domain)) + self._eip.get_gateways_list(domain) def eip_get_initialized_providers(self, domains): """ @@ -392,8 +265,7 @@ class Backend(object): eip_get_initialized_providers -> list of tuple(unicode, bool) """ - self._call_queue.put(("eip", "get_initialized_providers", - None, domains)) + self._eip.get_initialized_providers(domains) def eip_can_start(self, domain): """ @@ -406,8 +278,7 @@ class Backend(object): eip_can_start eip_cannot_start """ - self._call_queue.put(("eip", "can_start", - None, domain)) + self._eip.can_start(domain) def eip_check_dns(self, domain): """ @@ -420,13 +291,13 @@ class Backend(object): eip_dns_ok eip_dns_error """ - self._call_queue.put(("eip", "check_dns", None, domain)) + self._eip.check_dns(domain) def tear_fw_down(self): """ Signal the need to tear the fw down. """ - self._call_queue.put(("eip", "tear_fw_down", None)) + self._eip.tear_fw_down() def user_login(self, provider, username, password): """ @@ -447,8 +318,7 @@ class Backend(object): srp_auth_connection_error srp_auth_error """ - self._call_queue.put(("authenticate", "login", None, provider, - username, password)) + self._authenticate.login(provider, username, password) def user_logout(self): """ @@ -459,13 +329,13 @@ class Backend(object): srp_logout_error srp_not_logged_in_error """ - self._call_queue.put(("authenticate", "logout", None)) + self._authenticate.logout() def user_cancel_login(self): """ Cancel the ongoing login (if any). """ - self._call_queue.put(("authenticate", "cancel_login", None)) + self._authenticate.cancel_login() def user_change_password(self, current_password, new_password): """ @@ -482,8 +352,7 @@ class Backend(object): srp_password_change_badpw srp_password_change_error """ - self._call_queue.put(("authenticate", "change_password", None, - current_password, new_password)) + self._authenticate.change_password(current_password, new_password) def soledad_change_password(self, new_password): """ @@ -498,8 +367,7 @@ class Backend(object): srp_password_change_badpw srp_password_change_error """ - self._call_queue.put(("soledad", "change_password", None, - new_password)) + self._soledad.change_password(new_password) def user_get_logged_in_status(self): """ @@ -509,7 +377,7 @@ class Backend(object): srp_status_logged_in srp_status_not_logged_in """ - self._call_queue.put(("authenticate", "get_logged_in_status", None)) + self._authenticate.get_logged_in_status() def soledad_bootstrap(self, username, domain, password): """ @@ -527,8 +395,10 @@ class Backend(object): soledad_bootstrap_failed soledad_invalid_auth_token """ - self._call_queue.put(("soledad", "bootstrap", None, - username, domain, password)) + self._check_type(username, unicode) + self._check_type(domain, unicode) + self._check_type(password, unicode) + self._soledad.bootstrap(username, domain, password) def soledad_load_offline(self, username, password, uuid): """ @@ -543,20 +413,19 @@ class Backend(object): Signals: """ - self._call_queue.put(("soledad", "load_offline", None, - username, password, uuid)) + self._soledad.load_offline(username, password, uuid) def soledad_cancel_bootstrap(self): """ Cancel the ongoing soledad bootstrapping process (if any). """ - self._call_queue.put(("soledad", "cancel_bootstrap", None)) + self._soledad.cancel_bootstrap() def soledad_close(self): """ Close soledad database. """ - self._call_queue.put(("soledad", "close", None)) + self._soledad.close() def keymanager_list_keys(self): """ @@ -565,7 +434,7 @@ class Backend(object): Signals: keymanager_keys_list -> list """ - self._call_queue.put(("keymanager", "list_keys", None)) + self._keymanager.list_keys() def keymanager_export_keys(self, username, filename): """ @@ -580,8 +449,7 @@ class Backend(object): keymanager_export_ok keymanager_export_error """ - self._call_queue.put(("keymanager", "export_keys", None, - username, filename)) + self._keymanager.export_keys(username, filename) def keymanager_get_key_details(self, username): """ @@ -593,7 +461,7 @@ class Backend(object): Signals: keymanager_key_details """ - self._call_queue.put(("keymanager", "get_key_details", None, username)) + self._keymanager.get_key_details(username) def smtp_start_service(self, full_user_id, download_if_needed=False): """ @@ -605,8 +473,7 @@ class Backend(object): for the file :type download_if_needed: bool """ - self._call_queue.put(("mail", "start_smtp_service", None, - full_user_id, download_if_needed)) + self._mail.start_smtp_service(full_user_id, download_if_needed) def imap_start_service(self, full_user_id, offline=False): """ @@ -617,14 +484,13 @@ class Backend(object): :param offline: whether imap should start in offline mode or not. :type offline: bool """ - self._call_queue.put(("mail", "start_imap_service", None, - full_user_id, offline)) + self._mail.start_imap_service(full_user_id, offline) def smtp_stop_service(self): """ Stop the SMTP service. """ - self._call_queue.put(("mail", "stop_smtp_service", None)) + self._mail.stop_smtp_service() def imap_stop_service(self): """ @@ -633,4 +499,12 @@ class Backend(object): Signals: imap_stopped """ - self._call_queue.put(("mail", "stop_imap_service", None)) + self._mail.stop_imap_service() + + +def run_backend(bypass_checks=False): + # Ensure that the application quits using CTRL-C + signal.signal(signal.SIGINT, signal.SIG_DFL) + + backend = LeapBackend(bypass_checks=bypass_checks) + backend.run() diff --git a/src/leap/bitmask/backend/leapsignaler.py b/src/leap/bitmask/backend/leapsignaler.py index da8908fd..47df0911 100644 --- a/src/leap/bitmask/backend/leapsignaler.py +++ b/src/leap/bitmask/backend/leapsignaler.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# components.py +# leapsignaler.py # Copyright (C) 2013 LEAP # # This program is free software: you can redistribute it and/or modify @@ -14,372 +14,97 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -""" -Signaler for Backend/Frontend communication. -""" -import logging - from PySide import QtCore -logger = logging.getLogger(__name__) +from leap.bitmask.backend.signaler_qt import SignalerQt -class Signaler(QtCore.QObject): +class LeapSignaler(SignalerQt): """ - Signaler object, handles converting string commands to Qt signals. - - This is intended for the separation in frontend/backend, this will - live in the frontend. + Signaling server subclass, used to defines the API signals. """ + backend_bad_call = QtCore.Signal(object) - #################### - # These will only exist in the frontend - # Signals for the ProviderBootstrapper - prov_name_resolution = QtCore.Signal(object) - prov_https_connection = QtCore.Signal(object) - prov_download_provider_info = QtCore.Signal(object) - - prov_download_ca_cert = QtCore.Signal(object) - prov_check_ca_fingerprint = QtCore.Signal(object) - prov_check_api_certificate = QtCore.Signal(object) - - prov_problem_with_provider = QtCore.Signal(object) - - prov_unsupported_client = QtCore.Signal(object) - prov_unsupported_api = QtCore.Signal(object) - - prov_get_all_services = QtCore.Signal(object) - prov_get_supported_services = QtCore.Signal(object) - prov_get_details = QtCore.Signal(object) - prov_get_pinned_providers = QtCore.Signal(object) - - prov_cancelled_setup = QtCore.Signal(object) - - # Signals for SRPRegister - srp_registration_finished = QtCore.Signal(object) - srp_registration_failed = QtCore.Signal(object) - srp_registration_taken = QtCore.Signal(object) - - # Signals for EIP bootstrapping - eip_config_ready = QtCore.Signal(object) + eip_alien_openvpn_already_running = QtCore.Signal() + eip_can_start = QtCore.Signal() + eip_cancelled_setup = QtCore.Signal() + eip_cannot_start = QtCore.Signal() eip_client_certificate_ready = QtCore.Signal(object) - - eip_cancelled_setup = QtCore.Signal(object) - - # Signals for SRPAuth - srp_auth_ok = QtCore.Signal(object) - srp_auth_error = QtCore.Signal(object) - srp_auth_server_error = QtCore.Signal(object) - srp_auth_connection_error = QtCore.Signal(object) - srp_auth_bad_user_or_password = QtCore.Signal(object) - srp_logout_ok = QtCore.Signal(object) - srp_logout_error = QtCore.Signal(object) - srp_password_change_ok = QtCore.Signal(object) - srp_password_change_error = QtCore.Signal(object) - srp_password_change_badpw = QtCore.Signal(object) - srp_not_logged_in_error = QtCore.Signal(object) - srp_status_logged_in = QtCore.Signal(object) - srp_status_not_logged_in = QtCore.Signal(object) - - # Signals for EIP - eip_connected = QtCore.Signal(object) - eip_disconnected = QtCore.Signal(object) + eip_config_ready = QtCore.Signal(object) + eip_connected = QtCore.Signal() + eip_connection_aborted = QtCore.Signal() eip_connection_died = QtCore.Signal(object) - eip_connection_aborted = QtCore.Signal(object) - eip_stopped = QtCore.Signal(object) - - eip_dns_ok = QtCore.Signal(object) - eip_dns_error = QtCore.Signal(object) - - # EIP problems - eip_no_polkit_agent_error = QtCore.Signal(object) - eip_no_tun_kext_error = QtCore.Signal(object) - eip_no_pkexec_error = QtCore.Signal(object) - eip_openvpn_not_found_error = QtCore.Signal(object) - eip_openvpn_already_running = QtCore.Signal(object) - eip_alien_openvpn_already_running = QtCore.Signal(object) - eip_vpn_launcher_exception = QtCore.Signal(object) - + eip_disconnected = QtCore.Signal(object) + eip_dns_error = QtCore.Signal() + eip_dns_ok = QtCore.Signal() eip_get_gateways_list = QtCore.Signal(object) - eip_get_gateways_list_error = QtCore.Signal(object) - eip_uninitialized_provider = QtCore.Signal(object) + eip_get_gateways_list_error = QtCore.Signal() eip_get_initialized_providers = QtCore.Signal(object) - - # signals from parsing openvpn output - eip_network_unreachable = QtCore.Signal(object) - eip_process_restart_tls = QtCore.Signal(object) - eip_process_restart_ping = QtCore.Signal(object) - - # signals from vpnprocess.py + eip_network_unreachable = QtCore.Signal() + eip_no_pkexec_error = QtCore.Signal() + eip_no_polkit_agent_error = QtCore.Signal() + eip_no_tun_kext_error = QtCore.Signal() + eip_openvpn_already_running = QtCore.Signal() + eip_openvpn_not_found_error = QtCore.Signal() + eip_process_finished = QtCore.Signal(int) + eip_process_restart_ping = QtCore.Signal() + eip_process_restart_tls = QtCore.Signal() eip_state_changed = QtCore.Signal(dict) eip_status_changed = QtCore.Signal(dict) - eip_process_finished = QtCore.Signal(int) + eip_stopped = QtCore.Signal() eip_tear_fw_down = QtCore.Signal(object) - - # signals whether the needed files to start EIP exist or not - eip_can_start = QtCore.Signal(object) - eip_cannot_start = QtCore.Signal(object) - - # Signals for Soledad - soledad_bootstrap_failed = QtCore.Signal(object) - soledad_bootstrap_finished = QtCore.Signal(object) - soledad_offline_failed = QtCore.Signal(object) - soledad_offline_finished = QtCore.Signal(object) - soledad_invalid_auth_token = QtCore.Signal(object) - soledad_cancelled_bootstrap = QtCore.Signal(object) - soledad_password_change_ok = QtCore.Signal(object) - soledad_password_change_error = QtCore.Signal(object) - - # Keymanager signals - keymanager_export_ok = QtCore.Signal(object) - keymanager_export_error = QtCore.Signal(object) - keymanager_keys_list = QtCore.Signal(object) - - keymanager_import_ioerror = QtCore.Signal(object) - keymanager_import_datamismatch = QtCore.Signal(object) - keymanager_import_missingkey = QtCore.Signal(object) - keymanager_import_addressmismatch = QtCore.Signal(object) - keymanager_import_ok = QtCore.Signal(object) - + eip_uninitialized_provider = QtCore.Signal() + eip_vpn_launcher_exception = QtCore.Signal() + + imap_stopped = QtCore.Signal() + + keymanager_export_error = QtCore.Signal() + keymanager_export_ok = QtCore.Signal() + keymanager_import_addressmismatch = QtCore.Signal() + keymanager_import_datamismatch = QtCore.Signal() + keymanager_import_ioerror = QtCore.Signal() + keymanager_import_missingkey = QtCore.Signal() + keymanager_import_ok = QtCore.Signal() keymanager_key_details = QtCore.Signal(object) + keymanager_keys_list = QtCore.Signal(object) - # mail related signals - imap_stopped = QtCore.Signal(object) - - # This signal is used to warn the backend user that is doing something - # wrong - backend_bad_call = 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 - # it's going to emit defined here - PROV_NAME_RESOLUTION_KEY = "prov_name_resolution" - PROV_HTTPS_CONNECTION_KEY = "prov_https_connection" - PROV_DOWNLOAD_PROVIDER_INFO_KEY = "prov_download_provider_info" - PROV_DOWNLOAD_CA_CERT_KEY = "prov_download_ca_cert" - PROV_CHECK_CA_FINGERPRINT_KEY = "prov_check_ca_fingerprint" - PROV_CHECK_API_CERTIFICATE_KEY = "prov_check_api_certificate" - 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" - PROV_GET_ALL_SERVICES = "prov_get_all_services" - PROV_GET_SUPPORTED_SERVICES = "prov_get_supported_services" - PROV_GET_DETAILS = "prov_get_details" - PROV_GET_PINNED_PROVIDERS = "prov_get_pinned_providers" - - SRP_REGISTRATION_FINISHED = "srp_registration_finished" - SRP_REGISTRATION_FAILED = "srp_registration_failed" - SRP_REGISTRATION_TAKEN = "srp_registration_taken" - SRP_AUTH_OK = "srp_auth_ok" - SRP_AUTH_ERROR = "srp_auth_error" - SRP_AUTH_SERVER_ERROR = "srp_auth_server_error" - SRP_AUTH_CONNECTION_ERROR = "srp_auth_connection_error" - SRP_AUTH_BAD_USER_OR_PASSWORD = "srp_auth_bad_user_or_password" - SRP_LOGOUT_OK = "srp_logout_ok" - SRP_LOGOUT_ERROR = "srp_logout_error" - SRP_PASSWORD_CHANGE_OK = "srp_password_change_ok" - SRP_PASSWORD_CHANGE_ERROR = "srp_password_change_error" - SRP_PASSWORD_CHANGE_BADPW = "srp_password_change_badpw" - SRP_NOT_LOGGED_IN_ERROR = "srp_not_logged_in_error" - SRP_STATUS_LOGGED_IN = "srp_status_logged_in" - SRP_STATUS_NOT_LOGGED_IN = "srp_status_not_logged_in" - - EIP_CONFIG_READY = "eip_config_ready" - EIP_CLIENT_CERTIFICATE_READY = "eip_client_certificate_ready" - EIP_CANCELLED_SETUP = "eip_cancelled_setup" - - EIP_CONNECTED = "eip_connected" - EIP_DISCONNECTED = "eip_disconnected" - EIP_CONNECTION_DIED = "eip_connection_died" - EIP_CONNECTION_ABORTED = "eip_connection_aborted" - EIP_STOPPED = "eip_stopped" - - EIP_NO_POLKIT_AGENT_ERROR = "eip_no_polkit_agent_error" - EIP_NO_TUN_KEXT_ERROR = "eip_no_tun_kext_error" - EIP_NO_PKEXEC_ERROR = "eip_no_pkexec_error" - EIP_OPENVPN_NOT_FOUND_ERROR = "eip_openvpn_not_found_error" - EIP_OPENVPN_ALREADY_RUNNING = "eip_openvpn_already_running" - EIP_ALIEN_OPENVPN_ALREADY_RUNNING = "eip_alien_openvpn_already_running" - EIP_VPN_LAUNCHER_EXCEPTION = "eip_vpn_launcher_exception" - - EIP_GET_GATEWAYS_LIST = "eip_get_gateways_list" - EIP_GET_GATEWAYS_LIST_ERROR = "eip_get_gateways_list_error" - EIP_UNINITIALIZED_PROVIDER = "eip_uninitialized_provider" - EIP_GET_INITIALIZED_PROVIDERS = "eip_get_initialized_providers" - - EIP_NETWORK_UNREACHABLE = "eip_network_unreachable" - EIP_PROCESS_RESTART_TLS = "eip_process_restart_tls" - EIP_PROCESS_RESTART_PING = "eip_process_restart_ping" - - EIP_STATE_CHANGED = "eip_state_changed" - EIP_STATUS_CHANGED = "eip_status_changed" - EIP_PROCESS_FINISHED = "eip_process_finished" - EIP_TEAR_FW_DOWN = "eip_tear_fw_down" - - EIP_CAN_START = "eip_can_start" - EIP_CANNOT_START = "eip_cannot_start" - - EIP_DNS_OK = "eip_dns_ok" - EIP_DNS_ERROR = "eip_dns_error" - - SOLEDAD_BOOTSTRAP_FAILED = "soledad_bootstrap_failed" - SOLEDAD_BOOTSTRAP_FINISHED = "soledad_bootstrap_finished" - SOLEDAD_OFFLINE_FAILED = "soledad_offline_failed" - SOLEDAD_OFFLINE_FINISHED = "soledad_offline_finished" - SOLEDAD_INVALID_AUTH_TOKEN = "soledad_invalid_auth_token" - - SOLEDAD_PASSWORD_CHANGE_OK = "soledad_password_change_ok" - SOLEDAD_PASSWORD_CHANGE_ERROR = "soledad_password_change_error" - - SOLEDAD_CANCELLED_BOOTSTRAP = "soledad_cancelled_bootstrap" - - KEYMANAGER_EXPORT_OK = "keymanager_export_ok" - KEYMANAGER_EXPORT_ERROR = "keymanager_export_error" - KEYMANAGER_KEYS_LIST = "keymanager_keys_list" - - KEYMANAGER_IMPORT_IOERROR = "keymanager_import_ioerror" - KEYMANAGER_IMPORT_DATAMISMATCH = "keymanager_import_datamismatch" - KEYMANAGER_IMPORT_MISSINGKEY = "keymanager_import_missingkey" - KEYMANAGER_IMPORT_ADDRESSMISMATCH = "keymanager_import_addressmismatch" - KEYMANAGER_IMPORT_OK = "keymanager_import_ok" - KEYMANAGER_KEY_DETAILS = "keymanager_key_details" - - IMAP_STOPPED = "imap_stopped" - - BACKEND_BAD_CALL = "backend_bad_call" - - def __init__(self): - """ - Constructor for the Signaler - """ - QtCore.QObject.__init__(self) - self._signals = {} - - signals = [ - self.PROV_NAME_RESOLUTION_KEY, - self.PROV_HTTPS_CONNECTION_KEY, - self.PROV_DOWNLOAD_PROVIDER_INFO_KEY, - self.PROV_DOWNLOAD_CA_CERT_KEY, - self.PROV_CHECK_CA_FINGERPRINT_KEY, - self.PROV_CHECK_API_CERTIFICATE_KEY, - self.PROV_PROBLEM_WITH_PROVIDER_KEY, - self.PROV_UNSUPPORTED_CLIENT, - self.PROV_UNSUPPORTED_API, - self.PROV_CANCELLED_SETUP, - self.PROV_GET_ALL_SERVICES, - self.PROV_GET_SUPPORTED_SERVICES, - self.PROV_GET_DETAILS, - self.PROV_GET_PINNED_PROVIDERS, - - self.SRP_REGISTRATION_FINISHED, - self.SRP_REGISTRATION_FAILED, - self.SRP_REGISTRATION_TAKEN, - - self.EIP_CONFIG_READY, - self.EIP_CLIENT_CERTIFICATE_READY, - self.EIP_CANCELLED_SETUP, - - self.EIP_CONNECTED, - self.EIP_DISCONNECTED, - self.EIP_CONNECTION_DIED, - self.EIP_CONNECTION_ABORTED, - self.EIP_STOPPED, - - self.EIP_NO_POLKIT_AGENT_ERROR, - self.EIP_NO_TUN_KEXT_ERROR, - self.EIP_NO_PKEXEC_ERROR, - self.EIP_OPENVPN_NOT_FOUND_ERROR, - self.EIP_OPENVPN_ALREADY_RUNNING, - self.EIP_ALIEN_OPENVPN_ALREADY_RUNNING, - self.EIP_VPN_LAUNCHER_EXCEPTION, - - self.EIP_GET_GATEWAYS_LIST, - self.EIP_GET_GATEWAYS_LIST_ERROR, - self.EIP_UNINITIALIZED_PROVIDER, - self.EIP_GET_INITIALIZED_PROVIDERS, - - self.EIP_NETWORK_UNREACHABLE, - self.EIP_PROCESS_RESTART_TLS, - self.EIP_PROCESS_RESTART_PING, - - self.EIP_STATE_CHANGED, - self.EIP_STATUS_CHANGED, - self.EIP_PROCESS_FINISHED, - - self.EIP_CAN_START, - self.EIP_CANNOT_START, - - self.EIP_DNS_OK, - self.EIP_DNS_ERROR, - - self.SRP_AUTH_OK, - self.SRP_AUTH_ERROR, - self.SRP_AUTH_SERVER_ERROR, - self.SRP_AUTH_CONNECTION_ERROR, - self.SRP_AUTH_BAD_USER_OR_PASSWORD, - self.SRP_LOGOUT_OK, - self.SRP_LOGOUT_ERROR, - self.SRP_PASSWORD_CHANGE_OK, - self.SRP_PASSWORD_CHANGE_ERROR, - self.SRP_PASSWORD_CHANGE_BADPW, - self.SRP_NOT_LOGGED_IN_ERROR, - self.SRP_STATUS_LOGGED_IN, - self.SRP_STATUS_NOT_LOGGED_IN, - - self.SOLEDAD_BOOTSTRAP_FAILED, - self.SOLEDAD_BOOTSTRAP_FINISHED, - self.SOLEDAD_OFFLINE_FAILED, - self.SOLEDAD_OFFLINE_FINISHED, - self.SOLEDAD_INVALID_AUTH_TOKEN, - self.SOLEDAD_CANCELLED_BOOTSTRAP, - - self.SOLEDAD_PASSWORD_CHANGE_OK, - self.SOLEDAD_PASSWORD_CHANGE_ERROR, - - self.KEYMANAGER_EXPORT_OK, - self.KEYMANAGER_EXPORT_ERROR, - self.KEYMANAGER_KEYS_LIST, - - self.KEYMANAGER_IMPORT_IOERROR, - self.KEYMANAGER_IMPORT_DATAMISMATCH, - self.KEYMANAGER_IMPORT_MISSINGKEY, - self.KEYMANAGER_IMPORT_ADDRESSMISMATCH, - self.KEYMANAGER_IMPORT_OK, - self.KEYMANAGER_KEY_DETAILS, - - self.IMAP_STOPPED, - - self.BACKEND_BAD_CALL, - ] - - for sig in signals: - self._signals[sig] = getattr(self, sig) - - def signal(self, key, data=None): - """ - Emits a Qt signal based on the key provided, with the data if provided. - - :param key: string identifying the signal to emit - :type key: str - :param data: object to send with the data - :type data: object - - NOTE: The data object will be a serialized str in the backend, - and an unserialized object in the frontend, but for now we - just care about objects. - """ - # Right now it emits Qt signals. The backend version of this - # will do zmq.send_multipart, and the frontend version will be - # similar to this - - # for some reason emitting 'None' gives a segmentation fault. - if data is None: - data = '' - - try: - self._signals[key].emit(data) - except KeyError: - logger.error("Unknown key for signal %s!" % (key,)) + prov_cancelled_setup = QtCore.Signal() + prov_check_api_certificate = QtCore.Signal(object) + prov_check_ca_fingerprint = QtCore.Signal(object) + prov_download_ca_cert = QtCore.Signal(object) + prov_download_provider_info = QtCore.Signal(object) + prov_get_all_services = QtCore.Signal(object) + prov_get_details = QtCore.Signal(object) + prov_get_pinned_providers = QtCore.Signal(object) + prov_get_supported_services = QtCore.Signal(object) + prov_https_connection = QtCore.Signal(object) + prov_name_resolution = QtCore.Signal(object) + prov_problem_with_provider = QtCore.Signal() + prov_unsupported_api = QtCore.Signal() + prov_unsupported_client = QtCore.Signal() + + soledad_bootstrap_failed = QtCore.Signal() + soledad_bootstrap_finished = QtCore.Signal() + soledad_cancelled_bootstrap = QtCore.Signal() + soledad_invalid_auth_token = QtCore.Signal() + soledad_offline_failed = QtCore.Signal() + soledad_offline_finished = QtCore.Signal() + soledad_password_change_error = QtCore.Signal() + soledad_password_change_ok = QtCore.Signal() + + srp_auth_bad_user_or_password = QtCore.Signal() + srp_auth_connection_error = QtCore.Signal() + srp_auth_error = QtCore.Signal() + srp_auth_ok = QtCore.Signal() + srp_auth_server_error = QtCore.Signal() + srp_logout_error = QtCore.Signal() + srp_logout_ok = QtCore.Signal() + srp_not_logged_in_error = QtCore.Signal() + srp_password_change_badpw = QtCore.Signal() + srp_password_change_error = QtCore.Signal() + srp_password_change_ok = QtCore.Signal() + srp_registration_failed = QtCore.Signal() + srp_registration_finished = QtCore.Signal() + srp_registration_taken = QtCore.Signal() + srp_status_logged_in = QtCore.Signal() + srp_status_not_logged_in = QtCore.Signal() -- cgit v1.2.3 From 6dbd52e2f0d75ad9bf7c2f11e3384d8bab0520c9 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 18 Jun 2014 12:37:23 -0300 Subject: Use new backend/signaler and start logic in processes. --- src/leap/bitmask/app.py | 15 +++++++-- src/leap/bitmask/frontend_app.py | 5 +-- src/leap/bitmask/gui/eip_preferenceswindow.py | 5 +-- src/leap/bitmask/gui/eip_status.py | 6 ++-- src/leap/bitmask/gui/mainwindow.py | 45 ++++++++++++++------------- src/leap/bitmask/gui/preferenceswindow.py | 6 ++-- src/leap/bitmask/gui/wizard.py | 10 +++--- src/leap/bitmask/services/eip/conductor.py | 5 +-- 8 files changed, 54 insertions(+), 43 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index f1d87d18..9afe41be 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -43,9 +43,12 @@ import multiprocessing import os import sys +from leap.bitmask.backend.utils import generate_certificates + from leap.bitmask import __version__ as VERSION from leap.bitmask.config import flags from leap.bitmask.frontend_app import run_frontend +from leap.bitmask.backend.leapbackend import run_backend from leap.bitmask.logs.utils import create_logger from leap.bitmask.platform_init.locks import we_are_the_one_and_only from leap.bitmask.services.mail import plumber @@ -114,7 +117,6 @@ def start_app(): do_display_version(opts) options = { - 'bypass_checks': opts.danger, 'start_hidden': opts.start_hidden, 'debug': opts.debug, 'log_file': opts.log_file, @@ -170,8 +172,15 @@ def start_app(): logger.info('Starting app') - frontend = multiprocessing.Process(target=run_frontend, args=(options, )) - frontend.start() + generate_certificates() + + app = lambda: run_frontend(options=options) + gui_process = multiprocessing.Process(target=app) + gui_process.start() + + backend = lambda: run_backend(bypass_checks=opts.danger) + backend_process = multiprocessing.Process(target=backend) + backend_process.start() if __name__ == "__main__": diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index ed67a77a..12703518 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -25,7 +25,6 @@ from PySide import QtCore, QtGui from leap.bitmask.config import flags from leap.bitmask.gui import locale_rc # noqa - silence pylint from leap.bitmask.gui.mainwindow import MainWindow -# from leap.bitmask.logs.utils import create_logger import logging logger = logging.getLogger(__name__) @@ -61,7 +60,6 @@ def run_frontend(options): :param options: a dict of options parsed from the command line. :type options: dict """ - bypass_checks = options["bypass_checks"] start_hidden = options["start_hidden"] # We force the style if on KDE so that it doesn't load all the kde @@ -90,8 +88,7 @@ def run_frontend(options): qApp.setApplicationName("leap") qApp.setOrganizationDomain("leap.se") - window = MainWindow(bypass_checks=bypass_checks, - start_hidden=start_hidden) + window = MainWindow(start_hidden=start_hidden) sigint_window = partial(sigint_handler, window, logger=logger) signal.signal(signal.SIGINT, sigint_window) diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py index 530cd38d..caf663b1 100644 --- a/src/leap/bitmask/gui/eip_preferenceswindow.py +++ b/src/leap/bitmask/gui/eip_preferenceswindow.py @@ -33,7 +33,7 @@ class EIPPreferencesWindow(QtGui.QDialog): """ Window that displays the EIP preferences. """ - def __init__(self, parent, domain, backend): + def __init__(self, parent, domain, backend, leap_signaler): """ :param parent: parent object of the EIPPreferencesWindow. :type parent: QWidget @@ -46,6 +46,7 @@ class EIPPreferencesWindow(QtGui.QDialog): self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") self._settings = LeapSettings() + self._leap_signaler = leap_signaler self._backend = backend # Load UI @@ -248,7 +249,7 @@ class EIPPreferencesWindow(QtGui.QDialog): self.ui.cbGateways.setEnabled(False) def _backend_connect(self): - sig = self._backend.signaler + sig = self._leap_signaler sig.eip_get_gateways_list.connect(self._update_gateways_list) sig.eip_get_gateways_list_error.connect(self._gateways_list_error) sig.eip_uninitialized_provider.connect( diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index bd569343..01966d82 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -44,7 +44,7 @@ class EIPStatusWidget(QtGui.QWidget): RATE_STR = "%1.2f KB/s" TOTAL_STR = "%1.2f Kb" - def __init__(self, parent=None, eip_conductor=None): + def __init__(self, parent, eip_conductor, leap_signaler): """ :param parent: the parent of the widget. :type parent: QObject @@ -60,6 +60,8 @@ class EIPStatusWidget(QtGui.QWidget): self.ui = Ui_EIPStatus() self.ui.setupUi(self) + self._leap_signaler = leap_signaler + self.eip_conductor = eip_conductor self.eipconnection = eip_conductor.eip_connection @@ -98,7 +100,7 @@ class EIPStatusWidget(QtGui.QWidget): """ Connect backend signals. """ - signaler = self.eip_conductor._backend.signaler + signaler = self._leap_signaler signaler.eip_openvpn_already_running.connect( self._on_eip_openvpn_already_running) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index e53ab7f3..4b5d1c83 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -43,7 +43,8 @@ from leap.bitmask.platform_init import IS_WIN, IS_MAC, IS_LINUX from leap.bitmask.platform_init.initializers import init_platform from leap.bitmask.platform_init.initializers import init_signals -from leap.bitmask.backend import leapbackend +from leap.bitmask.backend.backend_proxy import BackendProxy +from leap.bitmask.backend.leapsignaler import LeapSignaler from leap.bitmask.services.eip import conductor as eip_conductor from leap.bitmask.services.mail import conductor as mail_conductor @@ -91,13 +92,10 @@ class MainWindow(QtGui.QMainWindow): # We give the services some time to a halt before forcing quit. SERVICES_STOP_TIMEOUT = 20000 # in milliseconds - def __init__(self, bypass_checks=False, start_hidden=False): + def __init__(self, start_hidden=False): """ Constructor for the client main window - :param bypass_checks: Set to true if the app should bypass first round - of checks for CA certificates at bootstrap - :type bypass_checks: bool :param start_hidden: Set to true if the app should not show the window but just the tray. :type start_hidden: bool @@ -119,15 +117,16 @@ class MainWindow(QtGui.QMainWindow): self.ui = Ui_MainWindow() self.ui.setupUi(self) self.menuBar().setNativeMenuBar(not IS_LINUX) - self._backend = leapbackend.Backend(bypass_checks) - self._backend.start() + + self._backend = BackendProxy() + + self._leap_signaler = LeapSignaler() + self._leap_signaler.start() self._settings = LeapSettings() # Login Widget - self._login_widget = LoginWidget( - self._settings, - self) + self._login_widget = LoginWidget(self._settings, self) self.ui.loginLayout.addWidget(self._login_widget) # Mail Widget @@ -144,8 +143,9 @@ class MainWindow(QtGui.QMainWindow): # EIP Control redux ######################################### self._eip_conductor = eip_conductor.EIPConductor( - self._settings, self._backend) - self._eip_status = EIPStatusWidget(self, self._eip_conductor) + self._settings, self._backend, self._leap_signaler) + self._eip_status = EIPStatusWidget(self, self._eip_conductor, + self._leap_signaler) init_signals.eip_missing_helpers.connect( self._disable_eip_missing_helpers) @@ -258,7 +258,6 @@ class MainWindow(QtGui.QMainWindow): self._logger_window = None - self._bypass_checks = bypass_checks self._start_hidden = start_hidden self._mail_conductor = mail_conductor.MailConductor(self._backend) @@ -283,7 +282,7 @@ class MainWindow(QtGui.QMainWindow): self._wizard_firstrun = True self._disconnect_and_untrack() self._wizard = Wizard(backend=self._backend, - bypass_checks=bypass_checks) + leap_signaler=self._leap_signaler) # Give this window time to finish init and then show the wizard QtDelayedCall(1, self._launch_wizard) self._wizard.accepted.connect(self._finish_init) @@ -342,7 +341,7 @@ class MainWindow(QtGui.QMainWindow): that we are tracking to disconnect later. :type only_tracked: bool """ - sig = self._backend.signaler + sig = self._leap_signaler conntrack = self._connect_and_track auth_err = self._authentication_error @@ -480,7 +479,7 @@ class MainWindow(QtGui.QMainWindow): if self._wizard is None: self._disconnect_and_untrack() self._wizard = Wizard(backend=self._backend, - bypass_checks=self._bypass_checks) + leap_signaler=self._leap_signaler) self._wizard.accepted.connect(self._finish_init) self._wizard.rejected.connect(self._rejected_wizard) @@ -575,7 +574,8 @@ class MainWindow(QtGui.QMainWindow): if self._provider_details is not None: mx_provided = MX_SERVICE in self._provider_details['services'] preferences = PreferencesWindow(self, user, domain, self._backend, - self._soledad_started, mx_provided) + self._soledad_started, mx_provided, + self._leap_signaler) self.soledad_ready.connect(preferences.set_soledad_ready) preferences.show() @@ -686,7 +686,9 @@ class MainWindow(QtGui.QMainWindow): Displays the EIP preferences window. """ domain = self._login_widget.get_selected_provider() - EIPPreferencesWindow(self, domain, self._backend).show() + pref = EIPPreferencesWindow(self, domain, + self._backend, self._leap_signaler) + pref.show() # # updates @@ -1277,7 +1279,7 @@ class MainWindow(QtGui.QMainWindow): if MX_SERVICE in self._enabled_services: btn_enabled = self._login_widget.set_logout_btn_enabled btn_enabled(False) - sig = self._backend.signaler + sig = self._leap_signaler sig.soledad_bootstrap_failed.connect(lambda: btn_enabled(True)) sig.soledad_bootstrap_finished.connect(lambda: btn_enabled(True)) @@ -1711,10 +1713,10 @@ class MainWindow(QtGui.QMainWindow): self._services_being_stopped = set(('imap', 'eip')) imap_stopped = lambda: self._remove_service('imap') - self._backend.signaler.imap_stopped.connect(imap_stopped) + self._leap_signaler.imap_stopped.connect(imap_stopped) eip_stopped = lambda: self._remove_service('eip') - self._backend.signaler.eip_stopped.connect(eip_stopped) + self._leap_signaler.eip_stopped.connect(eip_stopped) logger.debug('Stopping mail services') self._backend.imap_stop_service() @@ -1804,7 +1806,6 @@ class MainWindow(QtGui.QMainWindow): if IS_WIN: WindowsLock.release_all_locks() - self._backend.stop() self.close() QtDelayedCall(100, twisted_main.quit) diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index a3b81d38..8cf8752e 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -38,7 +38,8 @@ class PreferencesWindow(QtGui.QDialog): """ preferences_saved = QtCore.Signal() - def __init__(self, parent, username, domain, backend, soledad_started, mx): + def __init__(self, parent, username, domain, backend, soledad_started, mx, + leap_signaler): """ :param parent: parent object of the PreferencesWindow. :parent type: QWidget @@ -58,6 +59,7 @@ class PreferencesWindow(QtGui.QDialog): self._username = username self._domain = domain + self._leap_signaler = leap_signaler self._backend = backend self._soledad_started = soledad_started self._mx_provided = mx @@ -423,7 +425,7 @@ class PreferencesWindow(QtGui.QDialog): """ Helper to connect to backend signals """ - sig = self._backend.signaler + sig = self._leap_signaler sig.prov_get_supported_services.connect(self._load_services) diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index 4f67958f..79cccc44 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -49,16 +49,12 @@ class Wizard(QtGui.QWizard): REGISTER_USER_PAGE = 4 SERVICES_PAGE = 5 - def __init__(self, backend, bypass_checks=False): + def __init__(self, backend, leap_signaler): """ Constructor for the main Wizard. :param backend: Backend being used :type backend: Backend - :param bypass_checks: Set to true if the app should bypass - first round of checks for CA - certificates at bootstrap - :type bypass_checks: bool """ QtGui.QWizard.__init__(self) @@ -86,6 +82,8 @@ class Wizard(QtGui.QWizard): self._connect_and_track(self.ui.lnProvider.returnPressed, self._check_provider) + self._leap_signaler = leap_signaler + self._backend = backend self._backend_connect() @@ -789,7 +787,7 @@ class Wizard(QtGui.QWizard): """ Connects all the backend signals with the wizard. """ - sig = self._backend.signaler + sig = self._leap_signaler conntrack = self._connect_and_track conntrack(sig.prov_name_resolution, self._name_resolution) conntrack(sig.prov_https_connection, self._https_connection) diff --git a/src/leap/bitmask/services/eip/conductor.py b/src/leap/bitmask/services/eip/conductor.py index a8821160..dfd27f3d 100644 --- a/src/leap/bitmask/services/eip/conductor.py +++ b/src/leap/bitmask/services/eip/conductor.py @@ -33,7 +33,7 @@ logger = logging.getLogger(__name__) class EIPConductor(object): - def __init__(self, settings, backend, **kwargs): + def __init__(self, settings, backend, leap_signaler, **kwargs): """ Initializes EIP Conductor. @@ -46,6 +46,7 @@ class EIPConductor(object): self.eip_connection = EIPConnection() self.eip_name = get_service_display_name(EIP_SERVICE) self._settings = settings + self._leap_signaler = leap_signaler self._backend = backend self._eip_status = None @@ -76,7 +77,7 @@ class EIPConductor(object): """ Connect to backend signals. """ - signaler = self._backend.signaler + signaler = self._leap_signaler # for conductor signaler.eip_process_restart_tls.connect(self._do_eip_restart) -- cgit v1.2.3 From 5b34d32682188f4849a1e2a4aa8cf8b4c900b8d8 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 25 Jun 2014 12:35:04 -0300 Subject: Properly stop backend. --- src/leap/bitmask/gui/mainwindow.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 4b5d1c83..9b6dc8bd 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -18,6 +18,7 @@ Main window for Bitmask. """ import logging +import time from datetime import datetime @@ -1788,7 +1789,6 @@ class MainWindow(QtGui.QMainWindow): Final steps to quit the app, starting from here we don't care about running services or user interaction, just quitting. """ - # We can reach here because all the services are stopped or because a # timeout was triggered. Since we want to run this only once, we exit # if this is called twice. @@ -1801,6 +1801,10 @@ class MainWindow(QtGui.QMainWindow): self._backend.soledad_close() logger.debug('Final quit...') + self._leap_signaler.stop() + self._backend.stop() + time.sleep(0.05) # give the thread a little time to finish. + # Remove lockfiles on a clean shutdown. logger.debug('Cleaning pidfiles') if IS_WIN: -- cgit v1.2.3 From b66c1643eeb094a0f54d621ec3bf2c93173b767d Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 11 Jun 2014 15:35:10 -0300 Subject: Lowercase signals names. --- src/leap/bitmask/backend/components.py | 86 +++++++++++----------- src/leap/bitmask/crypto/srpauth.py | 20 ++--- src/leap/bitmask/crypto/srpregister.py | 6 +- src/leap/bitmask/provider/providerbootstrapper.py | 18 ++--- src/leap/bitmask/services/eip/eipbootstrapper.py | 6 +- src/leap/bitmask/services/eip/vpnprocess.py | 14 ++-- .../services/soledad/soledadbootstrapper.py | 16 ++-- 7 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index 19fcf283..5357bed5 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -197,7 +197,7 @@ class Provider(object): else: if self._signaler is not None: self._signaler.signal( - self._signaler.PROV_PROBLEM_WITH_PROVIDER_KEY) + self._signaler.prov_problem_with_provider) logger.error("Could not load provider configuration.") self._login_widget.set_enabled(True) @@ -234,7 +234,7 @@ class Provider(object): services = get_supported(self._get_services(domain)) self._signaler.signal( - self._signaler.PROV_GET_SUPPORTED_SERVICES, services) + self._signaler.prov_get_supported_services, services) def get_all_services(self, providers): """ @@ -253,7 +253,7 @@ class Provider(object): services_all = services_all.union(set(services)) self._signaler.signal( - self._signaler.PROV_GET_ALL_SERVICES, services_all) + self._signaler.prov_get_all_services, list(services_all)) def get_details(self, domain, lang=None): """ @@ -268,7 +268,7 @@ class Provider(object): prov_get_details -> dict """ self._signaler.signal( - self._signaler.PROV_GET_DETAILS, + self._signaler.prov_get_details, self._provider_config.get_light_config(domain, lang)) def get_pinned_providers(self): @@ -279,7 +279,7 @@ class Provider(object): prov_get_pinned_providers -> list of provider domains """ self._signaler.signal( - self._signaler.PROV_GET_PINNED_PROVIDERS, + self._signaler.prov_get_pinned_providers, PinnedProviders.domains()) @@ -324,7 +324,7 @@ class Register(object): partial(srpregister.register_user, username, password)) else: if self._signaler is not None: - self._signaler.signal(self._signaler.SRP_REGISTRATION_FAILED) + self._signaler.signal(self._signaler.srp_registration_failed) logger.error("Could not load provider configuration.") @@ -401,12 +401,12 @@ class EIP(object): if not self._can_start(domain): if self._signaler is not None: - self._signaler.signal(self._signaler.EIP_CONNECTION_ABORTED) + self._signaler.signal(self._signaler.eip_connection_aborted) return if not loaded: if self._signaler is not None: - self._signaler.signal(self._signaler.EIP_CONNECTION_ABORTED) + self._signaler.signal(self._signaler.eip_connection_aborted) logger.error("Tried to start EIP but cannot find any " "available provider!") return @@ -425,28 +425,28 @@ class EIP(object): if not self._provider_config.loaded(): # This means that the user didn't call setup_eip first. - self._signaler.signal(signaler.BACKEND_BAD_CALL, "EIP.start(), " + self._signaler.signal(signaler.backend_bad_call, "EIP.start(), " "no provider loaded") return try: self._start_eip(*args, **kwargs) except vpnprocess.OpenVPNAlreadyRunning: - signaler.signal(signaler.EIP_OPENVPN_ALREADY_RUNNING) + signaler.signal(signaler.eip_openvpn_already_running) except vpnprocess.AlienOpenVPNAlreadyRunning: - signaler.signal(signaler.EIP_ALIEN_OPENVPN_ALREADY_RUNNING) + signaler.signal(signaler.eip_alien_openvpn_already_running) except vpnlauncher.OpenVPNNotFoundException: - signaler.signal(signaler.EIP_OPENVPN_NOT_FOUND_ERROR) + signaler.signal(signaler.eip_openvpn_not_found_error) except vpnlauncher.VPNLauncherException: # TODO: this seems to be used for 'gateway not found' only. # see vpnlauncher.py - signaler.signal(signaler.EIP_VPN_LAUNCHER_EXCEPTION) + signaler.signal(signaler.eip_vpn_launcher_exception) except linuxvpnlauncher.EIPNoPolkitAuthAgentAvailable: - signaler.signal(signaler.EIP_NO_POLKIT_AGENT_ERROR) + signaler.signal(signaler.eip_no_polkit_agent_error) except linuxvpnlauncher.EIPNoPkexecAvailable: - signaler.signal(signaler.EIP_NO_PKEXEC_ERROR) + signaler.signal(signaler.eip_no_pkexec_error) except darwinvpnlauncher.EIPNoTunKextLoaded: - signaler.signal(signaler.EIP_NO_TUN_KEXT_ERROR) + signaler.signal(signaler.eip_no_tun_kext_error) except Exception as e: logger.error("Unexpected problem: {0!r}".format(e)) else: @@ -482,7 +482,7 @@ class EIP(object): while retry <= MAX_FW_WAIT_RETRIES: if self._vpn.is_fw_down(): - self._signaler.signal(self._signaler.EIP_STOPPED) + self._signaler.signal(self._signaler.eip_stopped) return else: #msg = "Firewall is not down yet, waiting... {0} of {1}" @@ -542,7 +542,7 @@ class EIP(object): filtered_domains.append((domain, is_initialized)) if self._signaler is not None: - self._signaler.signal(self._signaler.EIP_GET_INITIALIZED_PROVIDERS, + self._signaler.signal(self._signaler.eip_get_initialized_providers, filtered_domains) def tear_fw_down(self): @@ -566,7 +566,7 @@ class EIP(object): if not self._provider_is_initialized(domain): if self._signaler is not None: self._signaler.signal( - self._signaler.EIP_UNINITIALIZED_PROVIDER) + self._signaler.eip_uninitialized_provider) return eip_config = eipconfig.EIPConfig() @@ -580,14 +580,14 @@ class EIP(object): if not eip_loaded or provider_config is None: if self._signaler is not None: self._signaler.signal( - self._signaler.EIP_GET_GATEWAYS_LIST_ERROR) + self._signaler.eip_get_gateways_list_error) return gateways = eipconfig.VPNGatewaySelector(eip_config).get_gateways_list() if self._signaler is not None: self._signaler.signal( - self._signaler.EIP_GET_GATEWAYS_LIST, gateways) + self._signaler.eip_get_gateways_list, gateways) def _can_start(self, domain): """ @@ -643,10 +643,10 @@ class EIP(object): """ if self._can_start(domain): if self._signaler is not None: - self._signaler.signal(self._signaler.EIP_CAN_START) + self._signaler.signal(self._signaler.eip_can_start) else: if self._signaler is not None: - self._signaler.signal(self._signaler.EIP_CANNOT_START) + self._signaler.signal(self._signaler.eip_cannot_start) def check_dns(self, domain): """ @@ -665,7 +665,7 @@ class EIP(object): """ Callback handler for `do_check`. """ - self._signaler.signal(self._signaler.EIP_DNS_OK) + self._signaler.signal(self._signaler.eip_dns_ok) logger.debug("DNS check OK") def check_err(failure): @@ -677,7 +677,7 @@ class EIP(object): """ logger.debug("Can't resolve hostname. {0!r}".format(failure)) - self._signaler.signal(self._signaler.EIP_DNS_ERROR) + self._signaler.signal(self._signaler.eip_dns_error) # python 2.7.4 raises socket.error # python 2.7.5 raises socket.gaierror @@ -737,7 +737,7 @@ class Soledad(object): self._soledad_defer.addCallback(self._set_proxies_cb) else: if self._signaler is not None: - self._signaler.signal(self._signaler.SOLEDAD_BOOTSTRAP_FAILED) + self._signaler.signal(self._signaler.soledad_bootstrap_failed) logger.error("Could not load provider configuration.") return self._soledad_defer @@ -793,7 +793,7 @@ class Soledad(object): Password change callback. """ if self._signaler is not None: - self._signaler.signal(self._signaler.SOLEDAD_PASSWORD_CHANGE_OK) + self._signaler.signal(self._signaler.soledad_password_change_ok) def _change_password_error(self, failure): """ @@ -808,7 +808,7 @@ class Soledad(object): logger.error("Passphrase too short.") if self._signaler is not None: - self._signaler.signal(self._signaler.SOLEDAD_PASSWORD_CHANGE_ERROR) + self._signaler.signal(self._signaler.soledad_password_change_error) def change_password(self, new_password): """ @@ -866,7 +866,7 @@ class Keymanager(object): new_key = keys_file.read() except IOError as e: logger.error("IOError importing key. {0!r}".format(e)) - signal = self._signaler.KEYMANAGER_IMPORT_IOERROR + signal = self._signaler.keymanager_import_ioerror self._signaler.signal(signal) return @@ -876,19 +876,19 @@ class Keymanager(object): new_key) except (KeyAddressMismatch, KeyFingerprintMismatch) as e: logger.error(repr(e)) - signal = self._signaler.KEYMANAGER_IMPORT_DATAMISMATCH + signal = self._signaler.keymanager_import_datamismatch self._signaler.signal(signal) return if public_key is None or private_key is None: - signal = self._signaler.KEYMANAGER_IMPORT_MISSINGKEY + signal = self._signaler.keymanager_import_missingkey self._signaler.signal(signal) return current_public_key = keymanager.get_key(username, openpgp.OpenPGPKey) if public_key.address != current_public_key.address: logger.error("The key does not match the ID") - signal = self._signaler.KEYMANAGER_IMPORT_ADDRESSMISMATCH + signal = self._signaler.keymanager_import_addressmismatch self._signaler.signal(signal) return @@ -899,7 +899,7 @@ class Keymanager(object): keymanager.send_key(openpgp.OpenPGPKey) logger.debug('Import ok') - signal = self._signaler.KEYMANAGER_IMPORT_OK + signal = self._signaler.keymanager_import_ok self._signaler.signal(signal) @@ -923,17 +923,17 @@ class Keymanager(object): keys_file.write(private_key.key_data) logger.debug('Export ok') - self._signaler.signal(self._signaler.KEYMANAGER_EXPORT_OK) + self._signaler.signal(self._signaler.keymanager_export_ok) except IOError as e: logger.error("IOError exporting key. {0!r}".format(e)) - self._signaler.signal(self._signaler.KEYMANAGER_EXPORT_ERROR) + self._signaler.signal(self._signaler.keymanager_export_error) def list_keys(self): """ List all the keys stored in the local DB. """ keys = self._keymanager_proxy.get_all_keys_in_local_db() - self._signaler.signal(self._signaler.KEYMANAGER_KEYS_LIST, keys) + self._signaler.signal(self._signaler.keymanager_keys_list, keys) def get_key_details(self, username): """ @@ -942,7 +942,7 @@ class Keymanager(object): public_key = self._keymanager_proxy.get_key(username, openpgp.OpenPGPKey) details = (public_key.key_id, public_key.fingerprint) - self._signaler.signal(self._signaler.KEYMANAGER_KEY_DETAILS, details) + self._signaler.signal(self._signaler.keymanager_key_details, details) class Mail(object): @@ -1027,7 +1027,7 @@ class Mail(object): logger.debug('Waiting for imap service to stop.') cv.wait(self.SERVICE_STOP_TIMEOUT) logger.debug('IMAP stopped') - self._signaler.signal(self._signaler.IMAP_STOPPED) + self._signaler.signal(self._signaler.imap_stopped) def stop_imap_service(self): """ @@ -1080,7 +1080,7 @@ class Authenticate(object): return self._login_defer else: if self._signaler is not None: - self._signaler.signal(self._signaler.SRP_AUTH_ERROR) + self._signaler.signal(self._signaler.srp_auth_error) logger.error("Could not load provider configuration.") def cancel_login(self): @@ -1105,7 +1105,7 @@ class Authenticate(object): """ if not self._is_logged_in(): if self._signaler is not None: - self._signaler.signal(self._signaler.SRP_NOT_LOGGED_IN_ERROR) + self._signaler.signal(self._signaler.srp_not_logged_in_error) return return self._srp_auth.change_password(current_password, new_password) @@ -1117,7 +1117,7 @@ class Authenticate(object): """ if not self._is_logged_in(): if self._signaler is not None: - self._signaler.signal(self._signaler.SRP_NOT_LOGGED_IN_ERROR) + self._signaler.signal(self._signaler.srp_not_logged_in_error) return self._srp_auth.logout() @@ -1140,8 +1140,8 @@ class Authenticate(object): signal = None if self._is_logged_in(): - signal = self._signaler.SRP_STATUS_LOGGED_IN + signal = self._signaler.srp_status_logged_in else: - signal = self._signaler.SRP_STATUS_NOT_LOGGED_IN + signal = self._signaler.srp_status_not_logged_in self._signaler.signal(signal) diff --git a/src/leap/bitmask/crypto/srpauth.py b/src/leap/bitmask/crypto/srpauth.py index 192a9d5c..856d2c81 100644 --- a/src/leap/bitmask/crypto/srpauth.py +++ b/src/leap/bitmask/crypto/srpauth.py @@ -523,7 +523,7 @@ class SRPAuth(object): Password change callback. """ if self._signaler is not None: - self._signaler.signal(self._signaler.SRP_PASSWORD_CHANGE_OK) + self._signaler.signal(self._signaler.srp_password_change_ok) def _change_password_error(self, failure): """ @@ -535,9 +535,9 @@ class SRPAuth(object): return if failure.check(SRPAuthBadUserOrPassword): - self._signaler.signal(self._signaler.SRP_PASSWORD_CHANGE_BADPW) + self._signaler.signal(self._signaler.srp_password_change_badpw) else: - self._signaler.signal(self._signaler.SRP_PASSWORD_CHANGE_ERROR) + self._signaler.signal(self._signaler.srp_password_change_error) def authenticate(self, username, password): """ @@ -591,7 +591,7 @@ class SRPAuth(object): :type _: IGNORED """ logger.debug("Successful login!") - self._signaler.signal(self._signaler.SRP_AUTH_OK) + self._signaler.signal(self._signaler.srp_auth_ok) def _authenticate_error(self, failure): """ @@ -612,13 +612,13 @@ class SRPAuth(object): return if failure.check(SRPAuthBadUserOrPassword): - signal = self._signaler.SRP_AUTH_BAD_USER_OR_PASSWORD + signal = self._signaler.srp_auth_bad_user_or_password elif failure.check(SRPAuthConnectionError): - signal = self._signaler.SRP_AUTH_CONNECTION_ERROR + signal = self._signaler.srp_auth_connection_error elif failure.check(SRPAuthenticationError): - signal = self._signaler.SRP_AUTH_SERVER_ERROR + signal = self._signaler.srp_auth_server_error else: - signal = self._signaler.SRP_AUTH_ERROR + signal = self._signaler.srp_auth_error self._signaler.signal(signal) @@ -647,7 +647,7 @@ class SRPAuth(object): logger.warning("Something went wrong with the logout: %r" % (e,)) if self._signaler is not None: - self._signaler.signal(self._signaler.SRP_LOGOUT_ERROR) + self._signaler.signal(self._signaler.srp_logout_error) raise else: self.set_session_id(None) @@ -657,7 +657,7 @@ class SRPAuth(object): self._session = self._fetcher.session() logger.debug("Successfully logged out.") if self._signaler is not None: - self._signaler.signal(self._signaler.SRP_LOGOUT_OK) + self._signaler.signal(self._signaler.srp_logout_ok) def set_session_id(self, session_id): with self._session_id_lock: diff --git a/src/leap/bitmask/crypto/srpregister.py b/src/leap/bitmask/crypto/srpregister.py index f03dc469..86510de1 100644 --- a/src/leap/bitmask/crypto/srpregister.py +++ b/src/leap/bitmask/crypto/srpregister.py @@ -179,11 +179,11 @@ class SRPRegister(QtCore.QObject): return if status_code in self.STATUS_OK: - self._signaler.signal(self._signaler.SRP_REGISTRATION_FINISHED) + self._signaler.signal(self._signaler.srp_registration_finished) elif status_code == self.STATUS_TAKEN: - self._signaler.signal(self._signaler.SRP_REGISTRATION_TAKEN) + self._signaler.signal(self._signaler.srp_registration_taken) else: - self._signaler.signal(self._signaler.SRP_REGISTRATION_FAILED) + self._signaler.signal(self._signaler.srp_registration_failed) if __name__ == "__main__": diff --git a/src/leap/bitmask/provider/providerbootstrapper.py b/src/leap/bitmask/provider/providerbootstrapper.py index 8c96a8b5..5064f6a4 100644 --- a/src/leap/bitmask/provider/providerbootstrapper.py +++ b/src/leap/bitmask/provider/providerbootstrapper.py @@ -90,7 +90,7 @@ class ProviderBootstrapper(AbstractBootstrapper): self._provider_config = None self._download_if_needed = False if signaler is not None: - self._cancel_signal = signaler.PROV_CANCELLED_SETUP + self._cancel_signal = signaler.prov_cancelled_setup @property def verify(self): @@ -228,7 +228,7 @@ class ProviderBootstrapper(AbstractBootstrapper): # TODO split if not provider.supports_client(min_client_version): self._signaler.signal( - self._signaler.PROV_UNSUPPORTED_CLIENT) + self._signaler.prov_unsupported_client) raise UnsupportedClientVersionError() provider_definition, mtime = get_content(res) @@ -250,7 +250,7 @@ class ProviderBootstrapper(AbstractBootstrapper): 'Found: {1}.').format(api_supported, api_version) logger.error(error) - self._signaler.signal(self._signaler.PROV_UNSUPPORTED_API) + self._signaler.signal(self._signaler.prov_unsupported_api) raise UnsupportedProviderAPI(error) def run_provider_select_checks(self, domain, download_if_needed=False): @@ -271,10 +271,10 @@ class ProviderBootstrapper(AbstractBootstrapper): cb_chain = [ (self._check_name_resolution, - self._signaler.PROV_NAME_RESOLUTION_KEY), - (self._check_https, self._signaler.PROV_HTTPS_CONNECTION_KEY), + self._signaler.prov_name_resolution), + (self._check_https, self._signaler.prov_https_connection), (self._download_provider_info, - self._signaler.PROV_DOWNLOAD_PROVIDER_INFO_KEY) + self._signaler.prov_download_provider_info) ] return self.addCallbackChain(cb_chain) @@ -401,11 +401,11 @@ class ProviderBootstrapper(AbstractBootstrapper): self._download_if_needed = download_if_needed cb_chain = [ - (self._download_ca_cert, self._signaler.PROV_DOWNLOAD_CA_CERT_KEY), + (self._download_ca_cert, self._signaler.prov_download_ca_cert), (self._check_ca_fingerprint, - self._signaler.PROV_CHECK_CA_FINGERPRINT_KEY), + self._signaler.prov_check_ca_fingerprint), (self._check_api_certificate, - self._signaler.PROV_CHECK_API_CERTIFICATE_KEY) + self._signaler.prov_check_api_certificate) ] return self.addCallbackChain(cb_chain) diff --git a/src/leap/bitmask/services/eip/eipbootstrapper.py b/src/leap/bitmask/services/eip/eipbootstrapper.py index c77977ce..264eac2e 100644 --- a/src/leap/bitmask/services/eip/eipbootstrapper.py +++ b/src/leap/bitmask/services/eip/eipbootstrapper.py @@ -53,7 +53,7 @@ class EIPBootstrapper(AbstractBootstrapper): self._eip_config = None self._download_if_needed = False if signaler is not None: - self._cancel_signal = signaler.EIP_CANCELLED_SETUP + self._cancel_signal = signaler.eip_cancelled_setup def _download_config(self, *args): """ @@ -116,9 +116,9 @@ class EIPBootstrapper(AbstractBootstrapper): self._download_if_needed = download_if_needed cb_chain = [ - (self._download_config, self._signaler.EIP_CONFIG_READY), + (self._download_config, self._signaler.eip_config_ready), (self._download_client_certificates, - self._signaler.EIP_CLIENT_CERTIFICATE_READY) + self._signaler.eip_client_certificate_ready) ] return self.addCallbackChain(cb_chain) diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index b54f2925..3bda3059 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -118,10 +118,10 @@ class VPNObserver(object): """ sig = self._signaler signals = { - "network_unreachable": sig.EIP_NETWORK_UNREACHABLE, - "process_restart_tls": sig.EIP_PROCESS_RESTART_TLS, - "process_restart_ping": sig.EIP_PROCESS_RESTART_PING, - "initialization_completed": sig.EIP_CONNECTED + "network_unreachable": sig.eip_network_unreachable, + "process_restart_tls": sig.eip_process_restart_tls, + "process_restart_ping": sig.eip_process_restart_ping, + "initialization_completed": sig.eip_connected } return signals.get(event.lower()) @@ -594,7 +594,7 @@ class VPNManager(object): state = status_step if state != self._last_state: - self._signaler.signal(self._signaler.EIP_STATE_CHANGED, state) + self._signaler.signal(self._signaler.eip_state_changed, state) self._last_state = state def _parse_status_and_notify(self, output): @@ -632,7 +632,7 @@ class VPNManager(object): status = (tun_tap_read, tun_tap_write) if status != self._last_status: - self._signaler.signal(self._signaler.EIP_STATUS_CHANGED, status) + self._signaler.signal(self._signaler.eip_status_changed, status) self._last_status = status def get_state(self): @@ -869,7 +869,7 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager): if isinstance(exit_code, int): logger.debug("processExited, status %d" % (exit_code,)) self._signaler.signal( - self._signaler.EIP_PROCESS_FINISHED, exit_code) + self._signaler.eip_process_finished, exit_code) self._alive = False def processEnded(self, reason): diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index b9243add..a5904dce 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -141,7 +141,7 @@ class SoledadBootstrapper(AbstractBootstrapper): AbstractBootstrapper.__init__(self, signaler) if signaler is not None: - self._cancel_signal = signaler.SOLEDAD_CANCELLED_BOOTSTRAP + self._cancel_signal = signaler.soledad_cancelled_bootstrap self._provider_config = None self._soledad_config = None @@ -190,11 +190,11 @@ class SoledadBootstrapper(AbstractBootstrapper): self._uuid = uuid try: self.load_and_sync_soledad(uuid, offline=True) - self._signaler.signal(self._signaler.SOLEDAD_OFFLINE_FINISHED) + self._signaler.signal(self._signaler.soledad_offline_finished) except Exception as e: # TODO: we should handle more specific exceptions in here logger.exception(e) - self._signaler.signal(self._signaler.SOLEDAD_OFFLINE_FAILED) + self._signaler.signal(self._signaler.soledad_offline_failed) def _get_soledad_local_params(self, uuid, offline=False): """ @@ -390,7 +390,7 @@ class SoledadBootstrapper(AbstractBootstrapper): continue except InvalidAuthTokenError: self._signaler.signal( - self._signaler.SOLEDAD_INVALID_AUTH_TOKEN) + self._signaler.soledad_invalid_auth_token) raise except Exception as e: # XXX release syncing lock @@ -649,11 +649,11 @@ class SoledadBootstrapper(AbstractBootstrapper): self._password = password if flags.OFFLINE: - signal_finished = self._signaler.SOLEDAD_OFFLINE_FINISHED - signal_failed = self._signaler.SOLEDAD_OFFLINE_FAILED + signal_finished = self._signaler.soledad_offline_finished + signal_failed = self._signaler.soledad_offline_failed else: - signal_finished = self._signaler.SOLEDAD_BOOTSTRAP_FINISHED - signal_failed = self._signaler.SOLEDAD_BOOTSTRAP_FAILED + signal_finished = self._signaler.soledad_bootstrap_finished + signal_failed = self._signaler.soledad_bootstrap_failed try: self._download_config() -- cgit v1.2.3 From d35f12b00629d6578039a11ce5e18d0c58e3fa73 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 17 Jun 2014 18:42:37 -0300 Subject: Do backend calls using kwargs. --- src/leap/bitmask/gui/eip_preferenceswindow.py | 4 ++-- src/leap/bitmask/gui/mainwindow.py | 27 ++++++++++++++++----------- src/leap/bitmask/gui/preferenceswindow.py | 7 ++++--- src/leap/bitmask/gui/wizard.py | 9 +++++---- src/leap/bitmask/services/mail/conductor.py | 6 ++++-- 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py index caf663b1..306fdb8c 100644 --- a/src/leap/bitmask/gui/eip_preferenceswindow.py +++ b/src/leap/bitmask/gui/eip_preferenceswindow.py @@ -97,7 +97,7 @@ class EIPPreferencesWindow(QtGui.QDialog): if not providers: return - self._backend.eip_get_initialized_providers(providers) + self._backend.eip_get_initialized_providers(domains=providers) @QtCore.Slot(list) def _load_providers_in_combo(self, providers): @@ -175,7 +175,7 @@ class EIPPreferencesWindow(QtGui.QDialog): domain = self.ui.cbProvidersGateway.itemData(domain_idx) self._selected_domain = domain - self._backend.eip_get_gateways_list(domain) + self._backend.eip_get_gateways_list(domain=domain) @QtCore.Slot(list) def _update_gateways_list(self, gateways): diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 9b6dc8bd..24915d4f 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -605,12 +605,13 @@ class MainWindow(QtGui.QMainWindow): return self._trying_to_start_eip = settings.get_autostart_eip() - self._backend.eip_can_start(default_provider) + self._backend.eip_can_start(domain=default_provider) # If we don't want to start eip, we leave everything # initialized to quickly start it if not self._trying_to_start_eip: - self._backend.eip_setup(default_provider, skip_network=True) + self._backend.eip_setup(provider=default_provider, + skip_network=True) def _backend_can_start_eip(self): """ @@ -824,7 +825,7 @@ class MainWindow(QtGui.QMainWindow): """ providers = self._settings.get_configured_providers() - self._backend.provider_get_all_services(providers) + self._backend.provider_get_all_services(providers=providers) def _provider_get_all_services(self, services): self._set_eip_visible(EIP_SERVICE in services) @@ -1124,7 +1125,7 @@ class MainWindow(QtGui.QMainWindow): emit the corresponding signals inmediately """ domain = self._login_widget.get_selected_provider() - self._backend.provider_setup(domain) + self._backend.provider_setup(provider=domain) @QtCore.Slot(dict) def _load_provider_config(self, data): @@ -1141,7 +1142,7 @@ class MainWindow(QtGui.QMainWindow): """ if data[self._backend.PASSED_KEY]: selected_provider = self._login_widget.get_selected_provider() - self._backend.provider_bootstrap(selected_provider) + self._backend.provider_bootstrap(provider=selected_provider) else: logger.error(data[self._backend.ERROR_KEY]) self._login_problem_provider() @@ -1252,7 +1253,8 @@ class MainWindow(QtGui.QMainWindow): self._show_hide_unsupported_services() domain = self._login_widget.get_selected_provider() - self._backend.user_login(domain, username, password) + self._backend.user_login(provider=domain, + username=username, password=password) else: logger.error(data[self._backend.ERROR_KEY]) self._login_problem_provider() @@ -1319,7 +1321,7 @@ class MainWindow(QtGui.QMainWindow): """ domain = self._login_widget.get_selected_provider() lang = QtCore.QLocale.system().name() - self._backend.provider_get_details(domain, lang) + self._backend.provider_get_details(domain=domain, lang=lang) @QtCore.Slot() def _provider_get_details(self, details): @@ -1388,11 +1390,14 @@ class MainWindow(QtGui.QMainWindow): # this is mostly for internal use/debug for now. logger.warning("Sorry! Log-in at least one time.") return - self._backend.soledad_load_offline(full_user_id, password, uuid) + self._backend.soledad_load_offline(username=full_user_id, + password=password, uuid=uuid) else: if self._logged_user is not None: domain = self._login_widget.get_selected_provider() - self._backend.soledad_bootstrap(username, domain, password) + self._backend.soledad_bootstrap(username=username, + domain=domain, + password=password) ################################################################### # Service control methods: soledad @@ -1478,7 +1483,7 @@ class MainWindow(QtGui.QMainWindow): self._already_started_eip = True # check for connectivity - self._backend.eip_check_dns(domain) + self._backend.eip_check_dns(domain=domain) @QtCore.Slot() def _eip_dns_error(self): @@ -1542,7 +1547,7 @@ class MainWindow(QtGui.QMainWindow): self._eip_status.eip_button.setEnabled(False) domain = self._login_widget.get_selected_provider() - self._backend.eip_setup(domain) + self._backend.eip_setup(provider=domain) self._already_started_eip = True # we want to start soledad anyway after a certain timeout if eip diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 8cf8752e..3c9cd5d0 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -196,7 +196,8 @@ class PreferencesWindow(QtGui.QDialog): return self._set_changing_password(True) - self._backend.user_change_password(current_password, new_password) + self._backend.user_change_password(current_password=current_password, + new_password=new_password) @QtCore.Slot() def _srp_change_password_ok(self): @@ -210,7 +211,7 @@ class PreferencesWindow(QtGui.QDialog): logger.debug("SRP password changed successfully.") if self._mx_provided: - self._backend.soledad_change_password(new_password) + self._backend.soledad_change_password(new_password=new_password) else: self._change_password_success() @@ -359,7 +360,7 @@ class PreferencesWindow(QtGui.QDialog): save_services = partial(self._save_enabled_services, domain) self.ui.pbSaveServices.clicked.connect(save_services) - self._backend.provider_get_supported_services(domain) + self._backend.provider_get_supported_services(domain=domain) @QtCore.Slot(str) def _load_services(self, services): diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index 79cccc44..a20ef9fe 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -323,7 +323,8 @@ class Wizard(QtGui.QWizard): if user_ok and pass_ok: self._set_register_status(self.tr("Starting registration...")) - self._backend.user_register(self._domain, username, password) + self._backend.user_register(provider=self._domain, + username=username, password=password) self._username = username self._password = password else: @@ -475,7 +476,7 @@ class Wizard(QtGui.QWizard): self.ui.lblNameResolution.setPixmap(self.QUESTION_ICON) self._provider_select_defer = self._backend.\ - provider_setup(self._domain) + provider_setup(provider=self._domain) @QtCore.Slot(bool) def _skip_provider_checks(self, skip): @@ -576,7 +577,7 @@ class Wizard(QtGui.QWizard): True, self.SELECT_PROVIDER_PAGE) self._provider_checks_ok = True lang = QtCore.QLocale.system().name() - self._backend.provider_get_details(self._domain, lang) + self._backend.provider_get_details(domain=self._domain, lang=lang) else: new_data = { self._backend.PASSED_KEY: False, @@ -724,7 +725,7 @@ class Wizard(QtGui.QWizard): self.page(pageId).setSubTitle(sub_title) self.ui.lblDownloadCaCert.setPixmap(self.QUESTION_ICON) self._provider_setup_defer = self._backend.\ - provider_bootstrap(self._domain) + provider_bootstrap(provider=self._domain) if pageId == self.PRESENT_PROVIDER_PAGE: sub_title = self.tr("Description of services offered by {0}") diff --git a/src/leap/bitmask/services/mail/conductor.py b/src/leap/bitmask/services/mail/conductor.py index 98b40929..5e85368f 100644 --- a/src/leap/bitmask/services/mail/conductor.py +++ b/src/leap/bitmask/services/mail/conductor.py @@ -64,7 +64,8 @@ class IMAPControl(object): """ Start imap service. """ - self._backend.imap_start_service(self.userid, flags.OFFLINE) + self._backend.imap_start_service(full_user_id=self.userid, + offline=flags.OFFLINE) def stop_imap_service(self): """ @@ -146,7 +147,8 @@ class SMTPControl(object): :type download_if_needed: bool """ self.smtp_connection.qtsigs.connecting_signal.emit() - self._backend.smtp_start_service(self.userid, download_if_needed) + self._backend.smtp_start_service(full_user_id=self.userid, + download_if_needed=download_if_needed) def stop_smtp_service(self): """ -- cgit v1.2.3 From c46a93e290194cdeb1b4e1776d4bf0edde303072 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 25 Jun 2014 12:36:25 -0300 Subject: Add SIGINT handler. --- src/leap/bitmask/app.py | 4 ++++ src/leap/bitmask/frontend_app.py | 22 +++++----------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 9afe41be..eda4073c 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -41,6 +41,7 @@ # (thanks to: http://www.glassgiant.com/ascii/) import multiprocessing import os +import signal import sys from leap.bitmask.backend.utils import generate_certificates @@ -112,6 +113,9 @@ def start_app(): """ Starts the main event loop and launches the main window. """ + # Ensure that the application quits using CTRL-C + signal.signal(signal.SIGINT, signal.SIG_DFL) + # Parse arguments and store them opts = leap_argparse.get_options() do_display_version(opts) diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index 12703518..60f20e3c 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -18,8 +18,6 @@ import signal import sys import os -from functools import partial - from PySide import QtCore, QtGui from leap.bitmask.config import flags @@ -41,18 +39,6 @@ def sigint_handler(*args, **kwargs): mainwindow.quit() -def sigterm_handler(*args, **kwargs): - """ - Signal handler for SIGTERM. - This handler is actually passed to twisted reactor - """ - logger = kwargs.get('logger', None) - if logger: - logger.debug("SIGTERM catched. shutting down...") - mainwindow = args[0] - mainwindow.quit() - - def run_frontend(options): """ Run the GUI for the application. @@ -88,10 +74,12 @@ def run_frontend(options): qApp.setApplicationName("leap") qApp.setOrganizationDomain("leap.se") - window = MainWindow(start_hidden=start_hidden) + MainWindow(start_hidden=start_hidden) - sigint_window = partial(sigint_handler, window, logger=logger) - signal.signal(signal.SIGINT, sigint_window) + # sigint_window = partial(sigint_handler, window, logger=logger) + # signal.signal(signal.SIGINT, sigint_window) + # Ensure that the application quits using CTRL-C + signal.signal(signal.SIGINT, signal.SIG_DFL) sys.exit(qApp.exec_()) -- cgit v1.2.3 From 4a5e136f3b6e68d65df4a5be83504e05043aeaa1 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 19 Jun 2014 16:13:42 -0300 Subject: Add missing error/passed keys. --- src/leap/bitmask/backend/leapbackend.py | 3 +++ src/leap/bitmask/gui/mainwindow.py | 25 +++++++++++++++---------- src/leap/bitmask/gui/wizard.py | 27 +++++++++++++++------------ 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/leap/bitmask/backend/leapbackend.py b/src/leap/bitmask/backend/leapbackend.py index cc7227ad..d6b0954a 100644 --- a/src/leap/bitmask/backend/leapbackend.py +++ b/src/leap/bitmask/backend/leapbackend.py @@ -28,6 +28,9 @@ from leap.bitmask.backend.backend import Backend logger = logging.getLogger(__name__) +ERROR_KEY = "error" +PASSED_KEY = "passed" + class LeapBackend(Backend): """ diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 24915d4f..a77f0215 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -26,6 +26,11 @@ from PySide import QtCore, QtGui from leap.bitmask import __version__ as VERSION from leap.bitmask import __version_hash__ as VERSION_HASH + +# TODO: we should use a more granular signaling instead of passing error/ok as +# a result. +from leap.bitmask.backend.leapbackend import ERROR_KEY, PASSED_KEY + from leap.bitmask.config import flags from leap.bitmask.config.leapsettings import LeapSettings @@ -1140,11 +1145,11 @@ class MainWindow(QtGui.QMainWindow): backend.provider_setup() :type data: dict """ - if data[self._backend.PASSED_KEY]: + if data[PASSED_KEY]: selected_provider = self._login_widget.get_selected_provider() self._backend.provider_bootstrap(provider=selected_provider) else: - logger.error(data[self._backend.ERROR_KEY]) + logger.error(data[ERROR_KEY]) self._login_problem_provider() @QtCore.Slot() @@ -1246,7 +1251,7 @@ class MainWindow(QtGui.QMainWindow): Once the provider configuration is loaded, this starts the SRP authentication """ - if data[self._backend.PASSED_KEY]: + if data[PASSED_KEY]: username = self._login_widget.get_user() password = self._login_widget.get_password() @@ -1256,7 +1261,7 @@ class MainWindow(QtGui.QMainWindow): self._backend.user_login(provider=domain, username=username, password=password) else: - logger.error(data[self._backend.ERROR_KEY]) + logger.error(data[ERROR_KEY]) self._login_problem_provider() @QtCore.Slot() @@ -1579,12 +1584,12 @@ class MainWindow(QtGui.QMainWindow): Start the VPN thread if the eip configuration is properly loaded. """ - passed = data[self._backend.PASSED_KEY] + passed = data[PASSED_KEY] if not passed: error_msg = self.tr("There was a problem with the provider") self._eip_status.set_eip_status(error_msg, error=True) - logger.error(data[self._backend.ERROR_KEY]) + logger.error(data[ERROR_KEY]) self._already_started_eip = False return @@ -1602,11 +1607,11 @@ class MainWindow(QtGui.QMainWindow): This is used for intermediate bootstrapping stages, in case they fail. """ - passed = data[self._backend.PASSED_KEY] + passed = data[PASSED_KEY] if not passed: self._login_widget.set_status( self.tr("Unable to connect: Problem with provider")) - logger.error(data[self._backend.ERROR_KEY]) + logger.error(data[ERROR_KEY]) self._already_started_eip = False self._eip_status.aborted() @@ -1671,9 +1676,9 @@ class MainWindow(QtGui.QMainWindow): This is used for intermediate bootstrapping stages, in case they fail. """ - passed = data[self._backend.PASSED_KEY] + passed = data[PASSED_KEY] if not passed: - logger.error(data[self._backend.ERROR_KEY]) + logger.error(data[ERROR_KEY]) self._login_problem_provider() # diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index a20ef9fe..be5bde52 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -24,6 +24,10 @@ from functools import partial from PySide import QtCore, QtGui +# TODO: we should use a more granular signaling instead of passing error/ok as +# a result. +from leap.bitmask.backend.leapbackend import ERROR_KEY, PASSED_KEY + from leap.bitmask.config import flags from leap.bitmask.config.leapsettings import LeapSettings from leap.bitmask.services import get_service_display_name, get_supported @@ -511,8 +515,8 @@ class Wizard(QtGui.QWizard): :param complete_page: page id to complete :type complete_page: int """ - passed = data[self._backend.PASSED_KEY] - error = data[self._backend.ERROR_KEY] + passed = data[PASSED_KEY] + error = data[ERROR_KEY] if passed: label.setPixmap(self.OK_ICON) if complete: @@ -532,7 +536,7 @@ class Wizard(QtGui.QWizard): """ self._complete_task(data, self.ui.lblNameResolution) status = "" - passed = data[self._backend.PASSED_KEY] + passed = data[PASSED_KEY] if not passed: status = self.tr("Non-existent " "provider") @@ -552,10 +556,10 @@ class Wizard(QtGui.QWizard): """ self._complete_task(data, self.ui.lblHTTPS) status = "" - passed = data[self._backend.PASSED_KEY] + passed = data[PASSED_KEY] if not passed: status = self.tr("%s") \ - % (data[self._backend.ERROR_KEY]) + % (data[ERROR_KEY]) self.ui.lblProviderSelectStatus.setText(status) else: self.ui.lblProviderInfo.setPixmap(self.QUESTION_ICON) @@ -572,7 +576,7 @@ class Wizard(QtGui.QWizard): check. Since this check is the last of this set, it also completes the page if passed """ - if data[self._backend.PASSED_KEY]: + if data[PASSED_KEY]: self._complete_task(data, self.ui.lblProviderInfo, True, self.SELECT_PROVIDER_PAGE) self._provider_checks_ok = True @@ -580,14 +584,13 @@ class Wizard(QtGui.QWizard): self._backend.provider_get_details(domain=self._domain, lang=lang) else: new_data = { - self._backend.PASSED_KEY: False, - self._backend.ERROR_KEY: - self.tr("Unable to load provider configuration") + PASSED_KEY: False, + ERROR_KEY: self.tr("Unable to load provider configuration") } self._complete_task(new_data, self.ui.lblProviderInfo) status = "" - if not data[self._backend.PASSED_KEY]: + if not data[PASSED_KEY]: status = self.tr("Not a valid provider" "") self.ui.lblProviderSelectStatus.setText(status) @@ -618,7 +621,7 @@ class Wizard(QtGui.QWizard): Sets the status for the download of the CA certificate check """ self._complete_task(data, self.ui.lblDownloadCaCert) - passed = data[self._backend.PASSED_KEY] + passed = data[PASSED_KEY] if passed: self.ui.lblCheckCaFpr.setPixmap(self.QUESTION_ICON) @@ -631,7 +634,7 @@ class Wizard(QtGui.QWizard): Sets the status for the CA fingerprint check """ self._complete_task(data, self.ui.lblCheckCaFpr) - passed = data[self._backend.PASSED_KEY] + passed = data[PASSED_KEY] if passed: self.ui.lblCheckApiCert.setPixmap(self.QUESTION_ICON) -- cgit v1.2.3 From cf75e3575c33249a6f756dceb423c6ec7f6cd50e Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 2 Jul 2014 12:14:22 -0300 Subject: Move the backend starter to its own file. --- src/leap/bitmask/app.py | 2 +- src/leap/bitmask/backend/leapbackend.py | 9 --------- src/leap/bitmask/backend_app.py | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index eda4073c..43dabad3 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -49,7 +49,7 @@ from leap.bitmask.backend.utils import generate_certificates from leap.bitmask import __version__ as VERSION from leap.bitmask.config import flags from leap.bitmask.frontend_app import run_frontend -from leap.bitmask.backend.leapbackend import run_backend +from leap.bitmask.backend_app import run_backend from leap.bitmask.logs.utils import create_logger from leap.bitmask.platform_init.locks import we_are_the_one_and_only from leap.bitmask.services.mail import plumber diff --git a/src/leap/bitmask/backend/leapbackend.py b/src/leap/bitmask/backend/leapbackend.py index d6b0954a..4794d988 100644 --- a/src/leap/bitmask/backend/leapbackend.py +++ b/src/leap/bitmask/backend/leapbackend.py @@ -18,7 +18,6 @@ Backend for everything """ import logging -import signal import zope.interface import zope.proxy @@ -503,11 +502,3 @@ class LeapBackend(Backend): imap_stopped """ self._mail.stop_imap_service() - - -def run_backend(bypass_checks=False): - # Ensure that the application quits using CTRL-C - signal.signal(signal.SIGINT, signal.SIG_DFL) - - backend = LeapBackend(bypass_checks=bypass_checks) - backend.run() diff --git a/src/leap/bitmask/backend_app.py b/src/leap/bitmask/backend_app.py index e69de29b..bd3b8a1f 100644 --- a/src/leap/bitmask/backend_app.py +++ b/src/leap/bitmask/backend_app.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# backend_app.py +# Copyright (C) 2013, 2014 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 . +import signal + +from leap.bitmask.backend.leapbackend import LeapBackend + + +def run_backend(bypass_checks=False): + # Ensure that the application quits using CTRL-C + signal.signal(signal.SIGINT, signal.SIG_DFL) + + backend = LeapBackend(bypass_checks=bypass_checks) + backend.run() -- cgit v1.2.3 From 13c0b7cac822a33f7395e3f099a2d37251e2c759 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 2 Jul 2014 12:14:59 -0300 Subject: Ask the backend for the country code. Remove global variable in favor of a helper method that returns the country code. Needed in order to split backend/frontend. --- src/leap/bitmask/backend/api.py | 3 ++ src/leap/bitmask/backend/components.py | 42 ++++++++++++++++++++++++++++ src/leap/bitmask/backend/leapbackend.py | 13 +++++++++ src/leap/bitmask/backend/leapsignaler.py | 2 ++ src/leap/bitmask/config/flags.py | 2 -- src/leap/bitmask/gui/eip_status.py | 15 +++++++--- src/leap/bitmask/gui/mainwindow.py | 22 +++++++++++++-- src/leap/bitmask/services/eip/vpnlauncher.py | 6 ---- 8 files changed, 90 insertions(+), 15 deletions(-) diff --git a/src/leap/bitmask/backend/api.py b/src/leap/bitmask/backend/api.py index 012b3cbc..f8b8c699 100644 --- a/src/leap/bitmask/backend/api.py +++ b/src/leap/bitmask/backend/api.py @@ -26,6 +26,7 @@ API = ( "eip_can_start", "eip_cancel_setup", "eip_check_dns", + "eip_get_gateway_country_code", "eip_get_gateways_list", "eip_get_initialized_providers", "eip_setup", @@ -75,10 +76,12 @@ SIGNALS = ( "eip_disconnected", "eip_dns_error", "eip_dns_ok", + "eip_get_gateway_country_code", "eip_get_gateways_list", "eip_get_gateways_list_error", "eip_get_initialized_providers", "eip_network_unreachable", + "eip_no_gateway", "eip_no_pkexec_error", "eip_no_polkit_agent_error", "eip_no_tun_kext_error", diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index 5357bed5..fe7b566a 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -31,6 +31,7 @@ from twisted.python import log import zope.interface import zope.proxy +from leap.bitmask.config.leapsettings import LeapSettings from leap.bitmask.config.providerconfig import ProviderConfig from leap.bitmask.crypto.srpauth import SRPAuth from leap.bitmask.crypto.srpregister import SRPRegister @@ -589,6 +590,47 @@ class EIP(object): self._signaler.signal( self._signaler.eip_get_gateways_list, gateways) + def get_gateway_country_code(self, domain): + """ + Signal the country code for the currently used gateway for the given + provider. + + :param domain: the domain to get country code. + :type domain: str + + Signals: + eip_get_gateway_country_code -> str + eip_no_gateway + """ + leap_settings = LeapSettings() + + eip_config = eipconfig.EIPConfig() + provider_config = ProviderConfig.get_provider_config(domain) + + api_version = provider_config.get_api_version() + eip_config.set_api_version(api_version) + eip_config.load(eipconfig.get_eipconfig_path(domain)) + + gateway_selector = eipconfig.VPNGatewaySelector(eip_config) + gateway_conf = leap_settings.get_selected_gateway(domain) + + if gateway_conf == leap_settings.GATEWAY_AUTOMATIC: + gateways = gateway_selector.get_gateways() + else: + gateways = [gateway_conf] + + if not gateways: + self._signaler.signal(self._signaler.eip_no_gateway) + return + + # this only works for selecting the first gateway, as we're + # currently doing. + ccodes = gateway_selector.get_gateways_country_code() + gateway_ccode = ccodes[gateways[0]] + + self._signaler.signal(self._signaler.eip_get_gateway_country_code, + gateway_ccode) + def _can_start(self, domain): """ Returns True if it has everything that is needed to run EIP, diff --git a/src/leap/bitmask/backend/leapbackend.py b/src/leap/bitmask/backend/leapbackend.py index 4794d988..3bc7a513 100644 --- a/src/leap/bitmask/backend/leapbackend.py +++ b/src/leap/bitmask/backend/leapbackend.py @@ -256,6 +256,19 @@ class LeapBackend(Backend): """ self._eip.get_gateways_list(domain) + def eip_get_gateway_country_code(self, domain): + """ + Signal a list of gateways for the given provider. + + :param domain: the domain to get the gateways. + :type domain: str + + Signals: + eip_get_gateways_list -> str + eip_no_gateway + """ + self._eip.get_gateway_country_code(domain) + def eip_get_initialized_providers(self, domains): """ Signal a list of the given domains and if they are initialized or not. diff --git a/src/leap/bitmask/backend/leapsignaler.py b/src/leap/bitmask/backend/leapsignaler.py index 47df0911..7912fc20 100644 --- a/src/leap/bitmask/backend/leapsignaler.py +++ b/src/leap/bitmask/backend/leapsignaler.py @@ -37,10 +37,12 @@ class LeapSignaler(SignalerQt): eip_disconnected = QtCore.Signal(object) eip_dns_error = QtCore.Signal() eip_dns_ok = QtCore.Signal() + eip_get_gateway_country_code = QtCore.Signal(object) eip_get_gateways_list = QtCore.Signal(object) eip_get_gateways_list_error = QtCore.Signal() eip_get_initialized_providers = QtCore.Signal(object) eip_network_unreachable = QtCore.Signal() + eip_no_gateway = QtCore.Signal() eip_no_pkexec_error = QtCore.Signal() eip_no_polkit_agent_error = QtCore.Signal() eip_no_tun_kext_error = QtCore.Signal() diff --git a/src/leap/bitmask/config/flags.py b/src/leap/bitmask/config/flags.py index 2f3fdde4..6b70659d 100644 --- a/src/leap/bitmask/config/flags.py +++ b/src/leap/bitmask/config/flags.py @@ -55,5 +55,3 @@ OPENVPN_VERBOSITY = 1 # Skip the checks in the wizard, use for testing purposes only! SKIP_WIZARD_CHECKS = False - -CURRENT_VPN_COUNTRY = None diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index 01966d82..df9f3741 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -589,16 +589,23 @@ class EIPStatusWidget(QtGui.QWidget): self._systray.setIcon(QtGui.QIcon(selected_pixmap_tray)) self._eip_status_menu.setTitle(tray_message) - def set_provider(self, provider): + def set_provider(self, provider, country_code): + """ + Set the provider used right now, name and flag (if available). + + :param provider: the provider in use. + :type provider: str + :param country_code: the country code of the gateway in use. + :type country_code: str + """ self._provider = provider self.ui.lblEIPMessage.setText( self.tr("Routing traffic through: {0}").format( provider)) - ccode = flags.CURRENT_VPN_COUNTRY - if ccode is not None: - self.set_country_code(ccode) + if country_code is not None: + self.set_country_code(country_code) def set_country_code(self, code): """ diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index a77f0215..5549c9cb 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -412,6 +412,9 @@ class MainWindow(QtGui.QMainWindow): sig.eip_dns_error.connect(self._eip_dns_error) + sig.eip_get_gateway_country_code.connect(self._set_eip_provider) + sig.eip_no_gateway.connect(self._set_eip_provider) + # ================================================================== # Soledad signals @@ -1481,15 +1484,28 @@ class MainWindow(QtGui.QMainWindow): signal that currently is beeing processed under status_panel. After the refactor to EIPConductor this should not be necessary. """ - domain = self._login_widget.get_selected_provider() + self._already_started_eip = True - self._eip_status.set_provider(domain) + domain = self._login_widget.get_selected_provider() self._settings.set_defaultprovider(domain) - self._already_started_eip = True + + self._backend.eip_get_gateway_country_code(domain=domain) # check for connectivity self._backend.eip_check_dns(domain=domain) + @QtCore.Slot() + def _set_eip_provider(self, country_code=None): + """ + TRIGGERS: + Signaler.eip_get_gateway_country_code + Signaler.eip_no_gateway + + Set the current provider and country code in the eip status widget. + """ + domain = self._login_widget.get_selected_provider() + self._eip_status.set_provider(domain, country_code) + @QtCore.Slot() def _eip_dns_error(self): """ diff --git a/src/leap/bitmask/services/eip/vpnlauncher.py b/src/leap/bitmask/services/eip/vpnlauncher.py index 0731bee3..5e2a4743 100644 --- a/src/leap/bitmask/services/eip/vpnlauncher.py +++ b/src/leap/bitmask/services/eip/vpnlauncher.py @@ -136,12 +136,6 @@ class VPNLauncher(object): logger.error('No gateway was found!') raise VPNLauncherException('No gateway was found!') - # this only works for selecting the first gateway, as we're - # currently doing. - ccodes = gateway_selector.get_gateways_country_code() - gateway_ccode = ccodes[gateways[0]] - flags.CURRENT_VPN_COUNTRY = gateway_ccode - logger.debug("Using gateways ips: {0}".format(', '.join(gateways))) return gateways -- cgit v1.2.3 From 0cab909f9518273d95e371e5fb1061fb9b0a92fd Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 2 Jul 2014 12:52:31 -0300 Subject: Send the flag module values to the processes. Add serialize/deserialize to dict helper. --- src/leap/bitmask/app.py | 8 +++++--- src/leap/bitmask/backend_app.py | 13 ++++++++++++- src/leap/bitmask/frontend_app.py | 7 ++++++- src/leap/bitmask/util/__init__.py | 25 +++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 43dabad3..d1a2a111 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -53,7 +53,7 @@ from leap.bitmask.backend_app import run_backend from leap.bitmask.logs.utils import create_logger from leap.bitmask.platform_init.locks import we_are_the_one_and_only from leap.bitmask.services.mail import plumber -from leap.bitmask.util import leap_argparse +from leap.bitmask.util import leap_argparse, flags_to_dict from leap.bitmask.util.requirement_checker import check_requirements from leap.common.events import server as event_server @@ -178,11 +178,13 @@ def start_app(): generate_certificates() - app = lambda: run_frontend(options=options) + flags_dict = flags_to_dict() + + app = lambda: run_frontend(options, flags_dict) gui_process = multiprocessing.Process(target=app) gui_process.start() - backend = lambda: run_backend(bypass_checks=opts.danger) + backend = lambda: run_backend(opts.danger, flags_dict) backend_process = multiprocessing.Process(target=backend) backend_process.start() diff --git a/src/leap/bitmask/backend_app.py b/src/leap/bitmask/backend_app.py index bd3b8a1f..d4815d82 100644 --- a/src/leap/bitmask/backend_app.py +++ b/src/leap/bitmask/backend_app.py @@ -17,11 +17,22 @@ import signal from leap.bitmask.backend.leapbackend import LeapBackend +from leap.bitmask.util import dict_to_flags -def run_backend(bypass_checks=False): +def run_backend(bypass_checks, flags_dict): + """ + Run the backend for the application. + + :param bypass_checks: whether we should bypass the checks or not + :type bypass_checks: bool + :param flags_dict: a dict containing the flag values set on app start. + :type flags_dict: dict + """ # Ensure that the application quits using CTRL-C signal.signal(signal.SIGINT, signal.SIG_DFL) + dict_to_flags(flags_dict) + backend = LeapBackend(bypass_checks=bypass_checks) backend.run() diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index 60f20e3c..1fe4cd0a 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -23,6 +23,7 @@ from PySide import QtCore, QtGui from leap.bitmask.config import flags from leap.bitmask.gui import locale_rc # noqa - silence pylint from leap.bitmask.gui.mainwindow import MainWindow +from leap.bitmask.util import dict_to_flags import logging logger = logging.getLogger(__name__) @@ -39,13 +40,17 @@ def sigint_handler(*args, **kwargs): mainwindow.quit() -def run_frontend(options): +def run_frontend(options, flags_dict): """ Run the GUI for the application. :param options: a dict of options parsed from the command line. :type options: dict + :param flags_dict: a dict containing the flag values set on app start. + :type flags_dict: dict """ + dict_to_flags(flags_dict) + start_hidden = options["start_hidden"] # We force the style if on KDE so that it doesn't load all the kde diff --git a/src/leap/bitmask/util/__init__.py b/src/leap/bitmask/util/__init__.py index 25b86874..caa94ec7 100644 --- a/src/leap/bitmask/util/__init__.py +++ b/src/leap/bitmask/util/__init__.py @@ -129,3 +129,28 @@ def force_eval(items): return map(do_eval, items) else: return do_eval(items) + + +def dict_to_flags(values): + """ + Set the flags values given in the values dict. + If a value isn't provided then use the already existing one. + + :param values: the values to set. + :type values: dict. + """ + for k, v in values.items(): + setattr(flags, k, v) + + +def flags_to_dict(): + """ + Get the flags values in a dict. + + :return: the values of flags into a dict. + :rtype: dict. + """ + items = [i for i in dir(flags) if i[0] != '_'] + values = {i: getattr(flags, i) for i in items} + + return values -- cgit v1.2.3 From eab69607ba4a65acf5c7745134d74917c76c6bf8 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 2 Jul 2014 16:43:07 -0300 Subject: Remove twisted stopper from the GUI. --- src/leap/bitmask/gui/mainwindow.py | 3 --- src/leap/bitmask/gui/twisted_main.py | 43 ------------------------------------ 2 files changed, 46 deletions(-) delete mode 100644 src/leap/bitmask/gui/twisted_main.py diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 5549c9cb..9be6f15c 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -43,7 +43,6 @@ from leap.bitmask.gui.mail_status import MailStatusWidget from leap.bitmask.gui.preferenceswindow import PreferencesWindow from leap.bitmask.gui.systray import SysTray from leap.bitmask.gui.wizard import Wizard -from leap.bitmask.gui import twisted_main from leap.bitmask.platform_init import IS_WIN, IS_MAC, IS_LINUX from leap.bitmask.platform_init.initializers import init_platform @@ -1837,5 +1836,3 @@ class MainWindow(QtGui.QMainWindow): WindowsLock.release_all_locks() self.close() - - QtDelayedCall(100, twisted_main.quit) diff --git a/src/leap/bitmask/gui/twisted_main.py b/src/leap/bitmask/gui/twisted_main.py deleted file mode 100644 index b1ce0ead..00000000 --- a/src/leap/bitmask/gui/twisted_main.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -# twisted_main.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 . -""" -Main functions for integration of twisted reactor -""" -import logging - -from twisted.internet import error, reactor -from PySide import QtCore - -logger = logging.getLogger(__name__) - - -def stop(): - QtCore.QCoreApplication.sendPostedEvents() - QtCore.QCoreApplication.flush() - try: - reactor.stop() - logger.debug('Twisted reactor stopped') - except error.ReactorNotRunning: - logger.debug('Twisted reactor not running') - logger.debug("Done stopping all the things.") - - -def quit(): - """ - Stop the mainloop. - """ - reactor.callLater(0, stop) -- cgit v1.2.3 From c3f485e194eb32939755178b11d472e1e69a94ad Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 2 Jul 2014 16:43:58 -0300 Subject: Handle SIGINT/SIGTERM in processes. --- src/leap/bitmask/app.py | 36 +++++++++++++++++++++++++++++---- src/leap/bitmask/backend_app.py | 26 ++++++++++++++++++++++-- src/leap/bitmask/frontend_app.py | 43 ++++++++++++++++++++++++++++------------ 3 files changed, 86 insertions(+), 19 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index d1a2a111..fa244470 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -44,6 +44,8 @@ import os import signal import sys +from functools import partial + from leap.bitmask.backend.utils import generate_certificates from leap.bitmask import __version__ as VERSION @@ -109,12 +111,33 @@ def do_mail_plumbing(opts): # XXX catch when import is used w/o acct +def sigterm_handler(logger, gui_process, backend_process, signum, frame): + """ + Signal handler that quits the running app cleanly. + + :param logger: the configured logger object. + :type logger: logging.Logger + :param gui_process: the GUI process + :type gui_process: multiprocessing.Process + :param backend_process: the backend process + :type backend_process: multiprocessing.Process + :param signum: number of the signal received (e.g. SIGINT -> 2) + :type signum: int + :param frame: current stack frame + :type frame: frame or None + """ + logger.debug("SIGTERM catched, terminating processes.") + gui_process.terminate() + # Don't terminate the backend, the frontend takes care of that. + # backend_process.terminate() + + def start_app(): """ Starts the main event loop and launches the main window. """ - # Ensure that the application quits using CTRL-C - signal.signal(signal.SIGINT, signal.SIG_DFL) + # Ignore the signals since we handle them in the subprocesses + # signal.signal(signal.SIGINT, signal.SIG_IGN) # Parse arguments and store them opts = leap_argparse.get_options() @@ -181,13 +204,18 @@ def start_app(): flags_dict = flags_to_dict() app = lambda: run_frontend(options, flags_dict) - gui_process = multiprocessing.Process(target=app) + gui_process = multiprocessing.Process(target=app, name='Frontend') gui_process.start() backend = lambda: run_backend(opts.danger, flags_dict) - backend_process = multiprocessing.Process(target=backend) + backend_process = multiprocessing.Process(target=backend, name='Backend') backend_process.start() + handle_sigterm = partial(sigterm_handler, logger, + gui_process, backend_process) + signal.signal(signal.SIGTERM, handle_sigterm) + signal.signal(signal.SIGINT, handle_sigterm) + if __name__ == "__main__": start_app() diff --git a/src/leap/bitmask/backend_app.py b/src/leap/bitmask/backend_app.py index d4815d82..b6d00f2d 100644 --- a/src/leap/bitmask/backend_app.py +++ b/src/leap/bitmask/backend_app.py @@ -14,11 +14,32 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import logging +import multiprocessing import signal from leap.bitmask.backend.leapbackend import LeapBackend from leap.bitmask.util import dict_to_flags +logger = logging.getLogger(__name__) + + +def signal_handler(signum, frame): + """ + Signal handler that quits the running app cleanly. + + :param signum: number of the signal received (e.g. SIGINT -> 2) + :type signum: int + :param frame: current stack frame + :type frame: frame or None + """ + # Note: we don't stop the backend in here since the frontend signal handler + # will take care of that. + # In the future we may need to do the stop in here when the frontend and + # the backend are run separately (without multiprocessing) + pname = multiprocessing.current_process().name + logger.debug("{0}: SIGNAL #{1} catched.".format(pname, signum)) + def run_backend(bypass_checks, flags_dict): """ @@ -29,8 +50,9 @@ def run_backend(bypass_checks, flags_dict): :param flags_dict: a dict containing the flag values set on app start. :type flags_dict: dict """ - # Ensure that the application quits using CTRL-C - signal.signal(signal.SIGINT, signal.SIG_DFL) + # ignore SIGINT since app.py takes care of signaling SIGTERM to us. + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal_handler) dict_to_flags(flags_dict) diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index 1fe4cd0a..5dc42287 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -14,10 +14,13 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import multiprocessing import signal import sys import os +from functools import partial + from PySide import QtCore, QtGui from leap.bitmask.config import flags @@ -29,15 +32,20 @@ import logging logger = logging.getLogger(__name__) -def sigint_handler(*args, **kwargs): +def signal_handler(window, signum, frame): """ - Signal handler for SIGINT + Signal handler that quits the running app cleanly. + + :param window: a window with a `quit` callable + :type window: MainWindow + :param signum: number of the signal received (e.g. SIGINT -> 2) + :type signum: int + :param frame: current stack frame + :type frame: frame or None """ - logger = kwargs.get('logger', None) - if logger: - logger.debug("SIGINT catched. shutting down...") - mainwindow = args[0] - mainwindow.quit() + pname = multiprocessing.current_process().name + logger.debug("{0}: SIGNAL #{1} catched.".format(pname, signum)) + window.quit() def run_frontend(options, flags_dict): @@ -79,12 +87,21 @@ def run_frontend(options, flags_dict): qApp.setApplicationName("leap") qApp.setOrganizationDomain("leap.se") - MainWindow(start_hidden=start_hidden) - - # sigint_window = partial(sigint_handler, window, logger=logger) - # signal.signal(signal.SIGINT, sigint_window) - # Ensure that the application quits using CTRL-C - signal.signal(signal.SIGINT, signal.SIG_DFL) + # HACK: + # We need to do some 'python work' once in a while, otherwise, no python + # code will be called and the Qt event loop will prevent the signal + # handlers for SIGINT/SIGTERM to be called. + # see: http://stackoverflow.com/a/4939113/687989 + timer = QtCore.QTimer() + timer.start(500) # You may change this if you wish. + timer.timeout.connect(lambda: None) # Let the interpreter run each 500 ms. + + window = MainWindow(start_hidden=start_hidden) + + sigterm_handler = partial(signal_handler, window) + # ignore SIGINT since app.py takes care of signaling SIGTERM to us. + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, sigterm_handler) sys.exit(qApp.exec_()) -- cgit v1.2.3 From d33e9a2bc07ccd983a1321b254cc27ca6be989a3 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 8 Jul 2014 17:01:18 -0300 Subject: Add file docstrings. --- src/leap/bitmask/backend/backend_proxy.py | 4 ++++ src/leap/bitmask/backend/leapsignaler.py | 5 ++++- src/leap/bitmask/backend/signaler.py | 4 ++++ src/leap/bitmask/backend/signaler_qt.py | 4 ++++ src/leap/bitmask/backend/utils.py | 3 +++ src/leap/bitmask/backend_app.py | 3 +++ src/leap/bitmask/frontend_app.py | 3 +++ 7 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/leap/bitmask/backend/backend_proxy.py b/src/leap/bitmask/backend/backend_proxy.py index 3c8e5d0f..39ed322e 100644 --- a/src/leap/bitmask/backend/backend_proxy.py +++ b/src/leap/bitmask/backend/backend_proxy.py @@ -14,6 +14,10 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +""" +The BackendProxy handles calls from the GUI and forwards (through ZMQ) +to the backend. +""" import functools import Queue import threading diff --git a/src/leap/bitmask/backend/leapsignaler.py b/src/leap/bitmask/backend/leapsignaler.py index 7912fc20..a36e6fdc 100644 --- a/src/leap/bitmask/backend/leapsignaler.py +++ b/src/leap/bitmask/backend/leapsignaler.py @@ -14,6 +14,9 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +""" +Signaling server, used to define the API signals. +""" from PySide import QtCore from leap.bitmask.backend.signaler_qt import SignalerQt @@ -21,7 +24,7 @@ from leap.bitmask.backend.signaler_qt import SignalerQt class LeapSignaler(SignalerQt): """ - Signaling server subclass, used to defines the API signals. + Signaling server subclass, used to define the API signals. """ backend_bad_call = QtCore.Signal(object) diff --git a/src/leap/bitmask/backend/signaler.py b/src/leap/bitmask/backend/signaler.py index f8753771..6a0ec88a 100644 --- a/src/leap/bitmask/backend/signaler.py +++ b/src/leap/bitmask/backend/signaler.py @@ -14,6 +14,10 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +""" +Signaler client. +Receives signals from the backend and sends to the signaling server. +""" import Queue import threading import time diff --git a/src/leap/bitmask/backend/signaler_qt.py b/src/leap/bitmask/backend/signaler_qt.py index dabd6662..ce9c24f5 100644 --- a/src/leap/bitmask/backend/signaler_qt.py +++ b/src/leap/bitmask/backend/signaler_qt.py @@ -14,6 +14,10 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +""" +Signaling server. +Receives signals from the signaling client and emit Qt signals for the GUI. +""" import threading import time diff --git a/src/leap/bitmask/backend/utils.py b/src/leap/bitmask/backend/utils.py index 5d600689..b71fab9e 100644 --- a/src/leap/bitmask/backend/utils.py +++ b/src/leap/bitmask/backend/utils.py @@ -14,6 +14,9 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +""" +Backend utilities to handle ZMQ certificates. +""" import os import shutil diff --git a/src/leap/bitmask/backend_app.py b/src/leap/bitmask/backend_app.py index b6d00f2d..5c0e4803 100644 --- a/src/leap/bitmask/backend_app.py +++ b/src/leap/bitmask/backend_app.py @@ -14,6 +14,9 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +""" +Start point for the Backend. +""" import logging import multiprocessing import signal diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index 5dc42287..95d36538 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -14,6 +14,9 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +""" +Start point for the Frontend. +""" import multiprocessing import signal import sys -- cgit v1.2.3 From d8152277022eedf5a88c84cc5f099b2fc9ad6e46 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 9 Jul 2014 16:14:58 -0300 Subject: Comment out overly verbose logs for communication. --- src/leap/bitmask/backend/backend.py | 6 +++--- src/leap/bitmask/backend/backend_proxy.py | 10 ++++++---- src/leap/bitmask/backend/signaler.py | 10 ++++++---- src/leap/bitmask/backend/signaler_qt.py | 4 ++-- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/leap/bitmask/backend/backend.py b/src/leap/bitmask/backend/backend.py index 5f696f75..833f4368 100644 --- a/src/leap/bitmask/backend/backend.py +++ b/src/leap/bitmask/backend/backend.py @@ -86,7 +86,7 @@ class Backend(object): try: request = self._zmq_socket.recv(zmq.NOBLOCK) self._zmq_socket.send("OK") - logger.debug("Received request: '{0}'".format(request)) + # logger.debug("Received request: '{0}'".format(request)) self._process_request(request) except zmq.ZMQError as e: if e.errno != zmq.EAGAIN: @@ -180,8 +180,8 @@ class Backend(object): if kwargs is not None: method = lambda: func(**kwargs) - logger.debug("Running method: '{0}' " - "with args: '{1}' in a thread".format(api_method, kwargs)) + # logger.debug("Running method: '{0}' " + # "with args: '{1}' in a thread".format(api_method, kwargs)) # run the action in a thread and keep track of it d = threads.deferToThread(method) diff --git a/src/leap/bitmask/backend/backend_proxy.py b/src/leap/bitmask/backend/backend_proxy.py index 39ed322e..f683e465 100644 --- a/src/leap/bitmask/backend/backend_proxy.py +++ b/src/leap/bitmask/backend/backend_proxy.py @@ -131,14 +131,16 @@ class BackendProxy(object): :param request: the request to send. :type request: str """ - logger.debug("Sending request to backend: {0}".format(request)) + # logger.debug("Sending request to backend: {0}".format(request)) self._socket.send(request) try: # Get the reply. - response = self._socket.recv() - msg = "Received reply for '{0}' -> '{1}'".format(request, response) - logger.debug(msg) + self._socket.recv() + # response = self._socket.recv() + # msg = "Received reply for '{0}' -> '{1}'" + # msg = msg.format(request, response) + # logger.debug(msg) except zmq.error.Again as e: msg = "Timeout error contacting backend. {0!r}".format(e) logger.critical(msg) diff --git a/src/leap/bitmask/backend/signaler.py b/src/leap/bitmask/backend/signaler.py index 6a0ec88a..f21f0998 100644 --- a/src/leap/bitmask/backend/signaler.py +++ b/src/leap/bitmask/backend/signaler.py @@ -145,14 +145,16 @@ class Signaler(object): :param request: the request to send. :type request: str """ - logger.debug("Signaling '{0}'".format(request)) + # logger.debug("Signaling '{0}'".format(request)) self._socket.send(request) # Get the reply. try: - response = self._socket.recv() - msg = "Received reply for '{0}' -> '{1}'".format(request, response) - logger.debug(msg) + self._socket.recv() + # response = self._socket.recv() + # msg = "Received reply for '{0}' -> '{1}'" + # msg = msg.format(request, response) + # logger.debug(msg) except zmq.error.Again as e: msg = "Timeout error contacting signaler. {0!r}".format(e) logger.critical(msg) diff --git a/src/leap/bitmask/backend/signaler_qt.py b/src/leap/bitmask/backend/signaler_qt.py index ce9c24f5..4ec27a1e 100644 --- a/src/leap/bitmask/backend/signaler_qt.py +++ b/src/leap/bitmask/backend/signaler_qt.py @@ -72,7 +72,7 @@ class SignalerQt(QtCore.QThread): # Wait for next request from client try: request = socket.recv(zmq.NOBLOCK) - logger.debug("Received request: '{0}'".format(request)) + # logger.debug("Received request: '{0}'".format(request)) socket.send("OK") self._process_request(request) except zmq.ZMQError as e: @@ -116,7 +116,7 @@ class SignalerQt(QtCore.QThread): logger.warning("Signal not implemented, '{0}'".format(signal)) return - logger.debug("Emitting '{0}'".format(signal)) + # logger.debug("Emitting '{0}'".format(signal)) if data is None: qt_signal.emit() else: -- cgit v1.2.3 From 8186e43e1be9e70ab37c1dd923ac8f275c82b556 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 9 Jul 2014 16:33:50 -0300 Subject: Add PyZMQ dependency. --- pkg/requirements.pip | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/requirements.pip b/pkg/requirements.pip index 3d6b33a3..bf05aa28 100644 --- a/pkg/requirements.pip +++ b/pkg/requirements.pip @@ -19,6 +19,8 @@ python-daemon # this should not be needed for Windows. keyring zope.proxy +pyzmq + leap.common>=0.3.7 leap.soledad.client>=0.5.0 leap.keymanager>=0.3.8 -- cgit v1.2.3 From 525433088d6fbe3392af90942272dfd5dd2511d6 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 11 Jul 2014 12:28:20 -0300 Subject: Use main process to run frontend. Running the GUI in a child process gives problems on OSX. Also, change signal handling since we have less processes. --- src/leap/bitmask/app.py | 34 +++------------------------------- src/leap/bitmask/frontend_app.py | 7 +++---- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index fa244470..e80b9dd1 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -41,10 +41,8 @@ # (thanks to: http://www.glassgiant.com/ascii/) import multiprocessing import os -import signal import sys -from functools import partial from leap.bitmask.backend.utils import generate_certificates @@ -111,27 +109,6 @@ def do_mail_plumbing(opts): # XXX catch when import is used w/o acct -def sigterm_handler(logger, gui_process, backend_process, signum, frame): - """ - Signal handler that quits the running app cleanly. - - :param logger: the configured logger object. - :type logger: logging.Logger - :param gui_process: the GUI process - :type gui_process: multiprocessing.Process - :param backend_process: the backend process - :type backend_process: multiprocessing.Process - :param signum: number of the signal received (e.g. SIGINT -> 2) - :type signum: int - :param frame: current stack frame - :type frame: frame or None - """ - logger.debug("SIGTERM catched, terminating processes.") - gui_process.terminate() - # Don't terminate the backend, the frontend takes care of that. - # backend_process.terminate() - - def start_app(): """ Starts the main event loop and launches the main window. @@ -203,18 +180,13 @@ def start_app(): flags_dict = flags_to_dict() - app = lambda: run_frontend(options, flags_dict) - gui_process = multiprocessing.Process(target=app, name='Frontend') - gui_process.start() - backend = lambda: run_backend(opts.danger, flags_dict) backend_process = multiprocessing.Process(target=backend, name='Backend') + backend_process.daemon = True backend_process.start() - handle_sigterm = partial(sigterm_handler, logger, - gui_process, backend_process) - signal.signal(signal.SIGTERM, handle_sigterm) - signal.signal(signal.SIGINT, handle_sigterm) + run_frontend(options, flags_dict) + if __name__ == "__main__": diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index 95d36538..ffcb61ad 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -101,10 +101,9 @@ def run_frontend(options, flags_dict): window = MainWindow(start_hidden=start_hidden) - sigterm_handler = partial(signal_handler, window) - # ignore SIGINT since app.py takes care of signaling SIGTERM to us. - signal.signal(signal.SIGINT, signal.SIG_IGN) - signal.signal(signal.SIGTERM, sigterm_handler) + sig_handler = partial(signal_handler, window) + signal.signal(signal.SIGINT, sig_handler) + signal.signal(signal.SIGTERM, sig_handler) sys.exit(qApp.exec_()) -- cgit v1.2.3 From 0aee7d6cbc3f2c0b764056966eeddc4057eafd08 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 11 Jul 2014 17:24:20 -0300 Subject: Use specific settings for backend. This way we get rid of Qt on the backend side. The use of QSettings in the backend was breaking the app on OSX. --- src/leap/bitmask/backend/api.py | 1 + src/leap/bitmask/backend/components.py | 8 +- src/leap/bitmask/backend/leapbackend.py | 14 +++ src/leap/bitmask/backend/settings.py | 158 ++++++++++++++++++++++++++ src/leap/bitmask/crypto/srpauth.py | 4 +- src/leap/bitmask/gui/eip_preferenceswindow.py | 2 + src/leap/bitmask/gui/mainwindow.py | 2 + src/leap/bitmask/services/eip/vpnlauncher.py | 8 +- src/leap/bitmask/services/mail/plumber.py | 4 +- 9 files changed, 189 insertions(+), 12 deletions(-) create mode 100644 src/leap/bitmask/backend/settings.py diff --git a/src/leap/bitmask/backend/api.py b/src/leap/bitmask/backend/api.py index f8b8c699..b8533f36 100644 --- a/src/leap/bitmask/backend/api.py +++ b/src/leap/bitmask/backend/api.py @@ -45,6 +45,7 @@ API = ( "provider_get_pinned_providers", "provider_get_supported_services", "provider_setup", + "settings_set_selected_gateway", "smtp_start_service", "smtp_stop_service", "soledad_bootstrap", diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index fe7b566a..8b471b14 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -31,7 +31,7 @@ from twisted.python import log import zope.interface import zope.proxy -from leap.bitmask.config.leapsettings import LeapSettings +from leap.bitmask.backend.settings import Settings, GATEWAY_AUTOMATIC from leap.bitmask.config.providerconfig import ProviderConfig from leap.bitmask.crypto.srpauth import SRPAuth from leap.bitmask.crypto.srpregister import SRPRegister @@ -602,7 +602,7 @@ class EIP(object): eip_get_gateway_country_code -> str eip_no_gateway """ - leap_settings = LeapSettings() + settings = Settings() eip_config = eipconfig.EIPConfig() provider_config = ProviderConfig.get_provider_config(domain) @@ -612,9 +612,9 @@ class EIP(object): eip_config.load(eipconfig.get_eipconfig_path(domain)) gateway_selector = eipconfig.VPNGatewaySelector(eip_config) - gateway_conf = leap_settings.get_selected_gateway(domain) + gateway_conf = settings.get_selected_gateway(domain) - if gateway_conf == leap_settings.GATEWAY_AUTOMATIC: + if gateway_conf == GATEWAY_AUTOMATIC: gateways = gateway_selector.get_gateways() else: gateways = [gateway_conf] diff --git a/src/leap/bitmask/backend/leapbackend.py b/src/leap/bitmask/backend/leapbackend.py index 3bc7a513..d3c4fcda 100644 --- a/src/leap/bitmask/backend/leapbackend.py +++ b/src/leap/bitmask/backend/leapbackend.py @@ -24,6 +24,7 @@ import zope.proxy from leap.bitmask.backend import components from leap.bitmask.backend.backend import Backend +from leap.bitmask.backend.settings import Settings logger = logging.getLogger(__name__) @@ -41,6 +42,8 @@ class LeapBackend(Backend): """ Backend.__init__(self) + self._settings = Settings() + # Objects needed by several components, so we make a proxy and pass # them around self._soledad_proxy = zope.proxy.ProxyBase(None) @@ -515,3 +518,14 @@ class LeapBackend(Backend): imap_stopped """ self._mail.stop_imap_service() + + def settings_set_selected_gateway(self, provider, gateway): + """ + Set the selected gateway for a given provider. + + :param provider: provider domain + :type provider: str + :param gateway: gateway to use as default + :type gateway: str + """ + self._settings.set_selected_gateway(provider, gateway) diff --git a/src/leap/bitmask/backend/settings.py b/src/leap/bitmask/backend/settings.py new file mode 100644 index 00000000..5cb4c616 --- /dev/null +++ b/src/leap/bitmask/backend/settings.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +# settings.py +# Copyright (C) 2013, 2014 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 . +""" +Backend settings +""" +import ConfigParser +import logging +import os + +from leap.bitmask.util import get_path_prefix +from leap.common.check import leap_assert, leap_assert_type + +logger = logging.getLogger(__name__) + +# We need this one available for the default decorator +GATEWAY_AUTOMATIC = "Automatic" +GENERAL_SECTION = "General" + + +class Settings(object): + """ + Leap backend settings hanler. + """ + CONFIG_NAME = "leap-backend.conf" + + # keys + GATEWAY_KEY = "Gateway" + + def __init__(self): + """ + Create the ConfigParser object and read it. + """ + self._settings_path = os.path.join(get_path_prefix(), + "leap", self.CONFIG_NAME) + + self._settings = ConfigParser.ConfigParser() + self._settings.read(self._settings_path) + + self._add_section(GENERAL_SECTION) + + def _add_section(self, section): + """ + Add `section` to the config file and don't fail if already exists. + + :param section: the section to add. + :type section: str + """ + self._settings.read(self._settings_path) + try: + self._settings.add_section(section) + except ConfigParser.DuplicateSectionError: + pass + + def _save(self): + """ + Save the current state to the config file. + """ + with open(self._settings_path, 'wb') as f: + self._settings.write(f) + + def _get_value(self, section, key, default): + """ + Return the value for the fiven `key` in `section`. + If there's no such section/key, `default` is returned. + + :param section: the section to get the value from. + :type section: str + :param key: the key which value we want to get. + :type key: str + :param default: the value to return if there is no section/key. + :type default: object + + :rtype: object + """ + try: + return self._settings.get(section, key) + except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + return default + + def get_selected_gateway(self, provider): + """ + Return the configured gateway for the given `provider`. + + :param provider: provider domain + :type provider: str + + :rtype: str + """ + leap_assert(len(provider) > 0, "We need a nonempty provider") + return self._get_value(provider, self.GATEWAY_KEY, GATEWAY_AUTOMATIC) + + def set_selected_gateway(self, provider, gateway): + """ + Saves the configured gateway for the given provider + + :param provider: provider domain + :type provider: str + + :param gateway: gateway to use as default + :type gateway: str + """ + + leap_assert(len(provider) > 0, "We need a nonempty provider") + leap_assert_type(gateway, (str, unicode)) + + self._add_section(provider) + + self._settings.set(provider, self.GATEWAY_KEY, gateway) + self._save() + + def get_uuid(self, username): + """ + Gets the uuid for a given username. + + :param username: the full user identifier in the form user@provider + :type username: basestring + """ + leap_assert("@" in username, + "Expected username in the form user@provider") + user, provider = username.split('@') + + return self._get_value(provider, username, "") + + def set_uuid(self, username, value): + """ + Sets the uuid for a given username. + + :param username: the full user identifier in the form user@provider + :type username: str or unicode + :param value: the uuid to save or None to remove it + :type value: str or unicode or None + """ + leap_assert("@" in username, + "Expected username in the form user@provider") + user, provider = username.split('@') + + if value is None: + self._settings.remove_option(provider, username) + else: + leap_assert(len(value) > 0, "We cannot save an empty uuid") + self._add_section(provider) + self._settings.set(provider, username, value) + + self._save() diff --git a/src/leap/bitmask/crypto/srpauth.py b/src/leap/bitmask/crypto/srpauth.py index 856d2c81..67c686b0 100644 --- a/src/leap/bitmask/crypto/srpauth.py +++ b/src/leap/bitmask/crypto/srpauth.py @@ -32,7 +32,7 @@ from requests.adapters import HTTPAdapter from twisted.internet import threads from twisted.internet.defer import CancelledError -from leap.bitmask.config.leapsettings import LeapSettings +from leap.bitmask.backend.settings import Settings from leap.bitmask.util import request_helpers as reqhelper from leap.bitmask.util.compat import requests_has_max_retries from leap.bitmask.util.constants import REQUEST_TIMEOUT @@ -151,7 +151,7 @@ class SRPAuth(object): self._provider_config = provider_config self._signaler = signaler - self._settings = LeapSettings() + self._settings = Settings() # **************************************************** # # Dependency injection helpers, override this for more diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py index 306fdb8c..0f63972f 100644 --- a/src/leap/bitmask/gui/eip_preferenceswindow.py +++ b/src/leap/bitmask/gui/eip_preferenceswindow.py @@ -149,6 +149,8 @@ class EIPPreferencesWindow(QtGui.QDialog): gateway = self.ui.cbGateways.itemData(idx) self._settings.set_selected_gateway(provider, gateway) + self._backend.settings_set_selected_gateway(provider=provider, + gateway=gateway) msg = self.tr( "Gateway settings for provider '{0}' saved.").format(provider) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 9be6f15c..27c7f717 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -129,6 +129,8 @@ class MainWindow(QtGui.QMainWindow): self._leap_signaler.start() self._settings = LeapSettings() + # gateway = self._settings.get_selected_gateway(provider) + # self._backend.settings_set_selected_gateway(provider, gateway) # Login Widget self._login_widget = LoginWidget(self._settings, self) diff --git a/src/leap/bitmask/services/eip/vpnlauncher.py b/src/leap/bitmask/services/eip/vpnlauncher.py index 5e2a4743..e36fd76b 100644 --- a/src/leap/bitmask/services/eip/vpnlauncher.py +++ b/src/leap/bitmask/services/eip/vpnlauncher.py @@ -27,7 +27,7 @@ from abc import ABCMeta, abstractmethod from functools import partial from leap.bitmask.config import flags -from leap.bitmask.config.leapsettings import LeapSettings +from leap.bitmask.backend.settings import Settings, GATEWAY_AUTOMATIC from leap.bitmask.config.providerconfig import ProviderConfig from leap.bitmask.platform_init import IS_LINUX from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector @@ -122,12 +122,12 @@ class VPNLauncher(object): :rtype: list """ gateways = [] - leap_settings = LeapSettings() + settings = Settings() domain = providerconfig.get_domain() - gateway_conf = leap_settings.get_selected_gateway(domain) + gateway_conf = settings.get_selected_gateway(domain) gateway_selector = VPNGatewaySelector(eipconfig) - if gateway_conf == leap_settings.GATEWAY_AUTOMATIC: + if gateway_conf == GATEWAY_AUTOMATIC: gateways = gateway_selector.get_gateways() else: gateways = [gateway_conf] diff --git a/src/leap/bitmask/services/mail/plumber.py b/src/leap/bitmask/services/mail/plumber.py index 1ef0543e..fa33afcd 100644 --- a/src/leap/bitmask/services/mail/plumber.py +++ b/src/leap/bitmask/services/mail/plumber.py @@ -26,7 +26,7 @@ from functools import partial from twisted.internet import defer -from leap.bitmask.config.leapsettings import LeapSettings +from leap.bitmask.backend.settings import Settings from leap.bitmask.config.providerconfig import ProviderConfig from leap.bitmask.provider import get_provider_path from leap.bitmask.services.soledad.soledadbootstrapper import get_db_paths @@ -114,7 +114,7 @@ class MBOXPlumber(object): self.user = user self.mdir = mdir self.sol = None - self._settings = LeapSettings() + self._settings = Settings() provider_config_path = os.path.join(get_path_prefix(), get_provider_path(provider)) -- cgit v1.2.3 From cd8f6757b51a8eedc3e29c22656ade1ae22110fe Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 14 Jul 2014 12:08:37 -0300 Subject: Apply changes removed rebasing pullreq #679. https://github.com/leapcode/bitmask_client/pull/679/files --- src/leap/bitmask/frontend_app.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index ffcb61ad..51607d0b 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -27,7 +27,6 @@ from functools import partial from PySide import QtCore, QtGui from leap.bitmask.config import flags -from leap.bitmask.gui import locale_rc # noqa - silence pylint from leap.bitmask.gui.mainwindow import MainWindow from leap.bitmask.util import dict_to_flags @@ -35,20 +34,24 @@ import logging logger = logging.getLogger(__name__) -def signal_handler(window, signum, frame): +def signal_handler(window, pid, signum, frame): """ Signal handler that quits the running app cleanly. :param window: a window with a `quit` callable :type window: MainWindow + :param pid: process id of the main process. + :type pid: int :param signum: number of the signal received (e.g. SIGINT -> 2) :type signum: int :param frame: current stack frame :type frame: frame or None """ - pname = multiprocessing.current_process().name - logger.debug("{0}: SIGNAL #{1} catched.".format(pname, signum)) - window.quit() + my_pid = os.getpid() + if pid == my_pid: + pname = multiprocessing.current_process().name + logger.debug("{0}: SIGNAL #{1} catched.".format(pname, signum)) + window.quit() def run_frontend(options, flags_dict): @@ -101,7 +104,8 @@ def run_frontend(options, flags_dict): window = MainWindow(start_hidden=start_hidden) - sig_handler = partial(signal_handler, window) + my_pid = os.getpid() + sig_handler = partial(signal_handler, window, my_pid) signal.signal(signal.SIGINT, sig_handler) signal.signal(signal.SIGTERM, sig_handler) -- cgit v1.2.3 From d6cee5b46587367b558863292f71f5baafadc762 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 14 Jul 2014 12:23:13 -0300 Subject: pep8 fixes --- src/leap/bitmask/app.py | 1 - src/leap/bitmask/gui/mainwindow.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index e80b9dd1..88f6bc15 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -188,6 +188,5 @@ def start_app(): run_frontend(options, flags_dict) - if __name__ == "__main__": start_app() diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 27c7f717..3d489bd8 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -256,7 +256,7 @@ class MainWindow(QtGui.QMainWindow): # XXX should connect to mail_conductor.start_mail_service instead self.soledad_ready.connect(self._start_smtp_bootstrapping) self.soledad_ready.connect(self._start_imap_service) - ################################# end Qt Signals connection ######## + # ################################ end Qt Signals connection ######## init_platform() @@ -1295,7 +1295,7 @@ class MainWindow(QtGui.QMainWindow): sig.soledad_bootstrap_failed.connect(lambda: btn_enabled(True)) sig.soledad_bootstrap_finished.connect(lambda: btn_enabled(True)) - if not MX_SERVICE in self._provider_details['services']: + if MX_SERVICE not in self._provider_details['services']: self._set_mx_visible(False) def _start_eip_bootstrap(self): -- cgit v1.2.3 From fad58792a499f5b3e7ef28c68d0bae7f055c603d Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 14 Jul 2014 14:32:28 -0300 Subject: Use custom `mkdir` to create the tree if needed. --- src/leap/bitmask/backend/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/leap/bitmask/backend/utils.py b/src/leap/bitmask/backend/utils.py index b71fab9e..54a16fd7 100644 --- a/src/leap/bitmask/backend/utils.py +++ b/src/leap/bitmask/backend/utils.py @@ -23,6 +23,7 @@ import shutil import zmq.auth from leap.bitmask.util import get_path_prefix +from leap.common.files import mkdir_p KEYS_DIR = os.path.join(get_path_prefix(), 'leap', 'zmq_certificates') @@ -34,7 +35,7 @@ def generate_certificates(): # Create directory for certificates, remove old content if necessary if os.path.exists(KEYS_DIR): shutil.rmtree(KEYS_DIR) - os.mkdir(KEYS_DIR) + mkdir_p(KEYS_DIR) # create new keys in certificates dir # public_file, secret_file = create_certificates(...) -- cgit v1.2.3 From 662ae7107bde734cda8d4bc08f5b647650139b61 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 14 Jul 2014 15:20:05 -0300 Subject: Prevent quit() being called more than once. --- src/leap/bitmask/gui/mainwindow.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 3d489bd8..6959650b 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -187,6 +187,7 @@ class MainWindow(QtGui.QMainWindow): self._services_being_stopped = {} # used to know if we are in the final steps of quitting + self._quitting = False self._finally_quitting = False self._backend_connected_signals = [] @@ -1762,8 +1763,10 @@ class MainWindow(QtGui.QMainWindow): Start the quit sequence and wait for services to finish. Cleanup and close the main window before quitting. """ - # TODO separate the shutting down of services from the - # UI stuff. + if self._quitting: + return + + self._quitting = True # first thing to do quitting, hide the mainwindow and show tooltip. self.hide() -- cgit v1.2.3 From 57ac3750970777bb5b6e372e5eb00f3144098d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Mon, 14 Jul 2014 16:24:59 -0300 Subject: Support EIP in OSX --- src/leap/bitmask/backend/components.py | 3 ++- src/leap/bitmask/services/eip/darwinvpnlauncher.py | 4 +++- src/leap/bitmask/services/eip/vpnprocess.py | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index 8b471b14..8fa8b285 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -649,7 +649,8 @@ class EIP(object): launcher = get_vpn_launcher() ovpn_path = force_eval(launcher.OPENVPN_BIN_PATH) if not os.path.isfile(ovpn_path): - logger.error("Cannot start OpenVPN, binary not found") + logger.error("Cannot start OpenVPN, binary not found: %s" % + (ovpn_path,)) return False # check for other problems diff --git a/src/leap/bitmask/services/eip/darwinvpnlauncher.py b/src/leap/bitmask/services/eip/darwinvpnlauncher.py index 41d75052..f83e0170 100644 --- a/src/leap/bitmask/services/eip/darwinvpnlauncher.py +++ b/src/leap/bitmask/services/eip/darwinvpnlauncher.py @@ -46,7 +46,9 @@ class DarwinVPNLauncher(VPNLauncher): INSTALL_MSG = ("\"Bitmask needs administrative privileges to install " "missing scripts and fix permissions.\"") - INSTALL_PATH = os.path.realpath(os.getcwd() + "/../../") + # Hardcode the installation path for OSX for security, openvpn is + # run as root + INSTALL_PATH = "/Applications/Bitmask.app/" INSTALL_PATH_ESCAPED = os.path.realpath(os.getcwd() + "/../../") OPENVPN_BIN = 'openvpn.leap' OPENVPN_PATH = "%s/Contents/Resources/openvpn" % (INSTALL_PATH,) diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index 3bda3059..1c11a337 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -255,6 +255,9 @@ class VPN(object): """ Tear the firewall down using the privileged wrapper. """ + if IS_MAC: + # We don't support Mac so far + return True BM_ROOT = force_eval(linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT) exitCode = subprocess.call(["pkexec", BM_ROOT, "firewall", "stop"]) -- cgit v1.2.3 From 7c7c2d49b172d08e106e297101142747eaf78944 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 14 Jul 2014 16:23:20 -0300 Subject: Use polling to prevent communication issues. --- src/leap/bitmask/backend/signaler.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/leap/bitmask/backend/signaler.py b/src/leap/bitmask/backend/signaler.py index f21f0998..7401f3a0 100644 --- a/src/leap/bitmask/backend/signaler.py +++ b/src/leap/bitmask/backend/signaler.py @@ -38,6 +38,8 @@ class Signaler(object): """ PORT = "5667" SERVER = "tcp://localhost:%s" % PORT + POLL_TIMEOUT = 1000 # ms + POLL_TRIES = 3 def __init__(self): """ @@ -148,13 +150,23 @@ class Signaler(object): # logger.debug("Signaling '{0}'".format(request)) self._socket.send(request) - # Get the reply. - try: - self._socket.recv() - # response = self._socket.recv() - # msg = "Received reply for '{0}' -> '{1}'" - # msg = msg.format(request, response) - # logger.debug(msg) - except zmq.error.Again as e: - msg = "Timeout error contacting signaler. {0!r}".format(e) + poll = zmq.Poller() + poll.register(self._socket, zmq.POLLIN) + + reply = None + tries = 0 + + while tries < self.POLL_TRIES: + socks = dict(poll.poll(self.POLL_TIMEOUT)) + if socks.get(self._socket) == zmq.POLLIN: + reply = self._socket.recv() + break + + tries += 1 + + if reply is None: + msg = "Timeout error contacting backend." logger.critical(msg) + # else: + # msg = "Received reply for '{0}' -> '{1}'".format(request, reply) + # logger.debug(msg) -- cgit v1.2.3 From 986ff80c16efea0e32ff78012b2fd3c95b83e179 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 15 Jul 2014 16:24:49 -0300 Subject: Replace QThread with threading.Thread. --- src/leap/bitmask/backend/signaler_qt.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/leap/bitmask/backend/signaler_qt.py b/src/leap/bitmask/backend/signaler_qt.py index 4ec27a1e..433f18ed 100644 --- a/src/leap/bitmask/backend/signaler_qt.py +++ b/src/leap/bitmask/backend/signaler_qt.py @@ -33,7 +33,7 @@ import logging logger = logging.getLogger(__name__) -class SignalerQt(QtCore.QThread): +class SignalerQt(QtCore.QObject): """ Signaling server. Receives signals from the signaling client and emit Qt signals for the GUI. @@ -42,11 +42,24 @@ class SignalerQt(QtCore.QThread): BIND_ADDR = "tcp://127.0.0.1:%s" % PORT def __init__(self): - QtCore.QThread.__init__(self) + QtCore.QObject.__init__(self) + + # Note: we use a plain thread instead of a QThread since works better. + # The signaler was not responding on OSX if the worker loop was run in + # a QThread. + # Possibly, ZMQ was not getting cycles to do work because Qt not + # receiving focus or something. + self._worker_thread = threading.Thread(target=self._run) self._do_work = threading.Event() + + def start(self): + """ + Start the worker thread for the signaler server. + """ self._do_work.set() + self._worker_thread.start() - def run(self): + def _run(self): """ Start a loop to process the ZMQ requests from the signaler client. """ -- cgit v1.2.3 From 3fc88bbc70b3da4a2f8b371813bf87a42443f29c Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 15 Jul 2014 16:49:54 -0300 Subject: Add changes file for #5719, #5733, #5734. --- changes/refactor-changes | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changes/refactor-changes diff --git a/changes/refactor-changes b/changes/refactor-changes new file mode 100644 index 00000000..3516f8c4 --- /dev/null +++ b/changes/refactor-changes @@ -0,0 +1,3 @@ +- Split frontend/backend in different files. Closes #5719. +- Implement ZMQ based messaging system. Closes #5733. +- Launch the backend in a different process than the app. Closes #5734. -- cgit v1.2.3 From 432fcab9f838b0bfc81ed8d40d92b4b5d3854f24 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 16 Jul 2014 09:39:17 -0300 Subject: Code style fixes. --- src/leap/bitmask/__init__.py | 10 ++++----- src/leap/bitmask/backend/components.py | 6 ++--- src/leap/bitmask/config/provider_spec.py | 2 +- src/leap/bitmask/crypto/srpauth.py | 2 +- src/leap/bitmask/crypto/tests/fake_provider.py | 4 ++-- src/leap/bitmask/gui/eip_status.py | 11 +++++---- src/leap/bitmask/gui/statemachines.py | 11 +++++---- src/leap/bitmask/platform_init/initializers.py | 3 +-- src/leap/bitmask/provider/providerbootstrapper.py | 4 ++-- src/leap/bitmask/services/eip/conductor.py | 4 ++-- .../services/eip/tests/test_eipbootstrapper.py | 2 +- src/leap/bitmask/services/eip/vpnlauncher.py | 10 ++++----- src/leap/bitmask/services/eip/vpnprocess.py | 2 +- src/leap/bitmask/services/mail/plumber.py | 4 ++-- .../services/soledad/soledadbootstrapper.py | 2 +- .../services/tests/test_abstractbootstrapper.py | 2 +- src/leap/bitmask/util/leap_argparse.py | 26 +++++++++++----------- src/leap/bitmask/util/polkit_agent.py | 6 ++--- src/leap/bitmask/util/privilege_policies.py | 4 ++-- 19 files changed, 56 insertions(+), 59 deletions(-) diff --git a/src/leap/bitmask/__init__.py b/src/leap/bitmask/__init__.py index 03da1e2f..9ec5aae7 100644 --- a/src/leap/bitmask/__init__.py +++ b/src/leap/bitmask/__init__.py @@ -29,7 +29,7 @@ from leap.bitmask.util import first # place, it can't be technically imported, but that doesn't matter # because the import is never executed if False: - import _scrypt + import _scrypt # noqa - skip 'not used' warning def _is_release_version(version): @@ -66,16 +66,16 @@ try: IS_RELEASE_VERSION = _is_release_version(__version__) del get_versions except ImportError: - #running on a tree that has not run - #the setup.py setver + # running on a tree that has not run + # the setup.py setver pass __appname__ = "unknown" try: from leap.bitmask._appname import __appname__ except ImportError: - #running on a tree that has not run - #the setup.py setver + # running on a tree that has not run + # the setup.py setver pass __short_version__ = first(re.findall('\d+\.\d+\.\d+', __version__)) diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index 8fa8b285..b372db89 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -486,9 +486,9 @@ class EIP(object): self._signaler.signal(self._signaler.eip_stopped) return else: - #msg = "Firewall is not down yet, waiting... {0} of {1}" - #msg = msg.format(retry, MAX_FW_WAIT_RETRIES) - #logger.debug(msg) + # msg = "Firewall is not down yet, waiting... {0} of {1}" + # msg = msg.format(retry, MAX_FW_WAIT_RETRIES) + # logger.debug(msg) time.sleep(FW_WAIT_STEP) retry += 1 logger.warning("After waiting, firewall is not down... " diff --git a/src/leap/bitmask/config/provider_spec.py b/src/leap/bitmask/config/provider_spec.py index cf942c7b..a1d91b90 100644 --- a/src/leap/bitmask/config/provider_spec.py +++ b/src/leap/bitmask/config/provider_spec.py @@ -37,7 +37,7 @@ leap_provider_spec = { 'default': {u'en': u'Test Provider'} }, 'description': { - #'type': LEAPTranslatable, + # 'type': LEAPTranslatable, 'type': dict, 'format': 'translatable', 'default': {u'en': u'Test provider'} diff --git a/src/leap/bitmask/crypto/srpauth.py b/src/leap/bitmask/crypto/srpauth.py index 67c686b0..d59b3c31 100644 --- a/src/leap/bitmask/crypto/srpauth.py +++ b/src/leap/bitmask/crypto/srpauth.py @@ -24,7 +24,7 @@ import requests import srp import json -#this error is raised from requests +# this error is raised from requests from simplejson.decoder import JSONDecodeError from functools import partial from requests.adapters import HTTPAdapter diff --git a/src/leap/bitmask/crypto/tests/fake_provider.py b/src/leap/bitmask/crypto/tests/fake_provider.py index b8cdbb12..60a3ef0a 100755 --- a/src/leap/bitmask/crypto/tests/fake_provider.py +++ b/src/leap/bitmask/crypto/tests/fake_provider.py @@ -156,7 +156,7 @@ def getSession(self, sessionInterface=None): put the right cookie name in place """ if not self.session: - #cookiename = b"_".join([b'TWISTED_SESSION'] + self.sitepath) + # cookiename = b"_".join([b'TWISTED_SESSION'] + self.sitepath) cookiename = b"_".join([b'_session_id'] + self.sitepath) sessionCookie = self.getCookie(cookiename) if sessionCookie: @@ -321,7 +321,7 @@ class OpenSSLServerContextFactory(object): Create an SSL context. """ ctx = SSL.Context(SSL.SSLv23_METHOD) - #ctx = SSL.Context(SSL.TLSv1_METHOD) + # ctx = SSL.Context(SSL.TLSv1_METHOD) ctx.use_certificate_file(where('leaptestscert.pem')) ctx.use_privatekey_file(where('leaptestskey.pem')) diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index df9f3741..a707050a 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -24,7 +24,6 @@ from functools import partial from PySide import QtCore, QtGui -from leap.bitmask.config import flags from leap.bitmask.services import get_service_display_name, EIP_SERVICE from leap.bitmask.platform_init import IS_LINUX from leap.bitmask.util.averages import RateMovingAverage @@ -123,8 +122,8 @@ class EIPStatusWidget(QtGui.QWidget): # XXX we cannot connect this signal now because # it interferes with the proper notifications during restarts # without available network. - #signaler.eip_network_unreachable.connect( - #self._on_eip_network_unreachable) + # signaler.eip_network_unreachable.connect( + # self._on_eip_network_unreachable) def _make_status_clickable(self): """ @@ -326,7 +325,7 @@ class EIPStatusWidget(QtGui.QWidget): Triggered after a successful login. Enables the start button. """ - #logger.debug('Showing EIP start button') + # logger.debug('Showing EIP start button') self.eip_button.show() # Restore the eip action menu @@ -545,7 +544,7 @@ class EIPStatusWidget(QtGui.QWidget): elif vpn_state == "ALREADYRUNNING": # Put the following calls in Qt's event queue, otherwise # the UI won't update properly - #self.send_disconnect_signal() + # self.send_disconnect_signal() QtDelayedCall( 0, self.eipconnection.qtsigns.do_disconnect_signal.emit) msg = self.tr("Unable to start VPN, it's already running.") @@ -738,7 +737,7 @@ class EIPStatusWidget(QtGui.QWidget): self.set_eip_status_icon("error") def set_eipstatus_off(self, error=True): - # XXX this should be handled by the state machine. + # XXX this should be handled by the state machine. """ Sets eip status to off """ diff --git a/src/leap/bitmask/gui/statemachines.py b/src/leap/bitmask/gui/statemachines.py index 00a1387e..91f1f605 100644 --- a/src/leap/bitmask/gui/statemachines.py +++ b/src/leap/bitmask/gui/statemachines.py @@ -240,9 +240,9 @@ class CompositeMachine(QStateMachine): c2.qtsigs.disconnected_signal.connect(self.off_ev2_slot) # XXX why is this getting deletec in c++? - #Traceback (most recent call last): - #self.postEvent(self.events.on_ev2) - #RuntimeError: Internal C++ object (ConnectedEvent2) already deleted. + # Traceback (most recent call last): + # self.postEvent(self.events.on_ev2) + # RuntimeError: Internal C++ object (ConnectedEvent2) already deleted. # XXX trying the following workaround, since # I cannot find why in the world this is getting deleted :( # XXX refactor! @@ -318,9 +318,8 @@ class ConnectionMachineBuilder(object): components = self._conn.components if components is None: - # simple case: connection definition inherits directly from - # the abstract connection. - + # simple case: connection definition inherits directly from + # the abstract connection. leap_assert_type(self._conn, connections.AbstractLEAPConnection) return self._make_simple_machine(self._conn, **kwargs) diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py index 2d800703..e4d6f9b3 100644 --- a/src/leap/bitmask/platform_init/initializers.py +++ b/src/leap/bitmask/platform_init/initializers.py @@ -21,7 +21,6 @@ import logging import os import platform import stat -import sys import subprocess import tempfile @@ -103,7 +102,7 @@ def get_missing_helpers_dialog(): msg.setWindowTitle(msg.tr("Missing helper files")) msg.setText(msg.tr(WE_NEED_POWERS)) # but maybe the user really deserve to know more - #msg.setInformativeText(msg.tr(BECAUSE)) + # msg.setInformativeText(msg.tr(BECAUSE)) msg.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) msg.addButton("No, don't ask again", QtGui.QMessageBox.RejectRole) msg.setDefaultButton(QtGui.QMessageBox.Yes) diff --git a/src/leap/bitmask/provider/providerbootstrapper.py b/src/leap/bitmask/provider/providerbootstrapper.py index 5064f6a4..71edbb87 100644 --- a/src/leap/bitmask/provider/providerbootstrapper.py +++ b/src/leap/bitmask/provider/providerbootstrapper.py @@ -194,8 +194,8 @@ class ProviderBootstrapper(AbstractBootstrapper): verify = self.verify if mtime: # the provider.json exists - # So, we're getting it from the api.* and checking against - # the provider ca. + # So, we're getting it from the api.* and checking against + # the provider ca. try: provider_config = ProviderConfig() provider_config.load(provider_json) diff --git a/src/leap/bitmask/services/eip/conductor.py b/src/leap/bitmask/services/eip/conductor.py index dfd27f3d..bb07809a 100644 --- a/src/leap/bitmask/services/eip/conductor.py +++ b/src/leap/bitmask/services/eip/conductor.py @@ -202,7 +202,7 @@ class EIPConductor(object): # we bypass the on_eip_disconnected here plug_restart_on_disconnected() self.qtsigs.disconnected_signal.emit() - #QtDelayedCall(0, self.qtsigs.disconnected_signal.emit) + # QtDelayedCall(0, self.qtsigs.disconnected_signal.emit) # ...and reconnect the original signal again, after having used the # diversion QtDelayedCall(500, reconnect_disconnected_signal) @@ -301,7 +301,7 @@ class EIPConductor(object): # XXX FIXME --- check exitcode is != 0 really. # bitmask-root is masking the exitcode, so we might need # to fix it on that side. - #if exitCode != 0 and not self.user_stopped_eip: + # if exitCode != 0 and not self.user_stopped_eip: if not self.user_stopped_eip: eip_status_label = self._eip_status.tr( "{0} finished in an unexpected manner!") diff --git a/src/leap/bitmask/services/eip/tests/test_eipbootstrapper.py b/src/leap/bitmask/services/eip/tests/test_eipbootstrapper.py index 6640a860..1888f2c9 100644 --- a/src/leap/bitmask/services/eip/tests/test_eipbootstrapper.py +++ b/src/leap/bitmask/services/eip/tests/test_eipbootstrapper.py @@ -30,7 +30,7 @@ import time try: import unittest2 as unittest except ImportError: - import unittest + import unittest # noqa - skip 'unused import' warning from nose.twistedtools import deferred, reactor from twisted.internet import threads diff --git a/src/leap/bitmask/services/eip/vpnlauncher.py b/src/leap/bitmask/services/eip/vpnlauncher.py index e36fd76b..72e19413 100644 --- a/src/leap/bitmask/services/eip/vpnlauncher.py +++ b/src/leap/bitmask/services/eip/vpnlauncher.py @@ -169,11 +169,11 @@ class VPNLauncher(object): leap_assert_type(providerconfig, ProviderConfig) # XXX this still has to be changed on osx and windows accordingly - #kwargs = {} - #openvpn_possibilities = which(kls.OPENVPN_BIN, **kwargs) - #if not openvpn_possibilities: - #raise OpenVPNNotFoundException() - #openvpn = first(openvpn_possibilities) + # kwargs = {} + # openvpn_possibilities = which(kls.OPENVPN_BIN, **kwargs) + # if not openvpn_possibilities: + # raise OpenVPNNotFoundException() + # openvpn = first(openvpn_possibilities) # ----------------------------------------- openvpn_path = force_eval(kls.OPENVPN_BIN_PATH) diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index 1c11a337..d1a3fdaa 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -817,7 +817,7 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager): leap_assert_type(eipconfig, EIPConfig) leap_assert_type(providerconfig, ProviderConfig) - #leap_assert(not self.isRunning(), "Starting process more than once!") + # leap_assert(not self.isRunning(), "Starting process more than once!") self._eipconfig = eipconfig self._providerconfig = providerconfig diff --git a/src/leap/bitmask/services/mail/plumber.py b/src/leap/bitmask/services/mail/plumber.py index fa33afcd..1af65c5d 100644 --- a/src/leap/bitmask/services/mail/plumber.py +++ b/src/leap/bitmask/services/mail/plumber.py @@ -232,8 +232,8 @@ class MBOXPlumber(object): with open(mail_filename) as f: mail_string = f.read() - #uid = self._mbox.getUIDNext() - #print "saving with UID: %s" % uid + # uid = self._mbox.getUIDNext() + # print "saving with UID: %s" % uid d = self._mbox.messages.add_msg( mail_string, notify_on_disk=True) return d diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index a5904dce..c4e43bfe 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -134,7 +134,7 @@ class SoledadBootstrapper(AbstractBootstrapper): MAX_INIT_RETRIES = 10 MAX_SYNC_RETRIES = 10 WAIT_MAX_SECONDS = 600 - #WAIT_STEP_SECONDS = 1 + # WAIT_STEP_SECONDS = 1 WAIT_STEP_SECONDS = 5 def __init__(self, signaler=None): diff --git a/src/leap/bitmask/services/tests/test_abstractbootstrapper.py b/src/leap/bitmask/services/tests/test_abstractbootstrapper.py index 3ac126ac..c3fda9e1 100644 --- a/src/leap/bitmask/services/tests/test_abstractbootstrapper.py +++ b/src/leap/bitmask/services/tests/test_abstractbootstrapper.py @@ -1,4 +1,4 @@ -## -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # test_abstrctbootstrapper.py # Copyright (C) 2013 LEAP # diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py index c7fed0a3..cbd6d8a5 100644 --- a/src/leap/bitmask/util/leap_argparse.py +++ b/src/leap/bitmask/util/leap_argparse.py @@ -107,19 +107,19 @@ def build_parser(): 'against domains.') # Not in use, we might want to reintroduce them. - #parser.add_argument('-i', '--no-provider-checks', - #action="store_true", default=False, - #help="skips download of provider config files. gets " - #"config from local files only. Will fail if cannot " - #"find any") - #parser.add_argument('-k', '--no-ca-verify', - #action="store_true", default=False, - #help="(insecure). Skips verification of the server " - #"certificate used in TLS handshake.") - #parser.add_argument('-c', '--config', metavar="CONFIG FILE", nargs='?', - #action="store", dest="config_file", - #type=argparse.FileType('r'), - #help='optional config file') + # parser.add_argument('-i', '--no-provider-checks', + # action="store_true", default=False, + # help="skips download of provider config files. gets " + # "config from local files only. Will fail if cannot " + # "find any") + # parser.add_argument('-k', '--no-ca-verify', + # action="store_true", default=False, + # help="(insecure). Skips verification of the server " + # "certificate used in TLS handshake.") + # parser.add_argument('-c', '--config', metavar="CONFIG FILE", nargs='?', + # action="store", dest="config_file", + # type=argparse.FileType('r'), + # help='optional config file') return parser diff --git a/src/leap/bitmask/util/polkit_agent.py b/src/leap/bitmask/util/polkit_agent.py index 6fda2f88..7764f571 100644 --- a/src/leap/bitmask/util/polkit_agent.py +++ b/src/leap/bitmask/util/polkit_agent.py @@ -39,9 +39,9 @@ def _launch_agent(): logger.error('Exception while running polkit authentication agent ' '%s' % (exc,)) # XXX fix KDE launch. See: #3755 - #try: - #subprocess.call(KDE_PATH) - #except Exception as exc: + # try: + # subprocess.call(KDE_PATH) + # except Exception as exc: def launch(): diff --git a/src/leap/bitmask/util/privilege_policies.py b/src/leap/bitmask/util/privilege_policies.py index adc3503f..f894d73b 100644 --- a/src/leap/bitmask/util/privilege_policies.py +++ b/src/leap/bitmask/util/privilege_policies.py @@ -87,8 +87,8 @@ class LinuxPolicyChecker(PolicyChecker): else self.LINUX_POLKIT_FILE) def is_missing_policy_permissions(self): - # FIXME this name is quite confusing, it does not have anything to do with - # file permissions. + # FIXME this name is quite confusing, it does not have anything to do + # with file permissions. """ Returns True if we could not find the appropriate policykit file in place -- cgit v1.2.3 From 3d6629348aedf2a6863d242d96d64b3492e86f9a Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 16 Jul 2014 11:29:19 -0300 Subject: Increase timeout and retries. With this change we avoid the communication issues on OSX. --- src/leap/bitmask/backend/signaler.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/leap/bitmask/backend/signaler.py b/src/leap/bitmask/backend/signaler.py index 7401f3a0..574bfa71 100644 --- a/src/leap/bitmask/backend/signaler.py +++ b/src/leap/bitmask/backend/signaler.py @@ -38,8 +38,8 @@ class Signaler(object): """ PORT = "5667" SERVER = "tcp://localhost:%s" % PORT - POLL_TIMEOUT = 1000 # ms - POLL_TRIES = 3 + POLL_TIMEOUT = 2000 # ms + POLL_TRIES = 500 def __init__(self): """ @@ -156,13 +156,18 @@ class Signaler(object): reply = None tries = 0 - while tries < self.POLL_TRIES: + while True: socks = dict(poll.poll(self.POLL_TIMEOUT)) if socks.get(self._socket) == zmq.POLLIN: reply = self._socket.recv() break tries += 1 + if tries < self.POLL_TRIES: + logger.warning('Retrying receive... {0}/{1}'.format( + tries, self.POLL_TRIES)) + else: + break if reply is None: msg = "Timeout error contacting backend." -- cgit v1.2.3 From 934b6b64a4bd1f6b3a71b6821076532412977e30 Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 16 Jul 2014 12:02:08 -0700 Subject: firewall: correctly rewrite DNS packets originally for local network. --- pkg/linux/bitmask-root | 74 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/pkg/linux/bitmask-root b/pkg/linux/bitmask-root index 6fbafff9..58f9a103 100755 --- a/pkg/linux/bitmask-root +++ b/pkg/linux/bitmask-root @@ -55,6 +55,8 @@ VERSION = "1" SCRIPT = "bitmask-root" NAMESERVER = "10.42.0.1" BITMASK_CHAIN = "bitmask" +BITMASK_CHAIN_NAT_OUT = "bitmask" +BITMASK_CHAIN_NAT_POST = "bitmask_postrouting" IP = "/bin/ip" IPTABLES = "/sbin/iptables" @@ -565,11 +567,16 @@ def firewall_start(args): # the 'filter' and the 'nat' tables. if not ipv4_chain_exists(BITMASK_CHAIN): ip4tables("--new-chain", BITMASK_CHAIN) - if not ipv4_chain_exists(BITMASK_CHAIN, 'nat'): - ip4tables("--table", "nat", "--new-chain", BITMASK_CHAIN) + if not ipv4_chain_exists(BITMASK_CHAIN_NAT_OUT, 'nat'): + ip4tables("--table", "nat", "--new-chain", BITMASK_CHAIN_NAT_OUT) + if not ipv4_chain_exists(BITMASK_CHAIN_NAT_POST, 'nat'): + ip4tables("--table", "nat", "--new-chain", BITMASK_CHAIN_NAT_POST) if not ipv6_chain_exists(BITMASK_CHAIN): ip6tables("--new-chain", BITMASK_CHAIN) - ip4tables("--table", "nat", "--insert", "OUTPUT", "--jump", BITMASK_CHAIN) + ip4tables("--table", "nat", "--insert", "OUTPUT", + "--jump", BITMASK_CHAIN_NAT_OUT) + ip4tables("--table", "nat", "--insert", "POSTROUTING", + "--jump", BITMASK_CHAIN_NAT_POST) iptables("--insert", "OUTPUT", "--jump", BITMASK_CHAIN) # route all ipv4 DNS over VPN @@ -581,16 +588,32 @@ def firewall_start(args): "--jump", "ACCEPT") # rewrite all outgoing packets to use VPN DNS server # (DNS does sometimes use TCP!) - ip4tables("-t", "nat", "--append", BITMASK_CHAIN, "--protocol", "udp", + ip4tables("-t", "nat", "--append", BITMASK_CHAIN_NAT_OUT, "-p", "udp", "--dport", "53", "--jump", "DNAT", "--to", NAMESERVER+":53") - ip4tables("-t", "nat", "--append", BITMASK_CHAIN, "--protocol", "tcp", + ip4tables("-t", "nat", "--append", BITMASK_CHAIN_NAT_OUT, "-p", "tcp", "--dport", "53", "--jump", "DNAT", "--to", NAMESERVER+":53") - - # allow traffic to IPs on local network + # enable masquerading, so that DNS packets rewritten by DNAT will + # have the correct source IPs + ip4tables("-t", "nat", "--append", BITMASK_CHAIN_NAT_POST, + "--protocol", "udp", "--dport", "53", "--jump", "MASQUERADE") + ip4tables("-t", "nat", "--append", BITMASK_CHAIN_NAT_POST, + "--protocol", "tcp", "--dport", "53", "--jump", "MASQUERADE") + + # allow local network traffic if local_network_ipv4: + # allow local network destinations ip4tables("--append", BITMASK_CHAIN, "--destination", local_network_ipv4, "-o", default_device, "--jump", "ACCEPT") + # allow local network sources for DNS + # (required to allow local network DNS that gets rewritten by NAT + # to get passed through so that MASQUERADE can set correct source IP) + ip4tables("--append", BITMASK_CHAIN, + "--source", local_network_ipv4, "-o", default_device, + "-p", "udp", "--dport", "53", "--jump", "ACCEPT") + ip4tables("--append", BITMASK_CHAIN, + "--source", local_network_ipv4, "-o", default_device, + "-p", "tcp", "--dport", "53", "--jump", "ACCEPT") # allow multicast Simple Service Discovery Protocol ip4tables("--append", BITMASK_CHAIN, "--protocol", "udp", @@ -649,19 +672,34 @@ def firewall_stop(): command can be run at a time). """ ok = True + + # -t filter -D OUTPUT -j bitmask try: iptables("--delete", "OUTPUT", "--jump", BITMASK_CHAIN, throw=True) except subprocess.CalledProcessError as exc: debug("INFO: not able to remove bitmask firewall from OUTPUT chain " "(maybe it is already removed?)", exc) ok = False + + # -t nat -D OUTPUT -j bitmask try: ip4tables("-t", "nat", "--delete", "OUTPUT", - "--jump", BITMASK_CHAIN, throw=True) + "--jump", BITMASK_CHAIN_NAT_OUT, throw=True) except subprocess.CalledProcessError as exc: debug("INFO: not able to remove bitmask firewall from OUTPUT chain " "in 'nat' table (maybe it is already removed?)", exc) ok = False + + # -t nat -D POSTROUTING -j bitmask_postrouting + try: + ip4tables("-t", "nat", "--delete", "POSTROUTING", + "--jump", BITMASK_CHAIN_NAT_POST, throw=True) + except subprocess.CalledProcessError as exc: + debug("INFO: not able to remove bitmask firewall from POSTROUTING " + "chain in 'nat' table (maybe it is already removed?)", exc) + ok = False + + # -t filter --delete-chain bitmask try: ip4tables("--flush", BITMASK_CHAIN, throw=True) ip4tables("--delete-chain", BITMASK_CHAIN, throw=True) @@ -669,13 +707,28 @@ def firewall_stop(): debug("INFO: not able to flush and delete bitmask ipv4 firewall " "chain (maybe it is already destroyed?)", exc) ok = False + + # -t nat --delete-chain bitmask + try: + ip4tables("-t", "nat", "--flush", BITMASK_CHAIN_NAT_OUT, throw=True) + ip4tables("-t", "nat", "--delete-chain", + BITMASK_CHAIN_NAT_OUT, throw=True) + except subprocess.CalledProcessError as exc: + debug("INFO: not able to flush and delete bitmask ipv4 firewall " + "chain in 'nat' table (maybe it is already destroyed?)", exc) + ok = False + + # -t nat --delete-chain bitmask_postrouting try: - ip4tables("-t", "nat", "--flush", BITMASK_CHAIN, throw=True) - ip4tables("-t", "nat", "--delete-chain", BITMASK_CHAIN, throw=True) + ip4tables("-t", "nat", "--flush", BITMASK_CHAIN_NAT_POST, throw=True) + ip4tables("-t", "nat", "--delete-chain", + BITMASK_CHAIN_NAT_POST, throw=True) except subprocess.CalledProcessError as exc: debug("INFO: not able to flush and delete bitmask ipv4 firewall " "chain in 'nat' table (maybe it is already destroyed?)", exc) ok = False + + # -t filter --delete-chain bitmask (ipv6) try: ip6tables("--flush", BITMASK_CHAIN, throw=True) ip6tables("--delete-chain", BITMASK_CHAIN, throw=True) @@ -683,6 +736,7 @@ def firewall_stop(): debug("INFO: not able to flush and delete bitmask ipv6 firewall " "chain (maybe it is already destroyed?)", exc) ok = False + if not (ok or ipv4_chain_exists or ipv6_chain_exists): raise Exception("firewall might still be left up. " "Please try `firewall stop` again.") -- cgit v1.2.3 From 34bcc2c332b5986e9ecf809fef5a9912fa2ca3d8 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 17 Jul 2014 07:22:27 -0500 Subject: modify launcher entrypoint after refactor --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7cd48799..bfd7eff0 100755 --- a/setup.py +++ b/setup.py @@ -137,7 +137,7 @@ def get_versions(default={}, verbose=False): cmdclass["freeze_debianver"] = freeze_debianver parsed_reqs = utils.parse_requirements() -leap_launcher = 'bitmask=leap.bitmask.app:main' +leap_launcher = 'bitmask=leap.bitmask.app:start_app' from setuptools.command.develop import develop as _develop -- cgit v1.2.3 From b26e12543cd5571421a67b962e1b8d797e527f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 18 Jul 2014 11:19:04 -0300 Subject: Update relnotes --- relnotes.txt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/relnotes.txt b/relnotes.txt index 4fc33b39..925db87d 100644 --- a/relnotes.txt +++ b/relnotes.txt @@ -1,8 +1,8 @@ -ANNOUNCING Bitmask, the Internet Encryption Toolkit, release 0.5.3 +ANNOUNCING Bitmask, the Internet Encryption Toolkit, release 0.6.0 The LEAP team is pleased to announce the immediate availability of -version 0.5.3 of Bitmask, the Internet Encryption Toolkit, codename -"encrypt ALL THE THINGS". +version 0.6.0 of Bitmask, the Internet Encryption Toolkit, codename +"nothing to see here". https://downloads.leap.se/client/ @@ -21,7 +21,7 @@ WARNING (LINUX ONLY): If you ever run into the situation where you cannot access internet, open the terminal and run the following command: -$ pkexec /usr/sbin/bitmask-root firewall stop +$ pkexec /usr/local/sbin/bitmask-root firewall stop If for some reason that doesn't work, you will need to reboot your computer. @@ -43,9 +43,11 @@ NOT trust your life to it. WHAT CAN THIS VERSION OF BITMASK DO FOR ME? -Bitmask 0.5.3 improves greatly its Encrypted Internet support and -stability in general, among other various bug fixes. You can refer to -the CHANGELOG for the meat. +Bitmask 0.6.0 improves how email encryption/decryption works +internally to improve speed and CPU consumption. This release also +merges a big refactor we've been working on for some time, which will +give us room for a lot of improvements in the near future. You can +refer to the CHANGELOG for the meat. Encrypted Internet on Linux now helps you don't shoot yourself in the foot by leaking traffic outside of the secure connection it @@ -104,6 +106,6 @@ beyond any border. The LEAP team, -June 27, 2014 +July 18, 2014 Somewhere in the middle of the intertubes. EOF -- cgit v1.2.3 From 312746bc9b77f0f738ccf2192d81ab94fdf9d6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 18 Jul 2014 11:21:02 -0300 Subject: Fold in changes --- CHANGELOG.rst | 17 +++++++++++++++++ changes/bug-5875_better-initial-sync-message | 1 - changes/feature-5813_wizard-providers-order | 1 - changes/feature-5864_create_TUF_release_tool | 1 - changes/feature-5880_add-fingerprint-gui-polkit-support | 1 - changes/feature-reroute_dns_packets | 1 - changes/feature_decrypt-inline-bootstrap | 1 - changes/feature_package_osx | 1 - changes/refactor-changes | 3 --- 9 files changed, 17 insertions(+), 10 deletions(-) delete mode 100644 changes/bug-5875_better-initial-sync-message delete mode 100644 changes/feature-5813_wizard-providers-order delete mode 100644 changes/feature-5864_create_TUF_release_tool delete mode 100644 changes/feature-5880_add-fingerprint-gui-polkit-support delete mode 100644 changes/feature-reroute_dns_packets delete mode 100644 changes/feature_decrypt-inline-bootstrap delete mode 100644 changes/feature_package_osx delete mode 100644 changes/refactor-changes diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 92da1573..e8d24c95 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,23 @@ History 2014 ==== +0.6.0 July 18 -- the "nothing to see here" release: ++++++++++++++++++++++++++++++++++++++++++++++++++++ + +- Initial sync message is confusing. Closes #5875. +- Use preferred provider on first run. Closes #5813. +- Add TUF init repository and release tools. Closes #5864. +- Add support for fingerprint-gui's polkit agent. Closes #5880. +- Reroute DNS packets instead of blocking them, eliminating need to + muck around with resolv.conf. Closes #4633, #5655, #5738, #4823 +- Use inline decrypting for initial soledad syncrhonization, to wait + for secrets. +- Add the ability to create an osx bundle with py2app. Closes #5845. +- Split frontend/backend in different files. Closes #5719. +- Implement ZMQ based messaging system. Closes #5733. +- Launch the backend in a different process than the app. Closes + #5734. + 0.5.3 June 27 -- the "encrypt ALL THE THINGS" release: ++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/changes/bug-5875_better-initial-sync-message b/changes/bug-5875_better-initial-sync-message deleted file mode 100644 index 473093ed..00000000 --- a/changes/bug-5875_better-initial-sync-message +++ /dev/null @@ -1 +0,0 @@ -- Initial sync message is confusing. Closes #5875. diff --git a/changes/feature-5813_wizard-providers-order b/changes/feature-5813_wizard-providers-order deleted file mode 100644 index f4033d26..00000000 --- a/changes/feature-5813_wizard-providers-order +++ /dev/null @@ -1 +0,0 @@ -- Use preferred provider on first run. Closes #5813. diff --git a/changes/feature-5864_create_TUF_release_tool b/changes/feature-5864_create_TUF_release_tool deleted file mode 100644 index b9a4a3d5..00000000 --- a/changes/feature-5864_create_TUF_release_tool +++ /dev/null @@ -1 +0,0 @@ -- Add TUF init repository and release tools. Closes #5864. diff --git a/changes/feature-5880_add-fingerprint-gui-polkit-support b/changes/feature-5880_add-fingerprint-gui-polkit-support deleted file mode 100644 index c71093d5..00000000 --- a/changes/feature-5880_add-fingerprint-gui-polkit-support +++ /dev/null @@ -1 +0,0 @@ -- Add support for fingerprint-gui's polkit agent. Closes #5880. diff --git a/changes/feature-reroute_dns_packets b/changes/feature-reroute_dns_packets deleted file mode 100644 index beef3a1f..00000000 --- a/changes/feature-reroute_dns_packets +++ /dev/null @@ -1 +0,0 @@ -- reroute DNS packets instead of blocking them, eliminating need to muck around with resolv.conf. Closes #4633, #5655, #5738, #4823 \ No newline at end of file diff --git a/changes/feature_decrypt-inline-bootstrap b/changes/feature_decrypt-inline-bootstrap deleted file mode 100644 index 092d98ea..00000000 --- a/changes/feature_decrypt-inline-bootstrap +++ /dev/null @@ -1 +0,0 @@ -- Use inline decrypting for initial soledad syncrhonization, to wait for secrets. diff --git a/changes/feature_package_osx b/changes/feature_package_osx deleted file mode 100644 index cf5823bd..00000000 --- a/changes/feature_package_osx +++ /dev/null @@ -1 +0,0 @@ -- Add the ability to create an osx bundle with py2app. Closes #5845. \ No newline at end of file diff --git a/changes/refactor-changes b/changes/refactor-changes deleted file mode 100644 index 3516f8c4..00000000 --- a/changes/refactor-changes +++ /dev/null @@ -1,3 +0,0 @@ -- Split frontend/backend in different files. Closes #5719. -- Implement ZMQ based messaging system. Closes #5733. -- Launch the backend in a different process than the app. Closes #5734. -- cgit v1.2.3 From 68b1be8ef443b088cf5c1f7f964e1bd7ad42408e Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 21 Jul 2014 17:03:47 -0300 Subject: Add heartbeat to check if backend is alive. Send a 'ping' request every 2 secs to ensure that the backend is running. Use polling instead of recv on the backend_proxy. This was already implemented for the signaler. --- changes/add-backend-alive-check | 1 + src/leap/bitmask/backend/api.py | 2 + src/leap/bitmask/backend/backend.py | 6 ++- src/leap/bitmask/backend/backend_proxy.py | 64 ++++++++++++++++++++++++++----- src/leap/bitmask/gui/mainwindow.py | 22 +++++++++++ 5 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 changes/add-backend-alive-check diff --git a/changes/add-backend-alive-check b/changes/add-backend-alive-check new file mode 100644 index 00000000..40e12978 --- /dev/null +++ b/changes/add-backend-alive-check @@ -0,0 +1 @@ +- Add checks to ensure that the backend is alive or notify the user. Related to #5873. diff --git a/src/leap/bitmask/backend/api.py b/src/leap/bitmask/backend/api.py index b8533f36..4f52e470 100644 --- a/src/leap/bitmask/backend/api.py +++ b/src/leap/bitmask/backend/api.py @@ -18,10 +18,12 @@ Backend available API and SIGNALS definition. """ STOP_REQUEST = "stop" +PING_REQUEST = "PING" API = ( STOP_REQUEST, # this method needs to be defined in order to support the # backend stop action + PING_REQUEST, "eip_can_start", "eip_cancel_setup", diff --git a/src/leap/bitmask/backend/backend.py b/src/leap/bitmask/backend/backend.py index 833f4368..c895f8f5 100644 --- a/src/leap/bitmask/backend/backend.py +++ b/src/leap/bitmask/backend/backend.py @@ -23,7 +23,7 @@ from twisted.internet import defer, reactor, threads import zmq from zmq.auth.thread import ThreadAuthenticator -from leap.bitmask.backend.api import API +from leap.bitmask.backend.api import API, PING_REQUEST from leap.bitmask.backend.utils import get_backend_certificates from leap.bitmask.backend.signaler import Signaler @@ -146,6 +146,10 @@ class Backend(object): :param request_json: a json specification of a request. :type request_json: str """ + if request_json == PING_REQUEST: + # do not process request if it's just a ping + return + try: # request = zmq.utils.jsonapi.loads(request_json) # We use stdlib's json to ensure that we get unicode strings diff --git a/src/leap/bitmask/backend/backend_proxy.py b/src/leap/bitmask/backend/backend_proxy.py index f683e465..dc30d2cb 100644 --- a/src/leap/bitmask/backend/backend_proxy.py +++ b/src/leap/bitmask/backend/backend_proxy.py @@ -25,7 +25,7 @@ import time import zmq -from leap.bitmask.backend.api import API, STOP_REQUEST +from leap.bitmask.backend.api import API, STOP_REQUEST, PING_REQUEST from leap.bitmask.backend.utils import get_backend_certificates import logging @@ -40,6 +40,11 @@ class BackendProxy(object): PORT = '5556' SERVER = "tcp://localhost:%s" % PORT + POLL_TIMEOUT = 4000 # ms + POLL_TRIES = 3 + + PING_INTERVAL = 2 # secs + def __init__(self): self._socket = None @@ -62,6 +67,9 @@ class BackendProxy(object): socket.connect(self.SERVER) self._socket = socket + self._ping_at = 0 + self.online = False + self._call_queue = Queue.Queue() self._worker_caller = threading.Thread(target=self._worker) self._worker_caller.start() @@ -82,9 +90,26 @@ class BackendProxy(object): except Queue.Empty: pass time.sleep(0.01) + self._ping() logger.debug("BackendProxy worker stopped.") + def _reset_ping(self): + """ + Reset the ping timeout counter. + This is called for every ping and request. + """ + self._ping_at = time.time() + self.PING_INTERVAL + + def _ping(self): + """ + Heartbeat helper. + Sends a PING request just to know that the server is alive. + """ + if time.time() > self._ping_at: + self._send_request(PING_REQUEST) + self._reset_ping() + def _api_call(self, *args, **kwargs): """ Call the `api_method` method in backend (through zmq). @@ -134,16 +159,35 @@ class BackendProxy(object): # logger.debug("Sending request to backend: {0}".format(request)) self._socket.send(request) - try: - # Get the reply. - self._socket.recv() - # response = self._socket.recv() - # msg = "Received reply for '{0}' -> '{1}'" - # msg = msg.format(request, response) - # logger.debug(msg) - except zmq.error.Again as e: - msg = "Timeout error contacting backend. {0!r}".format(e) + poll = zmq.Poller() + poll.register(self._socket, zmq.POLLIN) + + reply = None + tries = 0 + + while True: + socks = dict(poll.poll(self.POLL_TIMEOUT)) + if socks.get(self._socket) == zmq.POLLIN: + reply = self._socket.recv() + break + + tries += 1 + if tries < self.POLL_TRIES: + logger.warning('Retrying receive... {0}/{1}'.format( + tries, self.POLL_TRIES)) + else: + break + + if reply is None: + msg = "Timeout error contacting backend." logger.critical(msg) + self.online = False + else: + # msg = "Received reply for '{0}' -> '{1}'".format(request, reply) + # logger.debug(msg) + self.online = True + # request received, no ping needed for other interval. + self._reset_ping() def __getattribute__(self, name): """ diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 6959650b..a15c4dd8 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -125,6 +125,11 @@ class MainWindow(QtGui.QMainWindow): self._backend = BackendProxy() + # periodically check if the backend is alive + self._backend_checker = QtCore.QTimer(self) + self._backend_checker.timeout.connect(self._check_backend_status) + self._backend_checker.start(2000) + self._leap_signaler = LeapSignaler() self._leap_signaler.start() @@ -334,6 +339,23 @@ class MainWindow(QtGui.QMainWindow): logger.error("Bad call to the backend:") logger.error(data) + @QtCore.Slot() + def _check_backend_status(self): + """ + TRIGGERS: + self._backend_checker.timeout + + Check that the backend is running. Otherwise show an error to the user. + """ + online = self._backend.online + if not online: + logger.critical("Backend is not online.") + QtGui.QMessageBox.critical( + self, self.tr("Application error"), + self.tr("There is a problem contacting the backend, please " + "restart Bitmask.")) + self._backend_checker.stop() + def _backend_connect(self, only_tracked=False): """ Connect to backend signals. -- cgit v1.2.3 From aeacb80a34dc7b8996278e35e9cc888d93e2f853 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 22 Jul 2014 16:10:10 -0300 Subject: Kill backend on quit if it does not respond. --- src/leap/bitmask/app.py | 2 +- src/leap/bitmask/frontend_app.py | 4 ++-- src/leap/bitmask/gui/mainwindow.py | 21 +++++++++++++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 88f6bc15..ab49ee37 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -185,7 +185,7 @@ def start_app(): backend_process.daemon = True backend_process.start() - run_frontend(options, flags_dict) + run_frontend(options, flags_dict, backend_pid=backend_process.pid) if __name__ == "__main__": diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py index 51607d0b..5ea89fc9 100644 --- a/src/leap/bitmask/frontend_app.py +++ b/src/leap/bitmask/frontend_app.py @@ -54,7 +54,7 @@ def signal_handler(window, pid, signum, frame): window.quit() -def run_frontend(options, flags_dict): +def run_frontend(options, flags_dict, backend_pid): """ Run the GUI for the application. @@ -102,7 +102,7 @@ def run_frontend(options, flags_dict): timer.start(500) # You may change this if you wish. timer.timeout.connect(lambda: None) # Let the interpreter run each 500 ms. - window = MainWindow(start_hidden=start_hidden) + window = MainWindow(start_hidden=start_hidden, backend_pid=backend_pid) my_pid = os.getpid() sig_handler = partial(signal_handler, window, my_pid) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index a15c4dd8..59a65b1e 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -22,6 +22,8 @@ import time from datetime import datetime +import psutil + from PySide import QtCore, QtGui from leap.bitmask import __version__ as VERSION @@ -97,7 +99,7 @@ class MainWindow(QtGui.QMainWindow): # We give the services some time to a halt before forcing quit. SERVICES_STOP_TIMEOUT = 20000 # in milliseconds - def __init__(self, start_hidden=False): + def __init__(self, start_hidden=False, backend_pid=None): """ Constructor for the client main window @@ -272,6 +274,7 @@ class MainWindow(QtGui.QMainWindow): self._logger_window = None self._start_hidden = start_hidden + self._backend_pid = backend_pid self._mail_conductor = mail_conductor.MailConductor(self._backend) self._mail_conductor.connect_mail_signals(self._mail_status) @@ -1819,6 +1822,15 @@ class MainWindow(QtGui.QMainWindow): # or if we reach the timeout QtDelayedCall(self.SERVICES_STOP_TIMEOUT, self.final_quit) + def _backend_kill(self): + """ + Send a kill signal to the backend process. + This is called if the backend does not respond to requests. + """ + if self._backend_pid is not None: + logger.debug("Killing backend") + psutil.Process(self._backend_pid).kill() + @QtCore.Slot() def _remove_service(self, service): """ @@ -1854,7 +1866,12 @@ class MainWindow(QtGui.QMainWindow): logger.debug('Final quit...') self._leap_signaler.stop() - self._backend.stop() + + if self._backend.online: + self._backend.stop() + else: + self._backend_kill() + time.sleep(0.05) # give the thread a little time to finish. # Remove lockfiles on a clean shutdown. -- cgit v1.2.3 From 227a31d8892c67c64beebe5135cc850dfa71c3c6 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 23 Jul 2014 12:03:44 -0300 Subject: Restrict access to the zmq certificates folder. --- changes/feature_restrict-certificates-permissions | 1 + src/leap/bitmask/backend/utils.py | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 changes/feature_restrict-certificates-permissions diff --git a/changes/feature_restrict-certificates-permissions b/changes/feature_restrict-certificates-permissions new file mode 100644 index 00000000..6bd6c015 --- /dev/null +++ b/changes/feature_restrict-certificates-permissions @@ -0,0 +1 @@ +- Restrict access to the zmq certificates folder. diff --git a/src/leap/bitmask/backend/utils.py b/src/leap/bitmask/backend/utils.py index 54a16fd7..65bf6753 100644 --- a/src/leap/bitmask/backend/utils.py +++ b/src/leap/bitmask/backend/utils.py @@ -19,6 +19,7 @@ Backend utilities to handle ZMQ certificates. """ import os import shutil +import stat import zmq.auth @@ -36,6 +37,8 @@ def generate_certificates(): if os.path.exists(KEYS_DIR): shutil.rmtree(KEYS_DIR) mkdir_p(KEYS_DIR) + # set permissions to: 0700 (U:rwx G:--- O:---) + os.chmod(KEYS_DIR, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) # create new keys in certificates dir # public_file, secret_file = create_certificates(...) -- cgit v1.2.3 From aec9c8ff50ed61cd484e3aefe638da6705b53ff8 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 23 Jul 2014 14:51:29 -0300 Subject: Set the standalone value for BaseConfig. --- changes/bug_set-standalone-flags-for-baseconfig | 1 + src/leap/bitmask/config/providerconfig.py | 2 ++ src/leap/bitmask/services/eip/eipconfig.py | 2 ++ 3 files changed, 5 insertions(+) create mode 100644 changes/bug_set-standalone-flags-for-baseconfig diff --git a/changes/bug_set-standalone-flags-for-baseconfig b/changes/bug_set-standalone-flags-for-baseconfig new file mode 100644 index 00000000..bf84d3e8 --- /dev/null +++ b/changes/bug_set-standalone-flags-for-baseconfig @@ -0,0 +1 @@ +- Set the standalone value for BaseConfig according to the global flags. diff --git a/src/leap/bitmask/config/providerconfig.py b/src/leap/bitmask/config/providerconfig.py index 7b979e61..57bc3a98 100644 --- a/src/leap/bitmask/config/providerconfig.py +++ b/src/leap/bitmask/config/providerconfig.py @@ -22,6 +22,7 @@ import logging import os from leap.bitmask import provider +from leap.bitmask.config import flags from leap.bitmask.config.provider_spec import leap_provider_spec from leap.bitmask.services import get_service_display_name from leap.bitmask.util import get_path_prefix @@ -43,6 +44,7 @@ class ProviderConfig(BaseConfig): Provider configuration abstraction class """ def __init__(self): + self.standalone = flags.STANDALONE BaseConfig.__init__(self) def get_light_config(self, domain, lang=None): diff --git a/src/leap/bitmask/services/eip/eipconfig.py b/src/leap/bitmask/services/eip/eipconfig.py index e7419b22..37c0c8ae 100644 --- a/src/leap/bitmask/services/eip/eipconfig.py +++ b/src/leap/bitmask/services/eip/eipconfig.py @@ -24,6 +24,7 @@ import time import ipaddr +from leap.bitmask.config import flags from leap.bitmask.config.providerconfig import ProviderConfig from leap.bitmask.services import ServiceConfig from leap.bitmask.services.eip.eipspec import get_schema @@ -220,6 +221,7 @@ class EIPConfig(ServiceConfig): OPENVPN_CIPHERS_REGEX = re.compile("[A-Z0-9\-]+") def __init__(self): + self.standalone = flags.STANDALONE ServiceConfig.__init__(self) self._api_version = None -- cgit v1.2.3 From d97a9804c1a6fb06d7ce39066f92a0259a6ab8c3 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 24 Jul 2014 12:54:06 -0300 Subject: Don't call the backend if it's not online. --- src/leap/bitmask/gui/mainwindow.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 59a65b1e..ff19e17f 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -1813,10 +1813,14 @@ class MainWindow(QtGui.QMainWindow): # Set this in case that the app is hidden QtGui.QApplication.setQuitOnLastWindowClosed(True) - self._stop_services() - self._really_quit = True + if not self._backend.online: + self.final_quit() + return + + self._stop_services() + # call final quit when all the services are stopped self.all_services_stopped.connect(self.final_quit) # or if we reach the timeout @@ -1859,11 +1863,12 @@ class MainWindow(QtGui.QMainWindow): if self._finally_quitting: return + logger.debug('Final quit...') self._finally_quitting = True - logger.debug('Closing soledad...') - self._backend.soledad_close() - logger.debug('Final quit...') + if self._backend.online: + logger.debug('Closing soledad...') + self._backend.soledad_close() self._leap_signaler.stop() -- cgit v1.2.3 From ce88b52295881a3dd1bc0efec8e72b1d3a235e87 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 22 Jul 2014 11:33:09 -0300 Subject: Add frontend alive check to the backend. Stop the backend if the frontend process does not exist any more and backend is not a daemon. --- changes/add-frontend-alive-check | 2 ++ src/leap/bitmask/app.py | 3 ++- src/leap/bitmask/backend/backend.py | 24 +++++++++++++++++++++++- src/leap/bitmask/backend/leapbackend.py | 4 ++-- src/leap/bitmask/backend_app.py | 5 +++-- 5 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 changes/add-frontend-alive-check diff --git a/changes/add-frontend-alive-check b/changes/add-frontend-alive-check new file mode 100644 index 00000000..54f88798 --- /dev/null +++ b/changes/add-frontend-alive-check @@ -0,0 +1,2 @@ +- Stop the backend if the frontend process does not exist any more and backend + is not a daemon. Related to #5873. diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index ab49ee37..37ded92a 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -180,7 +180,8 @@ def start_app(): flags_dict = flags_to_dict() - backend = lambda: run_backend(opts.danger, flags_dict) + frontend_pid = os.getpid() + backend = lambda: run_backend(opts.danger, flags_dict, frontend_pid) backend_process = multiprocessing.Process(target=backend, name='Backend') backend_process.daemon = True backend_process.start() diff --git a/src/leap/bitmask/backend/backend.py b/src/leap/bitmask/backend/backend.py index c895f8f5..67ffe35a 100644 --- a/src/leap/bitmask/backend/backend.py +++ b/src/leap/bitmask/backend/backend.py @@ -18,6 +18,8 @@ import json import threading import time +import psutil + from twisted.internet import defer, reactor, threads import zmq @@ -39,12 +41,16 @@ class Backend(object): PORT = '5556' BIND_ADDR = "tcp://127.0.0.1:%s" % PORT - def __init__(self): + PING_INTERVAL = 2 # secs + + def __init__(self, frontend_pid=None): """ Backend constructor, create needed instances. """ self._signaler = Signaler() + self._frontend_pid = frontend_pid + self._do_work = threading.Event() # used to stop the worker thread. self._zmq_socket = None @@ -81,6 +87,8 @@ class Backend(object): Note: we use a simple while since is less resource consuming than a Twisted's LoopingCall. """ + pid = self._frontend_pid + check_wait = 0 while self._do_work.is_set(): # Wait for next request from client try: @@ -93,6 +101,20 @@ class Backend(object): raise time.sleep(0.01) + check_wait += 0.01 + if pid is not None and check_wait > self.PING_INTERVAL: + check_wait = 0 + self._check_frontend_alive() + + def _check_frontend_alive(self): + """ + Check if the frontend is alive and stop the backend if it is not. + """ + pid = self._frontend_pid + if pid is not None and not psutil.pid_exists(pid): + logger.critical("The frontend is down!") + self.stop() + def _stop_reactor(self): """ Stop the Twisted reactor, but first wait a little for some threads to diff --git a/src/leap/bitmask/backend/leapbackend.py b/src/leap/bitmask/backend/leapbackend.py index d3c4fcda..6b0328ca 100644 --- a/src/leap/bitmask/backend/leapbackend.py +++ b/src/leap/bitmask/backend/leapbackend.py @@ -36,11 +36,11 @@ class LeapBackend(Backend): """ Backend server subclass, used to implement the API methods. """ - def __init__(self, bypass_checks=False): + def __init__(self, bypass_checks=False, frontend_pid=None): """ Constructor for the backend. """ - Backend.__init__(self) + Backend.__init__(self, frontend_pid) self._settings = Settings() diff --git a/src/leap/bitmask/backend_app.py b/src/leap/bitmask/backend_app.py index 5c0e4803..716ae4a7 100644 --- a/src/leap/bitmask/backend_app.py +++ b/src/leap/bitmask/backend_app.py @@ -44,7 +44,7 @@ def signal_handler(signum, frame): logger.debug("{0}: SIGNAL #{1} catched.".format(pname, signum)) -def run_backend(bypass_checks, flags_dict): +def run_backend(bypass_checks, flags_dict, frontend_pid=None): """ Run the backend for the application. @@ -59,5 +59,6 @@ def run_backend(bypass_checks, flags_dict): dict_to_flags(flags_dict) - backend = LeapBackend(bypass_checks=bypass_checks) + backend = LeapBackend(bypass_checks=bypass_checks, + frontend_pid=frontend_pid) backend.run() -- cgit v1.2.3 From beac7a8ce70acb17c320580538510761dff3ac93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 24 Jul 2014 16:33:25 -0300 Subject: Improve hide and show main window on Ubuntu --- changes/bug_show_hide_improved | 1 + src/leap/bitmask/gui/mainwindow.py | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 changes/bug_show_hide_improved diff --git a/changes/bug_show_hide_improved b/changes/bug_show_hide_improved new file mode 100644 index 00000000..39472074 --- /dev/null +++ b/changes/bug_show_hide_improved @@ -0,0 +1 @@ +- Improve Hide and Show Window behavior on Ubuntu. Fixes #5511. \ No newline at end of file diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index ff19e17f..383f97bb 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -999,6 +999,12 @@ class MainWindow(QtGui.QMainWindow): if not visible: QtGui.QApplication.setQuitOnLastWindowClosed(True) self.show() + if IS_LINUX: + # On ubuntu, activateWindow doesn't work reliably, so + # we do the following as a workaround. See + # https://bugreports.qt-project.org/browse/QTBUG-24932 + # for more details + QtGui.QX11Info.setAppUserTime(0) self.activateWindow() self.raise_() else: -- cgit v1.2.3 From 8c9005e665bb7a77f91cf298f49832f663b0df67 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 24 Jul 2014 17:58:56 -0300 Subject: Disable user/pass remembering temporarily. Set the keyring to None in order to simulate an always unavailable keyring, that way we avoid the possibility of the user running into the existing keyring issues. See https://leap.se/code/issues/4190 Update comparisons to do a proper comparison with `None`. Fix login widget 'enabled' changer in order to change the 'remember' widget *only* if we have an usable keyring. --- changes/disable-user-pass-remembering | 1 + src/leap/bitmask/gui/login.py | 3 ++- src/leap/bitmask/util/keyring_helpers.py | 10 +++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 changes/disable-user-pass-remembering diff --git a/changes/disable-user-pass-remembering b/changes/disable-user-pass-remembering new file mode 100644 index 00000000..45411001 --- /dev/null +++ b/changes/disable-user-pass-remembering @@ -0,0 +1 @@ +- Temporarily disable username/password remembering to avoid keyring issues. Related to #4190. diff --git a/src/leap/bitmask/gui/login.py b/src/leap/bitmask/gui/login.py index f66e71d9..baf29c23 100644 --- a/src/leap/bitmask/gui/login.py +++ b/src/leap/bitmask/gui/login.py @@ -216,7 +216,8 @@ class LoginWidget(QtGui.QWidget): """ self.ui.lnUser.setEnabled(enabled) self.ui.lnPassword.setEnabled(enabled) - self.ui.chkRemember.setEnabled(enabled) + if has_keyring(): + self.ui.chkRemember.setEnabled(enabled) self.ui.cmbProviders.setEnabled(enabled) self._set_cancel(not enabled) diff --git a/src/leap/bitmask/util/keyring_helpers.py b/src/leap/bitmask/util/keyring_helpers.py index ee2d7a1c..0512d988 100644 --- a/src/leap/bitmask/util/keyring_helpers.py +++ b/src/leap/bitmask/util/keyring_helpers.py @@ -34,6 +34,10 @@ except Exception: # dbus socket, or stuff like that. keyring = None +# XXX remember password disabled right now! +# see: https://leap.se/code/issues/4190 +keyring = None + logger = logging.getLogger(__name__) @@ -46,7 +50,7 @@ def _get_keyring_with_fallback(): This is a workaround for the cases in which the keyring module chooses an insecure keyring by default (ie, inside a virtualenv). """ - if not keyring: + if keyring is None: return None kr = keyring.get_keyring() if not canuse(kr): @@ -67,7 +71,7 @@ def has_keyring(): :rtype: bool """ - if not keyring: + if keyring is None: return False kr = _get_keyring_with_fallback() return canuse(kr) @@ -79,7 +83,7 @@ def get_keyring(): :rtype: keyringBackend or None """ - if not keyring: + if keyring is None: return False kr = _get_keyring_with_fallback() return kr if canuse(kr) else None -- cgit v1.2.3 From be27a8b548f01220f34c8f15dbc1973b110a790f Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 25 Jul 2014 11:13:23 -0300 Subject: Simplify and sort OS flags. --- src/leap/bitmask/platform_init/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/leap/bitmask/platform_init/__init__.py b/src/leap/bitmask/platform_init/__init__.py index 2a262a30..a9a97af9 100644 --- a/src/leap/bitmask/platform_init/__init__.py +++ b/src/leap/bitmask/platform_init/__init__.py @@ -22,7 +22,7 @@ import platform _system = platform.system() -IS_WIN = True if _system == "Windows" else False -IS_MAC = True if _system == "Darwin" else False -IS_LINUX = True if _system == "Linux" else False +IS_LINUX = _system == "Linux" +IS_MAC = _system == "Darwin" IS_UNIX = IS_MAC or IS_LINUX +IS_WIN = _system == "Windows" -- cgit v1.2.3 From 28553c41c3d879481923102a1b41ecd71641d0d8 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 25 Jul 2014 11:13:53 -0300 Subject: Add Linux autostart. Closes #4989. --- changes/add-linux-autostart | 1 + src/leap/bitmask/gui/mainwindow.py | 5 ++- src/leap/bitmask/util/autostart.py | 67 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 changes/add-linux-autostart create mode 100644 src/leap/bitmask/util/autostart.py diff --git a/changes/add-linux-autostart b/changes/add-linux-autostart new file mode 100644 index 00000000..44b175bc --- /dev/null +++ b/changes/add-linux-autostart @@ -0,0 +1 @@ +- Add autostart on Linux. Closes #4989. diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 383f97bb..2f78fd14 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -58,7 +58,7 @@ from leap.bitmask.services.mail import conductor as mail_conductor from leap.bitmask.services import EIP_SERVICE, MX_SERVICE -from leap.bitmask.util import make_address +from leap.bitmask.util import autostart, make_address from leap.bitmask.util.keyring_helpers import has_keyring from leap.bitmask.logs.leap_log_handler import LeapLogHandler @@ -108,6 +108,7 @@ class MainWindow(QtGui.QMainWindow): :type start_hidden: bool """ QtGui.QMainWindow.__init__(self) + autostart.set_autostart(True) # register leap events ######################################## register(signal=proto.UPDATER_NEW_UPDATES, @@ -1797,6 +1798,8 @@ class MainWindow(QtGui.QMainWindow): if self._quitting: return + autostart.set_autostart(False) + self._quitting = True # first thing to do quitting, hide the mainwindow and show tooltip. diff --git a/src/leap/bitmask/util/autostart.py b/src/leap/bitmask/util/autostart.py new file mode 100644 index 00000000..b8ac02b1 --- /dev/null +++ b/src/leap/bitmask/util/autostart.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# autostart.py +# Copyright (C) 2013, 2014 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 . +""" +Helpers to enable/disable bitmask's autostart. +""" +import flags +import logging +import os + +from leap.bitmask.platform_init import IS_LINUX + +logger = logging.getLogger(__name__) + + +DESKTOP_ENTRY = """\ +[Desktop Entry] +Version=1.0 +Encoding=UTF-8 +Type=Application +Name=Bitmask +Comment=Secure Communication +Exec=bitmask --start-hidden +Terminal=false +Icon=bitmask +""" + +DESKTOP_ENTRY_PATH = os.path.expanduser("~/.config/autostart/bitmask.desktop") + + +def set_autostart(enabled): + """ + Set the autostart mode to enabled or disabled depending on the parameter. + If `enabled` is `True`, save the autostart file to its place. Otherwise, + remove that file. + Right now we support only Linux autostart. + + :param enabled: whether the autostart should be enabled or not. + :type enabled: bool + """ + # we don't do autostart for bundle or systems different than Linux + if flags.STANDALONE or not IS_LINUX: + return + + if enabled: + with open(DESKTOP_ENTRY_PATH, 'w') as f: + f.write(DESKTOP_ENTRY) + else: + try: + os.remove(DESKTOP_ENTRY_PATH) + except OSError: # if the file does not exist + pass + except Exception as e: + logger.error("Problem disabling autostart, {0!r}".format(e)) -- cgit v1.2.3 From 89cb09b95f1c45a95c22c9ec99e7ba62fa6dd98b Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 25 Jul 2014 12:21:06 -0300 Subject: Fix misused import. --- src/leap/bitmask/util/autostart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/bitmask/util/autostart.py b/src/leap/bitmask/util/autostart.py index b8ac02b1..5f003bd8 100644 --- a/src/leap/bitmask/util/autostart.py +++ b/src/leap/bitmask/util/autostart.py @@ -17,10 +17,10 @@ """ Helpers to enable/disable bitmask's autostart. """ -import flags import logging import os +from leap.bitmask.config import flags from leap.bitmask.platform_init import IS_LINUX logger = logging.getLogger(__name__) -- cgit v1.2.3 From c4df5cff32524bf2fa9e5d43f08210361531d273 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 25 Jul 2014 12:41:25 -0300 Subject: Create the autostart path in case that does not exist. --- src/leap/bitmask/util/autostart.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/leap/bitmask/util/autostart.py b/src/leap/bitmask/util/autostart.py index 5f003bd8..d7a8afb8 100644 --- a/src/leap/bitmask/util/autostart.py +++ b/src/leap/bitmask/util/autostart.py @@ -22,6 +22,7 @@ import os from leap.bitmask.config import flags from leap.bitmask.platform_init import IS_LINUX +from leap.common.files import mkdir_p logger = logging.getLogger(__name__) @@ -38,7 +39,8 @@ Terminal=false Icon=bitmask """ -DESKTOP_ENTRY_PATH = os.path.expanduser("~/.config/autostart/bitmask.desktop") +DESKTOP_ENTRY_PATH = os.path.expanduser("~/.config/autostart/") +DESKTOP_ENTRY_FILE = os.path.join(DESKTOP_ENTRY_PATH, 'bitmask.desktop') def set_autostart(enabled): @@ -56,11 +58,12 @@ def set_autostart(enabled): return if enabled: - with open(DESKTOP_ENTRY_PATH, 'w') as f: + mkdir_p(DESKTOP_ENTRY_PATH) + with open(DESKTOP_ENTRY_FILE, 'w') as f: f.write(DESKTOP_ENTRY) else: try: - os.remove(DESKTOP_ENTRY_PATH) + os.remove(DESKTOP_ENTRY_FILE) except OSError: # if the file does not exist pass except Exception as e: -- cgit v1.2.3 From 2d75c8974a86de5fc5a8ea0a5efc7bcf305eb04e Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 25 Jul 2014 13:18:32 -0500 Subject: remove unused imports --- pkg/linux/bitmask-root | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/linux/bitmask-root b/pkg/linux/bitmask-root index 58f9a103..bdc365ac 100755 --- a/pkg/linux/bitmask-root +++ b/pkg/linux/bitmask-root @@ -34,7 +34,6 @@ not be teared down in the case of an error during launch. """ # TODO should be tested with python3, which can be the default on some distro. from __future__ import print_function -import atexit import os import re import signal @@ -42,7 +41,6 @@ import socket import syslog import subprocess import sys -import time import traceback cmdcheck = subprocess.check_output -- cgit v1.2.3 From b4405a5eb08201d53392b7607ed0d60e514db76b Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 25 Jul 2014 13:19:16 -0500 Subject: bump bitmask-root version number --- pkg/linux/bitmask-root | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/linux/bitmask-root b/pkg/linux/bitmask-root index bdc365ac..c9034b0d 100755 --- a/pkg/linux/bitmask-root +++ b/pkg/linux/bitmask-root @@ -49,7 +49,7 @@ cmdcheck = subprocess.check_output # CONSTANTS # -VERSION = "1" +VERSION = "2" SCRIPT = "bitmask-root" NAMESERVER = "10.42.0.1" BITMASK_CHAIN = "bitmask" -- cgit v1.2.3 From 72b6e999c883272cbe291ac610c528b5ae51077b Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 28 Jul 2014 16:49:44 -0300 Subject: Do `quit()` if the system requested the app to close. This fixes the issue where a system logout was blocked by bitmask since we were interpreting the closeEvent as if the user clicked on the 'X' button. Closes #5870. --- changes/5870_bitmask-prevent-system-logout-on-linux | 1 + src/leap/bitmask/gui/mainwindow.py | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 changes/5870_bitmask-prevent-system-logout-on-linux diff --git a/changes/5870_bitmask-prevent-system-logout-on-linux b/changes/5870_bitmask-prevent-system-logout-on-linux new file mode 100644 index 00000000..6feba46c --- /dev/null +++ b/changes/5870_bitmask-prevent-system-logout-on-linux @@ -0,0 +1 @@ +- Bitmask on linux prevents session logout. Closes #5870. diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 2f78fd14..4f7f519c 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -1138,6 +1138,11 @@ class MainWindow(QtGui.QMainWindow): """ Reimplementation of closeEvent to close to tray """ + if not e.spontaneous(): + # if the system requested the `close` then we should quit. + self.quit() + return + if QtGui.QSystemTrayIcon.isSystemTrayAvailable() and \ not self._really_quit: self._toggle_visible() -- cgit v1.2.3 From 0f72cb6694ea8f6d17f731c469f976250eab61f8 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 30 Jul 2014 18:07:33 -0300 Subject: Reduce services stop timeout. --- src/leap/bitmask/gui/mainwindow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 4f7f519c..c08ef08d 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -97,7 +97,7 @@ class MainWindow(QtGui.QMainWindow): EIP_START_TIMEOUT = 60000 # in milliseconds # We give the services some time to a halt before forcing quit. - SERVICES_STOP_TIMEOUT = 20000 # in milliseconds + SERVICES_STOP_TIMEOUT = 3000 # in milliseconds def __init__(self, start_hidden=False, backend_pid=None): """ -- cgit v1.2.3 From 08d89399eb41261d796e8b35427354581c0c8f4a Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 30 Jul 2014 18:07:37 -0300 Subject: Handle the system's quit correctly. Use a simple sleep to wait until the services are stopped and then kill the backend, always in a fixed time in order to avoid blocks/hangs on the system session logout. Closes #5870. --- src/leap/bitmask/gui/mainwindow.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index c08ef08d..46f0928f 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -197,6 +197,7 @@ class MainWindow(QtGui.QMainWindow): # used to know if we are in the final steps of quitting self._quitting = False self._finally_quitting = False + self._system_quit = False self._backend_connected_signals = [] self._backend_connect() @@ -1140,6 +1141,7 @@ class MainWindow(QtGui.QMainWindow): """ if not e.spontaneous(): # if the system requested the `close` then we should quit. + self._system_quit = True self.quit() return @@ -1809,7 +1811,7 @@ class MainWindow(QtGui.QMainWindow): # first thing to do quitting, hide the mainwindow and show tooltip. self.hide() - if self._systray is not None: + if not self._system_quit and self._systray is not None: self._systray.showMessage( self.tr('Quitting...'), self.tr('Bitmask is quitting, please wait.')) @@ -1833,10 +1835,18 @@ class MainWindow(QtGui.QMainWindow): self.final_quit() return - self._stop_services() - # call final quit when all the services are stopped self.all_services_stopped.connect(self.final_quit) + + self._stop_services() + + # we wait and call manually since during the system's logout the + # backend process can be killed and we won't get a response. + # XXX: also, for some reason the services stop timeout does not work. + if self._system_quit: + time.sleep(0.5) + self.final_quit() + # or if we reach the timeout QtDelayedCall(self.SERVICES_STOP_TIMEOUT, self.final_quit) @@ -1859,6 +1869,7 @@ class MainWindow(QtGui.QMainWindow): :param service: the service that we want to remove :type service: str """ + logger.debug("Removing service: {0}".format(service)) self._services_being_stopped.discard(service) if not self._services_being_stopped: @@ -1886,13 +1897,13 @@ class MainWindow(QtGui.QMainWindow): self._leap_signaler.stop() - if self._backend.online: - self._backend.stop() - else: - self._backend_kill() - + self._backend.stop() time.sleep(0.05) # give the thread a little time to finish. + if self._system_quit or not self._backend.online: + logger.debug("Killing the backend") + self._backend_kill() + # Remove lockfiles on a clean shutdown. logger.debug('Cleaning pidfiles') if IS_WIN: -- cgit v1.2.3 From 75eddcf502d23f761cbac26da34fcfd1ab669b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 31 Jul 2014 14:45:43 -0300 Subject: Enable keyring again --- src/leap/bitmask/gui/login.py | 3 +-- src/leap/bitmask/util/keyring_helpers.py | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/leap/bitmask/gui/login.py b/src/leap/bitmask/gui/login.py index baf29c23..8e0a3a15 100644 --- a/src/leap/bitmask/gui/login.py +++ b/src/leap/bitmask/gui/login.py @@ -216,8 +216,7 @@ class LoginWidget(QtGui.QWidget): """ self.ui.lnUser.setEnabled(enabled) self.ui.lnPassword.setEnabled(enabled) - if has_keyring(): - self.ui.chkRemember.setEnabled(enabled) + self.ui.chkRemember.setEnabled(enabled and has_keyring()) self.ui.cmbProviders.setEnabled(enabled) self._set_cancel(not enabled) diff --git a/src/leap/bitmask/util/keyring_helpers.py b/src/leap/bitmask/util/keyring_helpers.py index 0512d988..088e7b3d 100644 --- a/src/leap/bitmask/util/keyring_helpers.py +++ b/src/leap/bitmask/util/keyring_helpers.py @@ -34,10 +34,6 @@ except Exception: # dbus socket, or stuff like that. keyring = None -# XXX remember password disabled right now! -# see: https://leap.se/code/issues/4190 -keyring = None - logger = logging.getLogger(__name__) -- cgit v1.2.3 From d201ee8ff03905e008ce316bae81aa6cfe2caf58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 31 Jul 2014 15:50:28 -0300 Subject: Do not default to SecretService keyring --- src/leap/bitmask/util/keyring_helpers.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/leap/bitmask/util/keyring_helpers.py b/src/leap/bitmask/util/keyring_helpers.py index 088e7b3d..2b729b65 100644 --- a/src/leap/bitmask/util/keyring_helpers.py +++ b/src/leap/bitmask/util/keyring_helpers.py @@ -50,11 +50,8 @@ def _get_keyring_with_fallback(): return None kr = keyring.get_keyring() if not canuse(kr): - try: - kr_klass = keyring.backends.SecretService - kr = kr_klass.Keyring() - except AttributeError: - logger.warning("Keyring cannot find SecretService Backend") + logger.debug("No usable keyring found") + return None logger.debug("Selected keyring: %s" % (kr.__class__,)) if not canuse(kr): logger.debug("Not using default keyring since it is obsolete") -- cgit v1.2.3 From 3b469a2c006381ba9e56c1de92be4e87ad67e51b Mon Sep 17 00:00:00 2001 From: kali Date: Sun, 27 Jul 2014 19:00:37 -0500 Subject: Add cancel button for EIP connection. Closes: #4035 This falls in the "quick" workaround category. A proper state machine that extends the four basic connection states is hence needed. We have to accomodate design to have a connection-oriented state machine in the backend (I would favor a twisted protocol for this), and a more lightweight one that conducts the gui-level changes (ie, change the actions / buttons / labels accordingly). Since this "cancel" functionality has been long postponed, I chose to do one more ugly hack here, that is, show and hide dance with a button that just calls the bitmask-root to kill the vpn process. It should work well enough until we get to the reorganization needed for a clean process control for eip. --- changes/4035_add-eip-cancel-button | 1 + src/leap/bitmask/backend/api.py | 4 ++ src/leap/bitmask/backend/backend_proxy.py | 3 ++ src/leap/bitmask/backend/components.py | 7 +++- src/leap/bitmask/backend/leapbackend.py | 6 +++ src/leap/bitmask/backend/leapsignaler.py | 1 + src/leap/bitmask/gui/eip_status.py | 45 +++++++++++++++++++- src/leap/bitmask/gui/mainwindow.py | 64 ++++++++++++++++++----------- src/leap/bitmask/gui/ui/eip_status.ui | 41 ++++++++++-------- src/leap/bitmask/services/eip/conductor.py | 18 ++++++-- src/leap/bitmask/services/eip/vpnprocess.py | 12 ++++++ 11 files changed, 154 insertions(+), 48 deletions(-) create mode 100644 changes/4035_add-eip-cancel-button diff --git a/changes/4035_add-eip-cancel-button b/changes/4035_add-eip-cancel-button new file mode 100644 index 00000000..ea63f8a4 --- /dev/null +++ b/changes/4035_add-eip-cancel-button @@ -0,0 +1 @@ +- Add a button for cancelling ongoing EIP connection. Closes: #4035 diff --git a/src/leap/bitmask/backend/api.py b/src/leap/bitmask/backend/api.py index 4f52e470..3f6c0ad1 100644 --- a/src/leap/bitmask/backend/api.py +++ b/src/leap/bitmask/backend/api.py @@ -20,6 +20,8 @@ Backend available API and SIGNALS definition. STOP_REQUEST = "stop" PING_REQUEST = "PING" +# XXX this needs documentation. What is it used for? + API = ( STOP_REQUEST, # this method needs to be defined in order to support the # backend stop action @@ -56,6 +58,7 @@ API = ( "soledad_close", "soledad_load_offline", "tear_fw_down", + "bitmask_root_vpn_down", "user_cancel_login", "user_change_password", "user_get_logged_in_status", @@ -97,6 +100,7 @@ SIGNALS = ( "eip_status_changed", "eip_stopped", "eip_tear_fw_down", + "eip_bitmask_root_vpn_down", "eip_uninitialized_provider", "eip_vpn_launcher_exception", "imap_stopped", diff --git a/src/leap/bitmask/backend/backend_proxy.py b/src/leap/bitmask/backend/backend_proxy.py index dc30d2cb..e2611251 100644 --- a/src/leap/bitmask/backend/backend_proxy.py +++ b/src/leap/bitmask/backend/backend_proxy.py @@ -18,6 +18,8 @@ The BackendProxy handles calls from the GUI and forwards (through ZMQ) to the backend. """ +# XXX should document the relationship to the API here. + import functools import Queue import threading @@ -37,6 +39,7 @@ class BackendProxy(object): The BackendProxy handles calls from the GUI and forwards (through ZMQ) to the backend. """ + PORT = '5556' SERVER = "tcp://localhost:%s" % PORT diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py index b372db89..f721086b 100644 --- a/src/leap/bitmask/backend/components.py +++ b/src/leap/bitmask/backend/components.py @@ -69,7 +69,6 @@ class ILEAPComponent(zope.interface.Interface): """ Interface that every component for the backend should comply to """ - key = zope.interface.Attribute("Key id for this component") @@ -552,6 +551,12 @@ class EIP(object): """ self._vpn.tear_down_firewall() + def bitmask_root_vpn_down(self): + """ + Bring openvpn down, using bitmask-root helper. + """ + self._vpn.bitmask_root_vpn_down() + def get_gateways_list(self, domain): """ Signal a list of gateways for the given provider. diff --git a/src/leap/bitmask/backend/leapbackend.py b/src/leap/bitmask/backend/leapbackend.py index 6b0328ca..3b023563 100644 --- a/src/leap/bitmask/backend/leapbackend.py +++ b/src/leap/bitmask/backend/leapbackend.py @@ -317,6 +317,12 @@ class LeapBackend(Backend): """ self._eip.tear_fw_down() + def bitmask_root_vpn_down(self): + """ + Signal the need to bring vpn down. + """ + self._eip.bitmask_root_vpn_down() + def user_login(self, provider, username, password): """ Execute the whole authentication process for a user diff --git a/src/leap/bitmask/backend/leapsignaler.py b/src/leap/bitmask/backend/leapsignaler.py index a36e6fdc..c0fdffdc 100644 --- a/src/leap/bitmask/backend/leapsignaler.py +++ b/src/leap/bitmask/backend/leapsignaler.py @@ -58,6 +58,7 @@ class LeapSignaler(SignalerQt): eip_status_changed = QtCore.Signal(dict) eip_stopped = QtCore.Signal() eip_tear_fw_down = QtCore.Signal(object) + eip_bitmask_root_vpn_down = QtCore.Signal(object) eip_uninitialized_provider = QtCore.Signal() eip_vpn_launcher_exception = QtCore.Signal() diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index a707050a..f283b148 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -70,8 +70,11 @@ class EIPStatusWidget(QtGui.QWidget): self.ui.eip_bandwidth.hide() self.hide_fw_down_button() + self.hide_eip_cancel_button() self.ui.btnFwDown.clicked.connect( self._on_fw_down_button_clicked) + self.ui.btnEipCancel.clicked.connect( + self._on_eip_cancel_button_clicked) # Set the EIP status icons self.CONNECTING_ICON = None @@ -88,6 +91,7 @@ class EIPStatusWidget(QtGui.QWidget): self._provider = "" self.is_restart = False self.is_cold_start = True + self.user_cancelled = False self.missing_helpers = False @@ -285,6 +289,8 @@ class EIPStatusWidget(QtGui.QWidget): Triggered when the app activates eip. Disables the start/stop button. """ + # XXX hack -- we show the cancel button instead. + self.ui.btnEipStartStop.hide() self.set_startstop_enabled(False) msg = self.tr("Encrypted Internet is starting") self.set_eip_message(msg) @@ -295,6 +301,9 @@ class EIPStatusWidget(QtGui.QWidget): Triggered when a default provider_config has not been found. Disables the start button and adds instructions to the user. """ + # XXX this name is unfortunate. "disable" is also applied to a + # pushbutton being grayed out. + logger.debug('Hiding EIP start button') # you might be tempted to change this for a .setEnabled(False). # it won't work. it's under the claws of the state machine. @@ -327,6 +336,7 @@ class EIPStatusWidget(QtGui.QWidget): """ # logger.debug('Showing EIP start button') self.eip_button.show() + self.hide_eip_cancel_button() # Restore the eip action menu menu = self._systray.contextMenu() @@ -420,6 +430,28 @@ class EIPStatusWidget(QtGui.QWidget): self.set_eip_message(msg) self.set_eip_status("") + def hide_eip_cancel_button(self): + """ + Hide eip-cancel button. + """ + self.ui.btnEipCancel.hide() + + def show_eip_cancel_button(self): + """ + Show eip-cancel button. + """ + self.ui.btnEipCancel.show() + self.user_cancelled = False + + def _on_eip_cancel_button_clicked(self): + """ + Call backend to kill the openvpn process with root privileges. + """ + self.eip_conductor.cancelled = True + self.eip_conductor._backend.bitmask_root_vpn_down() + self.user_cancelled = True + self.hide_eip_cancel_button() + @QtCore.Slot(dict) def eip_stopped(self, restart=False, failed=False): """ @@ -433,6 +465,11 @@ class EIPStatusWidget(QtGui.QWidget): self._reset_traffic_rates() self.ui.eip_bandwidth.hide() + if self.user_cancelled: + self.eip_conductor._backend.tear_fw_down() + self.eip_button.show() + failed = False + # This is assuming the firewall works correctly, but we should test fw # status positively. # Or better call it from the conductor... @@ -447,6 +484,7 @@ class EIPStatusWidget(QtGui.QWidget): msg = failed_msg else: msg = clear_traffic + self.set_eip_message(msg) self.ui.lblEIPStatus.show() self.show() @@ -523,16 +561,19 @@ class EIPStatusWidget(QtGui.QWidget): self.eipconnection.qtsigs.connected_signal.emit() self._on_eip_connected() self.is_cold_start = False + self.hide_eip_cancel_button() + self.eip_button.show() # XXX should lookup vpn_state map in EIPConnection elif vpn_state == "AUTH": self.set_eip_status(self.tr("Authenticating...")) + + # XXX should be handled by a future state machine instead. + self.show_eip_cancel_button() # we wipe up any previous error info in the EIP message # when we detect vpn authentication is happening msg = self.tr("Encrypted Internet is starting") self.set_eip_message(msg) - # on the first-run path, we hadn't showed the button yet. - self.eip_button.show() elif vpn_state == "GET_CONFIG": self.set_eip_status(self.tr("Retrieving configuration...")) elif vpn_state == "WAIT": diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 46f0928f..25bda305 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -166,6 +166,9 @@ class MainWindow(QtGui.QMainWindow): self._disable_eip_missing_helpers) self.ui.eipLayout.addWidget(self._eip_status) + + # XXX we should get rid of the circular refs + # conductor <-> status, right now keeping state on the widget ifself. self._eip_conductor.add_eip_widget(self._eip_status) self._eip_conductor.connect_signals() @@ -180,6 +183,7 @@ class MainWindow(QtGui.QMainWindow): self.eip_needs_login.connect(self._eip_status.disable_eip_start) self.eip_needs_login.connect(self._disable_eip_start_action) + # XXX all this info about state should move to eip conductor too self._already_started_eip = False self._trying_to_start_eip = False @@ -561,7 +565,7 @@ class MainWindow(QtGui.QMainWindow): TRIGGERS: self.ui.action_show_logs.triggered - Displays the window with the history of messages logged until now + Display the window with the history of messages logged until now and displays the new ones on arrival. """ if self._logger_window is None: @@ -582,7 +586,7 @@ class MainWindow(QtGui.QMainWindow): TRIGGERS: self.ui.action_advanced_key_management.triggered - Displays the Advanced Key Management dialog. + Display the Advanced Key Management dialog. """ domain = self._login_widget.get_selected_provider() logged_user = "{0}@{1}".format(self._logged_user, domain) @@ -604,7 +608,7 @@ class MainWindow(QtGui.QMainWindow): self.ui.btnPreferences.clicked (disabled for now) self.ui.action_preferences - Displays the preferences window. + Display the preferences window. """ user = self._logged_user domain = self._login_widget.get_selected_provider() @@ -722,7 +726,7 @@ class MainWindow(QtGui.QMainWindow): self.ui.btnEIPPreferences.clicked self.ui.action_eip_preferences (disabled for now) - Displays the EIP preferences window. + Display the EIP preferences window. """ domain = self._login_widget.get_selected_provider() pref = EIPPreferencesWindow(self, domain, @@ -748,7 +752,7 @@ class MainWindow(QtGui.QMainWindow): TRIGGERS: self.new_updates - Displays the new updates label and sets the updates_content + Display the new updates label and sets the updates_content :param req: Request type :type req: leap.common.events.events_pb2.SignalRequest @@ -963,7 +967,7 @@ class MainWindow(QtGui.QMainWindow): :param reason: the reason why the tray got activated. :type reason: int - Displays the context menu from the tray icon + Display the context menu from the tray icon """ self._update_hideshow_menu() @@ -977,7 +981,7 @@ class MainWindow(QtGui.QMainWindow): def _update_hideshow_menu(self): """ - Updates the Hide/Show main window menu text based on the + Update the Hide/Show main window menu text based on the visibility of the window. """ get_action = lambda visible: ( @@ -994,7 +998,7 @@ class MainWindow(QtGui.QMainWindow): TRIGGERS: self._action_visible.triggered - Toggles the window visibility + Toggle the window visibility """ visible = self.isVisible() and self.isActiveWindow() @@ -1022,7 +1026,7 @@ class MainWindow(QtGui.QMainWindow): def _center_window(self): """ - Centers the mainwindow based on the desktop geometry + Center the main window based on the desktop geometry """ geometry = self._settings.get_geometry() state = self._settings.get_windowstate() @@ -1124,7 +1128,7 @@ class MainWindow(QtGui.QMainWindow): def changeEvent(self, e): """ - Reimplements the changeEvent method to minimize to tray + Reimplementation of changeEvent method to minimize to tray """ if not IS_MAC and \ QtGui.QSystemTrayIcon.isSystemTrayAvailable() and \ @@ -1158,7 +1162,7 @@ class MainWindow(QtGui.QMainWindow): def _first_run(self): """ - Returns True if there are no configured providers. False otherwise + Return True if there are no configured providers. False otherwise :rtype: bool """ @@ -1169,7 +1173,7 @@ class MainWindow(QtGui.QMainWindow): def _download_provider_config(self): """ - Starts the bootstrapping sequence. It will download the + Start the bootstrapping sequence. It will download the provider configuration if it's not present, otherwise will emit the corresponding signals inmediately """ @@ -1199,8 +1203,9 @@ class MainWindow(QtGui.QMainWindow): @QtCore.Slot() def _login_problem_provider(self): """ - Warns the user about a problem with the provider during login. + Warn the user about a problem with the provider during login. """ + # XXX triggers? self._login_widget.set_status( self.tr("Unable to login: Problem with provider")) self._login_widget.set_enabled(True) @@ -1211,7 +1216,7 @@ class MainWindow(QtGui.QMainWindow): TRIGGERS: self._login_widget.login - Starts the login sequence. Which involves bootstrapping the + Start the login sequence. Which involves bootstrapping the selected provider if the selection is valid (not empty), then start the SRP authentication, and as the last step bootstrapping the EIP service @@ -1256,7 +1261,7 @@ class MainWindow(QtGui.QMainWindow): TRIGGERS: self._login_widget.cancel_login - Stops the login sequence. + Stop the login sequence. """ logger.debug("Cancelling log in.") self._cancel_ongoing_defers() @@ -1280,7 +1285,7 @@ class MainWindow(QtGui.QMainWindow): Signaler.prov_cancelled_setup fired by self._backend.provider_cancel_setup() - This method re-enables the login widget and display a message for + Re-enable the login widget and display a message for the cancelled operation. """ self._login_widget.set_status(self.tr("Log in cancelled by the user.")) @@ -1340,7 +1345,7 @@ class MainWindow(QtGui.QMainWindow): def _start_eip_bootstrap(self): """ - Changes the stackedWidget index to the EIP status one and + Change the stackedWidget index to the EIP status one and triggers the eip bootstrapping. """ @@ -1384,7 +1389,7 @@ class MainWindow(QtGui.QMainWindow): def _provides_mx_and_enabled(self): """ - Defines if the current provider provides mx and if we have it enabled. + Define if the current provider provides mx and if we have it enabled. :returns: True if provides and is enabled, False otherwise :rtype: bool @@ -1401,7 +1406,7 @@ class MainWindow(QtGui.QMainWindow): def _provides_eip_and_enabled(self): """ - Defines if the current provider provides eip and if we have it enabled. + Define if the current provider provides eip and if we have it enabled. :returns: True if provides and is enabled, False otherwise :rtype: bool @@ -1503,14 +1508,14 @@ class MainWindow(QtGui.QMainWindow): @QtCore.Slot() def _disable_eip_start_action(self): """ - Disables the EIP start action in the systray menu. + Disable the EIP start action in the systray menu. """ self._action_eip_startstop.setEnabled(False) @QtCore.Slot() def _enable_eip_start_action(self): """ - Enables the EIP start action in the systray menu. + Enable the EIP start action in the systray menu. """ self._action_eip_startstop.setEnabled(True) self._eip_status.enable_eip_start() @@ -1567,7 +1572,7 @@ class MainWindow(QtGui.QMainWindow): def _try_autostart_eip(self): """ - Tries to autostart EIP + Try to autostart EIP. """ settings = self._settings default_provider = settings.get_defaultprovider() @@ -1587,6 +1592,8 @@ class MainWindow(QtGui.QMainWindow): :param autostart: we are autostarting EIP when this is True :type autostart: bool """ + # XXX should move to EIP conductor. + # during autostart we assume that the provider provides EIP if autostart: should_start = EIP_SERVICE in self._enabled_services @@ -1606,6 +1613,12 @@ class MainWindow(QtGui.QMainWindow): self._enable_eip_start_action() self._eip_status.set_eip_status( self.tr("Starting...")) + self._eip_status.show_eip_cancel_button() + + # We were disabling the button, but now that we have + # a cancel button we just hide it. It will be visible + # when the connection is completed successfully. + self._eip_status.eip_button.hide() self._eip_status.eip_button.setEnabled(False) domain = self._login_widget.get_selected_provider() @@ -1680,7 +1693,7 @@ class MainWindow(QtGui.QMainWindow): TRIGGERS: self._login_widget.logout - Starts the logout sequence + Start the logout sequence """ self._cancel_ongoing_defers() @@ -1708,7 +1721,7 @@ class MainWindow(QtGui.QMainWindow): TRIGGER: self._srp_auth.logout_ok - Switches the stackedWidget back to the login stage after + Switch the stackedWidget back to the login stage after logging out """ self._login_widget.done_logout() @@ -1729,7 +1742,7 @@ class MainWindow(QtGui.QMainWindow): self._backend.signaler.prov_https_connection self._backend.signaler.prov_download_ca_cert - If there was a problem, displays it, otherwise it does nothing. + If there was a problem, display it, otherwise it does nothing. This is used for intermediate bootstrapping stages, in case they fail. """ @@ -1783,6 +1796,7 @@ class MainWindow(QtGui.QMainWindow): imap_stopped = lambda: self._remove_service('imap') self._leap_signaler.imap_stopped.connect(imap_stopped) + # XXX change name, already used in conductor. eip_stopped = lambda: self._remove_service('eip') self._leap_signaler.eip_stopped.connect(eip_stopped) diff --git a/src/leap/bitmask/gui/ui/eip_status.ui b/src/leap/bitmask/gui/ui/eip_status.ui index 7216bb0a..e0996620 100644 --- a/src/leap/bitmask/gui/ui/eip_status.ui +++ b/src/leap/bitmask/gui/ui/eip_status.ui @@ -28,13 +28,26 @@ 0 - + Turn On + + + + Qt::Horizontal + + + + 40 + 0 + + + + @@ -86,7 +99,7 @@ - + @@ -105,20 +118,7 @@ - - - - Qt::Horizontal - - - - 40 - 0 - - - - - + @@ -239,7 +239,7 @@ - + Turn Off @@ -253,6 +253,13 @@ + + + + Cancel + + + diff --git a/src/leap/bitmask/services/eip/conductor.py b/src/leap/bitmask/services/eip/conductor.py index bb07809a..b755f283 100644 --- a/src/leap/bitmask/services/eip/conductor.py +++ b/src/leap/bitmask/services/eip/conductor.py @@ -137,6 +137,7 @@ class EIPConductor(object): else: self._eip_status.eip_pre_up() self.user_stopped_eip = False + self.cancelled = False self._eip_status.hide_fw_down_button() # Until we set an option in the preferences window, we'll assume that @@ -175,6 +176,9 @@ class EIPConductor(object): :param failed: whether this is the final step of a retry sequence :type failed: bool """ + # XXX we should NOT keep status in the widget, but we do for a series + # of hacks related to restarts. All status should be kept in a backend + # object, widgets should be just widgets. self._eip_status.is_restart = restart self.user_stopped_eip = not restart and not failed @@ -302,18 +306,26 @@ class EIPConductor(object): # bitmask-root is masking the exitcode, so we might need # to fix it on that side. # if exitCode != 0 and not self.user_stopped_eip: - if not self.user_stopped_eip: + + if not self.user_stopped_eip and not self.cancelled: + error = True eip_status_label = self._eip_status.tr( "{0} finished in an unexpected manner!") eip_status_label = eip_status_label.format(self.eip_name) - self._eip_status.eip_stopped() self._eip_status.set_eip_status_icon("error") self._eip_status.set_eip_status(eip_status_label, - error=True) + error=error) + self._eip_status.eip_stopped() signal = self.qtsigs.connection_died_signal self._eip_status.show_fw_down_button() self._eip_status.eip_failed_to_connect() + if self.cancelled: + signal = self.qtsigs.connection_aborted_signal + self._eip_status.set_eip_status_icon("error") + self._eip_status.eip_stopped() + self._eip_status.set_eip_status("", error=False) + if exitCode == 0 and IS_MAC: # XXX remove this warning after I fix cocoasudo. logger.warning("The above exit code MIGHT BE WRONG.") diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index d1a3fdaa..c6a7b98b 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -263,6 +263,18 @@ class VPN(object): BM_ROOT, "firewall", "stop"]) return True if exitCode is 0 else False + def bitmask_root_vpn_down(self): + """ + Bring openvpn down using the privileged wrapper. + """ + if IS_MAC: + # We don't support Mac so far + return True + BM_ROOT = force_eval(linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT) + exitCode = subprocess.call(["pkexec", + BM_ROOT, "openvpn", "stop"]) + return True if exitCode is 0 else False + def _kill_if_left_alive(self, tries=0): """ Check if the process is still alive, and send a -- cgit v1.2.3 From b0365dbb706af23375fd6676d2ffe4a64a824754 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 4 Aug 2014 15:29:57 -0300 Subject: Disable daemon mode, we use processes in the backend. --- changes/enable-child-process-in-backend | 1 + src/leap/bitmask/app.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 changes/enable-child-process-in-backend diff --git a/changes/enable-child-process-in-backend b/changes/enable-child-process-in-backend new file mode 100644 index 00000000..1b6f246e --- /dev/null +++ b/changes/enable-child-process-in-backend @@ -0,0 +1 @@ +- Disable daemon mode when we run the backend so we can spawn child processes on it. diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 37ded92a..ad886bc4 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -183,7 +183,9 @@ def start_app(): frontend_pid = os.getpid() backend = lambda: run_backend(opts.danger, flags_dict, frontend_pid) backend_process = multiprocessing.Process(target=backend, name='Backend') - backend_process.daemon = True + # we don't set the 'daemon mode' since we need to start child processes in + # the backend + # backend_process.daemon = True backend_process.start() run_frontend(options, flags_dict, backend_pid=backend_process.pid) -- cgit v1.2.3 From 2eb3805dc2c470feab5a3d3bc283e82ff7c6bce7 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 5 Aug 2014 18:03:05 -0300 Subject: Reduce height to fit on smaller resolutions. Closes #5722. --- changes/bug_use-smaller-window-to-fit-lower-resolutions | 1 + src/leap/bitmask/gui/ui/mainwindow.ui | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changes/bug_use-smaller-window-to-fit-lower-resolutions diff --git a/changes/bug_use-smaller-window-to-fit-lower-resolutions b/changes/bug_use-smaller-window-to-fit-lower-resolutions new file mode 100644 index 00000000..d9df69ae --- /dev/null +++ b/changes/bug_use-smaller-window-to-fit-lower-resolutions @@ -0,0 +1 @@ +- Use smaller height on the window so it fits better on smaller resolutions. Closes #5722. diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui index d755115a..1eeb636f 100644 --- a/src/leap/bitmask/gui/ui/mainwindow.ui +++ b/src/leap/bitmask/gui/ui/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 524 - 722 + 640 @@ -75,7 +75,7 @@ 0 0 524 - 667 + 589 @@ -297,7 +297,7 @@ 0 0 524 - 23 + 21 -- cgit v1.2.3 From 9a7485bcaf5460d68b7f601927500299bb2ca9f0 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 5 Aug 2014 12:43:25 -0500 Subject: fix uncatched error with missing polkit. Closes: #5955 --- changes/bug_5955_fix-vpnlauncher-error-ui | 1 + src/leap/bitmask/backend/backend.py | 10 ++++++++++ src/leap/bitmask/gui/eip_status.py | 11 ++++++++++- src/leap/bitmask/services/eip/conductor.py | 13 ++++++++++--- src/leap/bitmask/services/eip/vpnprocess.py | 19 ++++++++++++++++++- 5 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 changes/bug_5955_fix-vpnlauncher-error-ui diff --git a/changes/bug_5955_fix-vpnlauncher-error-ui b/changes/bug_5955_fix-vpnlauncher-error-ui new file mode 100644 index 00000000..d322288e --- /dev/null +++ b/changes/bug_5955_fix-vpnlauncher-error-ui @@ -0,0 +1 @@ +- Fix the handling of vpn launcher errors in the UI. Closes: #5955 diff --git a/src/leap/bitmask/backend/backend.py b/src/leap/bitmask/backend/backend.py index 67ffe35a..37535f37 100644 --- a/src/leap/bitmask/backend/backend.py +++ b/src/leap/bitmask/backend/backend.py @@ -14,6 +14,11 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . + +# FIXME this is missing module documentation. It would be fine to say a couple +# of lines about the whole backend architecture. +# TODO use txzmq bindings instead. + import json import threading import time @@ -38,7 +43,11 @@ class Backend(object): Backend server. Receives signals from backend_proxy and emit signals if needed. """ + # XXX this should not be hardcoded. Make it configurable. PORT = '5556' + + # XXX we might want to make this configurable per-platform, + # and use the most performant socket type on each one. BIND_ADDR = "tcp://127.0.0.1:%s" % PORT PING_INTERVAL = 2 # secs @@ -67,6 +76,7 @@ class Backend(object): # Start an authenticator for this context. auth = ThreadAuthenticator(context) auth.start() + # XXX do not hardcode this here. auth.allow('127.0.0.1') # Tell authenticator to use the certificate in a directory diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index f283b148..1e764a8c 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -367,6 +367,7 @@ class EIPStatusWidget(QtGui.QWidget): leap_assert_type(error, bool) if error: logger.error(status) + self.hide_eip_cancel_button() else: logger.debug(status) self._eip_status = status @@ -723,9 +724,17 @@ class EIPStatusWidget(QtGui.QWidget): def _on_eip_vpn_launcher_exception(self): # XXX We should implement again translatable exceptions so # we can pass a translatable string to the panel (usermessage attr) - self.set_eip_status("VPN Launcher error.", error=True) + # FIXME this logic should belong to the backend, not to this + # widget. self.set_eipstatus_off() + st = self.tr("VPN Launcher error. See the logs for more info.") + self.set_eip_status(st, error=True) + + msg = self.tr("Encrypted Internet failed to start") + self.set_eip_message(msg) + self.show_fw_down_button() + self.aborted() def _on_eip_no_polkit_agent_error(self): diff --git a/src/leap/bitmask/services/eip/conductor.py b/src/leap/bitmask/services/eip/conductor.py index b755f283..0ee56628 100644 --- a/src/leap/bitmask/services/eip/conductor.py +++ b/src/leap/bitmask/services/eip/conductor.py @@ -16,6 +16,9 @@ # along with this program. If not, see . """ EIP Conductor module. + +This handles Qt Signals and triggers the calls to the backend, +where the VPNProcess has been initialized. """ import logging @@ -90,7 +93,7 @@ class EIPConductor(object): def start_eip_machine(self, action): """ - Initializes and starts the EIP state machine. + Initialize and start the EIP state machine. Needs the reference to the eip_status widget not to be empty. :action: QtAction @@ -124,8 +127,12 @@ class EIPConductor(object): @QtCore.Slot() def _start_eip(self): """ - Starts EIP. + Start EIP. + + This set a couple of status flags and calls the start procedure in the + backend. """ + # TODO status should be kept in a singleton in the backend. st = self._eip_status is_restart = st and st.is_restart @@ -271,7 +278,7 @@ class EIPConductor(object): TRIGGERS: Signaler.eip_process_finished - Triggered when the EIP/VPN process finishes to set the UI + Triggered when the EIP/VPN process finishes, in order to set the UI accordingly. Ideally we would have the right exit code here, diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index c6a7b98b..c7159a93 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -202,7 +202,24 @@ class VPN(object): "aborting openvpn launch.") return - cmd = vpnproc.getCommand() + # FIXME it would be good to document where the + # errors here are catched, since we currently handle them + # at the frontend layer. This *should* move to be handled entirely + # in the backend. + # exception is indeed technically catched in backend, then converted + # into a signal, that is catched in the eip_status widget in the + # frontend, and converted into a signal to abort the connection that is + # sent to the backend again. + + # the whole exception catching should be done in the backend, without + # the ping-pong to the frontend, and without adding any logical checks + # in the frontend. We should just communicate UI changes to frontend, + # and abstract us away from anything else. + try: + cmd = vpnproc.getCommand() + except Exception: + logger.error("Error while getting vpn command...") + raise env = os.environ for key, val in vpnproc.vpn_env.items(): env[key] = val -- cgit v1.2.3 From f4e6d6a77b8b5498e9190c3d35841ef39222cfec Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 6 Aug 2014 14:46:37 -0300 Subject: Replace twisted thread with QThread. This fix the bug that prevents pastebin to work. Closes #5949. --- changes/bug-5949_pastebin-does-not-work | 1 + src/leap/bitmask/gui/loggerwindow.py | 113 +++++++++++++++++--------------- src/leap/bitmask/gui/mainwindow.py | 2 +- 3 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 changes/bug-5949_pastebin-does-not-work diff --git a/changes/bug-5949_pastebin-does-not-work b/changes/bug-5949_pastebin-does-not-work new file mode 100644 index 00000000..aa983184 --- /dev/null +++ b/changes/bug-5949_pastebin-does-not-work @@ -0,0 +1 @@ +- Replace twisted thread with QThread and get pastebin send working. Closes #5949. diff --git a/src/leap/bitmask/gui/loggerwindow.py b/src/leap/bitmask/gui/loggerwindow.py index 3a8354b1..c4f44dd7 100644 --- a/src/leap/bitmask/gui/loggerwindow.py +++ b/src/leap/bitmask/gui/loggerwindow.py @@ -18,11 +18,10 @@ """ History log window """ -import logging import cgi +import logging from PySide import QtCore, QtGui -from twisted.internet import threads from ui_loggerwindow import Ui_LoggerWindow @@ -38,6 +37,9 @@ class LoggerWindow(QtGui.QDialog): """ Window that displays a history of the logged messages in the app. """ + _paste_ok = QtCore.Signal(object) + _paste_error = QtCore.Signal(object) + def __init__(self, handler): """ Initialize the widget with the custom handler. @@ -45,9 +47,6 @@ class LoggerWindow(QtGui.QDialog): :param handler: Custom handler that supports history and signal. :type handler: LeapLogHandler. """ - from twisted.internet import reactor - self.reactor = reactor - QtGui.QDialog.__init__(self) leap_assert(handler, "We need a handler for the logger window") leap_assert_type(handler, LeapLogHandler) @@ -67,6 +66,9 @@ class LoggerWindow(QtGui.QDialog): self.ui.cbCaseInsensitive.stateChanged.connect(self._load_history) self.ui.btnPastebin.clicked.connect(self._pastebin_this) + self._paste_ok.connect(self._pastebin_ok) + self._paste_error.connect(self._pastebin_err) + self._current_filter = "" self._current_history = "" @@ -193,6 +195,45 @@ class LoggerWindow(QtGui.QDialog): self.ui.btnPastebin.setText(self.tr("Send to Pastebin.com")) self.ui.btnPastebin.setEnabled(True) + def _pastebin_ok(self, link): + """ + Handle a successful paste. + + :param link: the recently created pastebin link. + :type link: str + """ + self._set_pastebin_sending(False) + msg = self.tr("Your pastebin link {0}") + msg = msg.format(link) + + # We save the dialog in an instance member to avoid dialog being + # deleted right after we exit this method + self._msgBox = msgBox = QtGui.QMessageBox( + QtGui.QMessageBox.Information, self.tr("Pastebin OK"), msg) + msgBox.setWindowModality(QtCore.Qt.NonModal) + msgBox.show() + + def _pastebin_err(self, failure): + """ + Handle a failure in paste. + + :param failure: the exception that made the paste fail. + :type failure: Exception + """ + self._set_pastebin_sending(False) + logger.error(repr(failure)) + + msg = self.tr("Sending logs to Pastebin failed!") + if isinstance(failure, pastebin.PostLimitError): + msg = self.tr('Maximum posts per day reached') + + # We save the dialog in an instance member to avoid dialog being + # deleted right after we exit this method + self._msgBox = msgBox = QtGui.QMessageBox( + QtGui.QMessageBox.Critical, self.tr("Pastebin Error"), msg) + msgBox.setWindowModality(QtCore.Qt.NonModal) + msgBox.show() + def _pastebin_this(self): """ Send the current log history to pastebin.com and gives the user a link @@ -204,55 +245,19 @@ class LoggerWindow(QtGui.QDialog): """ content = self._current_history pb = pastebin.PastebinAPI() - link = pb.paste(PASTEBIN_API_DEV_KEY, content, - paste_name="Bitmask log", - paste_expire_date='1M') - - # convert to 'raw' link - link = "http://pastebin.com/raw.php?i=" + link.split('/')[-1] - - return link - - def pastebin_ok(link): - """ - Callback handler for `do_pastebin`. - - :param link: the recently created pastebin link. - :type link: str - """ - self._set_pastebin_sending(False) - msg = self.tr("Your pastebin link {0}") - msg = msg.format(link) - - # We save the dialog in an instance member to avoid dialog being - # deleted right after we exit this method - self._msgBox = msgBox = QtGui.QMessageBox( - QtGui.QMessageBox.Information, self.tr("Pastebin OK"), msg) - msgBox.setWindowModality(QtCore.Qt.NonModal) - msgBox.show() - - def pastebin_err(failure): - """ - Errback handler for `do_pastebin`. - - :param failure: the failure that triggered the errback. - :type failure: twisted.python.failure.Failure - """ - self._set_pastebin_sending(False) - logger.error(repr(failure)) - - msg = self.tr("Sending logs to Pastebin failed!") - if failure.check(pastebin.PostLimitError): - msg = self.tr('Maximum posts per day reached') + try: + link = pb.paste(PASTEBIN_API_DEV_KEY, content, + paste_name="Bitmask log", + paste_expire_date='1M') + # convert to 'raw' link + link = "http://pastebin.com/raw.php?i=" + link.split('/')[-1] - # We save the dialog in an instance member to avoid dialog being - # deleted right after we exit this method - self._msgBox = msgBox = QtGui.QMessageBox( - QtGui.QMessageBox.Critical, self.tr("Pastebin Error"), msg) - msgBox.setWindowModality(QtCore.Qt.NonModal) - msgBox.show() + self._paste_ok.emit(link) + except Exception as e: + self._paste_error.emit(e) self._set_pastebin_sending(True) - d = threads.deferToThread(do_pastebin) - d.addCallback(pastebin_ok) - d.addErrback(pastebin_err) + + self._paste_thread = QtCore.QThread() + self._paste_thread.run = lambda: do_pastebin() + self._paste_thread.start() diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 25bda305..1bc4c96f 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -1837,7 +1837,7 @@ class MainWindow(QtGui.QMainWindow): if self._wizard: self._wizard.close() - if self._logger_window: + if self._logger_window is not None: self._logger_window.close() # Set this in case that the app is hidden -- cgit v1.2.3 From e6a7feeb93a27ef36d15dddcb45c2cc4812a37b0 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 6 Aug 2014 16:03:49 -0300 Subject: Fix logger window blocking the bitmask quit(). - Set the logger window parent, - don't use an mainwindow instance variable to hold the window object. This fix have the side offect that prevent multiple log windows being created at the same time, but it does not causes any side effect or problem. --- changes/bug_logger-hangs-quit | 1 + src/leap/bitmask/gui/loggerwindow.py | 4 ++-- src/leap/bitmask/gui/mainwindow.py | 21 ++++++--------------- 3 files changed, 9 insertions(+), 17 deletions(-) create mode 100644 changes/bug_logger-hangs-quit diff --git a/changes/bug_logger-hangs-quit b/changes/bug_logger-hangs-quit new file mode 100644 index 00000000..b76f6218 --- /dev/null +++ b/changes/bug_logger-hangs-quit @@ -0,0 +1 @@ +- Fix logger window blocking the bitmask quit(). diff --git a/src/leap/bitmask/gui/loggerwindow.py b/src/leap/bitmask/gui/loggerwindow.py index c4f44dd7..360dd5f0 100644 --- a/src/leap/bitmask/gui/loggerwindow.py +++ b/src/leap/bitmask/gui/loggerwindow.py @@ -40,14 +40,14 @@ class LoggerWindow(QtGui.QDialog): _paste_ok = QtCore.Signal(object) _paste_error = QtCore.Signal(object) - def __init__(self, handler): + def __init__(self, parent, handler): """ Initialize the widget with the custom handler. :param handler: Custom handler that supports history and signal. :type handler: LeapLogHandler. """ - QtGui.QDialog.__init__(self) + QtGui.QDialog.__init__(self, parent) leap_assert(handler, "We need a handler for the logger window") leap_assert_type(handler, LeapLogHandler) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 1bc4c96f..389ff172 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -277,8 +277,6 @@ class MainWindow(QtGui.QMainWindow): self._wizard = None self._wizard_firstrun = False - self._logger_window = None - self._start_hidden = start_hidden self._backend_pid = backend_pid @@ -568,17 +566,13 @@ class MainWindow(QtGui.QMainWindow): Display the window with the history of messages logged until now and displays the new ones on arrival. """ - if self._logger_window is None: - leap_log_handler = self._get_leap_logging_handler() - if leap_log_handler is None: - logger.error('Leap logger handler not found') - return - else: - self._logger_window = LoggerWindow(handler=leap_log_handler) - self._logger_window.setVisible( - not self._logger_window.isVisible()) + leap_log_handler = self._get_leap_logging_handler() + if leap_log_handler is None: + logger.error('Leap logger handler not found') + return else: - self._logger_window.setVisible(not self._logger_window.isVisible()) + lw = LoggerWindow(self, handler=leap_log_handler) + lw.show() @QtCore.Slot() def _show_AKM(self): @@ -1837,9 +1831,6 @@ class MainWindow(QtGui.QMainWindow): if self._wizard: self._wizard.close() - if self._logger_window is not None: - self._logger_window.close() - # Set this in case that the app is hidden QtGui.QApplication.setQuitOnLastWindowClosed(True) -- cgit v1.2.3