From 12e238c81ed07879f1b55f327a67682233dc7ee3 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 30 Jun 2014 14:36:52 -0300 Subject: Handle undefined danger flag. --- src/leap/bitmask/util/leap_argparse.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py index 0717aea5..b7428488 100644 --- a/src/leap/bitmask/util/leap_argparse.py +++ b/src/leap/bitmask/util/leap_argparse.py @@ -132,4 +132,9 @@ def get_options(): """ parser = build_parser() opts, unknown = parser.parse_known_args() + + # we add this option manually since it's not defined for 'release version' + if not IS_RELEASE_VERSION: + opts.danger = False + return opts -- cgit v1.2.3 From cae0889b4a8d915855c375f6d828659931e24df2 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 30 Jun 2014 14:51:53 -0300 Subject: Add the flag manually if it is release version. --- src/leap/bitmask/util/leap_argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py index b7428488..c7fed0a3 100644 --- a/src/leap/bitmask/util/leap_argparse.py +++ b/src/leap/bitmask/util/leap_argparse.py @@ -134,7 +134,7 @@ def get_options(): opts, unknown = parser.parse_known_args() # we add this option manually since it's not defined for 'release version' - if not IS_RELEASE_VERSION: + if IS_RELEASE_VERSION: opts.danger = False return opts -- cgit v1.2.3 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