From 35c77f1e762acde8e5f635a99f9fad9e706d7d4f Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 31 Jan 2013 05:39:47 +0900 Subject: fix tests (resources hash + argparse) --- src/leap/util/tests/test_leap_argparse.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/leap/util') diff --git a/src/leap/util/tests/test_leap_argparse.py b/src/leap/util/tests/test_leap_argparse.py index 082919b7..4e2b811f 100644 --- a/src/leap/util/tests/test_leap_argparse.py +++ b/src/leap/util/tests/test_leap_argparse.py @@ -24,11 +24,11 @@ class LeapArgParseTest(unittest.TestCase): self.assertEqual( opts, Namespace( - config_file=None, debug=True, log_file=None, - no_provider_checks=False, - no_ca_verify=False, + #config_file=None, + #no_provider_checks=False, + #no_ca_verify=False, openvpn_verb=None)) if __name__ == "__main__": -- cgit v1.2.3 From 5ff29dc57e2877a14e705d09b7042cddf4165d0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 6 Mar 2013 15:27:23 -0300 Subject: Remove everything to start from scratch --- src/leap/util/__init__.py | 9 - src/leap/util/certs.py | 18 -- src/leap/util/coroutines.py | 109 ------------ src/leap/util/dicts.py | 268 ------------------------------ src/leap/util/fileutil.py | 120 ------------- src/leap/util/geo.py | 32 ---- src/leap/util/leap_argparse.py | 44 ----- src/leap/util/misc.py | 37 ----- src/leap/util/tests/__init__.py | 0 src/leap/util/tests/test_fileutil.py | 100 ----------- src/leap/util/tests/test_leap_argparse.py | 35 ---- src/leap/util/tests/test_translations.py | 22 --- src/leap/util/translations.py | 82 --------- src/leap/util/web.py | 40 ----- 14 files changed, 916 deletions(-) delete mode 100644 src/leap/util/__init__.py delete mode 100644 src/leap/util/certs.py delete mode 100644 src/leap/util/coroutines.py delete mode 100644 src/leap/util/dicts.py delete mode 100644 src/leap/util/fileutil.py delete mode 100644 src/leap/util/geo.py delete mode 100644 src/leap/util/leap_argparse.py delete mode 100644 src/leap/util/misc.py delete mode 100644 src/leap/util/tests/__init__.py delete mode 100644 src/leap/util/tests/test_fileutil.py delete mode 100644 src/leap/util/tests/test_leap_argparse.py delete mode 100644 src/leap/util/tests/test_translations.py delete mode 100644 src/leap/util/translations.py delete mode 100644 src/leap/util/web.py (limited to 'src/leap/util') diff --git a/src/leap/util/__init__.py b/src/leap/util/__init__.py deleted file mode 100644 index a70a9a8b..00000000 --- a/src/leap/util/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index f0f790e9..00000000 --- a/src/leap/util/certs.py +++ /dev/null @@ -1,18 +0,0 @@ -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/coroutines.py b/src/leap/util/coroutines.py deleted file mode 100644 index 0657fc04..00000000 --- a/src/leap/util/coroutines.py +++ /dev/null @@ -1,109 +0,0 @@ -# the problem of watching a stdout pipe from -# openvpn binary: using subprocess and coroutines -# acting as event consumers - -from __future__ import division, print_function - -import logging -from subprocess import PIPE, Popen -import sys -from threading import Thread - -logger = logging.getLogger(__name__) - -ON_POSIX = 'posix' in sys.builtin_module_names - - -# -# Coroutines goodies -# - -def coroutine(func): - def start(*args, **kwargs): - cr = func(*args, **kwargs) - cr.next() - return cr - return start - - -@coroutine -def process_events(callback): - """ - coroutine loop that receives - events sent and dispatch the callback. - :param callback: callback to be called\ -for each event - :type callback: callable - """ - try: - while True: - m = (yield) - if callable(callback): - callback(m) - else: - logger.debug('not a callable passed') - except GeneratorExit: - return - -# -# Threads -# - - -def launch_thread(target, args): - """ - launch and demonize thread. - :param target: target function that will run in thread - :type target: function - :param args: args to be passed to thread - :type args: list - """ - t = Thread(target=target, - args=args) - t.daemon = True - t.start() - return t - - -def watch_output(out, observers): - """ - initializes dict of observer coroutines - and pushes lines to each of them as they are received - from the watched output. - :param out: stdout of a process. - :type out: fd - :param observers: tuple of coroutines to send data\ -for each event - :type observers: tuple - """ - observer_dict = dict(((observer, process_events(observer)) - for observer in observers)) - for line in iter(out.readline, b''): - for obs in observer_dict: - observer_dict[obs].send(line) - out.close() - - -def spawn_and_watch_process(command, args, observers=None): - """ - spawns a subprocess with command, args, and launch - a watcher thread. - :param command: command to be executed in the subprocess - :type command: str - :param args: arguments - :type args: list - :param observers: tuple of observer functions to be called \ -for each line in the subprocess output. - :type observers: tuple - :return: a tuple containing the child process instance, and watcher_thread, - :rtype: (Subprocess, Thread) - """ - subp = Popen([command] + args, - stdout=PIPE, - stderr=PIPE, - bufsize=1, - close_fds=ON_POSIX) - watcher = launch_thread( - watch_output, - (subp.stdout, observers)) - return subp, watcher diff --git a/src/leap/util/dicts.py b/src/leap/util/dicts.py deleted file mode 100644 index 001ca96b..00000000 --- a/src/leap/util/dicts.py +++ /dev/null @@ -1,268 +0,0 @@ -# Backport of OrderedDict() class that runs -# on Python 2.4, 2.5, 2.6, 2.7 and pypy. -# Passes Python2.7's test suite and incorporates all the latest updates. - -try: - from thread import get_ident as _get_ident -except ImportError: - from dummy_thread import get_ident as _get_ident - -try: - from _abcoll import KeysView, ValuesView, ItemsView -except ImportError: - pass - - -class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as for regular - # dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly - # linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link which goes at the end - # of the linked list, and the inherited dictionary is updated - # with the new key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor - # nodes. - dict_delitem(self, key) - link_prev, link_next, key = self.__map.pop(key) - link_prev[1] = link_next - link_next[0] = link_prev - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - root = self.__root - curr = root[1] - while curr is not root: - yield curr[2] - curr = curr[1] - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - root = self.__root - curr = root[0] - while curr is not root: - yield curr[2] - curr = curr[0] - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.itervalues(): - del node[:] - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if - false. - ''' - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root[0] - link_prev = link[0] - link_prev[1] = root - root[0] = link_prev - else: - link = root[1] - link_next = link[1] - root[1] = link_next - link_next[0] = root - key = link[2] - del self.__map[key] - value = dict.pop(self, key) - return key, value - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - 'od.keys() -> list of keys in od' - return list(self) - - def values(self): - 'od.values() -> list of values in od' - return [self[key] for key in self] - - def items(self): - 'od.items() -> list of (key, value) pairs in od' - return [(key, self[key]) for key in self] - - def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' - return iter(self) - - def itervalues(self): - 'od.itervalues -> an iterator over the values in od' - for k in self: - yield self[k] - - def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) items in od' - for k in self: - yield (k, self[k]) - - def update(*args, **kwds): - '''od.update(E, **F) -> None. Update od from dict/iterable E and F. - - If E is a dict instance, does: for k in E: od[k] = E[k] - If E has a .keys() method, does: for k in E.keys(): - od[k] = E[k] - Or if E is an iterable of items, does: for k, v in E: od[k] = v - In either case, this is followed by: for k, v in F.items(): - od[k] = v - ''' - - if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) - elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') - self = args[0] - # Make progressively weaker assumptions about "other" - other = () - if len(args) == 2: - other = args[1] - if isinstance(other, dict): - for key in other: - self[key] = other[key] - elif hasattr(other, 'keys'): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in kwds.items(): - self[key] = value - - __update = update # let subclasses override update - # without breaking __init__ - - __marker = object() - - def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v - remove specified key and return the corresponding value. - If key is not found, d is returned if given, - otherwise KeyError is raised. - - ''' - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - def __repr__(self, _repr_running={}): - 'od.__repr__() <==> repr(od)' - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - finally: - del _repr_running[call_key] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - ''' - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. - Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - ''' - if isinstance(other, OrderedDict): - return len(self) == len(other) and self.items() == other.items() - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other - - # -- the following methods are only used in Python 2.7 -- - - def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) - - def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) - - def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) diff --git a/src/leap/util/fileutil.py b/src/leap/util/fileutil.py deleted file mode 100644 index 820ffe46..00000000 --- a/src/leap/util/fileutil.py +++ /dev/null @@ -1,120 +0,0 @@ -import errno -from itertools import chain -import logging -import os -import platform -import stat - - -logger = logging.getLogger() - - -def is_user_executable(fpath): - st = os.stat(fpath) - return bool(st.st_mode & stat.S_IXUSR) - - -def extend_path(): - ourplatform = platform.system() - if ourplatform == "Linux": - return "/usr/local/sbin:/usr/sbin" - # XXX add mac / win extended search paths? - - -def which(program, path=None): - """ - an implementation of which - that extends the path with - other locations, like sbin - (f.i., openvpn binary is likely to be there) - @param program: a string representing the binary we're looking for. - """ - def is_exe(fpath): - """ - check that path exists, - it's a file, - and is executable by the owner - """ - # we would check for access, - # but it's likely that we're - # using uid 0 + polkitd - - return os.path.isfile(fpath)\ - and is_user_executable(fpath) - - def ext_candidates(fpath): - yield fpath - for ext in os.environ.get("PATHEXT", "").split(os.pathsep): - yield fpath + ext - - def iter_path(pathset): - """ - returns iterator with - full path for a given path list - and the current target bin. - """ - for path in pathset.split(os.pathsep): - exe_file = os.path.join(path, program) - #print 'file=%s' % exe_file - for candidate in ext_candidates(exe_file): - if is_exe(candidate): - yield candidate - - fpath, fname = os.path.split(program) - if fpath: - if is_exe(program): - return program - else: - # extended iterator - # with extra path - if path is None: - path = os.environ['PATH'] - extended_path = chain( - iter_path(path), - iter_path(extend_path())) - for candidate in extended_path: - if candidate is not None: - return candidate - - # sorry bro. - return None - - -def mkdir_p(path): - """ - implements mkdir -p functionality - """ - try: - os.makedirs(path) - except OSError as exc: - if exc.errno == errno.EEXIST: - pass - else: - raise - - -def mkdir_f(path): - folder, fname = os.path.split(path) - mkdir_p(folder) - - -def check_and_fix_urw_only(_file): - """ - test for 600 mode and try - to set it if anything different found - """ - mode = stat.S_IMODE( - os.stat(_file).st_mode) - - if mode != int('600', 8): - try: - logger.warning( - 'bad permission on %s ' - 'attempting to set 600', - _file) - os.chmod(_file, stat.S_IRUSR | stat.S_IWUSR) - except OSError: - logger.error( - 'error while trying to chmod 600 %s', - _file) - raise diff --git a/src/leap/util/geo.py b/src/leap/util/geo.py deleted file mode 100644 index 54b29596..00000000 --- a/src/leap/util/geo.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -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 deleted file mode 100644 index 3412a72c..00000000 --- a/src/leap/util/leap_argparse.py +++ /dev/null @@ -1,44 +0,0 @@ -import argparse - - -def build_parser(): - """ - all the options for the leap arg parser - Some of these could be switched on only if debug flag is present! - """ - epilog = "Copyright 2012 The LEAP Encryption Access Project" - parser = argparse.ArgumentParser(description=""" -Launches the LEAP Client""", epilog=epilog) - parser.add_argument('-d', '--debug', action="store_true", - help=("Launches client in debug mode, writing debug" - "info to stdout")) - parser.add_argument('-l', '--logfile', metavar="LOG FILE", nargs='?', - action="store", dest="log_file", - #type=argparse.FileType('w'), - help='optional log file') - parser.add_argument('--openvpn-verbosity', nargs='?', - type=int, - action="store", dest="openvpn_verb", - help='verbosity level for openvpn logs [1-6]') - - # Not in use, we might want to reintroduce them. - #parser.add_argument('-i', '--no-provider-checks', - #action="store_true", default=False, - #help="skips download of provider config files. gets " - #"config from local files only. Will fail if cannot " - #"find any") - #parser.add_argument('-k', '--no-ca-verify', - #action="store_true", default=False, - #help="(insecure). Skips verification of the server " - #"certificate used in TLS handshake.") - #parser.add_argument('-c', '--config', metavar="CONFIG FILE", nargs='?', - #action="store", dest="config_file", - #type=argparse.FileType('r'), - #help='optional config file') - return parser - - -def init_leapc_args(): - parser = build_parser() - opts, unknown = parser.parse_known_args() - return parser, opts diff --git a/src/leap/util/misc.py b/src/leap/util/misc.py deleted file mode 100644 index d869a1ba..00000000 --- a/src/leap/util/misc.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -misc utils -""" -import psutil - -from leap.base.constants import OPENVPN_BIN - - -class ImproperlyConfigured(Exception): - """ - """ - - -def null_check(value, value_name): - try: - assert value is not None - 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/__init__.py b/src/leap/util/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/leap/util/tests/test_fileutil.py b/src/leap/util/tests/test_fileutil.py deleted file mode 100644 index f5131b3d..00000000 --- a/src/leap/util/tests/test_fileutil.py +++ /dev/null @@ -1,100 +0,0 @@ -import os -import platform -import shutil -import stat -import tempfile -import unittest - -from leap.util import fileutil - - -class FileUtilTest(unittest.TestCase): - """ - test our file utils - """ - - def setUp(self): - self.system = platform.system() - self.create_temp_dir() - - def tearDown(self): - self.remove_temp_dir() - - # - # helpers - # - - def create_temp_dir(self): - self.tmpdir = tempfile.mkdtemp() - - def remove_temp_dir(self): - shutil.rmtree(self.tmpdir) - - def get_file_path(self, filename): - return os.path.join( - self.tmpdir, - filename) - - def touch_exec_file(self): - fp = self.get_file_path('testexec') - open(fp, 'w').close() - os.chmod( - fp, - stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) - return fp - - def get_mode(self, fp): - return stat.S_IMODE(os.stat(fp).st_mode) - - # - # tests - # - - def test_is_user_executable(self): - """ - touch_exec_file creates in mode 700? - """ - # XXX could check access X_OK - - fp = self.touch_exec_file() - mode = self.get_mode(fp) - self.assertEqual(mode, int('700', 8)) - - def test_which(self): - """ - which implementation ok? - not a very reliable test, - but I cannot think of anything smarter now - I guess it's highly improbable that copy - """ - # XXX yep, we can change the syspath - # for the test... ! - - if self.system == "Linux": - self.assertEqual( - fileutil.which('cp'), - '/bin/cp') - - def test_mkdir_p(self): - """ - our own mkdir -p implementation ok? - """ - testdir = self.get_file_path( - os.path.join('test', 'foo', 'bar')) - self.assertEqual(os.path.isdir(testdir), False) - fileutil.mkdir_p(testdir) - self.assertEqual(os.path.isdir(testdir), True) - - def test_check_and_fix_urw_only(self): - """ - ensure check_and_fix_urx_only ok? - """ - fp = self.touch_exec_file() - mode = self.get_mode(fp) - self.assertEqual(mode, int('700', 8)) - fileutil.check_and_fix_urw_only(fp) - mode = self.get_mode(fp) - self.assertEqual(mode, int('600', 8)) - -if __name__ == "__main__": - unittest.main() diff --git a/src/leap/util/tests/test_leap_argparse.py b/src/leap/util/tests/test_leap_argparse.py deleted file mode 100644 index 4e2b811f..00000000 --- a/src/leap/util/tests/test_leap_argparse.py +++ /dev/null @@ -1,35 +0,0 @@ -from argparse import Namespace -import unittest - -from leap.util import leap_argparse - - -class LeapArgParseTest(unittest.TestCase): - """ - Test argparse options for eip client - """ - - def setUp(self): - """ - get the parser - """ - self.parser = leap_argparse.build_parser() - - def test_debug_mode(self): - """ - test debug mode option - """ - opts = self.parser.parse_args( - ['--debug']) - self.assertEqual( - opts, - Namespace( - debug=True, - log_file=None, - #config_file=None, - #no_provider_checks=False, - #no_ca_verify=False, - openvpn_verb=None)) - -if __name__ == "__main__": - unittest.main() diff --git a/src/leap/util/tests/test_translations.py b/src/leap/util/tests/test_translations.py deleted file mode 100644 index 794daeba..00000000 --- a/src/leap/util/tests/test_translations.py +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index f55c8fba..00000000 --- a/src/leap/util/translations.py +++ /dev/null @@ -1,82 +0,0 @@ -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: - 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 diff --git a/src/leap/util/web.py b/src/leap/util/web.py deleted file mode 100644 index 15de0561..00000000 --- a/src/leap/util/web.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -web related utilities -""" - - -class UsageError(Exception): - """ """ - - -def get_https_domain_and_port(full_domain): - """ - returns a tuple with domain and port - from a full_domain string that can - contain a colon - """ - full_domain = unicode(full_domain) - if full_domain is None: - return None, None - - https_sch = "https://" - http_sch = "http://" - - if full_domain.startswith(https_sch): - full_domain = full_domain.lstrip(https_sch) - elif full_domain.startswith(http_sch): - raise UsageError( - "cannot be called with a domain " - "that begins with 'http://'") - - domain_split = full_domain.split(':') - _len = len(domain_split) - if _len == 1: - domain, port = full_domain, 443 - elif _len == 2: - domain, port = domain_split - else: - raise UsageError( - "must be called with one only parameter" - "in the form domain[:port]") - return domain, port -- cgit v1.2.3 From a6afbf7b848030573f01480bce0959626330b02e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 6 Mar 2013 15:44:53 -0300 Subject: Add general app --- src/leap/util/__init__.py | 0 src/leap/util/leap_argparse.py | 61 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/leap/util/__init__.py create mode 100644 src/leap/util/leap_argparse.py (limited to 'src/leap/util') diff --git a/src/leap/util/__init__.py b/src/leap/util/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py new file mode 100644 index 00000000..78597f63 --- /dev/null +++ b/src/leap/util/leap_argparse.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# leap_argparse.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import argparse + + +def build_parser(): + """ + all the options for the leap arg parser + Some of these could be switched on only if debug flag is present! + """ + epilog = "Copyright 2012 The LEAP Encryption Access Project" + parser = argparse.ArgumentParser(description=""" +Launches the LEAP Client""", epilog=epilog) + parser.add_argument('-d', '--debug', action="store_true", + help=("Launches client in debug mode, writing debug" + "info to stdout")) + parser.add_argument('-l', '--logfile', metavar="LOG FILE", nargs='?', + action="store", dest="log_file", + #type=argparse.FileType('w'), + help='optional log file') + parser.add_argument('--openvpn-verbosity', nargs='?', + type=int, + action="store", dest="openvpn_verb", + help='verbosity level for openvpn logs [1-6]') + + # Not in use, we might want to reintroduce them. + #parser.add_argument('-i', '--no-provider-checks', + #action="store_true", default=False, + #help="skips download of provider config files. gets " + #"config from local files only. Will fail if cannot " + #"find any") + #parser.add_argument('-k', '--no-ca-verify', + #action="store_true", default=False, + #help="(insecure). Skips verification of the server " + #"certificate used in TLS handshake.") + #parser.add_argument('-c', '--config', metavar="CONFIG FILE", nargs='?', + #action="store", dest="config_file", + #type=argparse.FileType('r'), + #help='optional config file') + return parser + + +def init_leapc_args(): + parser = build_parser() + opts, unknown = parser.parse_known_args() + return parser, opts -- cgit v1.2.3 From 751638b4eb8208e1eaa1beaaed284da6b412bca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 7 Mar 2013 19:05:11 -0300 Subject: Change asserts for a custom leap_assert method Also: - Make SRPAuth and the Bootstrappers be a QObject instead of a QThread so we can use them inside another more generic thread - Add a generic CheckerThread that runs checks or whatever operation as long as it returns a boolean value - Closes the whole application if the wizard is rejected at the first run - Do not fail when the config directory doesn't exist - Set the wizard pixmap logo as LEAP's logo - Improve wizard checks - Make SRPRegister play nice with the CheckerThread --- src/leap/util/check.py | 61 +++++++++++++++++++++++ src/leap/util/checkerthread.py | 110 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/leap/util/check.py create mode 100644 src/leap/util/checkerthread.py (limited to 'src/leap/util') diff --git a/src/leap/util/check.py b/src/leap/util/check.py new file mode 100644 index 00000000..9787341a --- /dev/null +++ b/src/leap/util/check.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# check.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Set of functions to help checking situations +""" +import logging +import inspect +import traceback + + +logger = logging.getLogger(__name__) + + +def leap_assert(condition, message=""): + """ + Asserts the condition and displays the message if that's not + met. It also logs the error and its backtrace. + + @param condition: condition to check + @type condition: bool + @param message: message to display if the condition isn't met + @type message: str + """ + if not condition: + logger.error("Bug: %s" % (message,)) + try: + frame = inspect.currentframe() + stack_trace = traceback.format_stack(frame) + logger.error(''.join(stack_trace)) + except Exception as e: + logger.error("Bug in leap_assert: %r" % (e,)) + assert condition, message + + +def leap_assert_type(var, expectedType): + """ + Helper assert check for a variable's expected type + + @param var: variable to check + @type var: any + @param expectedType: type to check agains + @type expectedType: type + """ + leap_assert(isinstance(var, expectedType), + "Expected type %r instead of %r" % + (expectedType, type(var))) diff --git a/src/leap/util/checkerthread.py b/src/leap/util/checkerthread.py new file mode 100644 index 00000000..681c33e1 --- /dev/null +++ b/src/leap/util/checkerthread.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# providerbootstrapper.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Checker thread +""" + +import logging + +from PySide import QtCore + +from leap.util.check import leap_assert_type + +logger = logging.getLogger(__name__) + + +class CheckerThread(QtCore.QThread): + """ + Generic checker thread that can perform any type of operation as + long as it returns a boolean value that identifies how the + execution went. + """ + + IDLE_SLEEP_INTERVAL = 1 + + def __init__(self): + QtCore.QThread.__init__(self) + + self._checks = [] + self._checks_lock = QtCore.QMutex() + + self._should_quit = False + self._should_quit_lock = QtCore.QMutex() + + def get_should_quit(self): + """ + Returns wether this thread should quit + + @rtype: bool + @return: True if the thread should terminate itself, Flase otherwise + """ + + QtCore.QMutexLocker(self._should_quit_lock) + return self._should_quit + + def set_should_quit(self): + """ + Sets the should_quit flag to True so that this thread + terminates the first chance it gets + """ + QtCore.QMutexLocker(self._should_quit_lock) + self._should_quit = True + self.wait() + + def start(self): + """ + Starts the thread and resets the should_quit flag + """ + with QtCore.QMutexLocker(self._should_quit_lock): + self._should_quit = False + + QtCore.QThread.start(self) + + def add_checks(self, checks): + """ + Adds a list of checks to the ones being executed + + @param checks: check functions to perform + @type checkes: list + """ + with QtCore.QMutexLocker(self._checks_lock): + self._checks += checks + + def run(self): + """ + Main run loop for this thread. Executes the checks. + """ + shouldContinue = False + while True: + if self.get_should_quit(): + logger.debug("Quitting checker thread") + return + checkSomething = False + with QtCore.QMutexLocker(self._checks_lock): + if len(self._checks) > 0: + check = self._checks.pop(0) + shouldContinue = check() + leap_assert_type(shouldContinue, bool) + checkSomething = True + if not shouldContinue: + logger.debug("Something went wrong with the checks, " + "clearing...") + self._checks = [] + checkSomething = False + if not checkSomething: + self.sleep(self.IDLE_SLEEP_INTERVAL) -- cgit v1.2.3 From 926575bc811e8382100695a3396da7191fb43eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 8 Mar 2013 13:15:38 -0300 Subject: Add translation support Also: - Make OpenVPN use a random port every time - Logout in parallel so the UI doesn't block - Add the WAIT status from OpenVPN to the mainwindow displays - Support non-unix sockets in the LinuxVPNLauncher --- src/leap/util/leap_argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/util') diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py index 78597f63..83272a3d 100644 --- a/src/leap/util/leap_argparse.py +++ b/src/leap/util/leap_argparse.py @@ -20,7 +20,7 @@ import argparse def build_parser(): """ - all the options for the leap arg parser + All the options for the leap arg parser Some of these could be switched on only if debug flag is present! """ epilog = "Copyright 2012 The LEAP Encryption Access Project" -- cgit v1.2.3 From 2da60cd0f78378fdcb8f6364a798720281b34b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Tue, 12 Mar 2013 09:56:05 -0300 Subject: Check and try to fix certificate permissions --- src/leap/util/files.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/leap/util/files.py (limited to 'src/leap/util') diff --git a/src/leap/util/files.py b/src/leap/util/files.py new file mode 100644 index 00000000..f7fda39e --- /dev/null +++ b/src/leap/util/files.py @@ -0,0 +1,27 @@ +import os +import stat +import logging + +logger = logging.getLogger(__name__) + + +def check_and_fix_urw_only(cert): + """ + Test for 600 mode and try to set it if anything different found + + Might raise OSError + + @param cert: Certificate path + @type cert: str + """ + mode = stat.S_IMODE(os.stat(cert).st_mode) + + if mode != int('600', 8): + try: + logger.warning('Bad permission on %s attempting to set 600' % + (cert,)) + os.chmod(cert, stat.S_IRUSR | stat.S_IWUSR) + except OSError: + logger.error('Error while trying to chmod 600 %s' % + cert) + raise -- cgit v1.2.3 From 70c402fe170ca4e01159b03739b7cacda7b0dfd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 09:43:13 -0300 Subject: Add mtime check for existing json definitions before download Also, wait for threads to finish when quitting --- src/leap/util/checkerthread.py | 1 - src/leap/util/files.py | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'src/leap/util') diff --git a/src/leap/util/checkerthread.py b/src/leap/util/checkerthread.py index 681c33e1..3430a450 100644 --- a/src/leap/util/checkerthread.py +++ b/src/leap/util/checkerthread.py @@ -64,7 +64,6 @@ class CheckerThread(QtCore.QThread): """ QtCore.QMutexLocker(self._should_quit_lock) self._should_quit = True - self.wait() def start(self): """ diff --git a/src/leap/util/files.py b/src/leap/util/files.py index f7fda39e..8c7a5af3 100644 --- a/src/leap/util/files.py +++ b/src/leap/util/files.py @@ -1,6 +1,7 @@ import os import stat import logging +import time logger = logging.getLogger(__name__) @@ -25,3 +26,20 @@ def check_and_fix_urw_only(cert): logger.error('Error while trying to chmod 600 %s' % cert) raise + + +def get_mtime(filename): + """ + Returns the modified time or None if the file doesn't exist + + @param filename: path to check + @type filename: str + + @rtype: str + """ + try: + _mtime = os.stat(filename)[8] + mtime = time.strftime("%c GMT", time.gmtime(_mtime)) + return mtime + except OSError: + return None -- cgit v1.2.3 From 0ff122cf9fd0a76871093b595910fb7c0d3bfe85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 10:05:58 -0300 Subject: Pass mtime to pluggableconfig's load Also add a request_helpers file to util where all the helper methods for handling requests should go --- src/leap/util/request_helpers.py | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/leap/util/request_helpers.py (limited to 'src/leap/util') diff --git a/src/leap/util/request_helpers.py b/src/leap/util/request_helpers.py new file mode 100644 index 00000000..c5d0f3f5 --- /dev/null +++ b/src/leap/util/request_helpers.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# request_helpers.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Request helpers for backward compatible "parsing" of requests +""" + +import json + +from dateutil import parser as dateparser + + +def get_content(request): + """ + Returns the content by trying to get it from the json + property/function or from content, in that order. + Also returns the mtime for that content if available + + @param request: request as it is given by requests + @type request: Response + + @rtype: tuple (contents, mtime) + """ + + contents = "" + mtime = None + + if request.json: + if callable(request.json): + contents = json.dumps(request.json()) + else: + contents = json.dumps(request.json) + else: + contents = request.content + + mtime = None + last_modified = request.headers.get('last-modified', None) + if last_modified: + mtime = int(dateparser.parse(last_modified).strftime("%s")) + + return contents, mtime -- cgit v1.2.3 From 12d2835c7d1f3c3d11eaa587b2196c104e6859e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 10:21:15 -0300 Subject: Add mkdir_p method to util.files --- src/leap/util/files.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'src/leap/util') diff --git a/src/leap/util/files.py b/src/leap/util/files.py index 8c7a5af3..97741433 100644 --- a/src/leap/util/files.py +++ b/src/leap/util/files.py @@ -1,7 +1,29 @@ +# -*- coding: utf-8 -*- +# files.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Implements file helper methods +""" + import os import stat import logging import time +import errno logger = logging.getLogger(__name__) @@ -43,3 +65,22 @@ def get_mtime(filename): return mtime except OSError: return None + + +def mkdir_p(path): + """ + Creates the path and all the intermediate directories that don't + exist + + Might raise OSError + + @param path: path to create + @type path: str + """ + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise -- cgit v1.2.3 From a12906958e4d117daaf45bd42e7383d2344ea463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 10:38:25 -0300 Subject: Add util.certs and abstract digest there --- src/leap/util/certs.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/leap/util/certs.py (limited to 'src/leap/util') diff --git a/src/leap/util/certs.py b/src/leap/util/certs.py new file mode 100644 index 00000000..7cbd7519 --- /dev/null +++ b/src/leap/util/certs.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# certs.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Implements cert checks and helpers +""" + +from OpenSSL import crypto + + +def get_digest(cert_data, method): + """ + Returns the digest for the cert_data using the method specified + + @param cert_data: certificate data in string form + @type cert_data: str + @param method: method to be used for digest + @type method: str + + @rtype: str + """ + x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert_data) + digest = x509.digest(method).replace(":", "").lower() + + return digest -- cgit v1.2.3 From 60bcc7b27aa934a0d62033e7152b87d5af638491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 11:09:38 -0300 Subject: Add valid pemfile check before saving the downloaded client cert --- src/leap/util/certs.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'src/leap/util') diff --git a/src/leap/util/certs.py b/src/leap/util/certs.py index 7cbd7519..d6065474 100644 --- a/src/leap/util/certs.py +++ b/src/leap/util/certs.py @@ -19,8 +19,14 @@ Implements cert checks and helpers """ +import logging + from OpenSSL import crypto +from leap.util.check import leap_assert + +logger = logging.getLogger(__name__) + def get_digest(cert_data, method): """ @@ -37,3 +43,44 @@ def get_digest(cert_data, method): digest = x509.digest(method).replace(":", "").lower() return digest + + +def can_load_cert_and_pkey(string): + """ + Loads certificate and private key from a buffer, returns True if + everything went well, False otherwise + + @param string: buffer containing the cert and private key + @type string: str or any kind of buffer + + @rtype: bool + """ + + can_load = True + + try: + cert = crypto.load_certificate(crypto.FILETYPE_PEM, string) + key = crypto.load_privatekey(crypto.FILETYPE_PEM, string) + + leap_assert(cert, 'The certificate could not be loaded') + leap_assert(key, 'The private key could not be loaded') + except Exception as e: + can_load = False + logger.error("Something went wrong while trying to load " + "the certificate: %r" % (e,)) + + return can_load + + +def is_valid_pemfile(cert): + """ + Checks that the passed string is a valid pem certificate + + @param cert: String containing pem content + @type cert: str + + @rtype: bool + """ + leap_assert(cert, "We need a cert to load") + + return can_load_cert_and_pkey(cert) -- cgit v1.2.3 From 240d6b7762f7cc8f4c6fd229e4538aa9aa2262a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 11:33:42 -0300 Subject: Check validity for downloaded certs and re-download if needed --- src/leap/util/certs.py | 99 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) (limited to 'src/leap/util') diff --git a/src/leap/util/certs.py b/src/leap/util/certs.py index d6065474..a8bcd65e 100644 --- a/src/leap/util/certs.py +++ b/src/leap/util/certs.py @@ -19,15 +19,58 @@ Implements cert checks and helpers """ +import os +import time import logging from OpenSSL import crypto +from dateutil.parser import parse as dateparse from leap.util.check import leap_assert logger = logging.getLogger(__name__) +def get_cert_from_string(string): + """ + Returns the x509 from the contents of this string + + @param string: certificate contents as downloaded + @type string: str + + @return: x509 or None + """ + leap_assert(string, "We need something to load") + + x509 = None + try: + x509 = crypto.load_certificate(crypto.FILETYPE_PEM, string) + except Exception as e: + logger.error("Something went wrong while loading the certificate: %r" + % (e,)) + return x509 + + +def get_privatekey_from_string(string): + """ + Returns the private key from the contents of this string + + @param string: private key contents as downloaded + @type string: str + + @return: private key or None + """ + leap_assert(string, "We need something to load") + + pkey = None + try: + pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, string) + except Exception as e: + logger.error("Something went wrong while loading the certificate: %r" + % (e,)) + return pkey + + def get_digest(cert_data, method): """ Returns the digest for the cert_data using the method specified @@ -39,7 +82,7 @@ def get_digest(cert_data, method): @rtype: str """ - x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert_data) + x509 = get_cert_from_string(cert_data) digest = x509.digest(method).replace(":", "").lower() return digest @@ -55,12 +98,11 @@ def can_load_cert_and_pkey(string): @rtype: bool """ - can_load = True try: - cert = crypto.load_certificate(crypto.FILETYPE_PEM, string) - key = crypto.load_privatekey(crypto.FILETYPE_PEM, string) + cert = get_cert_from_string(string) + key = get_privatekey_from_string(string) leap_assert(cert, 'The certificate could not be loaded') leap_assert(key, 'The private key could not be loaded') @@ -84,3 +126,52 @@ def is_valid_pemfile(cert): leap_assert(cert, "We need a cert to load") return can_load_cert_and_pkey(cert) + + +def get_cert_time_boundaries(certfile): + """ + Returns the time boundaries for the certificate saved in certfile + + @param certfile: path to certificate + @type certfile: str + + @rtype: tuple (from, to) + """ + cert = get_cert_from_string(certfile) + leap_assert(cert, 'There was a problem loading the certificate') + + fromts, tots = (cert.get_notBefore(), cert.get_notAfter()) + from_, to_ = map( + lambda ts: time.gmtime(time.mktime(dateparse(ts).timetuple())), + (fromts, tots)) + return from_, to_ + + +def should_redownload(certfile, now=time.gmtime): + """ + Returns True if any of the checks don't pass, False otherwise + + @param certfile: path to certificate + @type certfile: str + @param now: current date function, ONLY USED FOR TESTING + + @rtype: bool + """ + exists = os.path.isfile(certfile) + + if not exists: + return True + + try: + with open(certfile, "r") as f: + if not is_valid_pemfile(f.read()): + return True + except: + return True + + valid_from, valid_to = get_cert_time_boundaries(certfile) + + if not (valid_from < now() < valid_to): + return True + + return False -- cgit v1.2.3 From df01ec4406134d9abbda3c3e1ae1933633827a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 11:37:16 -0300 Subject: Update name in file header --- src/leap/util/checkerthread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/util') diff --git a/src/leap/util/checkerthread.py b/src/leap/util/checkerthread.py index 3430a450..0e69eca3 100644 --- a/src/leap/util/checkerthread.py +++ b/src/leap/util/checkerthread.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# providerbootstrapper.py +# checkerthread.py # Copyright (C) 2013 LEAP # # This program is free software: you can redistribute it and/or modify -- cgit v1.2.3 From 4359515dafe572398262ce91bf88d4f122042981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 13:39:00 -0300 Subject: Add vpn already running checks --- src/leap/util/certs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/leap/util') diff --git a/src/leap/util/certs.py b/src/leap/util/certs.py index a8bcd65e..63c60c3d 100644 --- a/src/leap/util/certs.py +++ b/src/leap/util/certs.py @@ -162,14 +162,16 @@ def should_redownload(certfile, now=time.gmtime): if not exists: return True + certdata = None try: with open(certfile, "r") as f: - if not is_valid_pemfile(f.read()): + certdata = f.read() + if not is_valid_pemfile(certdata): return True except: return True - valid_from, valid_to = get_cert_time_boundaries(certfile) + valid_from, valid_to = get_cert_time_boundaries(certdata) if not (valid_from < now() < valid_to): return True -- cgit v1.2.3 From 6502ea97ca6dfe5341047341ecbe166276d75c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 17:06:55 -0300 Subject: Fix timestamp for file creation --- src/leap/util/files.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/leap/util') diff --git a/src/leap/util/files.py b/src/leap/util/files.py index 97741433..7c878e1d 100644 --- a/src/leap/util/files.py +++ b/src/leap/util/files.py @@ -60,8 +60,7 @@ def get_mtime(filename): @rtype: str """ try: - _mtime = os.stat(filename)[8] - mtime = time.strftime("%c GMT", time.gmtime(_mtime)) + mtime = time.ctime(os.path.getmtime(filename)) + " GMT" return mtime except OSError: return None -- cgit v1.2.3 From d0dfad6ac2af360de6421ce74a6831b5b81ad019 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 14 Mar 2013 07:08:31 +0900 Subject: namespace leap + leap.common split leap is a namespace package from here on. common folder will be deleted and moved to leap_pycommon repository. --- src/leap/util/__init__.py | 29 +++++++ src/leap/util/certs.py | 179 ----------------------------------------- src/leap/util/check.py | 61 -------------- src/leap/util/checkerthread.py | 2 +- src/leap/util/files.py | 85 ------------------- 5 files changed, 30 insertions(+), 326 deletions(-) delete mode 100644 src/leap/util/certs.py delete mode 100644 src/leap/util/check.py delete mode 100644 src/leap/util/files.py (limited to 'src/leap/util') diff --git a/src/leap/util/__init__.py b/src/leap/util/__init__.py index e69de29b..a4e49ae5 100644 --- a/src/leap/util/__init__.py +++ b/src/leap/util/__init__.py @@ -0,0 +1,29 @@ +""" +LEAP Encryption Access Project +website: U{https://leap.se/} +""" + +__version__ = "unknown" +try: + from leap._version import get_versions + __version__ = get_versions()['version'] + del get_versions +except ImportError: + #running on a tree that has not run + #the setup.py setver + pass + +__appname__ = "unknown" +try: + from leap._appname import __appname__ +except ImportError: + #running on a tree that has not run + #the setup.py setver + pass + +__full_version__ = __appname__ + '/' + str(__version__) + +# try: +# from leap._branding import BRANDING as __branding +# except ImportError: +# __branding = {} diff --git a/src/leap/util/certs.py b/src/leap/util/certs.py deleted file mode 100644 index 63c60c3d..00000000 --- a/src/leap/util/certs.py +++ /dev/null @@ -1,179 +0,0 @@ -# -*- coding: utf-8 -*- -# certs.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Implements cert checks and helpers -""" - -import os -import time -import logging - -from OpenSSL import crypto -from dateutil.parser import parse as dateparse - -from leap.util.check import leap_assert - -logger = logging.getLogger(__name__) - - -def get_cert_from_string(string): - """ - Returns the x509 from the contents of this string - - @param string: certificate contents as downloaded - @type string: str - - @return: x509 or None - """ - leap_assert(string, "We need something to load") - - x509 = None - try: - x509 = crypto.load_certificate(crypto.FILETYPE_PEM, string) - except Exception as e: - logger.error("Something went wrong while loading the certificate: %r" - % (e,)) - return x509 - - -def get_privatekey_from_string(string): - """ - Returns the private key from the contents of this string - - @param string: private key contents as downloaded - @type string: str - - @return: private key or None - """ - leap_assert(string, "We need something to load") - - pkey = None - try: - pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, string) - except Exception as e: - logger.error("Something went wrong while loading the certificate: %r" - % (e,)) - return pkey - - -def get_digest(cert_data, method): - """ - Returns the digest for the cert_data using the method specified - - @param cert_data: certificate data in string form - @type cert_data: str - @param method: method to be used for digest - @type method: str - - @rtype: str - """ - x509 = get_cert_from_string(cert_data) - digest = x509.digest(method).replace(":", "").lower() - - return digest - - -def can_load_cert_and_pkey(string): - """ - Loads certificate and private key from a buffer, returns True if - everything went well, False otherwise - - @param string: buffer containing the cert and private key - @type string: str or any kind of buffer - - @rtype: bool - """ - can_load = True - - try: - cert = get_cert_from_string(string) - key = get_privatekey_from_string(string) - - leap_assert(cert, 'The certificate could not be loaded') - leap_assert(key, 'The private key could not be loaded') - except Exception as e: - can_load = False - logger.error("Something went wrong while trying to load " - "the certificate: %r" % (e,)) - - return can_load - - -def is_valid_pemfile(cert): - """ - Checks that the passed string is a valid pem certificate - - @param cert: String containing pem content - @type cert: str - - @rtype: bool - """ - leap_assert(cert, "We need a cert to load") - - return can_load_cert_and_pkey(cert) - - -def get_cert_time_boundaries(certfile): - """ - Returns the time boundaries for the certificate saved in certfile - - @param certfile: path to certificate - @type certfile: str - - @rtype: tuple (from, to) - """ - cert = get_cert_from_string(certfile) - leap_assert(cert, 'There was a problem loading the certificate') - - fromts, tots = (cert.get_notBefore(), cert.get_notAfter()) - from_, to_ = map( - lambda ts: time.gmtime(time.mktime(dateparse(ts).timetuple())), - (fromts, tots)) - return from_, to_ - - -def should_redownload(certfile, now=time.gmtime): - """ - Returns True if any of the checks don't pass, False otherwise - - @param certfile: path to certificate - @type certfile: str - @param now: current date function, ONLY USED FOR TESTING - - @rtype: bool - """ - exists = os.path.isfile(certfile) - - if not exists: - return True - - certdata = None - try: - with open(certfile, "r") as f: - certdata = f.read() - if not is_valid_pemfile(certdata): - return True - except: - return True - - valid_from, valid_to = get_cert_time_boundaries(certdata) - - if not (valid_from < now() < valid_to): - return True - - return False diff --git a/src/leap/util/check.py b/src/leap/util/check.py deleted file mode 100644 index 9787341a..00000000 --- a/src/leap/util/check.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -# check.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Set of functions to help checking situations -""" -import logging -import inspect -import traceback - - -logger = logging.getLogger(__name__) - - -def leap_assert(condition, message=""): - """ - Asserts the condition and displays the message if that's not - met. It also logs the error and its backtrace. - - @param condition: condition to check - @type condition: bool - @param message: message to display if the condition isn't met - @type message: str - """ - if not condition: - logger.error("Bug: %s" % (message,)) - try: - frame = inspect.currentframe() - stack_trace = traceback.format_stack(frame) - logger.error(''.join(stack_trace)) - except Exception as e: - logger.error("Bug in leap_assert: %r" % (e,)) - assert condition, message - - -def leap_assert_type(var, expectedType): - """ - Helper assert check for a variable's expected type - - @param var: variable to check - @type var: any - @param expectedType: type to check agains - @type expectedType: type - """ - leap_assert(isinstance(var, expectedType), - "Expected type %r instead of %r" % - (expectedType, type(var))) diff --git a/src/leap/util/checkerthread.py b/src/leap/util/checkerthread.py index 0e69eca3..47a96ec5 100644 --- a/src/leap/util/checkerthread.py +++ b/src/leap/util/checkerthread.py @@ -23,7 +23,7 @@ import logging from PySide import QtCore -from leap.util.check import leap_assert_type +from leap.common.check import leap_assert_type logger = logging.getLogger(__name__) diff --git a/src/leap/util/files.py b/src/leap/util/files.py deleted file mode 100644 index 7c878e1d..00000000 --- a/src/leap/util/files.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- -# files.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Implements file helper methods -""" - -import os -import stat -import logging -import time -import errno - -logger = logging.getLogger(__name__) - - -def check_and_fix_urw_only(cert): - """ - Test for 600 mode and try to set it if anything different found - - Might raise OSError - - @param cert: Certificate path - @type cert: str - """ - mode = stat.S_IMODE(os.stat(cert).st_mode) - - if mode != int('600', 8): - try: - logger.warning('Bad permission on %s attempting to set 600' % - (cert,)) - os.chmod(cert, stat.S_IRUSR | stat.S_IWUSR) - except OSError: - logger.error('Error while trying to chmod 600 %s' % - cert) - raise - - -def get_mtime(filename): - """ - Returns the modified time or None if the file doesn't exist - - @param filename: path to check - @type filename: str - - @rtype: str - """ - try: - mtime = time.ctime(os.path.getmtime(filename)) + " GMT" - return mtime - except OSError: - return None - - -def mkdir_p(path): - """ - Creates the path and all the intermediate directories that don't - exist - - Might raise OSError - - @param path: path to create - @type path: str - """ - try: - os.makedirs(path) - except OSError as exc: - if exc.errno == errno.EEXIST and os.path.isdir(path): - pass - else: - raise -- cgit v1.2.3 From 1f4841079a72df874d0624f7908115ded295a82f Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 15 Mar 2013 01:13:30 +0900 Subject: remove branding remnant, add missing header --- src/leap/util/__init__.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'src/leap/util') diff --git a/src/leap/util/__init__.py b/src/leap/util/__init__.py index a4e49ae5..41358d38 100644 --- a/src/leap/util/__init__.py +++ b/src/leap/util/__init__.py @@ -1,6 +1,21 @@ +# -*- coding: utf-8 -*- +# __init__.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . """ -LEAP Encryption Access Project -website: U{https://leap.se/} +Initializes version and app info """ __version__ = "unknown" @@ -22,8 +37,3 @@ except ImportError: pass __full_version__ = __appname__ + '/' + str(__version__) - -# try: -# from leap._branding import BRANDING as __branding -# except ImportError: -# __branding = {} -- cgit v1.2.3 From 8f54774f6c3f779527718a0158ebd0efc4aab588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 15 Mar 2013 13:30:01 -0300 Subject: Handle configuration and paths in a standalone way Also, abstracts QSettings under LeapSettings and adds a way to define the VPN env in a platform dependant way. --- src/leap/util/leap_argparse.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/leap/util') diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py index 83272a3d..66b1a2a5 100644 --- a/src/leap/util/leap_argparse.py +++ b/src/leap/util/leap_argparse.py @@ -37,6 +37,10 @@ Launches the LEAP Client""", epilog=epilog) type=int, action="store", dest="openvpn_verb", help='verbosity level for openvpn logs [1-6]') + parser.add_argument('--standalone', action="store_true", + help='Makes the client use standalone' + 'directories for configuration and binary' + 'searching') # Not in use, we might want to reintroduce them. #parser.add_argument('-i', '--no-provider-checks', -- cgit v1.2.3 From bdc3e1b840c383f0d9236ad8d4df66633e87baf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 21 Mar 2013 10:38:48 -0300 Subject: Fixes as per review --- src/leap/util/leap_argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/util') diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py index 66b1a2a5..66268f6f 100644 --- a/src/leap/util/leap_argparse.py +++ b/src/leap/util/leap_argparse.py @@ -37,7 +37,7 @@ Launches the LEAP Client""", epilog=epilog) type=int, action="store", dest="openvpn_verb", help='verbosity level for openvpn logs [1-6]') - parser.add_argument('--standalone', action="store_true", + parser.add_argument('-s', '--standalone', action="store_true", help='Makes the client use standalone' 'directories for configuration and binary' 'searching') -- cgit v1.2.3 From 9bd35a826b398bd3bdd98824f17a14538d14abb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 21 Mar 2013 13:40:37 -0300 Subject: Calculate mtime in a more multiplatform way --- src/leap/util/request_helpers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/leap/util') diff --git a/src/leap/util/request_helpers.py b/src/leap/util/request_helpers.py index c5d0f3f5..019ff353 100644 --- a/src/leap/util/request_helpers.py +++ b/src/leap/util/request_helpers.py @@ -19,6 +19,8 @@ Request helpers for backward compatible "parsing" of requests """ +import time + import json from dateutil import parser as dateparser @@ -50,6 +52,7 @@ def get_content(request): mtime = None last_modified = request.headers.get('last-modified', None) if last_modified: - mtime = int(dateparser.parse(last_modified).strftime("%s")) + dt = dateparser.parse(unicode(last_modified)) + mtime = int(time.mktime(dt.timetuple()) + dt.microsecond / 1000000.0) return contents, mtime -- cgit v1.2.3 From 22342664951ac32756ceb7ade59ada90f92c8793 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 4 Apr 2013 01:47:12 +0900 Subject: Several fixes in wizard Closes:#2061 o Rewording of setup steps in wizard, to make them more meaningful to the non-technical user. Closes: #2061 o Fix typo in wizard o Fix multiple drawing of services if going back o Make registration errors show in red o Add a warning if EIP service needs admin password. Addresses part of #2062 --- src/leap/util/privilege_policies.py | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/leap/util/privilege_policies.py (limited to 'src/leap/util') diff --git a/src/leap/util/privilege_policies.py b/src/leap/util/privilege_policies.py new file mode 100644 index 00000000..5bf1b476 --- /dev/null +++ b/src/leap/util/privilege_policies.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# privilege_policies.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Helpers to determine if the needed policies for privilege escalation +are operative under this client run. +""" +import logging +import os +import platform + +from abc import ABCMeta, abstractmethod + +logger = logging.getLogger(__name__) + + +def is_missing_policy_permissions(): + """ + Returns True if we do not have implemented a policy checker for this + platform, or if the policy checker exists but it cannot find the + appropriate policy mechanisms in place. + """ + _system = platform.system() + platform_checker = _system + "PolicyChecker" + policy_checker = globals().get(platform_checker, None) + if not policy_checker: + # it is true that we miss permission to escalate + # privileges without asking for password each time. + logger.debug("we could not find a policy checker implementation " + "for %s" % (_system,)) + return True + return policy_checker().is_missing_policy_permissions() + + +class PolicyChecker: + """ + Abstract PolicyChecker class + """ + + __metaclass__ = ABCMeta + + @abstractmethod + def is_missing_policy_permissions(self): + """ + Returns True if we could not find any policy mechanisms that + are defined to be in used for this particular platform. + + @rtype: bool + """ + return True + + +class LinuxPolicyChecker(PolicyChecker): + """ + PolicyChecker for Linux + """ + LINUX_POLKIT_FILE = ("/usr/share/polkit-1/actions/" + "net.openvpn.gui.leap.policy") + + def is_missing_policy_permissions(self): + """ + Returns True if we could not find the appropriate policykit file + in place + """ + return not os.path.isfile(self.LINUX_POLKIT_FILE) -- cgit v1.2.3 From e75fe6ffd58b067b3fc32196ad245d26a1287e99 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 9 Apr 2013 00:43:13 +0900 Subject: fixes as per review --- src/leap/util/privilege_policies.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/leap/util') diff --git a/src/leap/util/privilege_policies.py b/src/leap/util/privilege_policies.py index 5bf1b476..e74c4d33 100644 --- a/src/leap/util/privilege_policies.py +++ b/src/leap/util/privilege_policies.py @@ -32,6 +32,8 @@ def is_missing_policy_permissions(): Returns True if we do not have implemented a policy checker for this platform, or if the policy checker exists but it cannot find the appropriate policy mechanisms in place. + + @rtype: bool """ _system = platform.system() platform_checker = _system + "PolicyChecker" @@ -74,5 +76,7 @@ class LinuxPolicyChecker(PolicyChecker): """ Returns True if we could not find the appropriate policykit file in place + + @rtype: bool """ return not os.path.isfile(self.LINUX_POLKIT_FILE) -- cgit v1.2.3 From 1fbf6db1276c5bca41c4cfbcc90818d9605c1938 Mon Sep 17 00:00:00 2001 From: Tomas Touceda Date: Fri, 12 Apr 2013 14:07:15 -0300 Subject: Add --danger option to not validate the first hop of certificates This is intended to be used while testing, not in production --- src/leap/util/leap_argparse.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/leap/util') diff --git a/src/leap/util/leap_argparse.py b/src/leap/util/leap_argparse.py index 66268f6f..8300e4d8 100644 --- a/src/leap/util/leap_argparse.py +++ b/src/leap/util/leap_argparse.py @@ -29,6 +29,8 @@ Launches the LEAP Client""", epilog=epilog) parser.add_argument('-d', '--debug', action="store_true", help=("Launches client in debug mode, writing debug" "info to stdout")) + parser.add_argument('--danger', action="store_true", + help=("Bypasses the certificate check for bootstrap")) parser.add_argument('-l', '--logfile', metavar="LOG FILE", nargs='?', action="store", dest="log_file", #type=argparse.FileType('w'), -- cgit v1.2.3 From 2dae2703fb8c2ae7e721ce83020c0dd10ff9ca33 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 May 2013 02:59:22 +0900 Subject: updated documentation * documentation reviewed after rewrite, ready for 0.2.1 * updated docstrings format to fit sphinx autodoc --- src/leap/util/checkerthread.py | 10 +++++----- src/leap/util/privilege_policies.py | 6 +++--- src/leap/util/request_helpers.py | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/leap/util') diff --git a/src/leap/util/checkerthread.py b/src/leap/util/checkerthread.py index 47a96ec5..02aa333f 100644 --- a/src/leap/util/checkerthread.py +++ b/src/leap/util/checkerthread.py @@ -48,10 +48,10 @@ class CheckerThread(QtCore.QThread): def get_should_quit(self): """ - Returns wether this thread should quit + Returns whether this thread should quit - @rtype: bool - @return: True if the thread should terminate itself, Flase otherwise + :return: True if the thread should terminate itself, Flase otherwise + :rtype: bool """ QtCore.QMutexLocker(self._should_quit_lock) @@ -78,8 +78,8 @@ class CheckerThread(QtCore.QThread): """ Adds a list of checks to the ones being executed - @param checks: check functions to perform - @type checkes: list + :param checks: check functions to perform + :type checkes: list """ with QtCore.QMutexLocker(self._checks_lock): self._checks += checks diff --git a/src/leap/util/privilege_policies.py b/src/leap/util/privilege_policies.py index e74c4d33..10224bcd 100644 --- a/src/leap/util/privilege_policies.py +++ b/src/leap/util/privilege_policies.py @@ -33,7 +33,7 @@ def is_missing_policy_permissions(): platform, or if the policy checker exists but it cannot find the appropriate policy mechanisms in place. - @rtype: bool + :rtype: bool """ _system = platform.system() platform_checker = _system + "PolicyChecker" @@ -60,7 +60,7 @@ class PolicyChecker: Returns True if we could not find any policy mechanisms that are defined to be in used for this particular platform. - @rtype: bool + :rtype: bool """ return True @@ -77,6 +77,6 @@ class LinuxPolicyChecker(PolicyChecker): Returns True if we could not find the appropriate policykit file in place - @rtype: bool + :rtype: bool """ return not os.path.isfile(self.LINUX_POLKIT_FILE) diff --git a/src/leap/util/request_helpers.py b/src/leap/util/request_helpers.py index 019ff353..e06dabb8 100644 --- a/src/leap/util/request_helpers.py +++ b/src/leap/util/request_helpers.py @@ -32,10 +32,10 @@ def get_content(request): property/function or from content, in that order. Also returns the mtime for that content if available - @param request: request as it is given by requests - @type request: Response + :param request: request as it is given by requests + :type request: Response - @rtype: tuple (contents, mtime) + :rtype: tuple (contents, mtime) """ contents = "" -- cgit v1.2.3 From 92d43151bad2f2ac6e292d725555249082462f81 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 27 May 2013 15:05:08 -0300 Subject: Add logging handler with history and signal emision --- src/leap/util/leap_log_handler.py | 99 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/leap/util/leap_log_handler.py (limited to 'src/leap/util') diff --git a/src/leap/util/leap_log_handler.py b/src/leap/util/leap_log_handler.py new file mode 100644 index 00000000..0e598032 --- /dev/null +++ b/src/leap/util/leap_log_handler.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# leap_log_handler.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Custom handler for the logger window. +""" +import logging + +from PySide import QtCore + + +class LeapLogHandler(logging.Handler, QtCore.QObject): + """ + Custom logging handler. It emits Qt signals so it can be plugged to a gui. + Also stores an history of logs that can be fetched after connect to a gui. + """ + # All dicts returned are of the form + # {'record': LogRecord, 'message': str} + new_log = QtCore.Signal(dict) + + MESSAGE_KEY = 'message' + RECORD_KEY = 'record' + + def __init__(self): + logging.Handler.__init__(self) + QtCore.QObject.__init__(self) + + self._log_history = [] + + def _set_format(self, logging_level): + """ + Sets the log format depending on the parameter. + It uses html and css to set the colors for the logs. + + :param logging_level: the debug level to define the color. + :type logging_level: str. + """ + html_style = { + 'DEBUG': "color: blue", + 'INFO': "color: black", + 'WARNING': "color: black; background: yellow;", + 'ERROR': "color: red", + 'CRITICAL': "color: red; font-weight: bold;" + } + + style_open = "" + style_close = "" + time = "%(asctime)s" + name = style_open + "%(name)s" + level = "%(levelname)s" + message = "%(message)s" + style_close + format_attrs = [time, name, level, message] + log_format = ' - '.join(format_attrs) + formatter = logging.Formatter(log_format) + self.setFormatter(formatter) + + def emit(self, logRecord): + """ + This method is fired every time that a record is logged by the + logging module. + This method reimplements logging.Handler.emit that is fired + in every logged message. + QObject.emit gets in the way on the PySide signal model but we + workarouded that issue. + + :param logRecord: the record emitted by the logging module. + :type logRecord: logging.LogRecord. + """ + 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) + + # WARNING: the new-style connection does NOT work because PySide + # translates the emit method to self.emit, and that collides with + # the emit method for logging.Handler + # self.new_log.emit(log_item) + QtCore.QObject.emit(self, QtCore.SIGNAL('new_log(PyObject)'), log_item) + + @property + def log_history(self): + """ + Returns the history of the logged messages. + """ + return self._log_history -- cgit v1.2.3 From 8a92157bd4359c3c41bcbcfbbd9f164ef633f205 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 30 May 2013 16:00:30 -0300 Subject: Add runtime requirements checker --- src/leap/util/requirement_checker.py | 101 +++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/leap/util/requirement_checker.py (limited to 'src/leap/util') diff --git a/src/leap/util/requirement_checker.py b/src/leap/util/requirement_checker.py new file mode 100644 index 00000000..3538f122 --- /dev/null +++ b/src/leap/util/requirement_checker.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# requirement_checker.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Utility to check the needed requirements. +""" + +import os +import logging + +from pkg_resources import (DistributionNotFound, + get_distribution, + Requirement, + resource_stream, + VersionConflict) + +logger = logging.getLogger(__name__) + + +def get_requirements(): + """ + This function returns a list with requirements. + It checks either if its running from the source or if its installed. + + :returns: a list with packages names, required for the app. + :return type: list of str. + """ + develop = True + requirements = [] + + try: + # if we are running from the source + from pkg import util + requirements = util.parse_requirements() + except ImportError: + develop = False + + # if we are running from the package + if not develop: + requires_file_name = os.path.join('leap', 'util', 'reqs.txt') + dist_name = Requirement.parse('leap-client') + + try: + with resource_stream(dist_name, requires_file_name) as stream: + requirements = [line.strip() for line in stream] + except Exception, e: + logger.error("Requirements file not found. %e", (e, )) + + return requirements + + +def check_requirements(): + """ + This function check the dependencies declared in the + requirement(s) file(s) and logs the results. + """ + logger.debug("Checking requirements...") + requirements = get_requirements() + + for package in requirements: + try: + get_distribution(package) + except VersionConflict: + required_package = Requirement.parse(package) + required_version = required_package.specs[0] + required_name = required_package.key + + installed_package = get_distribution(required_name) + installed_version = installed_package.version + installed_location = installed_package.location + + msg = "Error: version not satisfied. " + msg += "Expected %s, installed %s (path: %s)." % ( + required_version, installed_version, installed_location) + + result = "%s ... %s" % (package, msg) + logger.error(result) + except DistributionNotFound: + msg = "Error: package not found!" + result = "%s ... %s" % (package, msg) + logger.error(result) + else: + msg = "OK" + result = "%s ... %s" % (package, msg) + logger.debug(result) + + logger.debug('Done') -- cgit v1.2.3 From 4c726c1531abfe288604eaa4c1d347e85bed81eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Mon, 3 Jun 2013 15:02:41 -0300 Subject: Use Twisted's deferToThread and Deferreds to handle parallel tasks This removes CheckerThread --- src/leap/util/checkerthread.py | 109 ----------------------------------------- 1 file changed, 109 deletions(-) delete mode 100644 src/leap/util/checkerthread.py (limited to 'src/leap/util') diff --git a/src/leap/util/checkerthread.py b/src/leap/util/checkerthread.py deleted file mode 100644 index 02aa333f..00000000 --- a/src/leap/util/checkerthread.py +++ /dev/null @@ -1,109 +0,0 @@ -# -*- coding: utf-8 -*- -# checkerthread.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Checker thread -""" - -import logging - -from PySide import QtCore - -from leap.common.check import leap_assert_type - -logger = logging.getLogger(__name__) - - -class CheckerThread(QtCore.QThread): - """ - Generic checker thread that can perform any type of operation as - long as it returns a boolean value that identifies how the - execution went. - """ - - IDLE_SLEEP_INTERVAL = 1 - - def __init__(self): - QtCore.QThread.__init__(self) - - self._checks = [] - self._checks_lock = QtCore.QMutex() - - self._should_quit = False - self._should_quit_lock = QtCore.QMutex() - - def get_should_quit(self): - """ - Returns whether this thread should quit - - :return: True if the thread should terminate itself, Flase otherwise - :rtype: bool - """ - - QtCore.QMutexLocker(self._should_quit_lock) - return self._should_quit - - def set_should_quit(self): - """ - Sets the should_quit flag to True so that this thread - terminates the first chance it gets - """ - QtCore.QMutexLocker(self._should_quit_lock) - self._should_quit = True - - def start(self): - """ - Starts the thread and resets the should_quit flag - """ - with QtCore.QMutexLocker(self._should_quit_lock): - self._should_quit = False - - QtCore.QThread.start(self) - - def add_checks(self, checks): - """ - Adds a list of checks to the ones being executed - - :param checks: check functions to perform - :type checkes: list - """ - with QtCore.QMutexLocker(self._checks_lock): - self._checks += checks - - def run(self): - """ - Main run loop for this thread. Executes the checks. - """ - shouldContinue = False - while True: - if self.get_should_quit(): - logger.debug("Quitting checker thread") - return - checkSomething = False - with QtCore.QMutexLocker(self._checks_lock): - if len(self._checks) > 0: - check = self._checks.pop(0) - shouldContinue = check() - leap_assert_type(shouldContinue, bool) - checkSomething = True - if not shouldContinue: - logger.debug("Something went wrong with the checks, " - "clearing...") - self._checks = [] - checkSomething = False - if not checkSomething: - self.sleep(self.IDLE_SLEEP_INTERVAL) -- cgit v1.2.3 From e6b055fc2b054c1ab12e2554e9dbe73f47c647c0 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 5 Jun 2013 10:47:38 -0300 Subject: Bugfix str format in checker --- src/leap/util/requirement_checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/util') diff --git a/src/leap/util/requirement_checker.py b/src/leap/util/requirement_checker.py index 3538f122..1d9b9923 100644 --- a/src/leap/util/requirement_checker.py +++ b/src/leap/util/requirement_checker.py @@ -58,7 +58,7 @@ def get_requirements(): with resource_stream(dist_name, requires_file_name) as stream: requirements = [line.strip() for line in stream] except Exception, e: - logger.error("Requirements file not found. %e", (e, )) + logger.error("Requirements file not found. %r" % (e, )) return requirements -- cgit v1.2.3 From 9b2c0d673a1a8a4641508188c87662c9eacfd0ce Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 12 Jun 2013 02:15:20 +0900 Subject: workaround for pyside multiple inheritance problem --- src/leap/util/leap_log_handler.py | 74 ++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 17 deletions(-) (limited to 'src/leap/util') diff --git a/src/leap/util/leap_log_handler.py b/src/leap/util/leap_log_handler.py index 0e598032..3264e05c 100644 --- a/src/leap/util/leap_log_handler.py +++ b/src/leap/util/leap_log_handler.py @@ -14,32 +14,30 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - """ Custom handler for the logger window. """ import logging +from functools import partial from PySide import QtCore - -class LeapLogHandler(logging.Handler, QtCore.QObject): +class LogHandler(logging.Handler): """ - Custom logging handler. It emits Qt signals so it can be plugged to a gui. - Also stores an history of logs that can be fetched after connect to a gui. + This is the custom handler that implements our desired formatting + and also keeps a history of all the logged events. """ - # All dicts returned are of the form - # {'record': LogRecord, 'message': str} - new_log = QtCore.Signal(dict) MESSAGE_KEY = 'message' RECORD_KEY = 'record' - def __init__(self): - logging.Handler.__init__(self) - QtCore.QObject.__init__(self) + # TODO This is going to eat lots of memory after some time. + # Should be pruned at some moment. + _log_history = [] - self._log_history = [] + def __init__(self, qtsignal): + logging.Handler.__init__(self) + self._qtsignal = qtsignal def _set_format(self, logging_level): """ @@ -66,6 +64,7 @@ class LeapLogHandler(logging.Handler, QtCore.QObject): format_attrs = [time, name, level, message] log_format = ' - '.join(format_attrs) formatter = logging.Formatter(log_format) + self.setFormatter(formatter) def emit(self, logRecord): @@ -74,8 +73,6 @@ class LeapLogHandler(logging.Handler, QtCore.QObject): logging module. This method reimplements logging.Handler.emit that is fired in every logged message. - QObject.emit gets in the way on the PySide signal model but we - workarouded that issue. :param logRecord: the record emitted by the logging module. :type logRecord: logging.LogRecord. @@ -83,17 +80,60 @@ class LeapLogHandler(logging.Handler, QtCore.QObject): 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) + + +class HandlerAdapter(object): + """ + New style class that accesses all attributes from the LogHandler. + + Used as a workaround for a problem with multiple inheritance with Pyside + that surfaced under OSX with pyside 1.1.0. + """ + MESSAGE_KEY = 'message' + RECORD_KEY = 'record' + + def __init__(self, qtsignal): + self._handler = LogHandler(qtsignal=qtsignal) + + def setLevel(self, *args, **kwargs): + return self._handler.setLevel(*args, **kwargs) + + def handle(self, *args, **kwargs): + return self._handler.handle(*args, **kwargs) + + @property + def level(self): + return self._handler.level + + +class LeapLogHandler(QtCore.QObject, HandlerAdapter): + """ + Custom logging handler. It emits Qt signals so it can be plugged to a gui. + + Its inner handler also stores an history of logs that can be fetched after + having been connected to a gui. + """ + # All dicts returned are of the form + # {'record': LogRecord, 'message': str} + new_log = QtCore.Signal(dict) + + def __init__(self): + QtCore.QObject.__init__(self) + HandlerAdapter.__init__(self, qtsignal=self.qtsignal) + def qtsignal(self, log_item): # WARNING: the new-style connection does NOT work because PySide # translates the emit method to self.emit, and that collides with # the emit method for logging.Handler # self.new_log.emit(log_item) - QtCore.QObject.emit(self, QtCore.SIGNAL('new_log(PyObject)'), log_item) + QtCore.QObject.emit( + self, + QtCore.SIGNAL('new_log(PyObject)'), log_item) @property def log_history(self): """ Returns the history of the logged messages. """ - return self._log_history + return self._handler._log_history -- cgit v1.2.3 From ba27c14ba84c6869c187bdd09138bfae4424445d Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 13 Jun 2013 01:19:49 +0900 Subject: copy missing updown scripts if missing --- src/leap/util/leap_log_handler.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/leap/util') diff --git a/src/leap/util/leap_log_handler.py b/src/leap/util/leap_log_handler.py index 3264e05c..5b8ae789 100644 --- a/src/leap/util/leap_log_handler.py +++ b/src/leap/util/leap_log_handler.py @@ -18,10 +18,10 @@ Custom handler for the logger window. """ import logging -from functools import partial from PySide import QtCore + class LogHandler(logging.Handler): """ This is the custom handler that implements our desired formatting @@ -36,6 +36,11 @@ class LogHandler(logging.Handler): _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. + """ logging.Handler.__init__(self) self._qtsignal = qtsignal @@ -119,6 +124,10 @@ class LeapLogHandler(QtCore.QObject, HandlerAdapter): new_log = QtCore.Signal(dict) def __init__(self): + """ + LeapLogHandler initialization. + Initializes parent classes. + """ QtCore.QObject.__init__(self) HandlerAdapter.__init__(self, qtsignal=self.qtsignal) -- cgit v1.2.3 From e6e88154f3b274ff97474a26a36dd6453f55de0b Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 14 Jun 2013 12:51:18 -0300 Subject: Bugfix: add logs to history. Closes #2871. --- src/leap/util/leap_log_handler.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/leap/util') 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) -- cgit v1.2.3 From b36fe9cf87bc1917abc0667756f01e6d4609cc4c Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 17 Jun 2013 04:46:06 +0900 Subject: install missing files during linux initialization Closes: #2247, #2761 --- src/leap/util/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/leap/util') diff --git a/src/leap/util/__init__.py b/src/leap/util/__init__.py index 41358d38..5ceaede5 100644 --- a/src/leap/util/__init__.py +++ b/src/leap/util/__init__.py @@ -37,3 +37,13 @@ except ImportError: pass __full_version__ = __appname__ + '/' + str(__version__) + + +def first(things): + """ + Return the head of a collection. + """ + try: + return things[0] + except TypeError: + return None -- cgit v1.2.3 From 440f2036e36985e9536644b28af0e62cea8704eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 21 Jun 2013 15:31:24 -0300 Subject: Disable remembering when not using a proper keyring backend --- src/leap/util/keyring_helpers.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/leap/util/keyring_helpers.py (limited to 'src/leap/util') diff --git a/src/leap/util/keyring_helpers.py b/src/leap/util/keyring_helpers.py new file mode 100644 index 00000000..b3dd0175 --- /dev/null +++ b/src/leap/util/keyring_helpers.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# privilege_policies.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" + +""" + +import keyring + +OBSOLETE_KEYRINGS = [ + keyring.backends.file.EncryptedKeyring, + keyring.backends.file.PlaintextKeyring +] + +def has_keyring(): + """ + + """ + kr = keyring.get_keyring() + return kr is not None and kr.__class__ not in OBSOLETE_KEYRINGS -- cgit v1.2.3 From 3def1e7b086f8048d2b07158ee1fc6c8bd2b506e Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 24 Jun 2013 16:09:59 -0300 Subject: Add PySide tests helper. Look at https://github.com/jasonmccampbell/PySide/blob/master/tests/signals/signal_emission_gui_test.py for an usage example. The original file is: PySide/tests/util/helper/__init__.py --- src/leap/util/pyside_tests_helper.py | 133 +++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/leap/util/pyside_tests_helper.py (limited to 'src/leap/util') diff --git a/src/leap/util/pyside_tests_helper.py b/src/leap/util/pyside_tests_helper.py new file mode 100644 index 00000000..a010934a --- /dev/null +++ b/src/leap/util/pyside_tests_helper.py @@ -0,0 +1,133 @@ + +'''Helper classes and functions''' + +import os +import unittest + +from random import randint + +from PySide.QtCore import QCoreApplication, QTimer + +try: + from PySide.QtGui import QApplication +except ImportError: + has_gui = False +else: + has_gui = True + +def adjust_filename(filename, orig_mod_filename): + dirpath = os.path.dirname(os.path.abspath(orig_mod_filename)) + return os.path.join(dirpath, filename) + +class NoQtGuiError(Exception): + def __init__(self): + Exception.__init__(self, 'No QtGui found') + +class BasicPySlotCase(object): + '''Base class that tests python slots and signal emissions. + + Python slots are defined as any callable passed to QObject.connect(). + ''' + def setUp(self): + self.called = False + + def tearDown(self): + try: + del self.args + except: + pass + + def cb(self, *args): + '''Simple callback with arbitrary arguments. + + The test function must setup the 'args' attribute with a sequence + containing the arguments expected to be received by this slot. + Currently only a single connection is supported. + ''' + if tuple(self.args) == args: + self.called = True + else: + raise ValueError('Invalid arguments for callback') + + +_instance = None +_timed_instance = None + +if has_gui: + class UsesQApplication(unittest.TestCase): + '''Helper class to provide QApplication instances''' + + qapplication = True + + def setUp(self): + '''Creates the QApplication instance''' + + # Simple way of making instance a singleton + super(UsesQApplication, self).setUp() + global _instance + if _instance is None: + _instance = QApplication([]) + + self.app = _instance + + def tearDown(self): + '''Deletes the reference owned by self''' + del self.app + super(UsesQApplication, self).tearDown() + + + class TimedQApplication(unittest.TestCase): + '''Helper class with timed QApplication exec loop''' + + def setUp(self, timeout=100): + '''Setups this Application. + + timeout - timeout in milisseconds''' + global _timed_instance + if _timed_instance is None: + _timed_instance = QApplication([]) + + self.app = _timed_instance + QTimer.singleShot(timeout, self.app.quit) + + def tearDown(self): + '''Delete resources''' + del self.app +else: + class UsesQApplication(unittest.TestCase): + def setUp(self): + raise NoQtGuiError() + class TimedQapplication(unittest.TestCase): + def setUp(self): + raise NoQtGuiError() + + +_core_instance = None + +class UsesQCoreApplication(unittest.TestCase): + '''Helper class for test cases that require an QCoreApplication + Just connect or call self.exit_app_cb. When called, will ask + self.app to exit. + ''' + + def setUp(self): + '''Set up resources''' + + global _core_instance + if _core_instance is None: + _core_instance = QCoreApplication([]) + + self.app = _core_instance + + def tearDown(self): + '''Release resources''' + del self.app + + def exit_app_cb(self): + '''Quits the application''' + self.app.exit(0) + + +def random_string(size=5): + '''Generate random string with the given size''' + return ''.join(map(chr, [randint(33, 126) for x in range(size)])) -- cgit v1.2.3 From 36657c76b4c8511d5938dc6b81fde34c833ab7c7 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 25 Jun 2013 11:37:36 -0300 Subject: Change method for 'dependency injection' in test. --- src/leap/util/leap_log_handler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/leap/util') diff --git a/src/leap/util/leap_log_handler.py b/src/leap/util/leap_log_handler.py index e5bc87e1..271096d3 100644 --- a/src/leap/util/leap_log_handler.py +++ b/src/leap/util/leap_log_handler.py @@ -44,7 +44,7 @@ class LogHandler(logging.Handler): logging.Handler.__init__(self) self._qtsignal = qtsignal - def _set_format(self, logging_level): + def _get_format(self, logging_level): """ Sets the log format depending on the parameter. It uses html and css to set the colors for the logs. @@ -70,7 +70,7 @@ class LogHandler(logging.Handler): log_format = ' - '.join(format_attrs) formatter = logging.Formatter(log_format) - self.setFormatter(formatter) + return formatter def emit(self, logRecord): """ @@ -82,7 +82,7 @@ class LogHandler(logging.Handler): :param logRecord: the record emitted by the logging module. :type logRecord: logging.LogRecord. """ - self._set_format(logRecord.levelname) + self.setFormatter(self._get_format(logRecord.levelname)) log = self.format(logRecord) log_item = {self.RECORD_KEY: logRecord, self.MESSAGE_KEY: log} self._log_history.append(log_item) -- cgit v1.2.3 From 16ea8b5d78c990e75367d405619c55272ecf09e6 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 24 Jun 2013 17:08:34 -0300 Subject: Add test for the LEAP log handler --- src/leap/util/tests/__init__.py | 0 src/leap/util/tests/test_leap_log_handler.py | 118 +++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 src/leap/util/tests/__init__.py create mode 100644 src/leap/util/tests/test_leap_log_handler.py (limited to 'src/leap/util') diff --git a/src/leap/util/tests/__init__.py b/src/leap/util/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/leap/util/tests/test_leap_log_handler.py b/src/leap/util/tests/test_leap_log_handler.py new file mode 100644 index 00000000..ea509ea8 --- /dev/null +++ b/src/leap/util/tests/test_leap_log_handler.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +# test_leap_log_handler.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +tests for leap_log_handler +""" + +import unittest + +import logging + +from leap.util.leap_log_handler import LeapLogHandler +from leap.common.testing.basetest import BaseLeapTest +from leap.util.pyside_tests_helper import BasicPySlotCase + +from mock import Mock + + +class LeapLogHandlerTest(BaseLeapTest, BasicPySlotCase): + """ + LeapLogHandlerTest's tests. + """ + def _callback(self, *args): + """ + Simple callback to track if a signal was emitted. + """ + self.called = True + self.emitted_msg = args[0][LeapLogHandler.MESSAGE_KEY] + + def setUp(self): + BasicPySlotCase.setUp(self) + + # Create the logger + level = logging.DEBUG + self.logger = logging.getLogger(name='test') + self.logger.setLevel(level) + + # Create the handler + self.leap_handler = LeapLogHandler() + self.leap_handler.setLevel(level) + self.logger.addHandler(self.leap_handler) + + def tearDown(self): + BasicPySlotCase.tearDown(self) + try: + self.leap_handler.new_log.disconnect() + except Exception: + pass + + def test_history_starts_empty(self): + self.assertEqual(self.leap_handler.log_history, []) + + def test_one_log_captured(self): + self.logger.debug('test') + self.assertEqual(len(self.leap_handler.log_history), 1) + + def test_history_records_order(self): + self.logger.debug('test 01') + self.logger.debug('test 02') + self.logger.debug('test 03') + + logs = [] + for message in self.leap_handler.log_history: + logs.append(message[LeapLogHandler.RECORD_KEY].msg) + + self.assertIn('test 01', logs) + self.assertIn('test 02', logs) + self.assertIn('test 03', logs) + + def test_history_messages_order(self): + self.logger.debug('test 01') + self.logger.debug('test 02') + self.logger.debug('test 03') + + logs = [] + for message in self.leap_handler.log_history: + logs.append(message[LeapLogHandler.MESSAGE_KEY]) + + self.assertIn('test 01', logs[0]) + self.assertIn('test 02', logs[1]) + self.assertIn('test 03', logs[2]) + + def test_emits_signal(self): + log_format = '%(name)s - %(levelname)s - %(message)s' + formatter = logging.Formatter(log_format) + get_format = Mock(return_value=formatter) + self.leap_handler._handler._get_format = get_format + + self.leap_handler.new_log.connect(self._callback) + self.logger.debug('test') + + expected_log_msg = "test - DEBUG - test" + + # signal emitted + self.assertTrue(self.called) + + # emitted message + self.assertEqual(self.emitted_msg, expected_log_msg) + + # Mock called + self.assertTrue(get_format.called) + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From 77d9434c4a92528e13b008a50dfbcb5f2f1456a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Mon, 24 Jun 2013 22:45:04 -0300 Subject: pep8 fixes --- src/leap/util/keyring_helpers.py | 1 + src/leap/util/pyside_tests_helper.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/leap/util') diff --git a/src/leap/util/keyring_helpers.py b/src/leap/util/keyring_helpers.py index b3dd0175..b815d385 100644 --- a/src/leap/util/keyring_helpers.py +++ b/src/leap/util/keyring_helpers.py @@ -26,6 +26,7 @@ OBSOLETE_KEYRINGS = [ keyring.backends.file.PlaintextKeyring ] + def has_keyring(): """ diff --git a/src/leap/util/pyside_tests_helper.py b/src/leap/util/pyside_tests_helper.py index a010934a..5c0eb8d6 100644 --- a/src/leap/util/pyside_tests_helper.py +++ b/src/leap/util/pyside_tests_helper.py @@ -15,14 +15,17 @@ except ImportError: else: has_gui = True + def adjust_filename(filename, orig_mod_filename): dirpath = os.path.dirname(os.path.abspath(orig_mod_filename)) return os.path.join(dirpath, filename) + class NoQtGuiError(Exception): def __init__(self): Exception.__init__(self, 'No QtGui found') + class BasicPySlotCase(object): '''Base class that tests python slots and signal emissions. @@ -75,7 +78,6 @@ if has_gui: del self.app super(UsesQApplication, self).tearDown() - class TimedQApplication(unittest.TestCase): '''Helper class with timed QApplication exec loop''' @@ -97,13 +99,14 @@ else: class UsesQApplication(unittest.TestCase): def setUp(self): raise NoQtGuiError() + class TimedQapplication(unittest.TestCase): def setUp(self): raise NoQtGuiError() - _core_instance = None + class UsesQCoreApplication(unittest.TestCase): '''Helper class for test cases that require an QCoreApplication Just connect or call self.exit_app_cb. When called, will ask -- cgit v1.2.3