summaryrefslogtreecommitdiff
path: root/src/leap/mx/util
diff options
context:
space:
mode:
authorTomas Touceda <chiiph@leap.se>2013-04-24 16:47:07 -0300
committerTomas Touceda <chiiph@leap.se>2013-05-07 14:01:10 -0300
commitb1c2e24fb08f69a10a4d91a84ec0e31d91991190 (patch)
treeef5f2127f85b737fe035b190f6b0a7db83d7c142 /src/leap/mx/util
parentb322a390af83addcf9768ae59d31c31a3229b49d (diff)
Improve the rest and start everything from start_mx
Diffstat (limited to 'src/leap/mx/util')
-rw-r--r--src/leap/mx/util/config.py221
-rw-r--r--src/leap/mx/util/log.py143
-rw-r--r--src/leap/mx/util/net.py126
-rw-r--r--src/leap/mx/util/storage.py42
4 files changed, 0 insertions, 532 deletions
diff --git a/src/leap/mx/util/config.py b/src/leap/mx/util/config.py
deleted file mode 100644
index f655ca9..0000000
--- a/src/leap/mx/util/config.py
+++ /dev/null
@@ -1,221 +0,0 @@
-#! -*- encoding: utf-8 -*-
-"""
-Config file utilities.
-
-This module has an :attr:`config_filename`, which can be used to set the
-filename outside of function calls:
-
- >>> from leap.mx.util import config
- >>> config.config_filename = "blahblah.yaml"
-
-If not set anywhere, it will default to using the top level repository
-directory, i.e. "/.../leap_mx/leap_mx.conf", and will create that file with
-the default settings if it does not exist.
-
-The config file can be loaded/created with :func:`config.loadConfig`:
-
- >>> config.loadConfig()
-
-Once the config file is loaded, this module presents a highly object-oriented
-interface, so that sections taken from the config file become attribute of
-this module, and the name of their respective settings become attributes of
-the section names. Like this:
-
- >>> print config.basic.postfix_port
- 465
-
-@authors: Isis Lovecruft, <isis@leap.se> 0x2cdb8b35
-@version: 0.0.1
-@license: see included LICENSE file
-"""
-
-from os import path as ospath
-
-import sys
-import yaml
-
-from leap.mx.util import version, storage
-from leap.mx.exceptions import MissingConfig, UnsupportedOS
-
-
-filename = None
-config_version = None
-basic = storage.Storage()
-couch = storage.Storage()
-advanced = storage.Storage()
-
-PLATFORMS = {'LINUX': sys.platform.startswith("linux"),
- 'OPENBSD': sys.platform.startswith("openbsd"),
- 'FREEBSD': sys.platform.startswith("freebsd"),
- 'NETBSD': sys.platform.startswith("netbsd"),
- 'DARWIN': sys.platform.startswith("darwin"),
- 'SOLARIS': sys.platform.startswith("sunos"),
- 'WINDOWS': sys.platform.startswith("win32")}
-
-def getClientPlatform(platform_name=None):
- """
- Determine the client's operating system platform. Optionally, if
- :param:`platform_name` is given, check that this is indeed the platform
- we're operating on.
-
- @param platform_name: A string, upper-, lower-, or mixed case, of one
- of the keys in the :attr:`leap.util.version.PLATFORMS`
- dictionary. E.g. 'Linux' or 'OPENBSD', etc.
- @returns: A string specifying the platform name, and the boolean test
- used to determine it.
- """
- for name, test in PLATFORMS.items():
- if not platform_name or platform_name.upper() == name:
- if test:
- return name, test
-
-def _create_config_file(conffile):
- """
- Create the config file if it doesn't exist.
-
- @param conffile: The full path to the config file to write to.
- """
- with open(conffile, 'w+') as conf:
- conf.write("""
-#
-# mx.conf
-# =======
-# Configurable options for the leap_mx encrypting mail exchange.
-#
-# This file follows YAML markup format: http://yaml.org/spec/1.2/spec.html
-# Keep in mind that indentation matters.
-#
-
-basic:
- # Whether or not to log to file:
- enable_logfile: True
- # The name of the logfile:
- logfile: mx.log
- # Where is the spoolfile of messages to encrypt?:
- spoolfile: /var/mail/encrypt_me
-couch:
- # The couch username for authentication to a CouchDB instance:
- user: admin
- # The couch username's password:
- passwd: passwd
- # The CouchDB hostname or IP address to connect to:
- host: couchdb.example.com
- # The CouchDB port to connect to:
- port: 7001
-advanced:
- # Which port on localhost should postfix send check_recipient queries to?:
- check_recipient_access_port: 1347
- # Which port on localhost should postfix ask for UUIDs?:
- virtual_alias_map_port: 1348
- # Enable debugging output in the logger:
- debug: True
- # Print enough things really fast to make you look super 1337:
- noisy: False
-config_version: 0.0.2
-
-""")
- conf.flush()
- assert ospath.isfile(conffile), "Config file %s not created!" % conffile
-
-def _get_config_location(config_filename=None,
- use_dot_config_directory=False):
- """
- Get the full path and filename of the config file.
- """
- platform = getClientPlatform()[0]
-
- ## If not given, default to the application's name + '.conf'
- if not config_filename:
- if not filename:
- config_filename = "mx.conf"
- else:
- config_filename = filename
-
- ## Oh hell, it could be said only to beguile:
- ## That windoze users are capable of editing a .conf file.
- ## Also, what maddened wingnut would be so fool
- ## To run a mail exchange on a windoze nodule?
- ## I'm ignoring these loons for now. And pardon if I seem jaded,
- ## But srsly, this and that solaris sh*t should be deprecated.
- if not platform.endswith('LINUX') and not platform.endswith('BSD'):
- raise UnsupportedOS("Sorry, your operating system isn't supported.")
-
- where = None
- if use_dot_config_directory:
- ## xxx only install/import this in *nix
- from xdg import BaseDirectory
-
- dot_config_dirs = BaseDirectory.xdg_config_dirs
- for dir in dot_config_dirs:
- our_dir = ospath.join(dir, package_name)
- if ospath.isdir(our_dir):
- if config_filename in os.listdir(our_dir):
- where = ospath.abspath(our_dir)
- ## Use repo dir instead:
- if not where:
- where = version.getRepoDir()
-
- conffile = ospath.join(where, config_filename)
- try:
- with open(conffile) as cf: pass
- except IOError:
- _create_config_file(conffile)
- finally:
- return conffile
-
-def loadConfig(file=None):
- """
- Some of this is taken from OONI config code for now, and so this should be
- refacotored, along with the leap_client config code, so that we have
- similarly structured config files. It is perhaps desirable to also use
- soledad as a backend for remote setup and maintainance, and thus this code
- will need to hook into u1db (and potentially "pysqlcipher").
-
- Excuse the yaml for now, I just wanted something that works.
-
- @param file: (optional) If provided, use this filename.
- """
- if not file:
- file = _get_config_location()
-
- if ospath.isfile(file):
- with open(file, 'a+') as conf:
- config_contents = '\n'.join(conf.readlines())
- cfg = yaml.safe_load(config_contents)
-
- ## These become objects with their keys loaded as attributes:
- ##
- ## from leap.util import config
- ## config.basic.foo = bar
- ##
- try:
- for k, v in cfg['basic'].items():
- basic[k] = v
- except (AttributeError, KeyError): pass
-
- try:
- for k, v in cfg['advanced'].items():
- advanced[k] = v
- except (AttributeError, KeyError): pass
-
- try:
- for k, v in cfg['couch'].items():
- couch[k] = v
- except (AttributeError, KeyError): pass
-
- if 'config_version' in cfg:
- config_version = cfg['config_version']
- else:
- config_version = 'unknown'
-
- return basic, couch, advanced, config_version
- else:
- raise MissingConfig("Could not load config file.")
-
-
-## This is the name of the config file to use:
-## If not set, it defaults to 'leap_mx/leap_mx.conf'
-if not filename:
- filename = _get_config_location()
-else:
- filename = _get_config_location(config_filename=filename)
diff --git a/src/leap/mx/util/log.py b/src/leap/mx/util/log.py
deleted file mode 100644
index f31684d..0000000
--- a/src/leap/mx/util/log.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# -*- encoding: utf-8 -*-
-'''
-log.py
-------
-Logging for leap_mx.
-
-@authors: Isis Agora Lovecruft, <isis@leap.se> 0x2cdb8b35
-@licence: see included LICENSE file
-@copyright: 2013 Isis Agora Lovecruft
-'''
-
-from datetime import datetime
-from functools import wraps
-
-import logging
-import os
-import sys
-import time
-import traceback
-
-from twisted.python import log as txlog
-from twisted.python import util as txutil
-from twisted.python import logfile as txlogfile
-from twisted.python.failure import Failure
-
-from leap.mx.util import version, config
-
-
-class InvalidTimestampFormat(Exception):
- pass
-
-class UnprefixedLogfile(txlog.FileLogObserver):
- """Logfile with plain messages, without timestamp prefixes."""
- def emit(self, eventDict):
- text = txlog.textFromEventDict(eventDict)
- if text is None:
- return
-
- txutil.untilConcludes(self.write, "%s\n" % text)
- txutil.untilConcludes(self.flush)
-
-
-def utcDateNow():
- """The current date for UTC time."""
- return datetime.utcnow()
-
-def utcTimeNow():
- """Seconds since epoch in UTC time, as type float."""
- return time.mktime(time.gmtime())
-
-def dateToTime(date):
- """Convert datetime to seconds since epoch."""
- return time.mktime(date.timetuple())
-
-def prettyDateNow():
- """Pretty string for the local time."""
- return datetime.now().ctime()
-
-def utcPrettyDateNow():
- """Pretty string for UTC."""
- return datetime.utcnow().ctime()
-
-def timeToPrettyDate(time_val):
- """Convert seconds since epoch to date."""
- return time.ctime(time_val)
-
-def start(logfilename=None, logfiledir=None):
- """
- Start logging to stdout, and optionally to a logfile as well.
-
- @param logfile: The full path of the filename to store logs in.
- """
- txlog.startLoggingWithObserver(UnprefixedLogfile(sys.stdout).emit)
-
- if logfilename and logfiledir:
- if not os.path.isdir(logfiledir):
- os.makedirs(logfiledir)
- daily_logfile = txlogfile.DailyLogFile(logfilename, logfiledir)
- txlog.addObserver(txlog.FileLogObserver(daily_logfile).emit)
-
- txlog.msg("Starting %s, version %s, on %s UTC" % (version.getPackageName(),
- version.getVersion(),
- utcPrettyDateNow()))
- txlog.msg("Authors: %s" % version.getAuthors())
-
-def msg(msg, *arg, **kwarg):
- """Log a message at the INFO level."""
- print "[*] %s" % msg
-
-def debug(msg, *arg, **kwarg):
- """Log a message at the DEBUG level."""
- if config.advanced.debug:
- print "[d] %s" % msg
-
-def warn(msg, *arg, **kwarg):
- """Log a message at the WARN level."""
- if config.basic.show_warnings:
- txlog.logging.captureWarnings('true')
- print "[#] %s" % msg
-
-def err(msg, *arg, **kwarg):
- """Log a message at the ERROR level."""
- print "[!] %s" % msg
-
-def fail(*failure):
- """Log a message at the CRITICAL level."""
- logging.critical(failure)
- ## xxx should we take steps to exit here?
-
-def exception(error):
- """
- Catch an exception and print only the error message, then continue normal
- program execution.
-
- @param error: Can be error messages printed to stdout and to the
- logfile, or can be a twisted.python.failure.Failure instance.
- """
- if isinstance(error, Failure):
- error.printTraceback()
- else:
- exc_type, exc_value, exc_traceback = sys.exc_info()
- traceback.print_exception(exc_type, exc_value, exc_traceback)
-
-def catch(func):
- """
- Quick wrapper to add around test methods for debugging purposes,
- catches the given Exception. Use like so:
-
- >>> @log.catch
- def foo(bar):
- if bar == 'baz':
- raise Exception("catch me no matter what I am")
- >>> foo("baz")
- [!] catch me no matter what I am
-
- """
- @wraps(func)
- def _catch(*args, **kwargs):
- try:
- func(*args, **kwargs)
- except Exception, exc:
- exception(exc)
- return _catch
diff --git a/src/leap/mx/util/net.py b/src/leap/mx/util/net.py
deleted file mode 100644
index 64dbc90..0000000
--- a/src/leap/mx/util/net.py
+++ /dev/null
@@ -1,126 +0,0 @@
-#!/usr/bin/env python
-# -*- encoding: utf-8 -*-
-'''
-net.py
--------
-Utilities for networking.
-
-@authors: Isis Agora Lovecruft, <isis@leap.se> 0x2cdb8b35
-@license: see included LICENSE file
-@copyright: 2013 Isis Agora Lovecruft
-'''
-
-import ipaddr
-import sys
-import socket
-
-from random import randint
-
-from leap.mx.util import log
-
-
-PLATFORMS = {'LINUX': sys.platform.startswith("linux"),
- 'OPENBSD': sys.platform.startswith("openbsd"),
- 'FREEBSD': sys.platform.startswith("freebsd"),
- 'NETBSD': sys.platform.startswith("netbsd"),
- 'DARWIN': sys.platform.startswith("darwin"),
- 'SOLARIS': sys.platform.startswith("sunos"),
- 'WINDOWS': sys.platform.startswith("win32")}
-
-
-class UnsupportedPlatform(Exception):
- """Support for this platform is not currently available."""
-
-class IfaceError(Exception):
- """Could not find default network interface."""
-
-class PermissionsError(SystemExit):
- """This test requires admin or root privileges to run. Exiting..."""
-
-
-def checkIPaddress(addr):
- """
- Check that a given string is a valid IPv4 or IPv6 address.
-
- @param addr: Any string defining an IP address, i.e. '1.2.3.4' or '::1'.
- @returns: True if :param:`addr` defines a valid IPAddress, else False.
- """
- import ipaddr
-
- try:
- check = ipaddr.IPAddress(addr)
- except ValueError, ve:
- log.warn(ve.message)
- return False
- else:
- return True
-
-def getClientPlatform(platform_name=None):
- for name, test in PLATFORMS.items():
- if not platform_name or platform_name.upper() == name:
- if test:
- return name, test
-
-def getPosixIfaces():
- from twisted.internet.test import _posixifaces
- log.msg("Attempting to discover network interfaces...")
- ifaces = _posixifaces._interfaces()
- return ifaces
-
-def getWindowsIfaces():
- from twisted.internet.test import _win32ifaces
- log.msg("Attempting to discover network interfaces...")
- ifaces = _win32ifaces._interfaces()
- return ifaces
-
-def getIfaces(platform_name=None):
- client, test = getClientPlatform(platform_name)
- if client:
- if client == ('LINUX' or 'DARWIN') or client[-3:] == 'BSD':
- return getPosixIfaces()
- elif client == 'WINDOWS':
- return getWindowsIfaces()
- ## XXX fixme figure out how to get iface for Solaris
- else:
- raise UnsupportedPlatform
- else:
- raise UnsupportedPlatform
-
-def getRandomUnusedPort(addr=None):
- free = False
- while not free:
- port = randint(1024, 65535)
- s = socket.socket()
- try:
- s.bind((addr, port))
- free = True
- except:
- pass
- s.close()
- return port
-
-def getNonLoopbackIfaces(platform_name=None):
- try:
- ifaces = getIfaces(platform_name)
- except UnsupportedPlatform, up:
- log.err(up)
-
- if not ifaces:
- log.msg("Unable to discover network interfaces...")
- return None
- else:
- found = [{i[0]: i[2]} for i in ifaces if i[0] != 'lo']
- log.debug("Found non-loopback interfaces: %s" % found)
- for iface in ifaces:
- try:
- interface = checkInterfaces(found)
- except IfaceError, ie:
- log.err(ie)
- return None
- else:
- return interfaces
-
-
-def getLocalAddress():
- default_iface = getDefaultIface()
- return default_iface.ipaddr
diff --git a/src/leap/mx/util/storage.py b/src/leap/mx/util/storage.py
deleted file mode 100644
index c4c797a..0000000
--- a/src/leap/mx/util/storage.py
+++ /dev/null
@@ -1,42 +0,0 @@
-
-class Storage(dict):
- """
- A Storage object is like a dictionary except `obj.foo` can be used
- in addition to `obj['foo']`.
-
- >>> o = Storage(a=1)
- >>> o.a
- 1
- >>> o['a']
- 1
- >>> o.a = 2
- >>> o['a']
- 2
- >>> del o.a
- >>> o.a
- None
- """
- def __getattr__(self, key):
- try:
- return self[key]
- except KeyError, k:
- return None
-
- def __setattr__(self, key, value):
- self[key] = value
-
- def __delattr__(self, key):
- try:
- del self[key]
- except KeyError, k:
- raise AttributeError, k
-
- def __repr__(self):
- return '<Storage ' + dict.__repr__(self) + '>'
-
- def __getstate__(self):
- return dict(self)
-
- def __setstate__(self, value):
- for (k, v) in value.items():
- self[k] = v