summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/services
diff options
context:
space:
mode:
authorTomás Touceda <chiiph@leap.se>2014-05-16 16:18:59 -0300
committerTomás Touceda <chiiph@leap.se>2014-05-16 16:18:59 -0300
commit4c550c558dcb554b3ea1bc0246492e39e8532886 (patch)
tree3a18999e62eb18091d002e6c1c7222846a92947b /src/leap/bitmask/services
parent81715dc47d77934c4f67d2527a56c28f58f0345d (diff)
parentd52daa106a97562e1cc67c5a2f242f2ef9884508 (diff)
Merge branch 'release-0.5.1'0.5.1
Diffstat (limited to 'src/leap/bitmask/services')
-rw-r--r--src/leap/bitmask/services/abstractbootstrapper.py4
-rw-r--r--src/leap/bitmask/services/eip/eipbootstrapper.py22
-rw-r--r--src/leap/bitmask/services/eip/linuxvpnlauncher.py112
-rw-r--r--src/leap/bitmask/services/eip/vpnlauncher.py124
-rw-r--r--src/leap/bitmask/services/eip/vpnprocess.py261
-rw-r--r--src/leap/bitmask/services/mail/conductor.py119
-rw-r--r--src/leap/bitmask/services/mail/smtpbootstrapper.py135
-rw-r--r--src/leap/bitmask/services/soledad/soledadbootstrapper.py96
8 files changed, 424 insertions, 449 deletions
diff --git a/src/leap/bitmask/services/abstractbootstrapper.py b/src/leap/bitmask/services/abstractbootstrapper.py
index fc6bd3e9..77929b75 100644
--- a/src/leap/bitmask/services/abstractbootstrapper.py
+++ b/src/leap/bitmask/services/abstractbootstrapper.py
@@ -78,6 +78,7 @@ class AbstractBootstrapper(QtCore.QObject):
self._signal_to_emit = None
self._err_msg = None
self._signaler = signaler
+ self._cancel_signal = None
def _gui_errback(self, failure):
"""
@@ -95,7 +96,8 @@ class AbstractBootstrapper(QtCore.QObject):
if failure.check(CancelledError):
logger.debug("Defer cancelled.")
failure.trap(Exception)
- self._signaler.signal(self._signaler.PROV_CANCELLED_SETUP)
+ if self._signaler is not None and self._cancel_signal is not None:
+ self._signaler.signal(self._cancel_signal)
return
if self._signal_to_emit:
diff --git a/src/leap/bitmask/services/eip/eipbootstrapper.py b/src/leap/bitmask/services/eip/eipbootstrapper.py
index 5a238a1c..c77977ce 100644
--- a/src/leap/bitmask/services/eip/eipbootstrapper.py
+++ b/src/leap/bitmask/services/eip/eipbootstrapper.py
@@ -20,8 +20,6 @@ EIP bootstrapping
import logging
import os
-from PySide import QtCore
-
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.crypto.certs import download_client_cert
from leap.bitmask.services import download_service_config
@@ -41,17 +39,21 @@ class EIPBootstrapper(AbstractBootstrapper):
If a check fails, the subsequent checks are not executed
"""
- # All dicts returned are of the form
- # {"passed": bool, "error": str}
- download_config = QtCore.Signal(dict)
- download_client_certificate = QtCore.Signal(dict)
+ def __init__(self, signaler=None):
+ """
+ Constructor for the EIP bootstrapper object
- def __init__(self):
- AbstractBootstrapper.__init__(self)
+ :param signaler: Signaler object used to receive notifications
+ from the backend
+ :type signaler: Signaler
+ """
+ AbstractBootstrapper.__init__(self, signaler)
self._provider_config = None
self._eip_config = None
self._download_if_needed = False
+ if signaler is not None:
+ self._cancel_signal = signaler.EIP_CANCELLED_SETUP
def _download_config(self, *args):
"""
@@ -114,9 +116,9 @@ class EIPBootstrapper(AbstractBootstrapper):
self._download_if_needed = download_if_needed
cb_chain = [
- (self._download_config, self.download_config),
+ (self._download_config, self._signaler.EIP_CONFIG_READY),
(self._download_client_certificates,
- self.download_client_certificate)
+ self._signaler.EIP_CLIENT_CERTIFICATE_READY)
]
return self.addCallbackChain(cb_chain)
diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
index d24e7ae7..1f0813e0 100644
--- a/src/leap/bitmask/services/eip/linuxvpnlauncher.py
+++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
@@ -25,7 +25,6 @@ import sys
import time
from leap.bitmask.config import flags
-from leap.bitmask.util import privilege_policies
from leap.bitmask.util.privilege_policies import LinuxPolicyChecker
from leap.common.files import which
from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
@@ -36,6 +35,8 @@ from leap.bitmask.util import first
logger = logging.getLogger(__name__)
+COM = commands
+
class EIPNoPolkitAuthAgentAvailable(VPNLauncherException):
pass
@@ -64,9 +65,10 @@ def _is_auth_agent_running():
"""
# the [x] thing is to avoid grep match itself
polkit_options = [
- 'ps aux | grep polkit-[g]nome-authentication-agent-1',
- 'ps aux | grep polkit-[k]de-authentication-agent-1',
- 'ps aux | grep [l]xpolkit'
+ 'ps aux | grep "polkit-[g]nome-authentication-agent-1"',
+ 'ps aux | grep "polkit-[k]de-authentication-agent-1"',
+ 'ps aux | grep "polkit-[m]ate-authentication-agent-1"',
+ 'ps aux | grep "[l]xpolkit"'
]
is_running = [commands.getoutput(cmd) for cmd in polkit_options]
return any(is_running)
@@ -84,35 +86,39 @@ def _try_to_launch_agent():
# will do "sh -c 'foo'", so if we do not quoute it we'll end
# up with a invocation to the python interpreter. And that
# is bad.
+ logger.debug("Trying to launch polkit agent")
subprocess.call(["python -m leap.bitmask.util.polkit_agent"],
shell=True, env=env)
except Exception as exc:
logger.exception(exc)
+SYSTEM_CONFIG = "/etc/leap"
+leapfile = lambda f: "%s/%s" % (SYSTEM_CONFIG, f)
+
+
class LinuxVPNLauncher(VPNLauncher):
PKEXEC_BIN = 'pkexec'
- OPENVPN_BIN = 'openvpn'
- OPENVPN_BIN_PATH = os.path.join(
- get_path_prefix(), "..", "apps", "eip", OPENVPN_BIN)
-
- SYSTEM_CONFIG = "/etc/leap"
- UP_DOWN_FILE = "resolv-update"
- UP_DOWN_PATH = "%s/%s" % (SYSTEM_CONFIG, UP_DOWN_FILE)
+ BITMASK_ROOT = "/usr/sbin/bitmask-root"
# We assume this is there by our openvpn dependency, and
# we will put it there on the bundle too.
- # TODO adapt to the bundle path.
- OPENVPN_DOWN_ROOT_BASE = "/usr/lib/openvpn/"
- OPENVPN_DOWN_ROOT_FILE = "openvpn-plugin-down-root.so"
- OPENVPN_DOWN_ROOT_PATH = "%s/%s" % (
- OPENVPN_DOWN_ROOT_BASE,
- OPENVPN_DOWN_ROOT_FILE)
-
- UP_SCRIPT = DOWN_SCRIPT = UP_DOWN_PATH
- UPDOWN_FILES = (UP_DOWN_PATH,)
+ if flags.STANDALONE:
+ OPENVPN_BIN_PATH = "/usr/sbin/leap-openvpn"
+ else:
+ OPENVPN_BIN_PATH = "/usr/sbin/openvpn"
+
POLKIT_PATH = LinuxPolicyChecker.get_polkit_path()
- OTHER_FILES = (POLKIT_PATH, )
+
+ if flags.STANDALONE:
+ RESOLVCONF_BIN_PATH = "/usr/local/sbin/leap-resolvconf"
+ else:
+ # this only will work with debian/ubuntu distros.
+ RESOLVCONF_BIN_PATH = "/sbin/resolvconf"
+
+ # XXX openvpn binary TOO
+ OTHER_FILES = (POLKIT_PATH, BITMASK_ROOT, OPENVPN_BIN_PATH,
+ RESOLVCONF_BIN_PATH)
@classmethod
def maybe_pkexec(kls):
@@ -130,7 +136,7 @@ class LinuxVPNLauncher(VPNLauncher):
if _is_pkexec_in_system():
if not _is_auth_agent_running():
_try_to_launch_agent()
- time.sleep(0.5)
+ time.sleep(2)
if _is_auth_agent_running():
pkexec_possibilities = which(kls.PKEXEC_BIN)
leap_assert(len(pkexec_possibilities) > 0,
@@ -145,28 +151,6 @@ class LinuxVPNLauncher(VPNLauncher):
raise EIPNoPkexecAvailable()
@classmethod
- def missing_other_files(kls):
- """
- 'Extend' the VPNLauncher's missing_other_files to check if the polkit
- files is outdated, in the case of an standalone bundle.
- If the polkit file that is in OTHER_FILES exists but is not up to date,
- it is added to the missing list.
-
- :returns: a list of missing files
- :rtype: list of str
- """
- # we use `super` in order to send the class to use
- missing = super(LinuxVPNLauncher, kls).missing_other_files()
-
- if flags.STANDALONE:
- polkit_file = LinuxPolicyChecker.get_polkit_path()
- if polkit_file not in missing:
- if privilege_policies.is_policy_outdated(kls.OPENVPN_BIN_PATH):
- missing.append(polkit_file)
-
- return missing
-
- @classmethod
def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
socket_port="unix", openvpn_verb=1):
"""
@@ -197,6 +181,10 @@ class LinuxVPNLauncher(VPNLauncher):
command = super(LinuxVPNLauncher, kls).get_vpn_command(
eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+ command.insert(0, kls.BITMASK_ROOT)
+ command.insert(1, "openvpn")
+ command.insert(2, "start")
+
pkexec = kls.maybe_pkexec()
if pkexec:
command.insert(0, first(pkexec))
@@ -204,26 +192,44 @@ class LinuxVPNLauncher(VPNLauncher):
return command
@classmethod
- def cmd_for_missing_scripts(kls, frompath, pol_file):
+ def cmd_for_missing_scripts(kls, frompath):
"""
Returns a sh script that can copy the missing files.
- :param frompath: The path where the up/down scripts live
+ :param frompath: The path where the helper files live
:type frompath: str
- :param pol_file: The path where the dynamically generated
- policy file lives
- :type pol_file: str
:rtype: str
"""
- to = kls.SYSTEM_CONFIG
+ # no system config for now
+ # sys_config = kls.SYSTEM_CONFIG
+ (polkit_file, openvpn_bin_file,
+ bitmask_root_file, resolvconf_bin_file) = map(
+ lambda p: os.path.split(p)[-1],
+ (kls.POLKIT_PATH, kls.OPENVPN_BIN_PATH,
+ kls.BITMASK_ROOT, kls.RESOLVCONF_BIN_PATH))
cmd = '#!/bin/sh\n'
- cmd += 'mkdir -p "%s"\n' % (to, )
- cmd += 'cp "%s/%s" "%s"\n' % (frompath, kls.UP_DOWN_FILE, to)
- cmd += 'cp "%s" "%s"\n' % (pol_file, kls.POLKIT_PATH)
+ cmd += 'mkdir -p /usr/local/sbin\n'
+
+ cmd += 'cp "%s" "%s"\n' % (os.path.join(frompath, polkit_file),
+ kls.POLKIT_PATH)
cmd += 'chmod 644 "%s"\n' % (kls.POLKIT_PATH, )
+ cmd += 'cp "%s" "%s"\n' % (os.path.join(frompath, bitmask_root_file),
+ kls.BITMASK_ROOT)
+ cmd += 'chmod 744 "%s"\n' % (kls.BITMASK_ROOT, )
+
+ if flags.STANDALONE:
+ cmd += 'cp "%s" "%s"\n' % (
+ os.path.join(frompath, openvpn_bin_file),
+ kls.OPENVPN_BIN_PATH)
+ cmd += 'chmod 744 "%s"\n' % (kls.POLKIT_PATH, )
+
+ cmd += 'cp "%s" "%s"\n' % (
+ os.path.join(frompath, resolvconf_bin_file),
+ kls.RESOLVCONF_BIN_PATH)
+ cmd += 'chmod 744 "%s"\n' % (kls.POLKIT_PATH, )
return cmd
@classmethod
diff --git a/src/leap/bitmask/services/eip/vpnlauncher.py b/src/leap/bitmask/services/eip/vpnlauncher.py
index 99cae7f9..dcb48e8a 100644
--- a/src/leap/bitmask/services/eip/vpnlauncher.py
+++ b/src/leap/bitmask/services/eip/vpnlauncher.py
@@ -25,14 +25,12 @@ import stat
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.config.providerconfig import ProviderConfig
+from leap.bitmask.platform_init import IS_LINUX
from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
-from leap.bitmask.util import first
-from leap.bitmask.util import get_path_prefix
from leap.common.check import leap_assert, leap_assert_type
-from leap.common.files import which
+
logger = logging.getLogger(__name__)
@@ -107,10 +105,43 @@ class VPNLauncher(object):
@classmethod
@abstractmethod
+ def get_gateways(kls, eipconfig, providerconfig):
+ """
+ Return the selected gateways for a given provider, looking at the EIP
+ config file.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+
+ :rtype: list
+ """
+ gateways = []
+ leap_settings = LeapSettings()
+ domain = providerconfig.get_domain()
+ gateway_conf = leap_settings.get_selected_gateway(domain)
+
+ if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
+ gateway_selector = VPNGatewaySelector(eipconfig)
+ gateways = gateway_selector.get_gateways()
+ else:
+ gateways = [gateway_conf]
+
+ if not gateways:
+ logger.error('No gateway was found!')
+ raise VPNLauncherException('No gateway was found!')
+
+ logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
+ return gateways
+
+ @classmethod
+ @abstractmethod
def get_vpn_command(kls, eipconfig, providerconfig,
socket_host, socket_port, openvpn_verb=1):
"""
- Returns the platform dependant vpn launching command
+ Return the platform-dependant vpn command for launching openvpn.
Might raise:
OpenVPNNotFoundException,
@@ -134,16 +165,19 @@ class VPNLauncher(object):
leap_assert_type(eipconfig, EIPConfig)
leap_assert_type(providerconfig, ProviderConfig)
- kwargs = {}
- if flags.STANDALONE:
- kwargs['path_extension'] = os.path.join(
- get_path_prefix(), "..", "apps", "eip")
-
- openvpn_possibilities = which(kls.OPENVPN_BIN, **kwargs)
- if len(openvpn_possibilities) == 0:
+ # 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)
+ # -----------------------------------------
+ if not os.path.isfile(kls.OPENVPN_BIN_PATH):
+ logger.warning("Could not find openvpn bin in path %s" % (
+ kls.OPENVPN_BIN_PATH))
raise OpenVPNNotFoundException()
- openvpn = first(openvpn_possibilities)
+ openvpn = kls.OPENVPN_BIN_PATH
args = []
args += [
@@ -154,22 +188,7 @@ class VPNLauncher(object):
if openvpn_verb is not None:
args += ['--verb', '%d' % (openvpn_verb,)]
- gateways = []
- leap_settings = LeapSettings()
- domain = providerconfig.get_domain()
- gateway_conf = leap_settings.get_selected_gateway(domain)
-
- if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
- else:
- gateways = [gateway_conf]
-
- if not gateways:
- logger.error('No gateway was found!')
- raise VPNLauncherException('No gateway was found!')
-
- logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
+ gateways = kls.get_gateways(eipconfig, providerconfig)
for gw in gateways:
args += ['--remote', gw, '1194', 'udp']
@@ -177,11 +196,6 @@ class VPNLauncher(object):
args += [
'--client',
'--dev', 'tun',
- ##############################################################
- # persist-tun makes ping-restart fail because it leaves a
- # broken routing table
- ##############################################################
- # '--persist-tun',
'--persist-key',
'--tls-client',
'--remote-cert-tls',
@@ -194,15 +208,6 @@ class VPNLauncher(object):
user = getpass.getuser()
- ##############################################################
- # The down-root plugin fails in some situations, so we don't
- # drop privs for the time being
- ##############################################################
- # args += [
- # '--user', user,
- # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name
- # ]
-
if socket_port == "unix": # that's always the case for linux
args += [
'--management-client-user', user
@@ -226,20 +231,6 @@ class VPNLauncher(object):
'--down', '\"%s\"' % (kls.DOWN_SCRIPT,)
]
- ###########################################################
- # For the time being we are disabling the usage of the
- # down-root plugin, because it doesn't quite work as
- # expected (i.e. it doesn't run route -del as root
- # when finishing, so it fails to properly
- # restart/quit)
- ###########################################################
- # if _has_updown_scripts(kls.OPENVPN_DOWN_PLUGIN):
- # args += [
- # '--plugin', kls.OPENVPN_DOWN_ROOT,
- # '\'%s\'' % kls.DOWN_SCRIPT # for OSX
- # '\'script_type=down %s\'' % kls.DOWN_SCRIPT # for Linux
- # ]
-
args += [
'--cert', eipconfig.get_client_cert_path(providerconfig),
'--key', eipconfig.get_client_cert_path(providerconfig),
@@ -271,13 +262,18 @@ class VPNLauncher(object):
:rtype: list
"""
- leap_assert(kls.UPDOWN_FILES is not None,
- "Need to define UPDOWN_FILES for this particular "
- "launcher before calling this method")
- file_exist = partial(_has_updown_scripts, warn=False)
- zipped = zip(kls.UPDOWN_FILES, map(file_exist, kls.UPDOWN_FILES))
- missing = filter(lambda (path, exists): exists is False, zipped)
- return [path for path, exists in missing]
+ # FIXME
+ # XXX remove method when we ditch UPDOWN in osx and win too
+ if IS_LINUX:
+ return []
+ else:
+ leap_assert(kls.UPDOWN_FILES is not None,
+ "Need to define UPDOWN_FILES for this particular "
+ "launcher before calling this method")
+ file_exist = partial(_has_updown_scripts, warn=False)
+ zipped = zip(kls.UPDOWN_FILES, map(file_exist, kls.UPDOWN_FILES))
+ missing = filter(lambda (path, exists): exists is False, zipped)
+ return [path for path, exists in missing]
@classmethod
def missing_other_files(kls):
diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py
index 5c100036..1559ea8b 100644
--- a/src/leap/bitmask/services/eip/vpnprocess.py
+++ b/src/leap/bitmask/services/eip/vpnprocess.py
@@ -21,6 +21,7 @@ import logging
import os
import shutil
import socket
+import subprocess
import sys
from itertools import chain, repeat
@@ -33,14 +34,14 @@ except ImportError:
# psutil >= 2.0.0
from psutil import AccessDenied as psutil_AccessDenied
-from PySide import QtCore
-
+from leap.bitmask.config import flags
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.services.eip import get_vpn_launcher
+from leap.bitmask.services.eip import linuxvpnlauncher
from leap.bitmask.services.eip.eipconfig import EIPConfig
from leap.bitmask.services.eip.udstelnet import UDSTelnet
from leap.bitmask.util import first
-from leap.bitmask.platform_init import IS_MAC
+from leap.bitmask.platform_init import IS_MAC, IS_LINUX
from leap.common.check import leap_assert, leap_assert_type
logger = logging.getLogger(__name__)
@@ -52,28 +53,6 @@ from twisted.internet import error as internet_error
from twisted.internet.task import LoopingCall
-class VPNSignals(QtCore.QObject):
- """
- These are the signals that we use to let the UI know
- about the events we are polling.
- They are instantiated in the VPN object and passed along
- till the VPNProcess.
- """
- # signals for the process
- state_changed = QtCore.Signal(dict)
- status_changed = QtCore.Signal(dict)
- process_finished = QtCore.Signal(int)
-
- # signals that come from parsing
- # openvpn output
- network_unreachable = QtCore.Signal()
- process_restart_tls = QtCore.Signal()
- process_restart_ping = QtCore.Signal()
-
- def __init__(self):
- QtCore.QObject.__init__(self)
-
-
class VPNObserver(object):
"""
A class containing different patterns in the openvpn output that
@@ -90,19 +69,13 @@ class VPNObserver(object):
'PROCESS_RESTART_TLS': (
"SIGUSR1[soft,tls-error]",),
'PROCESS_RESTART_PING': (
- "SIGUSR1[soft,ping-restart]",),
+ "SIGTERM[soft,ping-restart]",),
'INITIALIZATION_COMPLETED': (
"Initialization Sequence Completed",),
}
- def __init__(self, qtsigs):
- """
- Initializer. Keeps a reference to the passed qtsigs object
- :param qtsigs: an object containing the different qt signals to
- be used to communicate with different parts of
- the application (the EIP state machine, for instance).
- """
- self._qtsigs = qtsigs
+ def __init__(self, signaler=None):
+ self._signaler = signaler
def watch(self, line):
"""
@@ -123,27 +96,29 @@ class VPNObserver(object):
return
sig = self._get_signal(event)
- if sig:
- sig.emit()
+ if sig is not None:
+ self._signaler.signal(sig)
return
else:
- logger.debug(
- 'We got %s event from openvpn output but we '
- 'could not find a matching signal for it.'
- % event)
+ logger.debug('We got %s event from openvpn output but we could '
+ 'not find a matching signal for it.' % event)
def _get_signal(self, event):
"""
Tries to get the matching signal from the eip signals
objects based on the name of the passed event (in lowercase)
- :param event: the name of the event that we want to get a signal
- for
+ :param event: the name of the event that we want to get a signal for
:type event: str
- :returns: a QtSignal, or None
- :rtype: QtSignal or None
+ :returns: a Signaler signal or None
+ :rtype: str or None
"""
- return getattr(self._qtsigs, event.lower(), None)
+ signals = {
+ "network_unreachable": self._signaler.EIP_NETWORK_UNREACHABLE,
+ "process_restart_tls": self._signaler.EIP_PROCESS_RESTART_TLS,
+ "process_restart_ping": self._signaler.EIP_PROCESS_RESTART_PING,
+ }
+ return signals.get(event.lower())
class OpenVPNAlreadyRunning(Exception):
@@ -181,14 +156,11 @@ class VPN(object):
self._vpnproc = None
self._pollers = []
self._reactor = reactor
- self._qtsigs = VPNSignals()
- # XXX should get it from config.flags
- self._openvpn_verb = kwargs.get(self.OPENVPN_VERB, None)
+ self._signaler = kwargs['signaler']
+ self._openvpn_verb = flags.OPENVPN_VERBOSITY
- @property
- def qtsigs(self):
- return self._qtsigs
+ self._user_stopped = False
def start(self, *args, **kwargs):
"""
@@ -200,19 +172,28 @@ class VPN(object):
:param kwargs: kwargs to be passed to the VPNProcess
:type kwargs: dict
"""
+ logger.debug('VPN: start')
+ self._user_stopped = False
self._stop_pollers()
- kwargs['qtsigs'] = self.qtsigs
kwargs['openvpn_verb'] = self._openvpn_verb
+ kwargs['signaler'] = self._signaler
# start the main vpn subprocess
vpnproc = VPNProcess(*args, **kwargs)
- #qtsigs=self.qtsigs,
- #openvpn_verb=self._openvpn_verb)
if vpnproc.get_openvpn_process():
logger.info("Another vpn process is running. Will try to stop it.")
vpnproc.stop_if_already_running()
+ # we try to bring the firewall up
+ if IS_LINUX:
+ gateways = vpnproc.getGateways()
+ firewall_up = self._launch_firewall(gateways)
+ if not firewall_up:
+ logger.error("Could not bring firewall up, "
+ "aborting openvpn launch.")
+ return
+
cmd = vpnproc.getCommand()
env = os.environ
for key, val in vpnproc.vpn_env.items():
@@ -230,9 +211,37 @@ class VPN(object):
self._pollers.extend(poll_list)
self._start_pollers()
+ def _launch_firewall(self, gateways):
+ """
+ Launch the firewall using the privileged wrapper.
+
+ :param gateways:
+ :type gateways: list
+
+ :returns: True if the exitcode of calling the root helper in a
+ subprocess is 0.
+ :rtype: bool
+ """
+ # XXX could check for wrapper existence, check it's root owned etc.
+ # XXX could check that the iptables rules are in place.
+
+ BM_ROOT = linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT
+ exitCode = subprocess.call(["pkexec",
+ BM_ROOT, "firewall", "start"] + gateways)
+ return True if exitCode is 0 else False
+
+ def _tear_down_firewall(self):
+ """
+ Tear the firewall down using the privileged wrapper.
+ """
+ BM_ROOT = linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT
+ exitCode = subprocess.call(["pkexec",
+ BM_ROOT, "firewall", "stop"])
+ return True if exitCode is 0 else False
+
def _kill_if_left_alive(self, tries=0):
"""
- Check if the process is still alive, and sends a
+ Check if the process is still alive, and send a
SIGKILL after a timeout period.
:param tries: counter of tries, used in recursion
@@ -242,6 +251,15 @@ class VPN(object):
while tries < self.TERMINATE_MAXTRIES:
if self._vpnproc.transport.pid is None:
logger.debug("Process has been happily terminated.")
+
+ # we try to tear the firewall down
+ if IS_LINUX and self._user_stopped:
+ firewall_down = self._tear_down_firewall()
+ if firewall_down:
+ logger.debug("Firewall down")
+ else:
+ logger.warning("Could not tear firewall down")
+
return
else:
logger.debug("Process did not die, waiting...")
@@ -262,8 +280,11 @@ class VPN(object):
Sends a kill signal to the process.
"""
self._stop_pollers()
- self._vpnproc.aborted = True
- self._vpnproc.killProcess()
+ if self._vpnproc is None:
+ logger.debug("There's no vpn process running to kill.")
+ else:
+ self._vpnproc.aborted = True
+ self._vpnproc.killProcess()
def terminate(self, shutdown=False):
"""
@@ -275,6 +296,10 @@ class VPN(object):
from twisted.internet import reactor
self._stop_pollers()
+ # We assume that the only valid shutodowns are initiated
+ # by an user action.
+ self._user_stopped = shutdown
+
# First we try to be polite and send a SIGTERM...
if self._vpnproc:
self._sentterm = True
@@ -282,12 +307,17 @@ class VPN(object):
# ...but we also trigger a countdown to be unpolite
# if strictly needed.
-
- # XXX Watch out! This will fail NOW since we are running
- # openvpn as root as a workaround for some connection issues.
reactor.callLater(
self.TERMINATE_WAIT, self._kill_if_left_alive)
+ if shutdown:
+ if IS_LINUX and self._user_stopped:
+ firewall_down = self._tear_down_firewall()
+ if firewall_down:
+ logger.debug("Firewall down")
+ else:
+ logger.warning("Could not tear firewall down")
+
def _start_pollers(self):
"""
Iterate through the registered observers
@@ -328,37 +358,21 @@ class VPNManager(object):
POLL_TIME = 2.5 if IS_MAC else 1.0
CONNECTION_RETRY_TIME = 1
- TS_KEY = "ts"
- STATUS_STEP_KEY = "status_step"
- OK_KEY = "ok"
- IP_KEY = "ip"
- REMOTE_KEY = "remote"
-
- TUNTAP_READ_KEY = "tun_tap_read"
- TUNTAP_WRITE_KEY = "tun_tap_write"
- TCPUDP_READ_KEY = "tcp_udp_read"
- TCPUDP_WRITE_KEY = "tcp_udp_write"
- AUTH_READ_KEY = "auth_read"
-
- def __init__(self, qtsigs=None):
+ def __init__(self, signaler=None):
"""
Initializes the VPNManager.
- :param qtsigs: a QObject containing the Qt signals used by the UI
- to give feedback about state changes.
- :type qtsigs: QObject
+ :param signaler: Signaler object used to send notifications to the
+ backend
+ :type signaler: backend.Signaler
"""
from twisted.internet import reactor
self._reactor = reactor
self._tn = None
- self._qtsigs = qtsigs
+ self._signaler = signaler
self._aborted = False
@property
- def qtsigs(self):
- return self._qtsigs
-
- @property
def aborted(self):
return self._aborted
@@ -552,17 +566,10 @@ class VPNManager(object):
continue
ts, status_step, ok, ip, remote = parts
- state_dict = {
- self.TS_KEY: ts,
- self.STATUS_STEP_KEY: status_step,
- self.OK_KEY: ok,
- self.IP_KEY: ip,
- self.REMOTE_KEY: remote
- }
-
- if state_dict != self._last_state:
- self.qtsigs.state_changed.emit(state_dict)
- self._last_state = state_dict
+ state = status_step
+ if state != self._last_state:
+ self._signaler.signal(self._signaler.EIP_STATE_CHANGED, state)
+ self._last_state = state
def _parse_status_and_notify(self, output):
"""
@@ -575,9 +582,7 @@ class VPNManager(object):
"""
tun_tap_read = ""
tun_tap_write = ""
- tcp_udp_read = ""
- tcp_udp_write = ""
- auth_read = ""
+
for line in output:
stripped = line.strip()
if stripped.endswith("STATISTICS") or stripped == "END":
@@ -585,28 +590,24 @@ class VPNManager(object):
parts = stripped.split(",")
if len(parts) < 2:
continue
- if parts[0].strip() == "TUN/TAP read bytes":
- tun_tap_read = parts[1]
- elif parts[0].strip() == "TUN/TAP write bytes":
- tun_tap_write = parts[1]
- elif parts[0].strip() == "TCP/UDP read bytes":
- tcp_udp_read = parts[1]
- elif parts[0].strip() == "TCP/UDP write bytes":
- tcp_udp_write = parts[1]
- elif parts[0].strip() == "Auth read bytes":
- auth_read = parts[1]
-
- status_dict = {
- self.TUNTAP_READ_KEY: tun_tap_read,
- self.TUNTAP_WRITE_KEY: tun_tap_write,
- self.TCPUDP_READ_KEY: tcp_udp_read,
- self.TCPUDP_WRITE_KEY: tcp_udp_write,
- self.AUTH_READ_KEY: auth_read
- }
- if status_dict != self._last_status:
- self.qtsigs.status_changed.emit(status_dict)
- self._last_status = status_dict
+ text, value = parts
+ # text can be:
+ # "TUN/TAP read bytes"
+ # "TUN/TAP write bytes"
+ # "TCP/UDP read bytes"
+ # "TCP/UDP write bytes"
+ # "Auth read bytes"
+
+ if text == "TUN/TAP read bytes":
+ tun_tap_read = value # download
+ elif text == "TUN/TAP write bytes":
+ tun_tap_write = value # upload
+
+ status = (tun_tap_read, tun_tap_write)
+ if status != self._last_status:
+ self._signaler.signal(self._signaler.EIP_STATUS_CHANGED, status)
+ self._last_status = status
def get_state(self):
"""
@@ -754,7 +755,7 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager):
"""
def __init__(self, eipconfig, providerconfig, socket_host, socket_port,
- qtsigs, openvpn_verb):
+ signaler, openvpn_verb):
"""
:param eipconfig: eip configuration object
:type eipconfig: EIPConfig
@@ -769,18 +770,17 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager):
socket, or port otherwise
:type socket_port: str
- :param qtsigs: a QObject containing the Qt signals used to notify the
- UI.
- :type qtsigs: QObject
+ :param signaler: Signaler object used to receive notifications to the
+ backend
+ :type signaler: backend.Signaler
:param openvpn_verb: the desired level of verbosity in the
openvpn invocation
:type openvpn_verb: int
"""
- VPNManager.__init__(self, qtsigs=qtsigs)
+ VPNManager.__init__(self, signaler=signaler)
leap_assert_type(eipconfig, EIPConfig)
leap_assert_type(providerconfig, ProviderConfig)
- leap_assert_type(qtsigs, QtCore.QObject)
#leap_assert(not self.isRunning(), "Starting process more than once!")
@@ -799,7 +799,7 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager):
# the parameter around.
self._openvpn_verb = openvpn_verb
- self._vpn_observer = VPNObserver(qtsigs)
+ self._vpn_observer = VPNObserver(signaler)
# processProtocol methods
@@ -835,7 +835,7 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager):
exit_code = reason.value.exitCode
if isinstance(exit_code, int):
logger.debug("processExited, status %d" % (exit_code,))
- self.qtsigs.process_finished.emit(exit_code)
+ self._signaler.signal(self._signaler.EIP_PROCESS_FINISHED, exit_code)
self._alive = False
def processEnded(self, reason):
@@ -889,9 +889,20 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager):
if not isinstance(c, str):
command[i] = c.encode(encoding)
- logger.debug("Running VPN with command: {0}".format(command))
+ logger.debug("Running VPN with command: ")
+ logger.debug("{0}".format(" ".join(command)))
return command
+ def getGateways(self):
+ """
+ Get the gateways from the appropiate launcher.
+
+ :rtype: list
+ """
+ gateways = self._launcher.get_gateways(
+ self._eipconfig, self._providerconfig)
+ return gateways
+
# shutdown
def killProcess(self):
diff --git a/src/leap/bitmask/services/mail/conductor.py b/src/leap/bitmask/services/mail/conductor.py
index 79f324dc..1766a39d 100644
--- a/src/leap/bitmask/services/mail/conductor.py
+++ b/src/leap/bitmask/services/mail/conductor.py
@@ -18,22 +18,18 @@
Mail Services Conductor
"""
import logging
-import os
-from PySide import QtCore
from zope.proxy import sameProxiedObjects
from leap.bitmask.gui import statemachines
-from leap.bitmask.services.mail import imap
from leap.bitmask.services.mail import connection as mail_connection
+from leap.bitmask.services.mail import imap
from leap.bitmask.services.mail.smtpbootstrapper import SMTPBootstrapper
from leap.bitmask.services.mail.smtpconfig import SMTPConfig
-from leap.bitmask.util import is_file
from leap.common.check import leap_assert
-
-from leap.common.events import register as leap_register
from leap.common.events import events_pb2 as leap_events
+from leap.common.events import register as leap_register
logger = logging.getLogger(__name__)
@@ -167,10 +163,6 @@ class IMAPControl(object):
class SMTPControl(object):
-
- PORT_KEY = "port"
- IP_KEY = "ip_address"
-
def __init__(self):
"""
Initializes smtp variables.
@@ -178,12 +170,8 @@ class SMTPControl(object):
self.smtp_config = SMTPConfig()
self.smtp_connection = None
self.smtp_machine = None
- self._smtp_service = None
- self._smtp_port = None
self.smtp_bootstrapper = SMTPBootstrapper()
- self.smtp_bootstrapper.download_config.connect(
- self.smtp_bootstrapped_stage)
leap_register(signal=leap_events.SMTP_SERVICE_STARTED,
callback=self._handle_smtp_events,
@@ -200,101 +188,27 @@ class SMTPControl(object):
"""
self.smtp_connection = smtp_connection
- def start_smtp_service(self, host, port, cert):
+ def start_smtp_service(self, provider_config, download_if_needed=False):
"""
- Starts the smtp service.
+ Starts the SMTP service.
- :param host: the hostname of the remove SMTP server.
- :type host: str
- :param port: the port of the remote SMTP server
- :type port: str
- :param cert: the client certificate for authentication
- :type cert: str
+ :param provider_config: Provider configuration
+ :type provider_config: ProviderConfig
+ :param download_if_needed: True if it should check for mtime
+ for the file
+ :type download_if_needed: bool
"""
- # TODO Make the encrypted_only configurable
- # TODO pick local smtp port in a better way
- # TODO remove hard-coded port and let leap.mail set
- # the specific default.
self.smtp_connection.qtsigs.connecting_signal.emit()
- from leap.mail.smtp import setup_smtp_gateway
- self._smtp_service, self._smtp_port = setup_smtp_gateway(
- port=2013,
- userid=self.userid,
- keymanager=self._keymanager,
- smtp_host=host,
- smtp_port=port,
- smtp_cert=cert,
- smtp_key=cert,
- encrypted_only=False)
+ self.smtp_bootstrapper.start_smtp_service(
+ provider_config, self.smtp_config, self._keymanager,
+ self.userid, download_if_needed)
def stop_smtp_service(self):
"""
- Stops the smtp service (port and factory).
+ Stops the SMTP service.
"""
self.smtp_connection.qtsigs.disconnecting_signal.emit()
- # TODO We should homogenize both services.
- if self._smtp_service is not None:
- logger.debug('Stopping smtp service.')
- self._smtp_port.stopListening()
- self._smtp_service.doStop()
-
- @QtCore.Slot()
- def smtp_bootstrapped_stage(self, data):
- """
- SLOT
- TRIGGERS:
- self.smtp_bootstrapper.download_config
-
- If there was a problem, displays it, otherwise it does nothing.
- This is used for intermediate bootstrapping stages, in case
- they fail.
-
- :param data: result from the bootstrapping stage for Soledad
- :type data: dict
- """
- passed = data[self.smtp_bootstrapper.PASSED_KEY]
- if not passed:
- logger.error(data[self.smtp_bootstrapper.ERROR_KEY])
- return
- logger.debug("Done bootstrapping SMTP")
- self.check_smtp_config()
-
- def check_smtp_config(self):
- """
- Checks smtp config and tries to download smtp client cert if needed.
- Currently called when smtp_bootstrapped_stage has successfuly finished.
- """
- logger.debug("Checking SMTP config...")
- leap_assert(self.smtp_bootstrapper._provider_config,
- "smtp bootstrapper does not have a provider_config")
-
- provider_config = self.smtp_bootstrapper._provider_config
- smtp_config = self.smtp_config
- hosts = smtp_config.get_hosts()
- # TODO handle more than one host and define how to choose
- if len(hosts) > 0:
- hostname = hosts.keys()[0]
- logger.debug("Using hostname %s for SMTP" % (hostname,))
- host = hosts[hostname][self.IP_KEY].encode("utf-8")
- port = hosts[hostname][self.PORT_KEY]
-
- client_cert = smtp_config.get_client_cert_path(
- provider_config,
- about_to_download=True)
-
- # XXX change this logic!
- # check_config should be called from within start_service,
- # and not the other way around.
- if not is_file(client_cert):
- self.smtp_bootstrapper._download_client_certificates()
- if os.path.isfile(client_cert):
- self.start_smtp_service(host, port, client_cert)
- else:
- logger.warning("Tried to download email client "
- "certificate, but could not find any")
-
- else:
- logger.warning("No smtp hosts configured")
+ self.smtp_bootstrapper.stop_smtp_service()
# handle smtp events
@@ -349,7 +263,7 @@ class MailConductor(IMAPControl, SMTPControl):
:param keymanager: a transparent proxy that eventually will point to a
Keymanager Instance.
- :type soledad: zope.proxy.ProxyBase
+ :type keymanager: zope.proxy.ProxyBase
"""
IMAPControl.__init__(self)
SMTPControl.__init__(self)
@@ -407,4 +321,5 @@ class MailConductor(IMAPControl, SMTPControl):
qtsigs.connecting_signal.connect(widget.mail_state_connecting)
qtsigs.disconnecting_signal.connect(widget.mail_state_disconnecting)
qtsigs.disconnected_signal.connect(widget.mail_state_disconnected)
- qtsigs.soledad_invalid_auth_token.connect(widget.soledad_invalid_auth_token)
+ qtsigs.soledad_invalid_auth_token.connect(
+ widget.soledad_invalid_auth_token)
diff --git a/src/leap/bitmask/services/mail/smtpbootstrapper.py b/src/leap/bitmask/services/mail/smtpbootstrapper.py
index 032d6357..7ecf8134 100644
--- a/src/leap/bitmask/services/mail/smtpbootstrapper.py
+++ b/src/leap/bitmask/services/mail/smtpbootstrapper.py
@@ -20,12 +20,13 @@ SMTP bootstrapping
import logging
import os
-from PySide import QtCore
-
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.crypto.certs import download_client_cert
from leap.bitmask.services import download_service_config
from leap.bitmask.services.abstractbootstrapper import AbstractBootstrapper
+from leap.bitmask.services.mail.smtpconfig import SMTPConfig
+from leap.bitmask.util import is_file
+
from leap.common import certs as leap_certs
from leap.common.check import leap_assert, leap_assert_type
from leap.common.files import check_and_fix_urw_only
@@ -33,27 +34,33 @@ from leap.common.files import check_and_fix_urw_only
logger = logging.getLogger(__name__)
+class NoSMTPHosts(Exception):
+ """This is raised when there is no SMTP host to use."""
+
+
class SMTPBootstrapper(AbstractBootstrapper):
"""
SMTP init procedure
"""
- # All dicts returned are of the form
- # {"passed": bool, "error": str}
- download_config = QtCore.Signal(dict)
+ PORT_KEY = "port"
+ IP_KEY = "ip_address"
def __init__(self):
AbstractBootstrapper.__init__(self)
self._provider_config = None
self._smtp_config = None
+ self._userid = None
self._download_if_needed = False
- def _download_config(self, *args):
+ self._smtp_service = None
+ self._smtp_port = None
+
+ def _download_config_and_cert(self):
"""
- Downloads the SMTP config for the given provider
+ Downloads the SMTP config and cert for the given provider.
"""
-
leap_assert(self._provider_config,
"We need a provider configuration!")
@@ -66,63 +73,101 @@ class SMTPBootstrapper(AbstractBootstrapper):
self._session,
self._download_if_needed)
- def _download_client_certificates(self, *args):
- """
- Downloads the SMTP client certificate for the given provider
+ hosts = self._smtp_config.get_hosts()
- We actually are downloading the certificate for the same uri as
- for the EIP config, but we duplicate these bits to allow mail
- service to be working in a provider that does not offer EIP.
- """
- # TODO factor out with eipboostrapper.download_client_certificates
- # TODO this shouldn't be a private method, it's called from
- # mainwindow.
- leap_assert(self._provider_config, "We need a provider configuration!")
- leap_assert(self._smtp_config, "We need an smtp configuration!")
+ if len(hosts) == 0:
+ raise NoSMTPHosts()
- logger.debug("Downloading SMTP client certificate for %s" %
- (self._provider_config.get_domain(),))
+ # TODO handle more than one host and define how to choose
+ hostname = hosts.keys()[0]
+ logger.debug("Using hostname %s for SMTP" % (hostname,))
- client_cert_path = self._smtp_config.\
- get_client_cert_path(self._provider_config,
- about_to_download=True)
+ client_cert_path = self._smtp_config.get_client_cert_path(
+ self._provider_config, about_to_download=True)
- # For re-download if something is wrong with the cert
- self._download_if_needed = self._download_if_needed and \
- not leap_certs.should_redownload(client_cert_path)
+ if not is_file(client_cert_path):
+ # For re-download if something is wrong with the cert
+ self._download_if_needed = (
+ self._download_if_needed and
+ not leap_certs.should_redownload(client_cert_path))
- if self._download_if_needed and \
- os.path.isfile(client_cert_path):
- check_and_fix_urw_only(client_cert_path)
- return
+ if self._download_if_needed and os.path.isfile(client_cert_path):
+ check_and_fix_urw_only(client_cert_path)
+ return
- download_client_cert(self._provider_config,
- client_cert_path,
- self._session)
+ download_client_cert(self._provider_config,
+ client_cert_path,
+ self._session)
- def run_smtp_setup_checks(self,
- provider_config,
- smtp_config,
- download_if_needed=False):
+ def _start_smtp_service(self):
+ """
+ Start the smtp service using the downloaded configurations.
+ """
+ # TODO Make the encrypted_only configurable
+ # TODO pick local smtp port in a better way
+ # TODO remove hard-coded port and let leap.mail set
+ # the specific default.
+ # TODO handle more than one host and define how to choose
+ hosts = self._smtp_config.get_hosts()
+ hostname = hosts.keys()[0]
+ host = hosts[hostname][self.IP_KEY].encode("utf-8")
+ port = hosts[hostname][self.PORT_KEY]
+ client_cert_path = self._smtp_config.get_client_cert_path(
+ self._provider_config, about_to_download=True)
+
+ from leap.mail.smtp import setup_smtp_gateway
+ self._smtp_service, self._smtp_port = setup_smtp_gateway(
+ port=2013,
+ userid=self._userid,
+ keymanager=self._keymanager,
+ smtp_host=host,
+ smtp_port=port,
+ smtp_cert=client_cert_path,
+ smtp_key=client_cert_path,
+ encrypted_only=False)
+
+ def start_smtp_service(self, provider_config, smtp_config, keymanager,
+ userid, download_if_needed=False):
"""
- Starts the checks needed for a new smtp setup
+ Starts the SMTP service.
:param provider_config: Provider configuration
:type provider_config: ProviderConfig
:param smtp_config: SMTP configuration to populate
:type smtp_config: SMTPConfig
+ :param keymanager: a transparent proxy that eventually will point to a
+ Keymanager Instance.
+ :type keymanager: zope.proxy.ProxyBase
+ :param userid: the user id, in the form "user@provider"
+ :type userid: str
:param download_if_needed: True if it should check for mtime
for the file
:type download_if_needed: bool
"""
leap_assert_type(provider_config, ProviderConfig)
+ leap_assert_type(smtp_config, SMTPConfig)
self._provider_config = provider_config
+ self._keymanager = keymanager
self._smtp_config = smtp_config
+ self._useid = userid
self._download_if_needed = download_if_needed
- cb_chain = [
- (self._download_config, self.download_config),
- ]
-
- self.addCallbackChain(cb_chain)
+ try:
+ self._download_config_and_cert()
+ logger.debug("Starting SMTP service.")
+ self._start_smtp_service()
+ except NoSMTPHosts:
+ logger.warning("There is no SMTP host to use.")
+ except Exception as e:
+ # TODO: we should handle more specific exceptions in here
+ logger.exception("Error while bootstrapping SMTP: %r" % (e, ))
+
+ def stop_smtp_service(self):
+ """
+ Stops the smtp service (port and factory).
+ """
+ if self._smtp_service is not None:
+ logger.debug('Stopping SMTP service.')
+ self._smtp_port.stopListening()
+ self._smtp_service.doStop()
diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py
index ad5ee4d0..6bb7c036 100644
--- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py
+++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py
@@ -139,7 +139,6 @@ class SoledadBootstrapper(AbstractBootstrapper):
download_config = QtCore.Signal(dict)
gen_key = QtCore.Signal(dict)
local_only_ready = QtCore.Signal(dict)
- soledad_timeout = QtCore.Signal()
soledad_invalid_auth_token = QtCore.Signal()
soledad_failed = QtCore.Signal()
@@ -159,8 +158,6 @@ class SoledadBootstrapper(AbstractBootstrapper):
self._srpauth = None
self._soledad = None
- self._soledad_retries = 0
-
@property
def keymanager(self):
return self._keymanager
@@ -177,26 +174,6 @@ class SoledadBootstrapper(AbstractBootstrapper):
"We need a provider config")
return SRPAuth(self._provider_config)
- # retries
-
- def cancel_bootstrap(self):
- self._soledad_retries = self.MAX_INIT_RETRIES
-
- def should_retry_initialization(self):
- """
- Return True if we should retry the initialization.
- """
- logger.debug("current retries: %s, max retries: %s" % (
- self._soledad_retries,
- self.MAX_INIT_RETRIES))
- return self._soledad_retries < self.MAX_INIT_RETRIES
-
- def increment_retries_count(self):
- """
- Increment the count of initialization retries.
- """
- self._soledad_retries += 1
-
# initialization
def load_offline_soledad(self, username, password, uuid):
@@ -265,6 +242,41 @@ class SoledadBootstrapper(AbstractBootstrapper):
# in the case of an invalid token we have already turned off mail and
# warned the user in _do_soledad_sync()
+ def _do_soledad_init(self, uuid, secrets_path, local_db_path,
+ server_url, cert_file, token):
+ """
+ Initialize soledad, retry if necessary and emit soledad_failed if we
+ can't succeed.
+
+ :param uuid: user identifier
+ :type uuid: str
+ :param secrets_path: path to secrets file
+ :type secrets_path: str
+ :param local_db_path: path to local db file
+ :type local_db_path: str
+ :param server_url: soledad server uri
+ :type server_url: str
+ :param cert_file: path to the certificate of the ca used
+ to validate the SSL certificate used by the remote
+ soledad server.
+ :type cert_file: str
+ :param auth token: auth token
+ :type auth_token: str
+ """
+ init_tries = self.MAX_INIT_RETRIES
+ while init_tries > 0:
+ try:
+ self._try_soledad_init(
+ uuid, secrets_path, local_db_path,
+ server_url, cert_file, token)
+ logger.debug("Soledad has been initialized.")
+ return
+ except Exception:
+ init_tries -= 1
+ continue
+
+ self.soledad_failed.emit()
+ raise SoledadInitError()
def load_and_sync_soledad(self, uuid=None, offline=False):
"""
@@ -283,10 +295,9 @@ class SoledadBootstrapper(AbstractBootstrapper):
server_url, cert_file = remote_param
try:
- self._try_soledad_init(
- uuid, secrets_path, local_db_path,
- server_url, cert_file, token)
- except Exception:
+ self._do_soledad_init(uuid, secrets_path, local_db_path,
+ server_url, cert_file, token)
+ except SoledadInitError:
# re-raise the exceptions from try_init,
# we're currently handling the retries from the
# soledad-launcher in the gui.
@@ -378,9 +389,13 @@ class SoledadBootstrapper(AbstractBootstrapper):
Try to initialize soledad.
:param uuid: user identifier
+ :type uuid: str
:param secrets_path: path to secrets file
+ :type secrets_path: str
:param local_db_path: path to local db file
+ :type local_db_path: str
:param server_url: soledad server uri
+ :type server_url: str
:param cert_file: path to the certificate of the ca used
to validate the SSL certificate used by the remote
soledad server.
@@ -409,34 +424,17 @@ class SoledadBootstrapper(AbstractBootstrapper):
# and return a subclass of SoledadInitializationFailed
# recoverable, will guarantee retries
- except socket.timeout:
- logger.debug("SOLEDAD initialization TIMED OUT...")
- self.soledad_timeout.emit()
- raise
- except socket.error as exc:
- logger.warning("Socket error while initializing soledad")
- self.soledad_timeout.emit()
- raise
- except BootstrapSequenceError as exc:
- logger.warning("Error while initializing soledad")
- self.soledad_timeout.emit()
+ except (socket.timeout, socket.error, BootstrapSequenceError):
+ logger.warning("Error while initializing Soledad")
raise
# unrecoverable
- except u1db_errors.Unauthorized:
- logger.error("Error while initializing soledad "
- "(unauthorized).")
- self.soledad_failed.emit()
- raise
- except u1db_errors.HTTPError as exc:
- logger.exception("Error while initializing soledad "
- "(HTTPError)")
- self.soledad_failed.emit()
+ except (u1db_errors.Unauthorized, u1db_errors.HTTPError):
+ logger.error("Error while initializing Soledad (u1db error).")
raise
except Exception as exc:
logger.exception("Unhandled error while initializating "
- "soledad: %r" % (exc,))
- self.soledad_failed.emit()
+ "Soledad: %r" % (exc,))
raise
def _try_soledad_sync(self):