summaryrefslogtreecommitdiff
path: root/src/leap
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap')
-rw-r--r--src/leap/app.py13
-rw-r--r--src/leap/base/auth.py17
-rw-r--r--src/leap/base/checks.py129
-rw-r--r--src/leap/base/config.py5
-rw-r--r--src/leap/base/constants.py3
-rw-r--r--src/leap/base/exceptions.py36
-rw-r--r--src/leap/base/network.py17
-rw-r--r--src/leap/base/pluggableconfig.py18
-rw-r--r--src/leap/base/specs.py6
-rw-r--r--src/leap/base/tests/test_checks.py38
-rw-r--r--src/leap/base/tests/test_providers.py10
-rw-r--r--src/leap/baseapp/dialogs.py9
-rw-r--r--src/leap/baseapp/eip.py25
-rw-r--r--src/leap/baseapp/log.py20
-rw-r--r--src/leap/baseapp/mainwindow.py23
-rw-r--r--src/leap/baseapp/network.py24
-rw-r--r--src/leap/baseapp/systray.py60
-rw-r--r--src/leap/crypto/__init__.py26
-rw-r--r--src/leap/eip/checks.py111
-rw-r--r--src/leap/eip/config.py51
-rw-r--r--src/leap/eip/eipconnection.py8
-rw-r--r--src/leap/eip/exceptions.py57
-rw-r--r--src/leap/eip/openvpnconnection.py80
-rw-r--r--src/leap/gui/firstrun/__init__.py5
-rw-r--r--src/leap/gui/firstrun/connect.py (renamed from src/leap/gui/firstrun/regvalidation.py)60
-rw-r--r--src/leap/gui/firstrun/intro.py2
-rw-r--r--src/leap/gui/firstrun/last.py8
-rw-r--r--src/leap/gui/firstrun/login.py13
-rw-r--r--src/leap/gui/firstrun/providerinfo.py9
-rw-r--r--src/leap/gui/firstrun/providerselect.py11
-rw-r--r--src/leap/gui/firstrun/providersetup.py47
-rw-r--r--src/leap/gui/firstrun/register.py26
-rwxr-xr-xsrc/leap/gui/firstrun/wizard.py12
-rw-r--r--src/leap/gui/progress.py7
-rw-r--r--src/leap/gui/tests/test_firstrun_login.py2
-rw-r--r--src/leap/gui/tests/test_firstrun_register.py2
-rw-r--r--src/leap/gui/tests/test_firstrun_wizard.py6
-rw-r--r--src/leap/soledad/__init__.py5
-rw-r--r--src/leap/soledad/backends/leap_backend.py3
-rw-r--r--src/leap/soledad/tests/test_sqlcipher.py2
-rw-r--r--src/leap/soledad/util.py2
-rw-r--r--src/leap/util/__init__.py9
-rw-r--r--src/leap/util/certs.py18
-rw-r--r--src/leap/util/geo.py32
-rw-r--r--src/leap/util/leap_argparse.py2
-rw-r--r--src/leap/util/misc.py20
-rw-r--r--src/leap/util/tests/test_translations.py22
-rw-r--r--src/leap/util/translations.py84
48 files changed, 874 insertions, 321 deletions
diff --git a/src/leap/app.py b/src/leap/app.py
index 334b58c8..912e390d 100644
--- a/src/leap/app.py
+++ b/src/leap/app.py
@@ -49,6 +49,13 @@ def main():
logger.addHandler(console)
#logger.debug(opts)
+ import os
+ ldlib = os.environ.get("LD_LIBRARY_PATH", None)
+ dyldlib = os.environ.get("DYLD_LIBRARY_PATH", None)
+
+ logger.debug("LD_LIBRARY_PATH %s" % ldlib)
+ logger.debug("DYLD_LIBRARY_PATH %s" % dyldlib)
+
logger.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
logger.info('LEAP client version %s', VERSION)
logger.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
@@ -82,6 +89,10 @@ def main():
app.setApplicationName("leap")
app.setOrganizationDomain("leap.se")
+ # XXX we could check here
+ # if leap-client is already running, and abort
+ # gracefully in that case.
+
if not QSystemTrayIcon.isSystemTrayAvailable():
QMessageBox.critical(None, "Systray",
"I couldn't detect"
@@ -108,6 +119,8 @@ def main():
# if not, it will be set visible
# from the systray menu.
window.show()
+ if sys.platform == "darwin":
+ window.raise_()
# run main loop
sys.exit(app.exec_())
diff --git a/src/leap/base/auth.py b/src/leap/base/auth.py
index ecc24179..f629972f 100644
--- a/src/leap/base/auth.py
+++ b/src/leap/base/auth.py
@@ -43,7 +43,6 @@ class LeapSRPRegister(object):
def __init__(self,
schema="https",
provider=None,
- #port=None,
verify=True,
register_path="1/users.json",
method="POST",
@@ -56,11 +55,6 @@ class LeapSRPRegister(object):
self.schema = schema
- # XXX FIXME
- #self.provider = provider
- #self.port = port
- # XXX splitting server,port
- # deprecate port call.
domain, port = get_https_domain_and_port(provider)
self.provider = domain
self.port = port
@@ -137,6 +131,9 @@ class SRPAuth(requests.auth.AuthBase):
self.server = server
self.verify = verify
+ logger.debug('SRPAuth. verify=%s' % verify)
+ logger.debug('server: %s. username=%s' % (server, username))
+
self.init_data = None
self.session = requests.session()
@@ -168,6 +165,9 @@ class SRPAuth(requests.auth.AuthBase):
except requests.exceptions.ConnectionError:
raise SRPAuthenticationError(
"No connection made (salt).")
+ except:
+ raise SRPAuthenticationError(
+ "Unknown error (salt).")
if init_session.status_code not in (200, ):
raise SRPAuthenticationError(
"No valid response (salt).")
@@ -245,7 +245,6 @@ class SRPAuth(requests.auth.AuthBase):
try:
assert self.srp_usr.authenticated()
logger.debug('user is authenticated!')
- print 'user is authenticated!'
except (AssertionError):
raise SRPAuthenticationError(
"Auth verification failed.")
@@ -268,6 +267,8 @@ def srpauth_protected(user=None, passwd=None, server=None, verify=True):
auth = SRPAuth(user, passwd, server, verify)
kwargs['auth'] = auth
kwargs['verify'] = verify
+ if not args:
+ logger.warning('attempting to get from empty uri!')
return fn(*args, **kwargs)
return wrapper
return srpauth
@@ -275,7 +276,7 @@ def srpauth_protected(user=None, passwd=None, server=None, verify=True):
def get_leap_credentials():
settings = QtCore.QSettings()
- full_username = settings.value('eip_username')
+ full_username = settings.value('username')
username, domain = full_username.split('@')
seed = settings.value('%s_seed' % domain, None)
password = leapkeyring.leap_get_password(full_username, seed=seed)
diff --git a/src/leap/base/checks.py b/src/leap/base/checks.py
index dc2602c2..8abdf774 100644
--- a/src/leap/base/checks.py
+++ b/src/leap/base/checks.py
@@ -1,16 +1,24 @@
# -*- coding: utf-8 -*-
import logging
import platform
+import re
import socket
import netifaces
import ping
import requests
+import sh
from leap.base import constants
from leap.base import exceptions
logger = logging.getLogger(name=__name__)
+_platform = platform.system()
+
+#EVENTS OF NOTE
+EVENT_CONNECT_REFUSED = "[ECONNREFUSED]: Connection refused (code=111)"
+
+ICMP_TARGET = "8.8.8.8"
class LeapNetworkChecker(object):
@@ -34,10 +42,13 @@ class LeapNetworkChecker(object):
if self.provider_gateway:
checker.ping_gateway(self.provider_gateway)
+ checker.parse_log_and_react([], ())
+
def check_internet_connection(self):
try:
# XXX remove this hardcoded random ip
# ping leap.se or eip provider instead...?
+ # XXX could use icmp instead..
requests.get('http://216.172.161.165')
except requests.ConnectionError as e:
error = "Unidentified Connection Error"
@@ -50,6 +61,9 @@ class LeapNetworkChecker(object):
raise exceptions.NoInternetConnection(error)
except (requests.HTTPError, requests.RequestException) as e:
raise exceptions.NoInternetConnection(e.message)
+
+ # XXX should redirect this to netcheck logger.
+ # and don't clutter main log.
logger.debug('Network appears to be up.')
def is_internet_up(self):
@@ -60,56 +74,100 @@ class LeapNetworkChecker(object):
return False
return True
- def check_tunnel_default_interface(self):
- """
- Raises an TunnelNotDefaultRouteError
- (including when no routes are present)
- """
- if not platform.system() == "Linux":
- raise NotImplementedError
+ def _get_route_table_linux(self):
- f = open("/proc/net/route")
- route_table = f.readlines()
- f.close()
+ with open("/proc/net/route") as f:
+ route_table = f.readlines()
#toss out header
route_table.pop(0)
-
if not route_table:
raise exceptions.TunnelNotDefaultRouteError()
+ return route_table
+ def _get_def_iface_osx(self):
+ default_iface = None
+ #gateway = None
+ routes = list(sh.route('-n', 'get', ICMP_TARGET, _iter=True))
+ iface = filter(lambda l: "interface" in l, routes)
+ if not iface:
+ return None, None
+ def_ifacel = re.findall('\w+\d', iface[0])
+ default_iface = def_ifacel[0] if def_ifacel else None
+ if not default_iface:
+ return None, None
+ _gw = filter(lambda l: "gateway" in l, routes)
+ gw = re.findall('\d+\.\d+\.\d+\.\d+', _gw[0])[0]
+ return default_iface, gw
+
+ def _get_tunnel_iface_linux(self):
+ # XXX review.
+ # valid also when local router has a default entry?
+ route_table = self._get_route_table_linux()
line = route_table.pop(0)
iface, destination = line.split('\t')[0:2]
if not destination == '00000000' or not iface == 'tun0':
raise exceptions.TunnelNotDefaultRouteError()
+ return True
- def get_default_interface_gateway(self):
- """only impletemented for linux so far."""
- if not platform.system() == "Linux":
+ def check_tunnel_default_interface(self):
+ """
+ Raises an TunnelNotDefaultRouteError
+ if tun0 is not the chosen default route
+ (including when no routes are present)
+ """
+ #logger.debug('checking tunnel default interface...')
+
+ if _platform == "Linux":
+ valid = self._get_tunnel_iface_linux()
+ return valid
+ elif _platform == "Darwin":
+ default_iface, gw = self._get_def_iface_osx()
+ #logger.debug('iface: %s', default_iface)
+ if default_iface != "tun0":
+ logger.debug('tunnel not default route! gw: %s', default_iface)
+ # XXX should catch this and act accordingly...
+ # but rather, this test should only be launched
+ # when we have successfully completed a connection
+ # ... TRIGGER: Connection stablished (or whatever it is)
+ # in the logs
+ raise exceptions.TunnelNotDefaultRouteError
+ else:
+ #logger.debug('PLATFORM !!! %s', _platform)
raise NotImplementedError
- # XXX use psutil
- f = open("/proc/net/route")
- route_table = f.readlines()
- f.close()
- #toss out header
- route_table.pop(0)
-
+ def _get_def_iface_linux(self):
default_iface = None
gateway = None
+
+ route_table = self._get_route_table_linux()
while route_table:
line = route_table.pop(0)
iface, destination, gateway = line.split('\t')[0:3]
if destination == '00000000':
default_iface = iface
break
+ return default_iface, gateway
+
+ def get_default_interface_gateway(self):
+ """
+ gets the interface we are going thru.
+ (this should be merged with check tunnel default interface,
+ imo...)
+ """
+ if _platform == "Linux":
+ default_iface, gw = self.get_def_iface_linux()
+ elif _platform == "Darwin":
+ default_iface, gw = self.get_def_iface_osx()
+ else:
+ raise NotImplementedError
if not default_iface:
raise exceptions.NoDefaultInterfaceFoundError
if default_iface not in netifaces.interfaces():
raise exceptions.InterfaceNotFoundError
-
- return default_iface, gateway
+ logger.debug('-- default iface', default_iface)
+ return default_iface, gw
def ping_gateway(self, gateway):
# TODO: Discuss how much packet loss (%) is acceptable.
@@ -118,7 +176,14 @@ class LeapNetworkChecker(object):
# -- is it a valid ip? (there's something in util)
# -- is it a domain?
# -- can we resolve? -- raise NoDNSError if not.
+
+ # XXX -- needs review!
+ # We cannout use this ping implementation; it needs root.
+ # We need to look for another, poors-man implementation
+ # or wrap around system traceroute (using sh module, fi)
+ # -- kali
packet_loss = ping.quiet_ping(gateway)[0]
+ logger.debug('packet loss %s' % packet_loss)
if packet_loss > constants.MAX_ICMP_PACKET_LOSS:
raise exceptions.NoConnectionToGateway
@@ -128,3 +193,21 @@ class LeapNetworkChecker(object):
return True
except socket.gaierror:
raise exceptions.CannotResolveDomainError
+
+ def parse_log_and_react(self, log, error_matrix=None):
+ """
+ compares the recent openvpn status log to
+ strings passed in and executes the callbacks passed in.
+ @param log: openvpn log
+ @type log: list of strings
+ @param error_matrix: tuples of strings and tuples of callbacks
+ @type error_matrix: tuples strings and call backs
+ """
+ for line in log:
+ # we could compile a regex here to save some cycles up -- kali
+ for each in error_matrix:
+ error, callbacks = each
+ if error in line:
+ for cb in callbacks:
+ if callable(cb):
+ cb()
diff --git a/src/leap/base/config.py b/src/leap/base/config.py
index 438d1993..e235e5c3 100644
--- a/src/leap/base/config.py
+++ b/src/leap/base/config.py
@@ -155,7 +155,7 @@ class JSONLeapConfig(BaseLeapConfig):
return False
def load(self, fromfile=None, from_uri=None, fetcher=None,
- force_download=False, verify=False):
+ force_download=False, verify=True):
if from_uri is not None:
fetched = self.fetch(
@@ -177,8 +177,7 @@ class JSONLeapConfig(BaseLeapConfig):
if not fetcher:
fetcher = self.fetcher
- logger.debug('verify: %s', verify)
- logger.debug('uri: %s', uri)
+ logger.debug('uri: %s (verify: %s)' % (uri, verify))
rargs = (uri, )
rkwargs = {'verify': verify}
diff --git a/src/leap/base/constants.py b/src/leap/base/constants.py
index b38723be..f5665e5f 100644
--- a/src/leap/base/constants.py
+++ b/src/leap/base/constants.py
@@ -1,6 +1,7 @@
"""constants to be used in base module"""
from leap import __branding
-APP_NAME = __branding.get("short_name", "leap")
+APP_NAME = __branding.get("short_name", "leap-client")
+OPENVPN_BIN = "openvpn"
# default provider placeholder
# using `example.org` we make sure that this
diff --git a/src/leap/base/exceptions.py b/src/leap/base/exceptions.py
index 227da953..2e31b33b 100644
--- a/src/leap/base/exceptions.py
+++ b/src/leap/base/exceptions.py
@@ -14,6 +14,7 @@ Exception attributes and their meaning/uses
* usermessage: the message that will be passed to user in ErrorDialogs
in Qt-land.
"""
+from leap.util.translations import translate
class LeapException(Exception):
@@ -22,6 +23,7 @@ class LeapException(Exception):
sets some parameters that we will check
during error checking routines
"""
+
critical = False
failfirst = False
warning = False
@@ -46,32 +48,50 @@ class ImproperlyConfigured(Exception):
pass
-class NoDefaultInterfaceFoundError(LeapException):
- message = "no default interface found"
- usermessage = "Looks like your computer is not connected to the internet"
+# NOTE: "Errors" (context) has to be a explicit string!
class InterfaceNotFoundError(LeapException):
# XXX should take iface arg on init maybe?
message = "interface not found"
+ usermessage = translate(
+ "Errors",
+ "Interface not found")
+
+
+class NoDefaultInterfaceFoundError(LeapException):
+ message = "no default interface found"
+ usermessage = translate(
+ "Errors",
+ "Looks like your computer "
+ "is not connected to the internet")
class NoConnectionToGateway(CriticalError):
message = "no connection to gateway"
- usermessage = "Looks like there are problems with your internet connection"
+ usermessage = translate(
+ "Errors",
+ "Looks like there are problems "
+ "with your internet connection")
class NoInternetConnection(CriticalError):
message = "No Internet connection found"
- usermessage = "It looks like there is no internet connection."
+ usermessage = translate(
+ "Errors",
+ "It looks like there is no internet connection.")
# and now we try to connect to our web to troubleshoot LOL :P
class CannotResolveDomainError(LeapException):
message = "Cannot resolve domain"
- usermessage = "Domain cannot be found"
+ usermessage = translate(
+ "Errors",
+ "Domain cannot be found")
-class TunnelNotDefaultRouteError(CriticalError):
+class TunnelNotDefaultRouteError(LeapException):
message = "Tunnel connection dissapeared. VPN down?"
- usermessage = "The Encrypted Connection was lost. Shutting down..."
+ usermessage = translate(
+ "Errors",
+ "The Encrypted Connection was lost.")
diff --git a/src/leap/base/network.py b/src/leap/base/network.py
index 765d8ea0..d841e692 100644
--- a/src/leap/base/network.py
+++ b/src/leap/base/network.py
@@ -21,8 +21,8 @@ class NetworkCheckerThread(object):
connection.
"""
def __init__(self, *args, **kwargs):
+
self.status_signals = kwargs.pop('status_signals', None)
- #self.watcher_cb = kwargs.pop('status_signals', None)
self.error_cb = kwargs.pop(
'error_cb',
lambda exc: logger.error("%s", exc.message))
@@ -48,6 +48,7 @@ class NetworkCheckerThread(object):
(self.error_cb,))
def stop(self):
+ self.process_handle.join(timeout=0.1)
self.shutdown.set()
logger.debug("network checked stopped.")
@@ -59,6 +60,7 @@ class NetworkCheckerThread(object):
#here all the observers in fail_callbacks expect one positional argument,
#which is exception so we can try by passing a lambda with logger to
#check it works.
+
def _network_checks_thread(self, fail_callbacks):
#TODO: replace this with waiting for a signal from openvpn
while True:
@@ -69,11 +71,15 @@ class NetworkCheckerThread(object):
# XXX ??? why do we sleep here???
# aa: If the openvpn isn't up and running yet,
# let's give it a moment to breath.
+ #logger.error('NOT DEFAULT ROUTE!----')
+ # Instead of this, we should flag when the
+ # iface IS SUPPOSED to be up imo. -- kali
sleep(1)
fail_observer_dict = dict(((
observer,
process_events(observer)) for observer in fail_callbacks))
+
while not self.shutdown.is_set():
try:
self.checker.check_tunnel_default_interface()
@@ -83,11 +89,18 @@ class NetworkCheckerThread(object):
for obs in fail_observer_dict:
fail_observer_dict[obs].send(exc)
sleep(ROUTE_CHECK_INTERVAL)
+
#reset event
+ # I see a problem with this. You cannot stop it, it
+ # resets itself forever. -- kali
+
+ # XXX use QTimer for the recurrent triggers,
+ # and ditch the sleeps.
+ logger.debug('resetting event')
self.shutdown.clear()
def _launch_recurrent_network_checks(self, fail_callbacks):
- #we need to wrap the fail callback in a tuple
+ # XXX reimplement using QTimer -- kali
watcher = launch_thread(
self._network_checks_thread,
(fail_callbacks,))
diff --git a/src/leap/base/pluggableconfig.py b/src/leap/base/pluggableconfig.py
index 0ca985ea..3517db6b 100644
--- a/src/leap/base/pluggableconfig.py
+++ b/src/leap/base/pluggableconfig.py
@@ -10,6 +10,8 @@ import urlparse
import jsonschema
+from leap.util.translations import LEAPTranslatable
+
logger = logging.getLogger(__name__)
@@ -118,7 +120,6 @@ adaptors['json'] = JSONAdaptor()
# to proper python types.
# TODO:
-# - multilingual object.
# - HTTPS uri
@@ -132,6 +133,20 @@ class DateType(object):
return time.strftime(self.fmt, data)
+class TranslatableType(object):
+ """
+ a type that casts to LEAPTranslatable objects.
+ Used for labels we get from providers and stuff.
+ """
+
+ def to_python(self, data):
+ return LEAPTranslatable(data)
+
+ # needed? we already have an extended dict...
+ #def get_prep_value(self, data):
+ #return dict(data)
+
+
class URIType(object):
def to_python(self, data):
@@ -164,6 +179,7 @@ types = {
'date': DateType(),
'uri': URIType(),
'https-uri': HTTPSURIType(),
+ 'translatable': TranslatableType(),
}
diff --git a/src/leap/base/specs.py b/src/leap/base/specs.py
index 962aa07d..f57d7e9c 100644
--- a/src/leap/base/specs.py
+++ b/src/leap/base/specs.py
@@ -22,12 +22,16 @@ leap_provider_spec = {
#'required': True,
},
'name': {
- 'type': dict, # XXX multilingual object?
+ #'type': LEAPTranslatable,
+ 'type': dict,
+ 'format': 'translatable',
'default': {u'en': u'Test Provider'}
#'required': True
},
'description': {
+ #'type': LEAPTranslatable,
'type': dict,
+ 'format': 'translatable',
'default': {u'en': u'Test provider'}
},
'enrollment_policy': {
diff --git a/src/leap/base/tests/test_checks.py b/src/leap/base/tests/test_checks.py
index 7a694f89..645e615c 100644
--- a/src/leap/base/tests/test_checks.py
+++ b/src/leap/base/tests/test_checks.py
@@ -37,6 +37,8 @@ class LeapNetworkCheckTest(BaseLeapTest):
"missing meth")
self.assertTrue(hasattr(checker, "ping_gateway"),
"missing meth")
+ self.assertTrue(hasattr(checker, "parse_log_and_react"),
+ "missing meth")
def test_checker_should_actually_call_all_tests(self):
checker = checks.LeapNetworkChecker()
@@ -45,6 +47,7 @@ class LeapNetworkCheckTest(BaseLeapTest):
self.assertTrue(mc.check_internet_connection.called, "not called")
self.assertTrue(mc.check_tunnel_default_interface.called, "not called")
self.assertTrue(mc.is_internet_up.called, "not called")
+ self.assertTrue(mc.parse_log_and_react.called, "not called")
# ping gateway only called if we pass provider_gw
checker = checks.LeapNetworkChecker(provider_gw="0.0.0.0")
@@ -54,6 +57,7 @@ class LeapNetworkCheckTest(BaseLeapTest):
self.assertTrue(mc.check_tunnel_default_interface.called, "not called")
self.assertTrue(mc.ping_gateway.called, "not called")
self.assertTrue(mc.is_internet_up.called, "not called")
+ self.assertTrue(mc.parse_log_and_react.called, "not called")
def test_get_default_interface_no_interface(self):
checker = checks.LeapNetworkChecker()
@@ -134,6 +138,40 @@ class LeapNetworkCheckTest(BaseLeapTest):
mock_ping.side_effect = exceptions.NoConnectionToGateway
checker.check_internet_connection()
+ def test_parse_log_and_react(self):
+ checker = checks.LeapNetworkChecker()
+ to_call = Mock()
+ log = [("leap.openvpn - INFO - Mon Nov 19 13:36:24 2012 "
+ "read UDPv4 [ECONNREFUSED]: Connection refused (code=111)"]
+ err_matrix = [(checks.EVENT_CONNECT_REFUSED, (to_call, ))]
+ checker.parse_log_and_react(log, err_matrix)
+ self.assertTrue(to_call.called)
+
+ log = [("2012-11-19 13:36:26,177 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 ERROR: Linux route delete command "
+ "failed: external program exited"),
+ ("2012-11-19 13:36:26,178 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 ERROR: Linux route delete command "
+ "failed: external program exited"),
+ ("2012-11-19 13:36:26,180 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 ERROR: Linux route delete command "
+ "failed: external program exited"),
+ ("2012-11-19 13:36:26,181 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 /sbin/ifconfig tun0 0.0.0.0"),
+ ("2012-11-19 13:36:26,182 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:24 2012 Linux ip addr del failed: external "
+ "program exited with error stat"),
+ ("2012-11-19 13:36:26,183 - leap.openvpn - INFO - "
+ "Mon Nov 19 13:36:26 2012 SIGTERM[hard,] received, process"
+ "exiting"), ]
+ to_call.reset_mock()
+ checker.parse_log_and_react(log, err_matrix)
+ self.assertFalse(to_call.called)
+
+ to_call.reset_mock()
+ checker.parse_log_and_react([], err_matrix)
+ self.assertFalse(to_call.called)
+
@unittest.skipUnless(_uid == 0, "root only")
def test_ping_gateway(self):
checker = checks.LeapNetworkChecker()
diff --git a/src/leap/base/tests/test_providers.py b/src/leap/base/tests/test_providers.py
index 9c11f270..f257f54d 100644
--- a/src/leap/base/tests/test_providers.py
+++ b/src/leap/base/tests/test_providers.py
@@ -15,10 +15,12 @@ from leap.base import providers
EXPECTED_DEFAULT_CONFIG = {
u"api_version": u"0.1.0",
- u"description": {u'en': u"Test provider"},
+ #u"description": "LEAPTranslatable<{u'en': u'Test provider'}>",
+ u"description": {u'en': u'Test provider'},
u"default_language": u"en",
#u"display_name": {u'en': u"Test Provider"},
u"domain": u"testprovider.example.org",
+ #u'name': "LEAPTranslatable<{u'en': u'Test Provider'}>",
u'name': {u'en': u'Test Provider'},
u"enrollment_policy": u"open",
#u"serial": 1,
@@ -66,6 +68,7 @@ class TestLeapProviderDefinition(BaseLeapTest):
self.definition.save(to=self.testfile, force=True)
deserialized = json.load(open(self.testfile, 'rb'))
self.maxDiff = None
+ #import ipdb;ipdb.set_trace()
self.assertEqual(deserialized, EXPECTED_DEFAULT_CONFIG)
def test_provider_dump_to_slug(self):
@@ -84,8 +87,9 @@ class TestLeapProviderDefinition(BaseLeapTest):
with open(self.testfile, 'w') as wf:
wf.write(json.dumps(EXPECTED_DEFAULT_CONFIG))
self.definition.load(fromfile=self.testfile)
- self.assertDictEqual(self.config,
- EXPECTED_DEFAULT_CONFIG)
+ #self.assertDictEqual(self.config,
+ #EXPECTED_DEFAULT_CONFIG)
+ self.assertItemsEqual(self.config, EXPECTED_DEFAULT_CONFIG)
def test_provider_validation(self):
self.definition.validate(self.config)
diff --git a/src/leap/baseapp/dialogs.py b/src/leap/baseapp/dialogs.py
index 3cb539cf..d256fc99 100644
--- a/src/leap/baseapp/dialogs.py
+++ b/src/leap/baseapp/dialogs.py
@@ -23,7 +23,8 @@ class ErrorDialog(QDialog):
def warningMessage(self, msg, label):
msgBox = QMessageBox(QMessageBox.Warning,
- "QMessageBox.warning()", msg,
+ "LEAP Client Error",
+ msg,
QMessageBox.NoButton, self)
msgBox.addButton("&Ok", QMessageBox.AcceptRole)
if msgBox.exec_() == QMessageBox.AcceptRole:
@@ -34,7 +35,8 @@ class ErrorDialog(QDialog):
def criticalMessage(self, msg, label):
msgBox = QMessageBox(QMessageBox.Critical,
- "QMessageBox.critical()", msg,
+ "LEAP Client Error",
+ msg,
QMessageBox.NoButton, self)
msgBox.addButton("&Ok", QMessageBox.AcceptRole)
msgBox.exec_()
@@ -49,7 +51,8 @@ class ErrorDialog(QDialog):
def confirmMessage(self, msg, label, action):
msgBox = QMessageBox(QMessageBox.Critical,
- "QMessageBox.critical()", msg,
+ self.tr("LEAP Client Error"),
+ msg,
QMessageBox.NoButton, self)
msgBox.addButton("&Ok", QMessageBox.AcceptRole)
msgBox.addButton("&Cancel", QMessageBox.RejectRole)
diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py
index 55ecfa79..4c1fb32d 100644
--- a/src/leap/baseapp/eip.py
+++ b/src/leap/baseapp/eip.py
@@ -9,6 +9,8 @@ from leap.baseapp.dialogs import ErrorDialog
from leap.baseapp import constants
from leap.eip import exceptions as eip_exceptions
from leap.eip.eipconnection import EIPConnection
+from leap.base.checks import EVENT_CONNECT_REFUSED
+from leap.util import geo
logger = logging.getLogger(name=__name__)
@@ -21,6 +23,7 @@ class EIPConductorAppMixin(object):
Connects the eip connect/disconnect logic
to the switches in the app (buttons/menu items).
"""
+ ERR_DIALOG = False
def __init__(self, *args, **kwargs):
opts = kwargs.pop('opts')
@@ -93,6 +96,15 @@ class EIPConductorAppMixin(object):
in the future we plan to derive errors to
our log viewer.
"""
+ if self.ERR_DIALOG:
+ logger.warning('another error dialog suppressed')
+ return
+
+ # XXX this is actually a one-shot.
+ # On the dialog there should be
+ # a reset signal binded to the ok button
+ # or something like that.
+ self.ERR_DIALOG = True
if getattr(error, 'usermessage', None):
message = error.usermessage
@@ -104,6 +116,7 @@ class EIPConductorAppMixin(object):
# launching dialog.
# (so Qt tests can assert stuff)
+
if error.critical:
logger.critical(error.message)
#critical error (non recoverable),
@@ -112,6 +125,7 @@ class EIPConductorAppMixin(object):
ErrorDialog(errtype="critical",
msg=message,
label="critical error")
+
elif error.warning:
logger.warning(error.message)
@@ -162,6 +176,8 @@ class EIPConductorAppMixin(object):
self.status_label.setText(con_status)
self.ip_label.setText(ip)
self.remote_label.setText(remote)
+ self.remote_country.setText(
+ geo.get_country_name(remote))
# status i/o
@@ -174,6 +190,11 @@ class EIPConductorAppMixin(object):
self.tun_read_bytes.setText(tun_read)
self.tun_write_bytes.setText(tun_write)
+ # connection information via management interface
+ log = self.conductor.get_log()
+ error_matrix = [(EVENT_CONNECT_REFUSED, (self.start_or_stopVPN, ))]
+ self.network_checker.checker.parse_log_and_react(log, error_matrix)
+
@QtCore.pyqtSlot()
def start_or_stopVPN(self):
"""
@@ -195,7 +216,7 @@ class EIPConductorAppMixin(object):
else:
# no errors, so go on.
if self.debugmode:
- self.startStopButton.setText('&Disconnect')
+ self.startStopButton.setText(self.tr('&Disconnect'))
self.eip_service_started = True
self.toggleEIPAct()
@@ -209,7 +230,7 @@ class EIPConductorAppMixin(object):
self.network_checker.stop()
self.conductor.disconnect()
if self.debugmode:
- self.startStopButton.setText('&Connect')
+ self.startStopButton.setText(self.tr('&Connect'))
self.eip_service_started = False
self.toggleEIPAct()
self.timer.stop()
diff --git a/src/leap/baseapp/log.py b/src/leap/baseapp/log.py
index 8a7f81c3..636e5bae 100644
--- a/src/leap/baseapp/log.py
+++ b/src/leap/baseapp/log.py
@@ -11,6 +11,7 @@ class LogPaneMixin(object):
a simple log pane
that writes new lines as they come
"""
+ EXCLUDES = ('MANAGEMENT',)
def createLogBrowser(self):
"""
@@ -21,7 +22,7 @@ class LogPaneMixin(object):
logging_layout = QtGui.QVBoxLayout()
self.logbrowser = QtGui.QTextBrowser()
- startStopButton = QtGui.QPushButton("&Connect")
+ startStopButton = QtGui.QPushButton(self.tr("&Connect"))
self.startStopButton = startStopButton
logging_layout.addWidget(self.logbrowser)
@@ -34,9 +35,10 @@ class LogPaneMixin(object):
grid = QtGui.QGridLayout()
self.updateTS = QtGui.QLabel('')
- self.status_label = QtGui.QLabel('Disconnected')
+ self.status_label = QtGui.QLabel(self.tr('Disconnected'))
self.ip_label = QtGui.QLabel('')
self.remote_label = QtGui.QLabel('')
+ self.remote_country = QtGui.QLabel('')
tun_read_label = QtGui.QLabel("tun read")
self.tun_read_bytes = QtGui.QLabel("0")
@@ -47,10 +49,11 @@ class LogPaneMixin(object):
grid.addWidget(self.status_label, 0, 1)
grid.addWidget(self.ip_label, 1, 0)
grid.addWidget(self.remote_label, 1, 1)
- grid.addWidget(tun_read_label, 2, 0)
- grid.addWidget(self.tun_read_bytes, 2, 1)
- grid.addWidget(tun_write_label, 3, 0)
- grid.addWidget(self.tun_write_bytes, 3, 1)
+ grid.addWidget(self.remote_country, 2, 1)
+ grid.addWidget(tun_read_label, 3, 0)
+ grid.addWidget(self.tun_read_bytes, 3, 1)
+ grid.addWidget(tun_write_label, 4, 0)
+ grid.addWidget(self.tun_write_bytes, 4, 1)
self.statusBox.setLayout(grid)
@@ -60,6 +63,7 @@ class LogPaneMixin(object):
simple slot: writes new line to logger Pane.
"""
msg = line[:-1]
- if self.debugmode:
+ if self.debugmode and all(map(lambda w: w not in msg,
+ LogPaneMixin.EXCLUDES)):
self.logbrowser.append(msg)
- vpnlogger.info(msg)
+ vpnlogger.info(msg)
diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py
index 02adab65..b1e5bccf 100644
--- a/src/leap/baseapp/mainwindow.py
+++ b/src/leap/baseapp/mainwindow.py
@@ -57,10 +57,10 @@ class LeapWindow(QtGui.QMainWindow,
settings = QtCore.QSettings()
self.provider_domain = settings.value("provider_domain", None)
- self.eip_username = settings.value("eip_username", None)
+ self.username = settings.value("username", None)
logger.debug('provider: %s', self.provider_domain)
- logger.debug('eip_username: %s', self.eip_username)
+ logger.debug('username: %s', self.username)
provider = self.provider_domain
EIPConductorAppMixin.__init__(
@@ -100,11 +100,11 @@ class LeapWindow(QtGui.QMainWindow,
self.startStopButton.clicked.connect(
lambda: self.start_or_stopVPN())
self.start_eipconnection.connect(
- lambda: self.start_or_stopVPN())
+ self.do_start_eipconnection)
self.shutdownSignal.connect(
self.cleanupAndQuit)
self.initNetworkChecker.connect(
- lambda: self.init_network_checker(self.provider_domain))
+ lambda: self.init_network_checker(self.conductor.provider))
# status change.
# TODO unify
@@ -147,9 +147,9 @@ class LeapWindow(QtGui.QMainWindow,
# launch wizard if needed
if need_wizard:
+ logger.debug('running first run wizard')
self.launch_first_run_wizard()
else: # no wizard needed
- logger.debug('running first run wizard')
self.initReady.emit()
def launch_first_run_wizard(self):
@@ -160,7 +160,7 @@ class LeapWindow(QtGui.QMainWindow,
wizard = FirstRunWizard(
self.conductor,
parent=self,
- eip_username=self.eip_username,
+ username=self.username,
start_eipconnection_signal=self.start_eipconnection,
eip_statuschange_signal=self.eipStatusChange,
quitcallback=self.onWizardCancel)
@@ -174,5 +174,16 @@ class LeapWindow(QtGui.QMainWindow,
self.cleanupAndQuit()
def runchecks_and_eipconnect(self):
+ """
+ shows icon and run init checks
+ """
self.show_systray_icon()
self.initchecks.begin()
+
+ def do_start_eipconnection(self):
+ """
+ shows icon and init eip connection
+ called from the end of wizard
+ """
+ self.show_systray_icon()
+ self.start_or_stopVPN()
diff --git a/src/leap/baseapp/network.py b/src/leap/baseapp/network.py
index a33265e5..a67f6340 100644
--- a/src/leap/baseapp/network.py
+++ b/src/leap/baseapp/network.py
@@ -17,6 +17,8 @@ class NetworkCheckerAppMixin(object):
initialize an instance of the Network Checker,
which gathers error and passes them on.
"""
+ ERR_NETERR = False
+
def __init__(self, *args, **kwargs):
provider = kwargs.pop('provider', None)
if provider:
@@ -41,11 +43,19 @@ class NetworkCheckerAppMixin(object):
slot that receives a network exceptions
and raises a user error message
"""
- logger.debug('handling network exception')
- logger.error(exc.message)
- dialog = ErrorDialog(parent=self)
+ # FIXME this should not HANDLE anything after
+ # the network check thread has been stopped.
- if exc.critical:
- dialog.criticalMessage(exc.usermessage, "network error")
- else:
- dialog.warningMessage(exc.usermessage, "network error")
+ logger.debug('handling network exception')
+ if not self.ERR_NETERR:
+ self.ERR_NETERR = True
+
+ logger.error(exc.message)
+ dialog = ErrorDialog(parent=self)
+ if exc.critical:
+ dialog.criticalMessage(exc.usermessage, "network error")
+ else:
+ dialog.warningMessage(exc.usermessage, "network error")
+
+ self.start_or_stopVPN()
+ self.network_checker.stop()
diff --git a/src/leap/baseapp/systray.py b/src/leap/baseapp/systray.py
index 0dd0f195..77eb3fe9 100644
--- a/src/leap/baseapp/systray.py
+++ b/src/leap/baseapp/systray.py
@@ -1,4 +1,6 @@
import logging
+import sys
+
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
@@ -73,7 +75,8 @@ class StatusAwareTrayIconMixin(object):
self.iconpath['connected'])),
self.ConnectionWidgets = con_widgets
- self.statusIconBox = QtGui.QGroupBox("EIP Connection Status")
+ self.statusIconBox = QtGui.QGroupBox(
+ self.tr("EIP Connection Status"))
statusIconLayout = QtGui.QHBoxLayout()
statusIconLayout.addWidget(self.ConnectionWidgets['disconnected'])
statusIconLayout.addWidget(self.ConnectionWidgets['connecting'])
@@ -81,7 +84,8 @@ class StatusAwareTrayIconMixin(object):
statusIconLayout.itemAt(1).widget().hide()
statusIconLayout.itemAt(2).widget().hide()
- self.leapConnStatus = QtGui.QLabel("<b>disconnected</b>")
+ self.leapConnStatus = QtGui.QLabel(
+ self.tr("<b>disconnected</b>"))
statusIconLayout.addWidget(self.leapConnStatus)
self.statusIconBox.setLayout(statusIconLayout)
@@ -111,26 +115,32 @@ class StatusAwareTrayIconMixin(object):
#self.trayIconMenu.customContextMenuRequested.connect(
#self.on_context_menu)
- def bad(self):
- logger.error('this should not be called')
+ #def bad(self):
+ #logger.error('this should not be called')
def createActions(self):
"""
creates actions to be binded to tray icon
"""
# XXX change action name on (dis)connect
- self.connAct = QtGui.QAction("Encryption ON turn &off", self,
- triggered=lambda: self.start_or_stopVPN())
-
- self.detailsAct = QtGui.QAction("&Details...",
- self,
- triggered=self.detailsWin)
- self.aboutAct = QtGui.QAction("&About", self,
- triggered=self.about)
- self.aboutQtAct = QtGui.QAction("About Q&t", self,
- triggered=QtGui.qApp.aboutQt)
- self.quitAction = QtGui.QAction("&Quit", self,
- triggered=self.cleanupAndQuit)
+ self.connAct = QtGui.QAction(
+ self.tr("Encryption ON turn &off"),
+ self,
+ triggered=lambda: self.start_or_stopVPN())
+
+ self.detailsAct = QtGui.QAction(
+ self.tr("&Details..."),
+ self,
+ triggered=self.detailsWin)
+ self.aboutAct = QtGui.QAction(
+ self.tr("&About"), self,
+ triggered=self.about)
+ self.aboutQtAct = QtGui.QAction(
+ self.tr("About Q&t"), self,
+ triggered=QtGui.qApp.aboutQt)
+ self.quitAction = QtGui.QAction(
+ self.tr("&Quit"), self,
+ triggered=self.cleanupAndQuit)
def toggleEIPAct(self):
# this is too simple by now.
@@ -139,15 +149,17 @@ class StatusAwareTrayIconMixin(object):
icon_status = self.conductor.get_icon_name()
if icon_status == "connected":
self.connAct.setEnabled(True)
- self.connAct.setText('Encryption ON turn o&ff')
+ self.connAct.setText(
+ self.tr('Encryption ON turn o&ff'))
return
if icon_status == "disconnected":
self.connAct.setEnabled(True)
- self.connAct.setText('Encryption OFF turn &on')
+ self.connAct.setText(
+ self.tr('Encryption OFF turn &on'))
return
if icon_status == "connecting":
self.connAct.setDisabled(True)
- self.connAct.setText('connecting...')
+ self.connAct.setText(self.tr('connecting...'))
return
def detailsWin(self):
@@ -156,18 +168,21 @@ class StatusAwareTrayIconMixin(object):
self.hide()
else:
self.show()
+ if sys.platform == "darwin":
+ self.raise_()
def about(self):
# move to widget
flavor = BRANDING.get('short_name', None)
- content = ("LEAP client<br>"
- "(version <b>%s</b>)<br>" % VERSION)
+ content = self.tr(
+ ("LEAP client<br>"
+ "(version <b>%s</b>)<br>" % VERSION))
if flavor:
content = content + ('<br>Flavor: <i>%s</i><br>' % flavor)
content = content + (
"<br><a href='https://leap.se/'>"
"https://leap.se</a>")
- QtGui.QMessageBox.about(self, "About", content)
+ QtGui.QMessageBox.about(self, self.tr("About"), content)
def setConnWidget(self, icon_name):
oldlayout = self.statusIconBox.layout()
@@ -205,6 +220,7 @@ class StatusAwareTrayIconMixin(object):
# is failing in a way beyond my understanding.
# (not working the first time it's clicked).
# this works however.
+ # XXX in osx it shows some glitches.
context_menu.exec_(self.trayIcon.geometry().center())
@QtCore.pyqtSlot()
diff --git a/src/leap/crypto/__init__.py b/src/leap/crypto/__init__.py
index e69de29b..8df3fc6c 100644
--- a/src/leap/crypto/__init__.py
+++ b/src/leap/crypto/__init__.py
@@ -0,0 +1,26 @@
+"""
+DEBUG! ----------- gnutls lib: libgnutls.26.dylib
+DEBUG! ----------- gnutls lib: /usr/local/lib/libgnutls.26.dylib
+DEBUG! ----------- gnutls lib: /opt/local/lib/libgnutls.26.dylib
+DEBUG! ----------- gnutls lib: libgnutls-extra.26.dylib
+DEBUG! ----------- gnutls lib: /usr/local/lib/libgnutls-extra.26.dylib
+DEBUG! ----------- gnutls lib: /opt/local/lib/libgnutls-extra.26.dylib
+"""
+import sys
+
+# hackaround pyinstaller ctypes dependencies discovery
+# See:
+# http://www.pyinstaller.org/wiki/Features/CtypesDependencySupport#SolutioninPyInstaller
+# gnutls.library.load_library is using a style of dep loading
+# unsupported by pyinstaller. So we force these imports here.
+
+if sys.platform == "darwin":
+ from ctypes import CDLL
+ try:
+ CDLL("libgnutls.26.dylib")
+ except OSError:
+ pass
+ try:
+ CDLL("libgnutls-extra.26.dylib")
+ except OSError:
+ pass
diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py
index 9ae6e5f5..0d07ef08 100644
--- a/src/leap/eip/checks.py
+++ b/src/leap/eip/checks.py
@@ -1,8 +1,9 @@
import logging
-import ssl
+#import ssl
#import platform
import time
import os
+import sys
import gnutls.crypto
#import netifaces
@@ -20,7 +21,10 @@ from leap.eip import config as eipconfig
from leap.eip import constants as eipconstants
from leap.eip import exceptions as eipexceptions
from leap.eip import specs as eipspecs
+from leap.util.certs import get_mac_cabundle
from leap.util.fileutil import mkdir_p
+from leap.util.web import get_https_domain_and_port
+from leap.util.misc import null_check
logger = logging.getLogger(name=__name__)
@@ -46,7 +50,7 @@ reachable and testable as a whole.
def get_branding_ca_cert(domain):
- # XXX deprecated
+ # deprecated
ca_file = BRANDING.get('provider_ca_file')
if ca_file:
return leapcerts.where(ca_file)
@@ -63,6 +67,10 @@ class ProviderCertChecker(object):
self.fetcher = fetcher
self.domain = domain
+ #XXX needs some kind of autoinit
+ #right now we set by hand
+ #by loading and reading provider config
+ self.apidomain = None
self.cacert = eipspecs.provider_ca_path(domain)
def run_all(
@@ -159,36 +167,33 @@ class ProviderCertChecker(object):
if autocacert and verify is True and self.cacert is not None:
logger.debug('verify cert: %s', self.cacert)
verify = self.cacert
- logger.debug('is https working?')
+ if sys.platform == "darwin":
+ verify = get_mac_cabundle()
+ logger.debug('checking https connection')
logger.debug('uri: %s (verify:%s)', uri, verify)
+
try:
self.fetcher.get(uri, verify=verify)
except requests.exceptions.SSLError as exc:
- logger.error("SSLError")
- # XXX RAISE! See #638
- #raise eipexceptions.HttpsBadCertError
- logger.warning('BUG #638 CERT VERIFICATION FAILED! '
- '(this should be CRITICAL)')
- logger.warning('SSLError: %s', exc.message)
+ raise eipexceptions.HttpsBadCertError
except requests.exceptions.ConnectionError:
logger.error('ConnectionError')
raise eipexceptions.HttpsNotSupported
else:
- logger.debug('True')
return True
def check_new_cert_needed(self, skip_download=False, verify=True):
- logger.debug('is new cert needed?')
+ # XXX add autocacert
if not self.is_cert_valid(do_raise=False):
- logger.debug('True')
+ logger.debug('cert needed: true')
self.download_new_client_cert(
skip_download=skip_download,
verify=verify)
return True
- logger.debug('False')
+ logger.debug('cert needed: false')
return False
def download_new_client_cert(self, uri=None, verify=True,
@@ -200,20 +205,20 @@ class ProviderCertChecker(object):
if uri is None:
uri = self._get_client_cert_uri()
# XXX raise InsecureURI or something better
- assert uri.startswith('https')
+ #assert uri.startswith('https')
if verify is True and self.cacert is not None:
verify = self.cacert
+ logger.debug('verify = %s', verify)
fgetfn = self.fetcher.get
if credentials:
user, passwd = credentials
-
- logger.debug('domain = %s', self.domain)
+ logger.debug('apidomain = %s', self.apidomain)
@srpauth_protected(user, passwd,
- server="https://%s" % self.domain,
+ server="https://%s" % self.apidomain,
verify=verify)
def getfn(*args, **kwargs):
return fgetfn(*args, **kwargs)
@@ -225,23 +230,23 @@ class ProviderCertChecker(object):
return fgetfn(*args, **kwargs)
try:
- # XXX FIXME!!!!
- # verify=verify
- # Workaround for #638. return to verification
- # when That's done!!!
- #req = self.fetcher.get(uri, verify=False)
- req = getfn(uri, verify=False)
+ req = getfn(uri, verify=verify)
req.raise_for_status()
except requests.exceptions.SSLError:
logger.warning('SSLError while fetching cert. '
'Look below for stack trace.')
# XXX raise better exception
- raise
+ return self.fail("SSLError")
+ except Exception as exc:
+ return self.fail(exc.message)
+
try:
+ logger.debug('validating cert...')
pemfile_content = req.content
valid = self.is_valid_pemfile(pemfile_content)
if not valid:
+ logger.warning('invalid cert')
return False
cert_path = self._get_client_cert_path()
self.write_cert(pemfile_content, to=cert_path)
@@ -276,10 +281,7 @@ class ProviderCertChecker(object):
cert = gnutls.crypto.X509Certificate(cert_s)
from_ = time.gmtime(cert.activation_time)
to_ = time.gmtime(cert.expiration_time)
- # FIXME BUG ON LEAP_CLI, certs are not valid on gmtime
- # See #1153
return from_ < now() < to_
- #return now() < to_
def is_valid_pemfile(self, cert_s=None):
"""
@@ -308,8 +310,7 @@ class ProviderCertChecker(object):
return u"https://%s/" % self.domain
def _get_client_cert_uri(self):
- # XXX get the whole thing from constants
- return "https://%s/1/cert" % self.domain
+ return "https://%s/1/cert" % self.apidomain
def _get_client_cert_path(self):
return eipspecs.client_cert_path(domain=self.domain)
@@ -336,6 +337,9 @@ class ProviderCertChecker(object):
with open(to, 'w') as cert_f:
cert_f.write(pemfile_content)
+ def set_api_domain(self, domain):
+ self.apidomain = domain
+
class EIPConfigChecker(object):
"""
@@ -355,10 +359,15 @@ class EIPConfigChecker(object):
# if not domain, get from config
self.domain = domain
+ self.apidomain = None
+ self.cacert = eipspecs.provider_ca_path(domain)
- self.eipconfig = eipconfig.EIPConfig(domain=domain)
self.defaultprovider = providers.LeapProviderDefinition(domain=domain)
+ self.defaultprovider.load()
+ self.eipconfig = eipconfig.EIPConfig(domain=domain)
+ self.set_api_domain()
self.eipserviceconfig = eipconfig.EIPServiceConfig(domain=domain)
+ self.eipserviceconfig.load()
def run_all(self, checker=None, skip_download=False):
"""
@@ -442,31 +451,41 @@ class EIPConfigChecker(object):
domain = config.get('provider', None)
uri = self._get_provider_definition_uri(domain=domain)
- # FIXME! Pass ca path verify!!!
- # BUG #638
- # FIXME FIXME FIXME
+ if sys.platform == "darwin":
+ verify = get_mac_cabundle()
+ else:
+ verify = True
+
self.defaultprovider.load(
from_uri=uri,
fetcher=self.fetcher,
- verify=False)
+ verify=verify)
self.defaultprovider.save()
def fetch_eip_service_config(self, skip_download=False,
force_download=False,
- config=None, uri=None, domain=None):
+ config=None, uri=None, # domain=None,
+ autocacert=True):
if skip_download:
return True
if config is None:
+ self.eipserviceconfig.load()
config = self.eipserviceconfig.config
if uri is None:
- if not domain:
- domain = self.domain or config.get('provider', None)
- uri = self._get_eip_service_uri(domain=domain)
+ #XXX
+ #if not domain:
+ #domain = self.domain or config.get('provider', None)
+ uri = self._get_eip_service_uri(
+ domain=self.apidomain)
+
+ if autocacert and self.cacert is not None:
+ verify = self.cacert
self.eipserviceconfig.load(
from_uri=uri,
fetcher=self.fetcher,
- force_download=force_download)
+ force_download=force_download,
+ verify=verify)
self.eipserviceconfig.save()
def check_complete_eip_config(self, config=None):
@@ -474,7 +493,6 @@ class EIPConfigChecker(object):
if config is None:
config = self.eipconfig.config
try:
- 'trying assertions'
assert 'provider' in config
assert config['provider'] is not None
# XXX assert there is gateway !!
@@ -513,3 +531,16 @@ class EIPConfigChecker(object):
uri = "https://%s/%s" % (domain, path)
logger.debug('getting eip service file from %s', uri)
return uri
+
+ def set_api_domain(self):
+ """sets api domain from defaultprovider config object"""
+ api = self.defaultprovider.config.get('api_uri', None)
+ # the caller is responsible for having loaded the config
+ # object at this point
+ if api:
+ api_dom = get_https_domain_and_port(api)
+ self.apidomain = "%s:%s" % api_dom
+
+ def get_api_domain(self):
+ """gets api domain"""
+ return self.apidomain
diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py
index 48e6e9a7..a60d7ed5 100644
--- a/src/leap/eip/config.py
+++ b/src/leap/eip/config.py
@@ -18,6 +18,8 @@ from leap.eip import specs as eipspecs
logger = logging.getLogger(name=__name__)
provider_ca_file = BRANDING.get('provider_ca_file', None)
+_platform = platform.system()
+
class EIPConfig(baseconfig.JSONLeapConfig):
spec = eipspecs.eipconfig_spec
@@ -210,8 +212,13 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs):
# interface. unix sockets or telnet interface for win.
# XXX take them from the config object.
- ourplatform = platform.system()
- if ourplatform in ("Linux", "Mac"):
+ if _platform == "Windows":
+ opts.append('--management')
+ opts.append('localhost')
+ # XXX which is a good choice?
+ opts.append('7777')
+
+ if _platform in ("Linux", "Darwin"):
opts.append('--management')
if socket_path is None:
@@ -219,16 +226,20 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs):
opts.append(socket_path)
opts.append('unix')
- if ourplatform == "Windows":
- opts.append('--management')
- opts.append('localhost')
- # XXX which is a good choice?
- opts.append('7777')
+ opts.append('--script-security')
+ opts.append('2')
+
+ if _platform == "Linux":
+ opts.append("--up")
+ opts.append("/etc/openvpn/update-resolv-conf")
+ opts.append("--down")
+ opts.append("/etc/openvpn/update-resolv-conf")
# certs
client_cert_path = eipspecs.client_cert_path(provider)
ca_cert_path = eipspecs.provider_ca_path(provider)
+ # XXX FIX paths for MAC
opts.append('--cert')
opts.append(client_cert_path)
opts.append('--key')
@@ -242,7 +253,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs):
#if daemon is True:
#opts.append('--daemon')
- logger.debug('vpn options: %s', opts)
+ logger.debug('vpn options: %s', ' '.join(opts))
return opts
@@ -262,7 +273,7 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None,
# XXX get use_pkexec from config instead.
- if platform.system() == "Linux" and use_pkexec and do_pkexec_check:
+ if _platform == "Linux" and use_pkexec and do_pkexec_check:
# check for both pkexec
# AND a suitable authentication
@@ -282,8 +293,16 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None,
raise eip_exceptions.EIPNoPolkitAuthAgentAvailable
command.append('pkexec')
+
if vpnbin is None:
- ovpn = which('openvpn')
+ if _platform == "Darwin":
+ # XXX Should hardcode our installed path
+ # /Applications/LEAPClient.app/Contents/Resources/openvpn.leap
+ openvpn_bin = "openvpn.leap"
+ else:
+ openvpn_bin = "openvpn"
+ #XXX hardcode for darwin
+ ovpn = which(openvpn_bin)
else:
ovpn = vpnbin
if ovpn:
@@ -299,7 +318,17 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None,
# XXX check len and raise proper error
- return [command[0], command[1:]]
+ if _platform == "Darwin":
+ OSX_ASADMIN = 'do shell script "%s" with administrator privileges'
+ # XXX fix workaround for Nones
+ _command = [x if x else " " for x in command]
+ # XXX debugging!
+ # XXX get openvpn log path from debug flags
+ _command.append('--log')
+ _command.append('/tmp/leap_openvpn.log')
+ return ["osascript", ["-e", OSX_ASADMIN % ' '.join(_command)]]
+ else:
+ return [command[0], command[1:]]
def check_vpn_keys(provider=None):
diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py
index 27734f80..20b45e36 100644
--- a/src/leap/eip/eipconnection.py
+++ b/src/leap/eip/eipconnection.py
@@ -27,6 +27,8 @@ class StatusMixIn(object):
# Should separate EIPConnectionStatus (self.status)
# from the OpenVPN state/status command and parsing.
+ ERR_CONNREFUSED = False
+
def connection_state(self):
"""
returns the current connection state
@@ -49,10 +51,12 @@ class StatusMixIn(object):
state = self.get_connection_state()
except eip_exceptions.ConnectionRefusedError:
# connection refused. might be not ready yet.
- logger.warning('connection refused')
+ if not self.ERR_CONNREFUSED:
+ logger.warning('connection refused')
+ self.ERR_CONNREFUSED = True
return
if not state:
- logger.debug('no state')
+ #logger.debug('no state')
return
(ts, status_step,
ok, ip, remote) = state
diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py
index 41eed77a..b7d398c3 100644
--- a/src/leap/eip/exceptions.py
+++ b/src/leap/eip/exceptions.py
@@ -33,6 +33,7 @@ TODO:
"""
from leap.base.exceptions import LeapException
+from leap.util.translations import translate
# This should inherit from LeapException
@@ -62,53 +63,69 @@ class Warning(EIPClientError):
class EIPNoPolkitAuthAgentAvailable(CriticalError):
message = "No polkit authentication agent could be found"
- usermessage = ("We could not find any authentication "
- "agent in your system.<br/>"
- "Make sure you have "
- "<b>polkit-gnome-authentication-agent-1</b> "
- "running and try again.")
+ usermessage = translate(
+ "EIPErrors",
+ "We could not find any authentication "
+ "agent in your system.<br/>"
+ "Make sure you have "
+ "<b>polkit-gnome-authentication-agent-1</b> "
+ "running and try again.")
class EIPNoPkexecAvailable(Warning):
message = "No pkexec binary found"
- usermessage = ("We could not find <b>pkexec</b> in your "
- "system.<br/> Do you want to try "
- "<b>setuid workaround</b>? "
- "(<i>DOES NOTHING YET</i>)")
+ usermessage = translate(
+ "EIPErrors",
+ "We could not find <b>pkexec</b> in your "
+ "system.<br/> Do you want to try "
+ "<b>setuid workaround</b>? "
+ "(<i>DOES NOTHING YET</i>)")
failfirst = True
class EIPNoCommandError(EIPClientError):
message = "no suitable openvpn command found"
- usermessage = ("No suitable openvpn command found. "
- "<br/>(Might be a permissions problem)")
+ usermessage = translate(
+ "EIPErrors",
+ "No suitable openvpn command found. "
+ "<br/>(Might be a permissions problem)")
class EIPBadCertError(Warning):
# XXX this should be critical and fail close
message = "cert verification failed"
- usermessage = "there is a problem with provider certificate"
+ usermessage = translate(
+ "EIPErrors",
+ "there is a problem with provider certificate")
class LeapBadConfigFetchedError(Warning):
message = "provider sent a malformed json file"
- usermessage = "an error occurred during configuratio of leap services"
+ usermessage = translate(
+ "EIPErrors",
+ "an error occurred during configuratio of leap services")
-class OpenVPNAlreadyRunning(EIPClientError):
+class OpenVPNAlreadyRunning(CriticalError):
message = "Another OpenVPN Process is already running."
- usermessage = ("Another OpenVPN Process has been detected."
- "Please close it before starting leap-client")
+ usermessage = translate(
+ "EIPErrors",
+ "Another OpenVPN Process has been detected. "
+ "Please close it before starting leap-client")
class HttpsNotSupported(LeapException):
message = "connection refused while accessing via https"
- usermessage = "Server does not allow secure connections."
+ usermessage = translate(
+ "EIPErrors",
+ "Server does not allow secure connections")
class HttpsBadCertError(LeapException):
message = "verification error on cert"
- usermessage = "Server certificate could not be verified."
+ usermessage = translate(
+ "EIPErrors",
+ "Server certificate could not be verified")
#
# errors still needing some love
@@ -117,7 +134,9 @@ class HttpsBadCertError(LeapException):
class EIPInitNoKeyFileError(CriticalError):
message = "No vpn keys found in the expected path"
- usermessage = "We could not find your eip certs in the expected path"
+ usermessage = translate(
+ "EIPErrors",
+ "We could not find your eip certs in the expected path")
class EIPInitBadKeyFilePermError(Warning):
diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py
index c2dc71a6..05979ff7 100644
--- a/src/leap/eip/openvpnconnection.py
+++ b/src/leap/eip/openvpnconnection.py
@@ -2,17 +2,21 @@
OpenVPN Connection
"""
from __future__ import (print_function)
+from functools import partial
import logging
import os
import psutil
import shutil
+import select
import socket
-from functools import partial
+from time import sleep
logger = logging.getLogger(name=__name__)
from leap.base.connection import Connection
+from leap.base.constants import OPENVPN_BIN
from leap.util.coroutines import spawn_and_watch_process
+from leap.util.misc import get_openvpn_pids
from leap.eip.udstelnet import UDSTelnet
from leap.eip import config as eip_config
@@ -83,7 +87,7 @@ class OpenVPNManagement(object):
try:
self._connect_to_management()
except eip_exceptions.MissingSocketError:
- logger.warning('missing management socket')
+ #logger.warning('missing management socket')
return []
try:
if hasattr(self, 'tn'):
@@ -92,14 +96,19 @@ class OpenVPNManagement(object):
logger.error('socket error')
self._close_management_socket(announce=False)
return []
- buf = self.tn.read_until(b"END", 2)
- self._seek_to_eof()
- blist = buf.split('\r\n')
- if blist[-1].startswith('END'):
- del blist[-1]
- return blist
- else:
- return []
+ try:
+ buf = self.tn.read_until(b"END", 2)
+ self._seek_to_eof()
+ blist = buf.split('\r\n')
+ if blist[-1].startswith('END'):
+ del blist[-1]
+ return blist
+ else:
+ return []
+ except socket.error as exc:
+ logger.debug('socket error: %s' % exc.message)
+ except select.error as exc:
+ logger.debug('select error: %s' % exc.message)
def _send_short_command(self, cmd):
"""
@@ -271,22 +280,20 @@ to be triggered for each one of them.
# checks
+
def _check_if_running_instance(self):
"""
check if openvpn is already running
"""
- try:
- for process in psutil.get_process_list():
- if process.name == "openvpn":
- logger.debug('an openvpn instance is already running.')
- logger.debug('attempting to stop openvpn instance.')
- if not self._stop_openvpn():
- raise eip_exceptions.OpenVPNAlreadyRunning
-
- except psutil.error.NoSuchProcess:
- logger.debug('detected a process which died. passing.')
-
- logger.debug('no openvpn instance found.')
+ openvpn_pids = get_openvpn_pids()
+ if openvpn_pids:
+ logger.debug('an openvpn instance is already running.')
+ logger.debug('attempting to stop openvpn instance.')
+ if not self._stop_openvpn():
+ raise eip_exceptions.OpenVPNAlreadyRunning
+ return
+ else:
+ logger.debug('no openvpn instance found.')
def _set_ovpn_command(self):
try:
@@ -327,12 +334,13 @@ to be triggered for each one of them.
#deprecate watcher_cb,
#use _only_ signal_maps instead
- logger.debug('_launch_openvpn called')
+ #logger.debug('_launch_openvpn called')
if self.watcher_cb is not None:
linewrite_callback = self.watcher_cb
else:
#XXX get logger instead
- linewrite_callback = lambda line: print('watcher: %s' % line)
+ linewrite_callback = lambda line: logger.debug(
+ 'watcher: %s' % line)
# the partial is not
# being applied now because we're not observing the process
@@ -340,7 +348,8 @@ to be triggered for each one of them.
# here since it will be handy for observing patterns in the
# thru-the-manager updates (with regex)
observers = (linewrite_callback,
- partial(lambda con_status, line: None, self.status))
+ partial(lambda con_status,
+ line: linewrite_callback, self.status))
subp, watcher = spawn_and_watch_process(
self.command,
self.args,
@@ -355,23 +364,24 @@ to be triggered for each one of them.
interface
"""
# XXX method a bit too long, split
- logger.debug("terminating openvpn process...")
+ logger.debug("atempting to terminate openvpn process...")
if self.connected():
try:
self._send_command("signal SIGTERM\n")
+ sleep(1)
+ if not self.subp: # XXX ???
+ return True
except socket.error:
logger.warning('management socket died')
return
- if self.subp:
- # ???
- return True
#shutting openvpn failured
#try patching in old openvpn host and trying again
+ # XXX could be more than one!
process = self._get_openvpn_process()
if process:
- logger.debug('process :%s' % process)
+ logger.debug('process: %s' % process.name)
cmdline = process.cmdline
manag_flag = "--management"
@@ -392,9 +402,11 @@ to be triggered for each one of them.
return True
def _get_openvpn_process(self):
- # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"]
- # return plist[0] if plist else None
- for process in psutil.get_process_list():
- if process.name == "openvpn":
+ for process in psutil.process_iter():
+ if OPENVPN_BIN in process.name:
return process
return None
+
+ def get_log(self, lines=1):
+ log = self._send_command("log %s" % lines)
+ return log
diff --git a/src/leap/gui/firstrun/__init__.py b/src/leap/gui/firstrun/__init__.py
index d380b75a..2a523d6a 100644
--- a/src/leap/gui/firstrun/__init__.py
+++ b/src/leap/gui/firstrun/__init__.py
@@ -6,6 +6,7 @@ except ValueError:
pass
import intro
+import connect
import last
import login
import mixins
@@ -13,10 +14,10 @@ import providerinfo
import providerselect
import providersetup
import register
-import regvalidation
__all__ = [
'intro',
+ 'connect',
'last',
'login',
'mixins',
@@ -24,4 +25,4 @@ __all__ = [
'providerselect',
'providersetup',
'register',
- 'regvalidation'] # ,'wizard']
+ ] # ,'wizard']
diff --git a/src/leap/gui/firstrun/regvalidation.py b/src/leap/gui/firstrun/connect.py
index b86583e0..b7688380 100644
--- a/src/leap/gui/firstrun/regvalidation.py
+++ b/src/leap/gui/firstrun/connect.py
@@ -2,15 +2,7 @@
Provider Setup Validation Page,
used in First Run Wizard
"""
-# XXX This page is called regvalidation
-# but it's implementing functionality in the former
-# connect page.
-# We should remame it to connect again, when we integrate
-# the login branch of the wizard.
-
import logging
-#import json
-#import socket
from PyQt4 import QtGui
@@ -25,15 +17,15 @@ from leap.gui.constants import APP_LOGO
logger = logging.getLogger(__name__)
-class RegisterUserValidationPage(ValidationPage):
+class ConnectionPage(ValidationPage):
def __init__(self, parent=None):
- super(RegisterUserValidationPage, self).__init__(parent)
- self.current_page = "signupvalidation"
+ super(ConnectionPage, self).__init__(parent)
+ self.current_page = "connect"
- title = "Connecting..."
+ title = self.tr("Connecting...")
# XXX uh... really?
- subtitle = "Checking connection with provider."
+ subtitle = self.tr("Checking connection with provider.")
self.setTitle(title)
self.setSubTitle(subtitle)
@@ -52,32 +44,31 @@ class RegisterUserValidationPage(ValidationPage):
wizard = self.wizard()
full_domain = self.field('provider_domain')
domain, port = get_https_domain_and_port(full_domain)
- _domain = u"%s:%s" % (domain, port) if port != 443 else unicode(domain)
- # FIXME #BUG 638 FIXME FIXME FIXME
- verify = False # !!!!!!!!!!!!!!!!
- # FIXME #BUG 638 FIXME FIXME FIXME
+ pconfig = wizard.eipconfigchecker(domain=domain)
+ # this should be persisted...
+ pconfig.defaultprovider.load()
+ pconfig.set_api_domain()
+
+ pCertChecker = wizard.providercertchecker(
+ domain=domain)
+ pCertChecker.set_api_domain(pconfig.apidomain)
###########################################
# Set Credentials.
# username and password are in different fields
# if they were stored in log_in or sign_up pages.
- is_signup = self.field("is_signup")
+ from_login = wizard.from_login
unamek_base = 'userName'
passwk_base = 'userPassword'
- unamek = 'login_%s' % unamek_base if not is_signup else unamek_base
- passwk = 'login_%s' % passwk_base if not is_signup else passwk_base
+ unamek = 'login_%s' % unamek_base if from_login else unamek_base
+ passwk = 'login_%s' % passwk_base if from_login else passwk_base
username = self.field(unamek)
password = self.field(passwk)
credentials = username, password
- eipconfigchecker = wizard.eipconfigchecker(domain=_domain)
- #XXX change for _domain (sanitized)
- pCertChecker = wizard.providercertchecker(
- domain=full_domain)
-
yield(("head_sentinel", 0), lambda: None)
##################################################
@@ -85,8 +76,7 @@ class RegisterUserValidationPage(ValidationPage):
##################################################
def fetcheipconf():
try:
- eipconfigchecker.fetch_eip_service_config(
- domain=full_domain)
+ pconfig.fetch_eip_service_config()
# XXX get specific exception
except Exception as exc:
@@ -102,8 +92,7 @@ class RegisterUserValidationPage(ValidationPage):
def fetcheipcert():
try:
downloaded = pCertChecker.download_new_client_cert(
- credentials=credentials,
- verify=verify)
+ credentials=credentials)
if not downloaded:
logger.error('Could not download client cert.')
return False
@@ -111,6 +100,9 @@ class RegisterUserValidationPage(ValidationPage):
except auth.SRPAuthenticationError as exc:
return self.fail(self.tr(
"Authentication error: %s" % exc.message))
+
+ except Exception as exc:
+ return self.fail(exc.message)
else:
return True
@@ -182,8 +174,8 @@ class RegisterUserValidationPage(ValidationPage):
called after _do_checks has finished
(connected to checker thread finished signal)
"""
- is_signup = self.field("is_signup")
- prevpage = "signup" if is_signup else "login"
+ from_login = self.wizard().from_login
+ prevpage = "login" if from_login else "signup"
wizard = self.wizard()
if self.errors:
@@ -200,11 +192,11 @@ class RegisterUserValidationPage(ValidationPage):
def nextId(self):
wizard = self.wizard()
- if not wizard:
- return
+ #if not wizard:
+ #return
return wizard.get_page_index('lastpage')
def initializePage(self):
- super(RegisterUserValidationPage, self).initializePage()
+ super(ConnectionPage, self).initializePage()
self.set_undone()
self.completeChanged.emit()
diff --git a/src/leap/gui/firstrun/intro.py b/src/leap/gui/firstrun/intro.py
index 0a7484e2..0425b764 100644
--- a/src/leap/gui/firstrun/intro.py
+++ b/src/leap/gui/firstrun/intro.py
@@ -50,7 +50,7 @@ class IntroPage(QtGui.QWizardPage):
layout.addWidget(radiobuttonGroup)
self.setLayout(layout)
- self.registerField('is_signup', self.sign_up)
+ #self.registerField('is_signup', self.sign_up)
def validatePage(self):
return True
diff --git a/src/leap/gui/firstrun/last.py b/src/leap/gui/firstrun/last.py
index 1d8caca4..e097b2ae 100644
--- a/src/leap/gui/firstrun/last.py
+++ b/src/leap/gui/firstrun/last.py
@@ -15,7 +15,7 @@ class LastPage(QtGui.QWizardPage):
def __init__(self, parent=None):
super(LastPage, self).__init__(parent)
- self.setTitle("Connecting to Encrypted Internet Proxy service...")
+ self.setTitle(self.tr("Connecting to Encrypted Internet Proxy service..."))
self.setPixmap(
QtGui.QWizard.LogoPixmap,
@@ -51,13 +51,13 @@ class LastPage(QtGui.QWizardPage):
statusline.setText(status)
def set_finished_status(self):
- self.setTitle('You are now using an encrypted connection!')
+ self.setTitle(self.tr('You are now using an encrypted connection!'))
finishText = self.wizard().buttonText(
QtGui.QWizard.FinishButton)
finishText = finishText.replace('&', '')
- self.label.setText(
+ self.label.setText(self.tr(
"Click '<i>%s</i>' to end the wizard and "
- "save your settings." % finishText)
+ "save your settings." % finishText))
# XXX init network checker
# trigger signal
diff --git a/src/leap/gui/firstrun/login.py b/src/leap/gui/firstrun/login.py
index e7afee9f..e39eecc0 100644
--- a/src/leap/gui/firstrun/login.py
+++ b/src/leap/gui/firstrun/login.py
@@ -21,8 +21,8 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
super(LogInPage, self).__init__(parent)
self.current_page = "login"
- self.setTitle("Log In")
- self.setSubTitle("Log in with your credentials.")
+ self.setTitle(self.tr("Log In"))
+ self.setSubTitle(self.tr("Log in with your credentials."))
self.current_page = "login"
self.setPixmap(
@@ -35,7 +35,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
self.do_confirm_next = False
def setupUI(self):
- userNameLabel = QtGui.QLabel("User &name:")
+ userNameLabel = QtGui.QLabel(self.tr("User &name:"))
userNameLineEdit = QtGui.QLineEdit()
userNameLineEdit.cursorPositionChanged.connect(
self.reset_validation_status)
@@ -50,7 +50,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
#'username@provider.example.org')
self.userNameLineEdit = userNameLineEdit
- userPasswordLabel = QtGui.QLabel("&Password:")
+ userPasswordLabel = QtGui.QLabel(self.tr("&Password:"))
self.userPasswordLineEdit = QtGui.QLineEdit()
self.userPasswordLineEdit.setEchoMode(
QtGui.QLineEdit.Password)
@@ -77,7 +77,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
layout.addWidget(self.valFrame, 4, 2, 4, 2)
self.valFrame.hide()
- self.nextText("Log in")
+ self.nextText(self.tr("Log in"))
self.setLayout(layout)
#self.registerField('is_login_wizard')
@@ -108,7 +108,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
# page here as a mean to catch
# srp authentication errors while
wizard = self.wizard()
- eipconfigchecker = wizard.eipconfigchecker()
+ eipconfigchecker = wizard.eipconfigchecker(domain=domain)
########################
# 1) try name resolution
@@ -321,6 +321,7 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
self.setField('provider_domain', domain)
self.setField('login_userName', username)
self.setField('login_userPassword', password)
+ self.wizard().from_login = True
return True
diff --git a/src/leap/gui/firstrun/providerinfo.py b/src/leap/gui/firstrun/providerinfo.py
index c5b2984c..357378df 100644
--- a/src/leap/gui/firstrun/providerinfo.py
+++ b/src/leap/gui/firstrun/providerinfo.py
@@ -6,6 +6,7 @@ import logging
from PyQt4 import QtGui
from leap.gui.constants import APP_LOGO
+from leap.util.translations import translate
logger = logging.getLogger(__name__)
@@ -62,6 +63,7 @@ class ProviderInfoPage(QtGui.QWizardPage):
# this should be better handled with signals !!
self.displayName = displayName
self.description = description
+ self.description.setWordWrap(True)
self.enrollment_policy = enrollment_policy
def show_provider_info(self):
@@ -72,7 +74,7 @@ class ProviderInfoPage(QtGui.QWizardPage):
lang = "en"
pconfig = self.wizard().providerconfig
- dn = pconfig.get('display_name')
+ dn = pconfig.get('name')
display_name = dn[lang] if dn else ''
domain_name = self.field('provider_domain')
@@ -80,7 +82,10 @@ class ProviderInfoPage(QtGui.QWizardPage):
"<b>%s</b> https://%s" % (display_name, domain_name))
desc = pconfig.get('description')
- description_text = desc[lang] if desc else ''
+
+ #description_text = desc[lang] if desc else ''
+ description_text = translate(desc) if desc else ''
+
self.description.setText(
"<i>%s</i>" % description_text)
diff --git a/src/leap/gui/firstrun/providerselect.py b/src/leap/gui/firstrun/providerselect.py
index fd48f7f9..ccecd519 100644
--- a/src/leap/gui/firstrun/providerselect.py
+++ b/src/leap/gui/firstrun/providerselect.py
@@ -64,7 +64,7 @@ class SelectProviderPage(InlineValidationPage):
providerNameLabel.setBuddy(providerNameEdit)
# add regex validator
- providerDomainRe = QtCore.QRegExp(r"^[a-z\d_-.]+$")
+ providerDomainRe = QtCore.QRegExp(r"^[a-z1-9_\-\.]+$")
providerNameEdit.setValidator(
QtGui.QRegExpValidator(providerDomainRe, self))
self.providerNameEdit = providerNameEdit
@@ -101,7 +101,7 @@ class SelectProviderPage(InlineValidationPage):
self.certInfo.setWordWrap(True)
self.certWarning = QtGui.QLabel("")
self.trustProviderCertCheckBox = QtGui.QCheckBox(
- "&Trust this provider certificate.")
+ self.tr("&Trust this provider certificate."))
self.trustProviderCertCheckBox.stateChanged.connect(
self.onTrustCheckChanged)
@@ -287,8 +287,6 @@ class SelectProviderPage(InlineValidationPage):
wizard.set_providerconfig(
eipconfigchecker.defaultprovider.config)
except requests.exceptions.SSLError:
- # XXX we should have catched this before.
- # but cert checking is broken.
return self.fail(self.tr(
"Could not get info from provider."))
except requests.exceptions.ConnectionError:
@@ -344,9 +342,10 @@ class SelectProviderPage(InlineValidationPage):
def add_cert_info(self, certinfo): # pragma: no cover XXX
self.certWarning.setText(
- "Do you want to <b>trust this provider certificate?</b>")
+ self.tr("Do you want to <b>trust this provider certificate?</b>"))
+ # XXX Check if this needs to abstracted to remove certinfo
self.certInfo.setText(
- 'SHA-256 fingerprint: <i>%s</i><br>' % certinfo)
+ self.tr('SHA-256 fingerprint: <i>%s</i><br>' % certinfo))
self.certInfo.setWordWrap(True)
self.certinfoGroup.show()
diff --git a/src/leap/gui/firstrun/providersetup.py b/src/leap/gui/firstrun/providersetup.py
index 1a362794..981e3214 100644
--- a/src/leap/gui/firstrun/providersetup.py
+++ b/src/leap/gui/firstrun/providersetup.py
@@ -4,6 +4,8 @@ used if First Run Wizard
"""
import logging
+import requests
+
from PyQt4 import QtGui
from leap.base import exceptions as baseexceptions
@@ -20,8 +22,8 @@ class ProviderSetupValidationPage(ValidationPage):
self.current_page = "providersetupvalidation"
# XXX needed anymore?
- is_signup = self.field("is_signup")
- self.is_signup = is_signup
+ #is_signup = self.field("is_signup")
+ #self.is_signup = is_signup
self.setTitle(self.tr("Provider setup"))
self.setSubTitle(
@@ -110,26 +112,15 @@ class ProviderSetupValidationPage(ValidationPage):
#########################
def validatecacert():
- pass
- #api_uri = pconfig.get('api_uri', None)
- #try:
- #api_cert_verified = pCertChecker.verify_api_https(api_uri)
- #except requests.exceptions.SSLError as exc:
- #logger.error('BUG #638. %s' % exc.message)
- # XXX RAISE! See #638
- # bypassing until the hostname is fixed.
- # We probably should raise yet-another-warning
- # here saying user that the hostname "XX.XX.XX.XX' does not
- # match 'foo.bar.baz'
- #api_cert_verified = True
-
- #if not api_cert_verified:
- # XXX update validationMsg
- # should catch exception
- #return False
-
- #???
- #ca_cert_path = checker.ca_cert_path
+ api_uri = pconfig.get('api_uri', None)
+ try:
+ pCertChecker.verify_api_https(api_uri)
+ except requests.exceptions.SSLError as exc:
+ return self.fail("Validation Error")
+ except Exception as exc:
+ return self.fail(exc.msg)
+ else:
+ return True
yield((self.tr('Validating api certificate'), 90), validatecacert)
@@ -141,8 +132,8 @@ class ProviderSetupValidationPage(ValidationPage):
called after _do_checks has finished
(connected to checker thread finished signal)
"""
- prevpage = "providerselection" if self.is_signup else "login"
wizard = self.wizard()
+ prevpage = "login" if wizard.from_login else "providerselection"
if self.errors:
logger.debug('going back with errors')
@@ -158,14 +149,12 @@ class ProviderSetupValidationPage(ValidationPage):
def nextId(self):
wizard = self.wizard()
- if not wizard:
- return
- is_signup = self.field('is_signup')
- if is_signup is True:
- next_ = 'signup'
- if is_signup is False:
+ from_login = wizard.from_login
+ if from_login:
# XXX bad name. change to connect again.
next_ = 'signupvalidation'
+ else:
+ next_ = 'signup'
return wizard.get_page_index(next_)
def initializePage(self):
diff --git a/src/leap/gui/firstrun/register.py b/src/leap/gui/firstrun/register.py
index 4c811093..741b9267 100644
--- a/src/leap/gui/firstrun/register.py
+++ b/src/leap/gui/firstrun/register.py
@@ -45,7 +45,7 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
self.focused_field = False
def setupUI(self):
- userNameLabel = QtGui.QLabel("User &name:")
+ userNameLabel = QtGui.QLabel(self.tr("User &name:"))
userNameLineEdit = QtGui.QLineEdit()
userNameLineEdit.cursorPositionChanged.connect(
self.reset_validation_status)
@@ -57,20 +57,20 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
QtGui.QRegExpValidator(usernameRe, self))
self.userNameLineEdit = userNameLineEdit
- userPasswordLabel = QtGui.QLabel("&Password:")
+ userPasswordLabel = QtGui.QLabel(self.tr("&Password:"))
self.userPasswordLineEdit = QtGui.QLineEdit()
self.userPasswordLineEdit.setEchoMode(
QtGui.QLineEdit.Password)
userPasswordLabel.setBuddy(self.userPasswordLineEdit)
- userPassword2Label = QtGui.QLabel("Password (again):")
+ userPassword2Label = QtGui.QLabel(self.tr("Password (again):"))
self.userPassword2LineEdit = QtGui.QLineEdit()
self.userPassword2LineEdit.setEchoMode(
QtGui.QLineEdit.Password)
userPassword2Label.setBuddy(self.userPassword2LineEdit)
rememberPasswordCheckBox = QtGui.QCheckBox(
- "&Remember username and password.")
+ self.tr("&Remember username and password."))
rememberPasswordCheckBox.setChecked(True)
self.registerField('userName*', self.userNameLineEdit)
@@ -224,11 +224,17 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
generator that yields actual checks
that are executed in a separate thread
"""
+ wizard = self.wizard()
+
provider = self.field('provider_domain')
username = self.userNameLineEdit.text()
password = self.userPasswordLineEdit.text()
password2 = self.userPassword2LineEdit.text()
+ pconfig = wizard.eipconfigchecker(domain=provider)
+ pconfig.defaultprovider.load()
+ pconfig.set_api_domain()
+
def checkpass():
# we better have here
# some call to a password checker...
@@ -263,14 +269,11 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
self, "showStepsFrame")
def register():
- # XXX FIXME!
- verify = False
signup = auth.LeapSRPRegister(
schema="https",
- provider=provider,
- verify=verify)
- #import ipdb;ipdb.set_trace()
+ provider=pconfig.apidomain,
+ verify=pconfig.cacert)
try:
ok, req = signup.register_user(
username, password)
@@ -381,7 +384,4 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
def nextId(self):
wizard = self.wizard()
- #if not wizard:
- #return
- # XXX this should be called connect
- return wizard.get_page_index('signupvalidation')
+ return wizard.get_page_index('connect')
diff --git a/src/leap/gui/firstrun/wizard.py b/src/leap/gui/firstrun/wizard.py
index 89209401..427f9df8 100755
--- a/src/leap/gui/firstrun/wizard.py
+++ b/src/leap/gui/firstrun/wizard.py
@@ -59,8 +59,8 @@ def get_pages_dict():
('providersetupvalidation',
firstrun.providersetup.ProviderSetupValidationPage),
('signup', firstrun.register.RegisterUserPage),
- ('signupvalidation',
- firstrun.regvalidation.RegisterUserValidationPage),
+ ('connect',
+ firstrun.connect.ConnectionPage),
('lastpage', firstrun.last.LastPage)
))
@@ -72,7 +72,7 @@ class FirstRunWizard(QtGui.QWizard):
conductor_instance,
parent=None,
pages_dict=None,
- eip_username=None,
+ username=None,
providers=None,
success_cb=None, is_provider_setup=False,
trusted_certs=None,
@@ -92,7 +92,7 @@ class FirstRunWizard(QtGui.QWizard):
# in the connection page, before the wizard has ended.
self.conductor = conductor_instance
- self.eip_username = eip_username
+ self.username = username
self.providers = providers
# success callback
@@ -129,7 +129,7 @@ class FirstRunWizard(QtGui.QWizard):
# by setting 1st page??
#self.is_previously_registered = is_previously_registered
# XXX ??? ^v
- self.is_previously_registered = bool(self.eip_username)
+ self.is_previously_registered = bool(self.username)
self.from_login = False
pages_dict = pages_dict or get_pages_dict()
@@ -233,7 +233,7 @@ class FirstRunWizard(QtGui.QWizard):
settings.setValue("remember_user_and_pass", remember_pass)
if remember_pass:
- settings.setValue("eip_username", full_username)
+ settings.setValue("username", full_username)
seed = self.get_random_str(10)
settings.setValue("%s_seed" % provider, seed)
diff --git a/src/leap/gui/progress.py b/src/leap/gui/progress.py
index ffea80de..ca4f6cc3 100644
--- a/src/leap/gui/progress.py
+++ b/src/leap/gui/progress.py
@@ -118,11 +118,12 @@ class StepsTableWidget(QtGui.QTableWidget):
self.setSelectionMode(
QtGui.QAbstractItemView.NoSelection)
width = self.width()
+
# WTF? Here init width is 100...
# but on populating is 456... :(
+ #logger.debug('init table. width=%s' % width)
# XXX do we need this initial?
- logger.debug('init table. width=%s' % width)
self.horizontalHeader().resizeSection(0, width * 0.7)
# this disables the table grid.
@@ -286,7 +287,7 @@ class WithStepsMixIn(object):
pagename = getattr(self, 'prev_page', None)
if pagename is None: # pragma: no cover
return
- logger.debug('cleaning wizard errors for %s' % pagename)
+ #logger.debug('cleaning wizard errors for %s' % pagename)
self.wizard().set_validation_error(pagename, None)
def populateStepsTable(self):
@@ -318,7 +319,7 @@ class WithStepsMixIn(object):
table = self.stepsTableWidget
FIRST_COLUMN_PERCENT = 0.70
width = table.width()
- logger.debug('populate table. width=%s' % width)
+ #logger.debug('populate table. width=%s' % width)
table.horizontalHeader().resizeSection(0, width * FIRST_COLUMN_PERCENT)
def set_item_icon(self, img=ICON_CHECKMARK, current=True):
diff --git a/src/leap/gui/tests/test_firstrun_login.py b/src/leap/gui/tests/test_firstrun_login.py
index fa800c23..6c45b8ef 100644
--- a/src/leap/gui/tests/test_firstrun_login.py
+++ b/src/leap/gui/tests/test_firstrun_login.py
@@ -100,7 +100,7 @@ class RegisterUserPageUITestCase(qunittest.TestCase):
pages = OrderedDict((
(self.pagename, TestPage),
('providersetupvalidation',
- firstrun.regvalidation.RegisterUserValidationPage)))
+ firstrun.connect.ConnectionPage)))
self.wizard = firstrun.wizard.FirstRunWizard(None, pages_dict=pages)
self.page = self.wizard.page(self.wizard.get_page_index(self.pagename))
diff --git a/src/leap/gui/tests/test_firstrun_register.py b/src/leap/gui/tests/test_firstrun_register.py
index 3447fe9d..d3be8897 100644
--- a/src/leap/gui/tests/test_firstrun_register.py
+++ b/src/leap/gui/tests/test_firstrun_register.py
@@ -113,7 +113,7 @@ class RegisterUserPageUITestCase(qunittest.TestCase):
pages = OrderedDict((
(self.pagename, TestPage),
('signupvalidation',
- firstrun.regvalidation.RegisterUserValidationPage)))
+ firstrun.connect.ConnectionPage)))
self.wizard = firstrun.wizard.FirstRunWizard(None, pages_dict=pages)
self.page = self.wizard.page(self.wizard.get_page_index(self.pagename))
diff --git a/src/leap/gui/tests/test_firstrun_wizard.py b/src/leap/gui/tests/test_firstrun_wizard.py
index 091cd932..b6c34000 100644
--- a/src/leap/gui/tests/test_firstrun_wizard.py
+++ b/src/leap/gui/tests/test_firstrun_wizard.py
@@ -29,8 +29,8 @@ PAGES_DICT = dict((
('providersetupvalidation',
firstrun.providersetup.ProviderSetupValidationPage),
('signup', firstrun.register.RegisterUserPage),
- ('signupvalidation',
- firstrun.regvalidation.RegisterUserValidationPage),
+ ('connect',
+ firstrun.connect.ConnectionPage),
('lastpage', firstrun.last.LastPage)
))
@@ -94,7 +94,7 @@ class FirstRunWizardTestCase(qunittest.TestCase):
calls = [call("FirstRunWizardDone", True),
call("provider_domain", "testprovider"),
call("remember_user_and_pass", True),
- call("eip_username", "testuser@testprovider"),
+ call("username", "testuser@testprovider"),
call("testprovider_seed", RANDOMSTR)]
mqs().setValue.assert_has_calls(calls, any_order=True)
diff --git a/src/leap/soledad/__init__.py b/src/leap/soledad/__init__.py
index 26e982ba..1473da38 100644
--- a/src/leap/soledad/__init__.py
+++ b/src/leap/soledad/__init__.py
@@ -45,6 +45,11 @@ class Soledad(object):
# Management of secret for symmetric encryption
#-------------------------------------------------------------------------
+
+ #-------------------------------------------------------------------------
+ # Management of secret for symmetric encryption
+ #-------------------------------------------------------------------------
+
def _has_secret(self):
"""
Verify if secret for symmetric encryption exists on local encrypted file.
diff --git a/src/leap/soledad/backends/leap_backend.py b/src/leap/soledad/backends/leap_backend.py
index 5a7dfa2f..3e859f7c 100644
--- a/src/leap/soledad/backends/leap_backend.py
+++ b/src/leap/soledad/backends/leap_backend.py
@@ -12,12 +12,11 @@ from leap.soledad.util import GPGWrapper
import uuid
-import uuid
-
class NoDefaultKey(Exception):
pass
+
class NoSoledadInstance(Exception):
pass
diff --git a/src/leap/soledad/tests/test_sqlcipher.py b/src/leap/soledad/tests/test_sqlcipher.py
index cfccf605..9e3b4052 100644
--- a/src/leap/soledad/tests/test_sqlcipher.py
+++ b/src/leap/soledad/tests/test_sqlcipher.py
@@ -335,4 +335,4 @@ class SQLCipherEncryptionTest(unittest.TestCase):
SQLCipherDatabase(self.DB_FILE, PASSWORD)
raise DatabaseError("SQLCipher backend should not be able to open non-encrypted dbs.")
except DatabaseIsNotEncrypted:
- pass \ No newline at end of file
+ pass
diff --git a/src/leap/soledad/util.py b/src/leap/soledad/util.py
index a26bff31..040c70ab 100644
--- a/src/leap/soledad/util.py
+++ b/src/leap/soledad/util.py
@@ -52,5 +52,3 @@ class GPGWrapper(gnupg.GPG):
data.close()
return result
-
-
diff --git a/src/leap/util/__init__.py b/src/leap/util/__init__.py
index e69de29b..a70a9a8b 100644
--- a/src/leap/util/__init__.py
+++ b/src/leap/util/__init__.py
@@ -0,0 +1,9 @@
+import logging
+logger = logging.getLogger(__name__)
+
+try:
+ import pygeoip
+ HAS_GEOIP = True
+except ImportError:
+ logger.debug('PyGeoIP not found. Disabled Geo support.')
+ HAS_GEOIP = False
diff --git a/src/leap/util/certs.py b/src/leap/util/certs.py
new file mode 100644
index 00000000..f0f790e9
--- /dev/null
+++ b/src/leap/util/certs.py
@@ -0,0 +1,18 @@
+import os
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+def get_mac_cabundle():
+ # hackaround bundle error
+ # XXX this needs a better fix!
+ f = os.path.split(__file__)[0]
+ sep = os.path.sep
+ f_ = sep.join(f.split(sep)[:-2])
+ verify = os.path.join(f_, 'cacert.pem')
+ #logger.error('VERIFY PATH = %s' % verify)
+ exists = os.path.isfile(verify)
+ #logger.error('do exist? %s', exists)
+ if exists:
+ return verify
diff --git a/src/leap/util/geo.py b/src/leap/util/geo.py
new file mode 100644
index 00000000..54b29596
--- /dev/null
+++ b/src/leap/util/geo.py
@@ -0,0 +1,32 @@
+"""
+experimental geo support.
+not yet a feature.
+in debian, we rely on the (optional) geoip-database
+"""
+import os
+import platform
+
+from leap.util import HAS_GEOIP
+
+GEOIP = None
+
+if HAS_GEOIP:
+ import pygeoip # we know we can :)
+
+ GEOIP_PATH = None
+
+ if platform.system() == "Linux":
+ PATH = "/usr/share/GeoIP/GeoIP.dat"
+ if os.path.isfile(PATH):
+ GEOIP_PATH = PATH
+ GEOIP = pygeoip.GeoIP(GEOIP_PATH, pygeoip.MEMORY_CACHE)
+
+
+def get_country_name(ip):
+ if not GEOIP:
+ return
+ try:
+ country = GEOIP.country_name_by_addr(ip)
+ except pygeoip.GeoIPError:
+ country = None
+ return country if country else "-"
diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py
index 2f996a31..5b0775cc 100644
--- a/src/leap/util/leap_argparse.py
+++ b/src/leap/util/leap_argparse.py
@@ -37,5 +37,5 @@ Launches main LEAP Client""", epilog=epilog)
def init_leapc_args():
parser = build_parser()
- opts = parser.parse_args()
+ opts, unknown = parser.parse_known_args()
return parser, opts
diff --git a/src/leap/util/misc.py b/src/leap/util/misc.py
index 3c26892b..aa3ebe25 100644
--- a/src/leap/util/misc.py
+++ b/src/leap/util/misc.py
@@ -1,6 +1,9 @@
"""
misc utils
"""
+import psutil
+
+from leap.base.constants import OPENVPN_BIN
class ImproperlyConfigured(Exception):
@@ -14,3 +17,20 @@ def null_check(value, value_name):
except AssertionError:
raise ImproperlyConfigured(
"%s parameter cannot be None" % value_name)
+
+def get_openvpn_pids():
+ # binary name might change
+
+ openvpn_pids = []
+ for p in psutil.process_iter():
+ try:
+ # XXX Not exact!
+ # Will give false positives.
+ # we should check that cmdline BEGINS
+ # with openvpn or with our wrapper
+ # (pkexec / osascript / whatever)
+ if OPENVPN_BIN in ' '.join(p.cmdline):
+ openvpn_pids.append(p.pid)
+ except psutil.error.AccessDenied:
+ pass
+ return openvpn_pids
diff --git a/src/leap/util/tests/test_translations.py b/src/leap/util/tests/test_translations.py
new file mode 100644
index 00000000..794daeba
--- /dev/null
+++ b/src/leap/util/tests/test_translations.py
@@ -0,0 +1,22 @@
+import unittest
+
+from leap.util import translations
+
+
+class TrasnlationsTestCase(unittest.TestCase):
+ """
+ tests for translation functions and classes
+ """
+
+ def setUp(self):
+ self.trClass = translations.LEAPTranslatable
+
+ def test_trasnlatable(self):
+ tr = self.trClass({"en": "house", "es": "casa"})
+ eq = self.assertEqual
+ eq(tr.tr(to="es"), "casa")
+ eq(tr.tr(to="en"), "house")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/leap/util/translations.py b/src/leap/util/translations.py
new file mode 100644
index 00000000..d782cfe4
--- /dev/null
+++ b/src/leap/util/translations.py
@@ -0,0 +1,84 @@
+import inspect
+import logging
+
+from PyQt4.QtCore import QCoreApplication
+from PyQt4.QtCore import QLocale
+
+logger = logging.getLogger(__name__)
+
+"""
+here I could not do all that I wanted.
+the context is not getting passed to the xml file.
+Looks like pylupdate4 is somehow a hack that does not
+parse too well the python ast.
+I guess we could generate the xml for ourselves as a last recourse.
+"""
+
+# XXX BIG NOTE:
+# RESIST the temptation to get the translate function
+# more compact, or have the Context argument passed as a variable
+# Its name HAS to be explicit due to how the pylupdate parser
+# works.
+
+
+qtTranslate = QCoreApplication.translate
+
+
+def translate(*args, **kwargs):
+ """
+ our magic function.
+ translate(Context, text, comment)
+ """
+ if len(args) == 1:
+ obj = args[0]
+ if isinstance(obj, LEAPTranslatable) and hasattr(obj, 'tr'):
+ return obj.tr()
+
+ klsname = None
+ try:
+ # get class value from instance
+ # using live object inspection
+ prev_frame = inspect.stack()[1][0]
+ locals_ = inspect.getargvalues(prev_frame).locals
+ self = locals_.get('self')
+ if self:
+
+ # Trying to get the class name
+ # but this is useless, the parser
+ # has already got the context.
+ klsname = self.__class__.__name__
+ #print 'KLSNAME -- ', klsname
+ except:
+ logger.error('error getting stack frame')
+
+ if klsname and len(args) == 1:
+ nargs = (klsname,) + args
+ return qtTranslate(*nargs)
+
+ else:
+ #nargs = ('default', ) + args
+ #import pdb4qt; pdb4qt.set_trace()
+ return qtTranslate(*args)
+
+
+class LEAPTranslatable(dict):
+ """
+ An extended dict that implements a .tr method
+ so it can be translated on the fly by our
+ magic translate method
+ """
+
+ try:
+ locale = str(QLocale.system().name()).split('_')[0]
+ except:
+ logger.warning("could not get system locale!")
+ print "could not get system locale!"
+ locale = "en"
+
+ def tr(self, to=None):
+ if not to:
+ to = self.locale
+ _tr = self.get(to, None)
+ if not _tr:
+ _tr = self.get("en", None)
+ return _tr