diff options
-rw-r--r-- | changes/feature_2790-autoselect_vpn_gateway_based_on_timezone | 1 | ||||
-rw-r--r-- | src/leap/crypto/srpauth.py | 22 | ||||
-rw-r--r-- | src/leap/gui/mainwindow.py | 3 | ||||
-rw-r--r-- | src/leap/gui/statuspanel.py | 2 | ||||
-rw-r--r-- | src/leap/services/abstractbootstrapper.py | 7 | ||||
-rw-r--r-- | src/leap/services/eip/eipconfig.py | 91 | ||||
-rw-r--r-- | src/leap/services/eip/vpnlaunchers.py | 12 | ||||
-rw-r--r-- | src/leap/util/leap_log_handler.py | 9 |
8 files changed, 128 insertions, 19 deletions
diff --git a/changes/feature_2790-autoselect_vpn_gateway_based_on_timezone b/changes/feature_2790-autoselect_vpn_gateway_based_on_timezone new file mode 100644 index 00000000..9f378b79 --- /dev/null +++ b/changes/feature_2790-autoselect_vpn_gateway_based_on_timezone @@ -0,0 +1 @@ + o Autoselect VPN gateway based on timezone. Closes #2790. diff --git a/src/leap/crypto/srpauth.py b/src/leap/crypto/srpauth.py index bcd24de3..0e95ae64 100644 --- a/src/leap/crypto/srpauth.py +++ b/src/leap/crypto/srpauth.py @@ -24,6 +24,7 @@ import json #this error is raised from requests from simplejson.decoder import JSONDecodeError +from functools import partial from PySide import QtCore from twisted.internet import threads @@ -321,6 +322,9 @@ class SRPAuth(QtCore.QObject): self.set_session_id(session_id) + def _threader(self, cb, res, *args, **kwargs): + return threads.deferToThread(cb, res, *args, **kwargs) + def authenticate(self, username, password): """ Executes the whole authentication process for a user @@ -341,10 +345,17 @@ class SRPAuth(QtCore.QObject): username=username, password=password) - d.addCallback(self._start_authentication, username=username, - password=password) - d.addCallback(self._process_challenge, username=username) - d.addCallback(self._verify_session) + d.addCallback( + partial(self._threader, + self._start_authentication), + username=username, + password=password) + d.addCallback( + partial(self._threader, + self._process_challenge), + username=username) + d.addCallback(partial(self._threader, + self._verify_session)) return d @@ -459,7 +470,8 @@ class SRPAuth(QtCore.QObject): :type failure: twisted.python.failure.Failure """ logger.error("Error logging in %s" % (failure,)) - self.authentication_finished.emit(False, "%s" % (failure,)) + self.authentication_finished.emit(False, "%s" % (failure.value,)) + failure.trap(Exception) def get_session_id(self): return self.__instance.get_session_id() diff --git a/src/leap/gui/mainwindow.py b/src/leap/gui/mainwindow.py index c58cd4e3..80876c27 100644 --- a/src/leap/gui/mainwindow.py +++ b/src/leap/gui/mainwindow.py @@ -1149,7 +1149,8 @@ class MainWindow(QtGui.QMainWindow): """ passed = data[self._provider_bootstrapper.PASSED_KEY] if not passed: - self._set_status(data[self._provider_bootstrapper.ERROR_KEY]) + self._login_widget.set_status( + data[self._provider_bootstrapper.ERROR_KEY]) self._already_started_eip = False def _eip_finished(self, exitCode): diff --git a/src/leap/gui/statuspanel.py b/src/leap/gui/statuspanel.py index 53c19e86..554903d8 100644 --- a/src/leap/gui/statuspanel.py +++ b/src/leap/gui/statuspanel.py @@ -189,7 +189,7 @@ class StatusPanelWidget(QtGui.QWidget): "it's already " "running."))) else: - self._set_eip_status(status) + self.set_eip_status(status) def set_eip_status_icon(self, status): """ diff --git a/src/leap/services/abstractbootstrapper.py b/src/leap/services/abstractbootstrapper.py index 2cbd56bc..f0937197 100644 --- a/src/leap/services/abstractbootstrapper.py +++ b/src/leap/services/abstractbootstrapper.py @@ -22,6 +22,8 @@ import logging import requests +from functools import partial + from PySide import QtCore from twisted.internet import threads from leap.common.check import leap_assert, leap_assert_type @@ -128,6 +130,9 @@ class AbstractBootstrapper(QtCore.QObject): logger.debug("Emitting %s" % (signal,)) signal.emit({self.PASSED_KEY: True, self.ERROR_KEY: ""}) + def _callback_threader(self, cb, res, *args, **kwargs): + return threads.deferToThread(cb, res, *args, **kwargs) + def addCallbackChain(self, callbacks): """ Creates a callback/errback chain on another thread using @@ -148,7 +153,7 @@ class AbstractBootstrapper(QtCore.QObject): if d is None: d = threads.deferToThread(cb) else: - d.addCallback(cb) + d.addCallback(partial(self._callback_threader, cb)) d.addErrback(self._errback, signal=sig) d.addCallback(self._gui_notify, signal=sig) d.addErrback(self._gui_errback) diff --git a/src/leap/services/eip/eipconfig.py b/src/leap/services/eip/eipconfig.py index 0a7d2b23..f7d03963 100644 --- a/src/leap/services/eip/eipconfig.py +++ b/src/leap/services/eip/eipconfig.py @@ -21,6 +21,8 @@ Provider configuration import logging import os import re +import datetime +import time import ipaddr @@ -32,6 +34,79 @@ from leap.services.eip.eipspec import eipservice_config_spec logger = logging.getLogger(__name__) +class VPNGatewaySelector(object): + """ + VPN Gateway selector. + """ + + def __init__(self, eipconfig): + ''' + Constructor for VPNGatewaySelector. + + :param eipconfig: a valid EIP Configuration. + :type eipconfig: EIPConfig + ''' + leap_assert_type(eipconfig, EIPConfig) + self._local_offset = 0 # defaults to GMT + self._local_timezone = None + self._set_local_offset() + self._eipconfig = eipconfig + + def _get_best_gateway(self): + """ + Returns index of the closest gateway, using timezones offsets. + + :rtype: int + """ + best_gateway = (-1, 99) # gateway, distance + locations = self._eipconfig.get_locations() + gateways = self._eipconfig.get_gateways() + for idx, gateway in enumerate(gateways): + gateway_offset = int(locations[gateway['location']]['timezone']) + gateway_distance = self._get_timezone_distance(gateway_offset) + if gateway_distance < best_gateway[1]: + best_gateway = (idx, gateway_distance) + + return best_gateway[0] + + def get_best_gateway_ip(self): + """ + Returns the ip of the best possible gateway. + + :rtype: An IPv4Address or IPv6Address object. + """ + best_gateway = self._get_best_gateway() + gateway_ip = self._eipconfig.get_gateway_ip(best_gateway) + + return gateway_ip + + def _get_timezone_distance(self, offset): + ''' + Returns the distance between the local timezone and + the one with offset 'offset'. + + :param offset: the distance of a timezone to GMT. + :type offset: int + :returns: distance between local offset and param offset. + :rtype: int + ''' + delta1 = datetime.timedelta(hours=offset) + delta2 = self._local_offset + diff = abs(delta1 - delta2) + hours = diff.seconds / (60 * 60) + return hours + + def _set_local_offset(self): + ''' + Sets the distance between GMT and the local timezone. + ''' + local_offset = time.timezone + if time.daylight: + local_offset = time.altzone + + self._local_offset = datetime.timedelta(seconds=-local_offset) + + class EIPConfig(BaseConfig): """ Provider configuration abstraction class @@ -56,6 +131,14 @@ class EIPConfig(BaseConfig): # TODO: create an abstraction for gateways return self._safe_get_value("gateways") + def get_locations(self): + ''' + Returns a list of locations + + :rtype: dict + ''' + return self._safe_get_value("locations") + def get_openvpn_configuration(self): """ Returns a dictionary containing the openvpn configuration @@ -63,8 +146,8 @@ class EIPConfig(BaseConfig): These are sanitized with alphanumeric whitelist. - @returns: openvpn configuration dict - @rtype: C{dict} + :returns: openvpn configuration dict + :rtype: C{dict} """ ovpncfg = self._safe_get_value("openvpn_configuration") config = {} @@ -84,7 +167,9 @@ class EIPConfig(BaseConfig): def get_gateway_ip(self, index=0): """ - Returns the ip of the gateway + Returns the ip of the gateway. + + :rtype: An IPv4Address or IPv6Address object. """ gateways = self.get_gateways() leap_assert(len(gateways) > 0, "We don't have any gateway!") diff --git a/src/leap/services/eip/vpnlaunchers.py b/src/leap/services/eip/vpnlaunchers.py index 6c2ff006..fa2989bc 100644 --- a/src/leap/services/eip/vpnlaunchers.py +++ b/src/leap/services/eip/vpnlaunchers.py @@ -34,7 +34,7 @@ from functools import partial from leap.common.check import leap_assert, leap_assert_type from leap.common.files import which from leap.config.providerconfig import ProviderConfig -from leap.services.eip.eipconfig import EIPConfig +from leap.services.eip.eipconfig import EIPConfig, VPNGatewaySelector logger = logging.getLogger(__name__) @@ -228,7 +228,8 @@ class LinuxVPNLauncher(VPNLauncher): # TODO: handle verbosity - gateway_ip = str(eipconfig.get_gateway_ip(0)) + gateway_selector = VPNGatewaySelector(eipconfig) + gateway_ip = gateway_selector.get_best_gateway_ip() logger.debug("Using gateway ip %s" % (gateway_ip,)) @@ -391,7 +392,9 @@ class DarwinVPNLauncher(VPNLauncher): # TODO: handle verbosity - gateway_ip = str(eipconfig.get_gateway_ip(0)) + gateway_selector = VPNGatewaySelector(eipconfig) + gateway_ip = gateway_selector.get_best_gateway_ip() + logger.debug("Using gateway ip %s" % (gateway_ip,)) args += [ @@ -530,7 +533,8 @@ class WindowsVPNLauncher(VPNLauncher): # TODO: handle verbosity - gateway_ip = str(eipconfig.get_gateway_ip(0)) + gateway_selector = VPNGatewaySelector(eipconfig) + gateway_ip = gateway_selector.get_best_gateway_ip() logger.debug("Using gateway ip %s" % (gateway_ip,)) diff --git a/src/leap/util/leap_log_handler.py b/src/leap/util/leap_log_handler.py index 5b8ae789..e5bc87e1 100644 --- a/src/leap/util/leap_log_handler.py +++ b/src/leap/util/leap_log_handler.py @@ -31,16 +31,16 @@ class LogHandler(logging.Handler): MESSAGE_KEY = 'message' RECORD_KEY = 'record' - # TODO This is going to eat lots of memory after some time. - # Should be pruned at some moment. - _log_history = [] - def __init__(self, qtsignal): """ LogHander initialization. Calls parent method and keeps a reference to the qtsignal that will be used to fire the gui update. """ + # TODO This is going to eat lots of memory after some time. + # Should be pruned at some moment. + self._log_history = [] + logging.Handler.__init__(self) self._qtsignal = qtsignal @@ -85,6 +85,7 @@ class LogHandler(logging.Handler): self._set_format(logRecord.levelname) log = self.format(logRecord) log_item = {self.RECORD_KEY: logRecord, self.MESSAGE_KEY: log} + self._log_history.append(log_item) self._qtsignal(log_item) |