diff --git a/bin/CryptoBoxRootActions b/bin/CryptoBoxRootActions index 08683a8..e668f3b 100755 --- a/bin/CryptoBoxRootActions +++ b/bin/CryptoBoxRootActions @@ -31,6 +31,8 @@ Syntax: this script will always return with an exitcode 0 (true), if "check" is the only argument """ +__revision__ = "$Id" + import os import sys import subprocess diff --git a/bin/CryptoBoxWebserver b/bin/CryptoBoxWebserver index 5160253..e01559e 100755 --- a/bin/CryptoBoxWebserver +++ b/bin/CryptoBoxWebserver @@ -24,16 +24,42 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" import os, sys import cryptobox.web.sites from cryptobox.core.exceptions import * from optparse import OptionParser +## check python version +(ver_major, ver_minor, ver_sub, ver_desc, ver_subsub) = sys.version_info +if (ver_major < 2) or ((ver_major == 2) and (ver_minor < 4)): + sys.stderr.write("You need a python version >= 2.4\n") + sys.stderr.write("Current version is: %s\n" % sys.version) + sys.exit(1) + +## check cherrypy dependency try: import cherrypy except: - print "Could not import the cherrypy module! Try 'apt-get install python-cherrypy'." + sys.stderr.write("Could not import the cherrypy module!\n") + sys.stderr.write("Try 'apt-get install python-cherrypy'.\n") + sys.exit(1) + +## check clearsilver dependency +try: + import neo_cgi, neo_util +except: + sys.stderr.write("Could not import the clearsilver module!\n") + sys.stderr.write("Try 'apt-get install python-clearsilver'.\n") + sys.exit(1) + +## check configobj dependency +try: + import configobj, validate +except: + sys.stderr.write("Could not import the configobj or validate module!\n") + sys.stderr.write("Try 'apt-get install python-configobj'.\n") sys.exit(1) @@ -129,17 +155,22 @@ def close_open_files(): def write_pid_file(pid_file): + if os.path.exists(pid_file): + sys.stderr.write( + "Warning: pid file (%s) already exists - overwriting ...\n" % pid_file) try: pidf = open(pid_file,"w") pidf.write(str(os.getpid())) pidf.close() except (IOError, OSError), errMsg: - sys.stderr.write("Warning: failed to write pid file (%s): %s\n" % (pid_file, errMsg)) + sys.stderr.write( + "Warning: failed to write pid file (%s): %s\n" % (pid_file, errMsg)) ## it is just a warning - no need to break def parseOptions(): - version = "%prog" + cryptobox.core.main.VERSION + import cryptobox + version = "%prog" + cryptobox.__version__ parser = OptionParser(version=version) parser.set_defaults(conffile="/etc/cryptobox-server/cryptobox.conf", pidfile="/var/run/cryptobox-server/webserver.pid", diff --git a/bin/do_pylint.sh b/bin/do_pylint.sh new file mode 100755 index 0000000..b53f984 --- /dev/null +++ b/bin/do_pylint.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# +# set some environmental variables for pylint +# + +PROJ_DIR=$(dirname "$0")/.. +PROJ_DIR=$(cd "$PROJ_DIR"; pwd) + +PYLINTRC=$PROJ_DIR/src/pylintrc +PYTHONPATH=$PROJ_DIR/src + +function check_for_filename() +{ + # maybe the argument is a file instead of a module name + if echo "$1" | grep -q "\.py$" && test -e "$1" + then local FILE_DIR=$(dirname "$1") + local MODULE=$(basename "${1%.py}") + ARGS="${ARGS} ${MODULE}" + PYTHONPATH="${PYTHONPATH}:${FILE_DIR}" + else ARGS="${ARGS} ${1}" + fi +} + +while test $# -gt 0 + do check_for_filename "$1" + shift + done + +export PYTHONPATH +export PYLINTRC + +pylint $ARGS + diff --git a/debian/cryptobox-server.init b/debian/cryptobox-server.init index 2b2aead..ddd10bf 100644 --- a/debian/cryptobox-server.init +++ b/debian/cryptobox-server.init @@ -16,7 +16,6 @@ # Short-Description: start CryptoBox webserver ### END INIT INFO - # read the default setting file, if it exists [ -r /etc/default/cryptobox-server ] && source /etc/default/cryptobox-server @@ -33,6 +32,7 @@ CONF_FILE=/etc/cryptobox-server/cryptobox.conf [ "$NO_START" = "1" ] && exit 0 DAEMON=/usr/sbin/CryptoBoxWebserver +PYTHON_EXEC=/usr/bin/python PIDFILE=/var/run/cryptobox-server/webserver.pid DESC="CryptoBox Daemon (webinterface)" OPTIONS="-B --pidfile=$PIDFILE --config=$CONF_FILE --logfile=$LOGFILE --host=$HOST --port=$PORT $SERVER_OPTS" @@ -45,11 +45,7 @@ test -e "$DAEMON" || exit 0 case "$1" in start ) - # TODO: mount config dir - # TODO: create certificate - # TODO: run stunnel - # the lines above should go into the live-cd scripts - ## create the directory of the pid file if necessary + # create the directory of the pid file if necessary PIDDIR=$(dirname "$PIDFILE") if [ -d "$PIDDIR" ] then mkdir -p "$PIDDIR" @@ -60,14 +56,21 @@ case "$1" in if start-stop-daemon \ --chuid $RUNAS: --quiet --start \ --user $RUNAS --pidfile "$PIDFILE" \ - --exec /usr/bin/python --startas "$DAEMON" -- $OPTIONS + --startas "$PYTHON_EXEC" -- "$DAEMON" $OPTIONS then log_end_msg 0 else log_end_msg 1 fi ;; stop ) log_daemon_msg "Stopping cryptobox webserver" "$DESC" - if start-stop-daemon --quiet --stop \ + # if there is no pid file for some reason, then we try to find the process + if test ! -e "$PIDFILE" + then if start-stop-daemon --quiet --stop --user "$RUNAS" --exec "$PYTHON_EXEC" + then log_end_msg 0 + else log_end_msg 1 + fi + # there is a pid file - great! + elif start-stop-daemon --quiet --stop \ --pidfile "$PIDFILE" \ --user "$RUNAS" then test -e "$PIDFILE" && rm "$PIDFILE" diff --git a/plugins/date/date.py b/plugins/date/date.py index 17d895a..6206551 100644 --- a/plugins/date/date.py +++ b/plugins/date/date.py @@ -18,29 +18,39 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""Change date and time. +""" + +__revision__ = "$Id" + import cryptobox.plugins.base class date(cryptobox.plugins.base.CryptoBoxPlugin): + """The date feature of the CryptoBox. + """ - pluginCapabilities = [ "system" ] - pluginVisibility = [ "preferences" ] - requestAuth = False + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = False rank = 10 - def doAction(self, store=None, year=0, month=0, day=0, hour=0, minute=0): + def do_action(self, store=None, year=0, month=0, day=0, hour=0, minute=0): + """The action handler. + """ import datetime if store: try: year, month, day = int(year), int(month), int(day) hour, minute = int(hour), int(minute) - new_date = datetime.datetime(year, month, day, hour, minute) + ## check if the values are valid + datetime.datetime(year, month, day, hour, minute) except ValueError: self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" - self.__prepareFormData() + self.__prepare_form_data() return "form_date" date = "%02d%02d%02d%02d%d" % (month, day, hour, minute, year) - if self.__setDate(date): + if self.__set_date(date): self.cbox.log.info("changed date to: %s" % date) self.hdf["Data.Success"] = "Plugins.date.DateChanged" return None @@ -48,33 +58,42 @@ class date(cryptobox.plugins.base.CryptoBoxPlugin): ## a failure should usually be an invalid date (we do not check it really) self.cbox.log.info("failed to set date: %s" % date) self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" - self.__prepareFormData() + self.__prepare_form_data() return "form_date" else: - self.__prepareFormData() + self.__prepare_form_data() return "form_date" - def getStatus(self): - now = self.__getCurrentDate() - return "%d/%d/%d/%d/%d/%d" % (now.year, now.month, now.day, now.hour, now.minute, now.second) + def get_status(self): + """Retrieve the status of the feature. + """ + now = self.__get_current_date() + return "%d/%d/%d/%d/%d/%d" % \ + (now.year, now.month, now.day, now.hour, now.minute, now.second) - def __prepareFormData(self): - date = self.__getCurrentDate() - self.hdf[self.hdf_prefix + "year"] = date.year - self.hdf[self.hdf_prefix + "month"] = date.month - self.hdf[self.hdf_prefix + "day"] = date.day - self.hdf[self.hdf_prefix + "hour"] = date.hour - self.hdf[self.hdf_prefix + "minute"] = date.minute + def __prepare_form_data(self): + """Set some hdf values. + """ + cur_date = self.__get_current_date() + self.hdf[self.hdf_prefix + "year"] = cur_date.year + self.hdf[self.hdf_prefix + "month"] = cur_date.month + self.hdf[self.hdf_prefix + "day"] = cur_date.day + self.hdf[self.hdf_prefix + "hour"] = cur_date.hour + self.hdf[self.hdf_prefix + "minute"] = cur_date.minute - def __getCurrentDate(self): + def __get_current_date(self): + """Retrieve the current date and time. + """ import datetime - return datetime.datetime(2000,1,1).now() + return datetime.datetime(2000, 1, 1).now() - def __setDate(self, date): + def __set_date(self, date): + """Set a new date and time. + """ import subprocess import os proc = subprocess.Popen( @@ -83,7 +102,7 @@ class date(cryptobox.plugins.base.CryptoBoxPlugin): self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "plugin", - os.path.join(self.pluginDir, "root_action.py"), + os.path.join(self.plugin_dir, "root_action.py"), date]) proc.wait() return proc.returncode == 0 diff --git a/plugins/date/root_action.py b/plugins/date/root_action.py index d83d27f..a0c2846 100755 --- a/plugins/date/root_action.py +++ b/plugins/date/root_action.py @@ -19,6 +19,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + ## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script PLUGIN_TYPE = "cryptobox" diff --git a/plugins/date/unittests.py b/plugins/date/unittests.py index ac9ea57..a09fa1a 100644 --- a/plugins/date/unittests.py +++ b/plugins/date/unittests.py @@ -18,18 +18,20 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_get_date(self): """retrieve the current date""" - date = self._getCurrentDate() + date = self._get_current_date() def test_change_date(self): """set the date back and forth""" - now = self._getCurrentDate() + now = self._get_current_date() ## copy current time new_date = dict(now) ## move three minutes forward (more is not nice because of screensavers) @@ -38,10 +40,10 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): new_date["hour"] = now["hour"] + ((now["minute"] + 3) / 60) ## move forward ... self._setDate(new_date) - self.assertEquals(new_date, self._getCurrentDate()) + self.assertEquals(new_date, self._get_current_date()) ## ... and backward self._setDate(now) - self.assertEquals(now, self._getCurrentDate()) + self.assertEquals(now, self._get_current_date()) def test_try_broken_date(self): @@ -54,8 +56,8 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): self.cmd.find("invalid value for date") - def _getCurrentDate(self): - date_url = self.URL + "date" + def _get_current_date(self): + date_url = self.url + "date" self.register_auth(date_url) self.cmd.go(date_url) self.cmd.find("Data.Status.Plugins.date=([0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+)$", "m") @@ -73,7 +75,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def _setDate(self, date): """for now we have to use this function instead of the one below""" - date_url = self.URL + "date?weblang=en&store=1&year=%s&month=%s&day=%s&hour=%s&minute=%s"\ + date_url = self.url + "date?weblang=en&store=1&year=%s&month=%s&day=%s&hour=%s&minute=%s"\ % (str(date["year"]), str(date["month"]), str(date["day"]), str(date["hour"]), str(date["minute"])) self.register_auth(date_url) @@ -83,7 +85,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def _setDateBroken(self, date): """this should work, but the parsing of twill seems to be broken as soon as the twill bug is fixed, we should use this function""" - date_url = self.URL + "date" + date_url = self.url + "date" self.register_auth(date_url) self.cmd.go(date_url) self.cmd.formvalue("set_date", "year", str(date["year"])) diff --git a/plugins/disks/disks.py b/plugins/disks/disks.py index a9f5c92..f7e4b06 100644 --- a/plugins/disks/disks.py +++ b/plugins/disks/disks.py @@ -18,21 +18,31 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""The disks feature of the CryptoBox. +""" + +__revision__ = "$Id" + import cryptobox.plugins.base class disks(cryptobox.plugins.base.CryptoBoxPlugin): + """The disk feature of the CryptoBox. + """ - pluginCapabilities = [ "system" ] - pluginVisibility = [ "menu" ] - requestAuth = False + plugin_capabilities = [ "system" ] + plugin_visibility = [ "menu" ] + request_auth = False rank = 10 - def doAction(self): - self.cbox.reReadContainerList() + def do_action(self): + """The action handler. + """ + self.cbox.reread_container_list() return "disks" - def getStatus(self): - return ":".join([e.getDevice() for e in self.cbox.getContainerList()]) - + def get_status(self): + """Retrieve the current status of the feature. + """ + return ":".join([e.get_device() for e in self.cbox.get_container_list()]) diff --git a/plugins/disks/unittests.py b/plugins/disks/unittests.py index 56316a1..d8aa0d9 100644 --- a/plugins/disks/unittests.py +++ b/plugins/disks/unittests.py @@ -18,24 +18,26 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): '''display all devices''' - self.register_auth(self.URL) - self.cmd.go(self.URL + "disks?weblang=en") + self.register_auth(self.url) + self.cmd.go(self.url + "disks?weblang=en") self.cmd.find("Available disks") def test_is_device_in_list(self): """check if the device-under-test is in the device list""" - self.register_auth(self.URL) - self.cmd.go(self.URL + "disks?weblang=en") + self.register_auth(self.url) + self.cmd.go(self.url + "disks?weblang=en") self.cmd.find("Available disks") self.cmd.find(u'Data.Status.Plugins.disks=(.*)$', "m") devices = self.locals["__match__"].split(":") self.assertTrue(len(devices)>0) - self.assertTrue(("/dev/%s1" % self.device in devices) or ("/dev/%s2" % self.device in devices)) + self.assertTrue("/dev/%s" % self.device in devices) diff --git a/plugins/help/help.py b/plugins/help/help.py index 19e1e68..6b4ce9b 100644 --- a/plugins/help/help.py +++ b/plugins/help/help.py @@ -18,26 +18,34 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""The help feature of the CryptoBox. +""" + +__revision__ = "$Id" + import cryptobox.plugins.base class help(cryptobox.plugins.base.CryptoBoxPlugin): + """The help feature of the CryptoBox. + """ - pluginCapabilities = [ "system" ] - pluginVisibility = [ "menu" ] - requestAuth = False + plugin_capabilities = [ "system" ] + plugin_visibility = [ "menu" ] + request_auth = False rank = 80 default_lang = 'en' default_page = "CryptoBoxUser" - def doAction(self, page=""): + def do_action(self, page=""): '''prints the offline wikipage ''' - import re,os + import re, os ## check for invalid characters and if the page exists in the default language if page and \ not re.search(u'\W', page) and \ - os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], self.default_lang, page + '.html')): + os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], + self.default_lang, page + '.html')): ## everything is ok pass else: @@ -49,20 +57,23 @@ class help(cryptobox.plugins.base.CryptoBoxPlugin): ## store the name of the page self.hdf[self.hdf_prefix + "Page"] = page ## choose the right language - for l in self.site.langOrder: - if os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], l, page + '.html')): - lang = l + for lang in self.site.lang_order: + if os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], + lang, page + '.html')): + doc_lang = lang break else: - lang = self.default_lang - self.hdf[self.hdf_prefix + "Language"] = lang + doc_lang = self.default_lang + self.hdf[self.hdf_prefix + "Language"] = doc_lang ## store the current setting for a later "getStatus" call - self.current_lang = lang + self.current_lang = doc_lang self.current_page = page return "doc" - def getStatus(self): + def get_status(self): + """Retrieve the current status of the feature. + """ return "%s:%s" % (self.current_lang, self.current_page) diff --git a/plugins/help/unittests.py b/plugins/help/unittests.py index 94fa4cc..0d9b46c 100644 --- a/plugins/help/unittests.py +++ b/plugins/help/unittests.py @@ -18,6 +18,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass from twill.errors import * @@ -27,7 +29,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): '''help pages should be available in different languages''' ## check english help pages - self.cmd.go(self.URL + "help?weblang=en") + self.cmd.go(self.url + "help?weblang=en") self.cmd.find("Table of Contents") self.cmd.find("Getting started") (lang,page) = self._getHelpStatus() @@ -35,7 +37,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): self.assertTrue(page == "CryptoBoxUser") ## check german help pages - self.cmd.go(self.URL + "help?weblang=de") + self.cmd.go(self.url + "help?weblang=de") self.cmd.find("Table of Contents") self.cmd.find("Wie geht es los") (lang,page) = self._getHelpStatus() @@ -43,7 +45,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): self.assertTrue(page == "CryptoBoxUser") ## check slovene help pages - self.cmd.go(self.URL + "help?weblang=sl") + self.cmd.go(self.url + "help?weblang=sl") self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") ## add a slovene text here, as soon as the help is translated (lang,page) = self._getHelpStatus() @@ -52,7 +54,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): self.assertTrue(page == "CryptoBoxUser") ## check french help pages - self.cmd.go(self.URL + "help?weblang=fr") + self.cmd.go(self.url + "help?weblang=fr") self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") ## add a french text here, as soon as the help is translated (lang,page) = self._getHelpStatus() @@ -61,7 +63,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): self.assertTrue(page == "CryptoBoxUser") ## test a random language - it should fall back to english - self.cmd.go(self.URL + "help?weblang=foobar") + self.cmd.go(self.url + "help?weblang=foobar") self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") (lang,page) = self._getHelpStatus() self.assertTrue(lang == "en") @@ -70,18 +72,18 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_help_pages(self): """check invalid page requests""" - self.cmd.go(self.URL + "help?page=foobar") + self.cmd.go(self.url + "help?page=foobar") (lang,page) = self._getHelpStatus() self.assertTrue(page == "CryptoBoxUser") - self.cmd.go(self.URL + "help?page=CryptoBoxUser") + self.cmd.go(self.url + "help?page=CryptoBoxUser") (lang,page) = self._getHelpStatus() self.assertTrue(page == "CryptoBoxUser") def test_help_default_languages(self): """check invalid page requests""" - self.cmd.go(self.URL + "help?weblang=foobar") + self.cmd.go(self.url + "help?weblang=foobar") (lang,page) = self._getHelpStatus() self.assertTrue(lang == "en") diff --git a/plugins/language_selection/language_selection.py b/plugins/language_selection/language_selection.py index 9d2bfcd..6436bba 100644 --- a/plugins/language_selection/language_selection.py +++ b/plugins/language_selection/language_selection.py @@ -18,20 +18,31 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""The language_selection feature of the CryptoBox. +""" + +__revision__ = "$Id" + import cryptobox.plugins.base class language_selection(cryptobox.plugins.base.CryptoBoxPlugin): + """The language_selection feature of the CryptoBox. + """ - pluginCapabilities = [ "system" ] - pluginVisibility = [ "menu", "preferences" ] - requestAuth = False + plugin_capabilities = [ "system" ] + plugin_visibility = [ "menu", "preferences" ] + request_auth = False rank = 60 - def doAction(self): + def do_action(self): + """Show all available languages. + """ return "language_selection" - def getStatus(self): - return ":".join(self.site.langOrder) + def get_status(self): + """The current status of the feature is defined as the current language. + """ + return ":".join(self.site.lang_order) diff --git a/plugins/language_selection/unittests.py b/plugins/language_selection/unittests.py index 2f0eb6f..1f06609 100644 --- a/plugins/language_selection/unittests.py +++ b/plugins/language_selection/unittests.py @@ -18,19 +18,21 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - url = self.URL + "language_selection?weblang=en" + url = self.url + "language_selection?weblang=en" self.register_auth(url) self.cmd.go(url) self.cmd.find('hoose an interface language') def test_check_language_list(self): - url = self.URL + "language_selection" + url = self.url + "language_selection" self.register_auth(url) self.cmd.go(url) self.cmd.find(u'Data.Status.Plugins.language_selection=(.*)$', "m") diff --git a/plugins/logs/logs.py b/plugins/logs/logs.py index 5187ce8..e4ddd18 100644 --- a/plugins/logs/logs.py +++ b/plugins/logs/logs.py @@ -18,56 +18,76 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # + +"""The logs feature of the CryptoBox. +""" + +__revision__ = "$Id" + import cryptobox.plugins.base import os class logs(cryptobox.plugins.base.CryptoBoxPlugin): + """The logs feature of the CryptoBox. + """ - pluginCapabilities = [ "system" ] - pluginVisibility = [ "preferences" ] - requestAuth = False + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = False rank = 90 - def doAction(self, lines=50, size=3000, pattern=None): + def do_action(self, lines=50, size=3000, pattern=None): + """Show the latest part of the log file. + """ import re ## filter input try: lines = int(lines) - if lines <= 0: raise(ValueError) + if lines <= 0: + raise(ValueError) except ValueError: lines = 50 try: size = int(size) - if size <= 0: raise(ValueError) + if size <= 0: + raise(ValueError) except ValueError: size = 3000 if not pattern is None: pattern = str(pattern) - if re.search(u'\W', pattern): pattern = None - self.hdf[self.hdf_prefix + "Content"] = self.__getLogContent(lines, size, pattern) - self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.abspath(os.path.join(self.pluginDir, "logs.css")) + if re.search(u'\W', pattern): + pattern = None + self.hdf[self.hdf_prefix + "Content"] = self.__get_log_content( + lines, size, pattern) + self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.abspath(os.path.join( + self.plugin_dir, "logs.css")) return "show_log" - def getStatus(self): + def get_status(self): + """The current status includes the log configuration details. + """ return "%s:%s:%s" % ( self.cbox.prefs["Log"]["Level"], self.cbox.prefs["Log"]["Destination"], self.cbox.prefs["Log"]["Details"]) - def __getLogContent(self, lines, maxSize, pattern): - import re + def __get_log_content(self, lines, max_size, pattern): + """Filter, sort and shorten the log content. + """ if pattern: content = [] current_length = 0 - for line in self.cbox.getLogData(): + for line in self.cbox.get_log_data(): if line.find(pattern) != -1: content.append(line) current_length += len(line) - if lines and len(content) >=lines: break - if maxSize and current_length >=maxSize: break + if lines and len(content) >= lines: + break + if max_size and current_length >= max_size: + break else: - content = self.cbox.getLogData(lines, maxSize) + content = self.cbox.get_log_data(lines, max_size) return "
".join(content) diff --git a/plugins/logs/unittests.py b/plugins/logs/unittests.py index fe18d6b..c51b04f 100644 --- a/plugins/logs/unittests.py +++ b/plugins/logs/unittests.py @@ -18,12 +18,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_logs(self): - log_url = self.URL + "logs" + log_url = self.url + "logs" self.register_auth(log_url) self.cmd.go(log_url) self.cmd.find('class="console"') @@ -31,13 +33,13 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_write_logs(self): log_text = "unittest - just a marker - please ignore" self.cbox.log.error(log_text) - log_url = self.URL + "logs" + log_url = self.url + "logs" self.register_auth(log_url) self.cmd.go(log_url + "?pattern=ERROR") self.cmd.find(log_text) def test_invalid_args(self): - log_url = self.URL + "logs" + log_url = self.url + "logs" self.cmd.go(log_url + "?lines=10") self.cmd.find('class="console"') self.cmd.go(log_url + "?lines=0") diff --git a/plugins/network/network.py b/plugins/network/network.py index f1f481a..01318c8 100644 --- a/plugins/network/network.py +++ b/plugins/network/network.py @@ -18,23 +18,32 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""The network feature of the CryptoBox. +""" + +__revision__ = "$Id" + import subprocess import os import cryptobox.plugins.base ## specify (in seconds), how long we should wait before redirecting and ip change -REDIRECT_DELAY=20 -CHANGE_IP_DELAY=1 +REDIRECT_DELAY = 10 +CHANGE_IP_DELAY = 2 class network(cryptobox.plugins.base.CryptoBoxPlugin): + """The network feature of the CryptoBox. + """ - pluginCapabilities = [ "system" ] - pluginVisibility = [ "preferences" ] - requestAuth = True + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = True rank = 30 - def doAction(self, store=None, redirected="", ip1="", ip2="", ip3="", ip4=""): + def do_action(self, store=None, redirected="", ip1="", ip2="", ip3="", ip4=""): + """Show a form containing the current IP - change it if requested. + """ ## if we were redirected, then we should display the default page self.cbox.log.debug("executing network plugin") if redirected == "1": @@ -46,58 +55,68 @@ class network(cryptobox.plugins.base.CryptoBoxPlugin): try: for ip_in in (ip1, ip2, ip3, ip4): if (int(ip_in) < 0) or (int(ip_in) > 255): - self.cbox.log.info("invalid IP supplied: %s" % str((ip1,ip2,ip3,ip4))) + self.cbox.log.info("invalid IP supplied: %s" % \ + str((ip1, ip2, ip3, ip4))) raise ValueError - ip = "%d.%d.%d.%d" % (int(ip1), int(ip2), int(ip3), int(ip4)) + new_ip = "%d.%d.%d.%d" % (int(ip1), int(ip2), int(ip3), int(ip4)) except ValueError: self.hdf["Data.Warning"] = "Plugins.network.InvalidIP" - self.__prepareFormData() + self.__prepare_form_data() return "form_network" - if self.__setIP(ip): - self.cbox.log.info("the IP was successfully changed: %s" % ip) + if self.__set_ip(new_ip): + self.cbox.log.info("the IP was successfully changed: %s" % new_ip) self.hdf["Data.Success"] = "Plugins.network.IPChanged" - self.hdf["Data.Redirect.URL"] = self.__getRedirectDestination(ip) + self.hdf["Data.Redirect.URL"] = self.__get_redirect_destination(new_ip) self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY return None else: - self.cbox.log.warn("failed to change IP address to: %s" % ip) + self.cbox.log.warn("failed to change IP address to: %s" % new_ip) self.hdf["Data.Warning"] = "Plugins.network.InvalidIP" - self.__prepareFormData() + self.__prepare_form_data() return "form_network" else: self.cbox.log.debug("network plugin: show form") ## just show the form - self.__prepareFormData() + self.__prepare_form_data() return "form_network" - def getStatus(self): - return "%d.%d.%d.%d" % self.__getCurrentIP() + def get_status(self): + """The current IP is the status of this feature. + """ + return "%d.%d.%d.%d" % self.__get_current_ip() - def __getRedirectDestination(self, ip): + def __get_redirect_destination(self, ip): + """Put the new URL together. + """ import cherrypy req = cherrypy.request base_parts = req.base.split(":") - dest = "%s:%s" % (base_parts[0], ip) + dest = "%s://%s" % (base_parts[0], ip) if len(base_parts) == 3: dest += ":%s" % base_parts[2] return dest - def __prepareFormData(self): - (oc1, oc2, oc3, oc4) = self.__getCurrentIP() + def __prepare_form_data(self): + """Set some hdf values. + """ + (oc1, oc2, oc3, oc4) = self.__get_current_ip() self.hdf[self.hdf_prefix + "ip.oc1"] = oc1 self.hdf[self.hdf_prefix + "ip.oc2"] = oc2 self.hdf[self.hdf_prefix + "ip.oc3"] = oc3 self.hdf[self.hdf_prefix + "ip.oc4"] = oc4 - def __getCurrentIP(self): + def __get_current_ip(self): + """Retrieve the current IP. + """ import re import imp ## load some values from the root_action.py script - root_action_plug = imp.load_source("root_action", os.path.join(self.pluginDir, "root_action.py")) + root_action_plug = imp.load_source("root_action", + os.path.join(self.plugin_dir, "root_action.py")) ## get the current IP of the network interface proc = subprocess.Popen( shell = False, @@ -106,20 +125,25 @@ class network(cryptobox.plugins.base.CryptoBoxPlugin): root_action_plug.IFCONFIG_BIN, root_action_plug.IFACE]) (stdout, stderr) = proc.communicate() - if proc.returncode != 0: return (0,0,0,0) + if proc.returncode != 0: + return (0,0,0,0) ## this regex matches the four numbers of the IP match = re.search(u'inet [\w]+:(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s', stdout) if match: ## use the previously matched numbers return tuple([int(e) for e in match.groups()]) else: - return (0,0,0,0) + return (0, 0, 0, 0) - def __setIP(self, ip): + def __set_ip(self, new_ip): + """Change the IP. + """ import threading ## call the root_action script after some seconds - so we can deliver the page before - def delayedIPchange(): + def delayed_ip_change(): + """A threaded function to change the IP. + """ import time time.sleep(CHANGE_IP_DELAY) proc = subprocess.Popen( @@ -129,19 +153,17 @@ class network(cryptobox.plugins.base.CryptoBoxPlugin): self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "plugin", - os.path.join(self.pluginDir, "root_action.py"), - ip]) + os.path.join(self.plugin_dir, "root_action.py"), + new_ip]) proc.wait() if proc.returncode != 0: - self.cbox.log.warn("failed to change IP address: %s" % ip) + self.cbox.log.warn("failed to change IP address: %s" % new_ip) self.cbox.log.warn("error output: %s" % str(proc.stderr.read())) return thread = threading.Thread() - thread.run = delayedIPchange + thread.run = delayed_ip_change thread.setDaemon(True) thread.start() # TODO: how could we guess, if it failed? return True - - diff --git a/plugins/network/root_action.py b/plugins/network/root_action.py index 4625a0c..83edb12 100755 --- a/plugins/network/root_action.py +++ b/plugins/network/root_action.py @@ -19,6 +19,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + #TODO: add netmask and gateway diff --git a/plugins/network/unittests.py b/plugins/network/unittests.py index 4c2e779..f49a5d4 100644 --- a/plugins/network/unittests.py +++ b/plugins/network/unittests.py @@ -18,6 +18,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass from network import CHANGE_IP_DELAY @@ -28,43 +30,47 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): '''change of network address''' ## the time module is necessary for the CHANGE_IP_DELAY import time - self.register_auth(self.URL + "network") - self.cmd.go(self.URL + "network") + self.register_auth(self.url + "network") + ## do not follow redirects - they would break the test otherwise + self.cmd.config("acknowledge_equiv_refresh", 0) + self.cmd.go(self.url + "network") ## extract the current IP from the network plugin output - def getCurrentIP(): - self.cmd.go(self.URL + "network") + def get_current_ip(): + self.register_auth(self.url + "network") + self.cmd.go(self.url + "network") self.cmd.find(u'Data.Status.Plugins.network=([0-9\.]*)$', "m") return self.locals["__match__"] - origIPtext = getCurrentIP() - origIPocts = origIPtext.split(".") + orig_ip_text = get_current_ip() + orig_ip_octs = orig_ip_text.split(".") ## check, if the original IP is valid (contains four octets) - self.assertEquals(4, len(origIPocts)) - wrongIP = "192.168.123.321" - def setIP((ip1, ip2, ip3, ip4)): - self.cmd.go(self.URL + "network") + self.assertEquals(4, len(orig_ip_octs)) + def set_ip((ip1, ip2, ip3, ip4)): + self.cmd.go(self.url + "network") self.cmd.formvalue("set_ip", "ip1", str(ip1)) self.cmd.formvalue("set_ip", "ip2", str(ip2)) self.cmd.formvalue("set_ip", "ip3", str(ip3)) self.cmd.formvalue("set_ip", "ip4", str(ip4)) self.cmd.submit() ## sleep a little bit longer than the delay necessary for ip-change - time.sleep(CHANGE_IP_DELAY + 0.2) - setIP([1,-2,0,1]) - self.assertEquals(origIPtext, getCurrentIP()) - setIP([1,0,0,256]) - self.assertEquals(origIPtext, getCurrentIP()) - setIP([1,"foo",0,1]) - self.assertEquals(origIPtext, getCurrentIP()) - setIP([10,12,0,2]) - self.assertEquals("10.12.0.2", getCurrentIP()) - setIP(origIPocts) - self.assertEquals(origIPtext, getCurrentIP()) + time.sleep(CHANGE_IP_DELAY + 3) + set_ip([1,-2,0,1]) + self.assertEquals(orig_ip_text, get_current_ip()) + set_ip([1,0,0,256]) + self.assertEquals(orig_ip_text, get_current_ip()) + set_ip([1,"foo",0,1]) + self.assertEquals(orig_ip_text, get_current_ip()) + new_ip = orig_ip_octs[:] + new_ip[3] = str((int(orig_ip_octs[3]) + 128) % 256) + set_ip(new_ip) + self.assertEquals(".".join(new_ip), get_current_ip()) + set_ip(orig_ip_octs) + self.assertEquals(orig_ip_text, get_current_ip()) def test_inputs(self): - self.register_auth(self.URL + "network") - self.cmd.go(self.URL + "network" + "?redirected=1") + self.register_auth(self.url + "network") + self.cmd.go(self.url + "network" + "?redirected=1") self.cmd.notfind("problem") - self.cmd.go(self.URL + "network" + "?store=1") + self.cmd.go(self.url + "network" + "?store=1") self.cmd.find("invalid network address") diff --git a/plugins/partition/partition.py b/plugins/partition/partition.py index 43c2518..6d7cda6 100644 --- a/plugins/partition/partition.py +++ b/plugins/partition/partition.py @@ -18,114 +18,141 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""The partition feature of the CryptoBox. +""" + +__revision__ = "$Id" + import subprocess import os import logging import cryptobox.core.tools as cbxTools import cryptobox.plugins.base -class partition(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "system" ] - pluginVisibility = [ "preferences" ] - requestAuth = True +PARTTYPES = { + "windows" : ["0xC", "vfat"], + "linux" : ["L", "ext3"]} + +CONFIGPARTITION = { + "size" : 5, # size of configuration partition (if necessary) in MB + "type" : "L", + "fs" : "ext2"} + + +class partition(cryptobox.plugins.base.CryptoBoxPlugin): + """The partition feature of the CryptoBox. + """ + + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = True rank = 80 - PartTypes = { - "windows" : ["0xC", "vfat"], - "linux" : ["L", "ext3"]} - - ConfigPartition = { - "size" : 5, # size of configuration partition (if necessary) in MB - "type" : "L", - "fs" : "ext2"} - - - def doAction(self, **args): + def do_action(self, **args): + """Show the partitioning form and execute the requested action. + """ ## load default hdf values - self.__prepareDataset() + self.__prepare_dataset() ## retrieve some values from 'args' - defaults are empty - self.device = self.__getSelectedDevice(args) - self.withConfigPartition = self.__isWithConfigPartition() + self.device = self.__get_selected_device(args) + self.with_config_partition = self.__is_with_config_partition() self.cbox.log.debug("partition plugin: selected device=%s" % str(self.device)) - self.deviceSize = self.__getAvailableDeviceSize(self.device) + self.device_size = self.__get_available_device_size(self.device) try: step = args["step"] del args["step"] except KeyError: step = "select_device" try: - ## this way of selecting the easy setup is necessary: see select_device.py for details - if args["easy"]: step = "easy" + ## this way of selecting the easy setup is necessary: + ## see select_device.cs for details (button values for ie) + if args["easy"]: + step = "easy" except KeyError: pass ## no (or invalid) device was supplied if not self.device: step = "select_device" if step == "add_partition": - return self.__actionAddPartition(args) + return self.__action_add_partition(args) elif step == "del_partition": - return self.__actionDelPartition(args) + return self.__action_del_partition(args) elif step == "finish": - return self.__actionFinish(args) + return self.__action_finish(args) elif step == "easy": - return self.__actionEasySetup(args) + return self.__action_easy_setup() else: # for "select_device" and for invalid targets - return self.__actionSelectDevice(args) + return self.__action_select_device() - def getStatus(self): - return "%s / %s / %s" % (self.device, self.deviceSize, self.withConfigPartition) + def get_status(self): + """The status of this plugin is the selected device and some information. + """ + return "%s / %s / %s" % (self.device, self.device_size, + self.with_config_partition) - def __prepareDataset(self): - self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.join(self.pluginDir, "partition.css") + def __prepare_dataset(self): + """Set some hdf values. + """ + self.hdf[self.hdf_prefix + "StyleSheetFile"] = \ + os.path.join(self.plugin_dir, "partition.css") - def __getSelectedDevice(self, args): + def __get_selected_device(self, args): + """Check the selected device (valid, not busy, ...). + """ try: device = args["block_device"] except KeyError: return None - if not self.__isDeviceValid(device): + if not self.__is_device_valid(device): return None - if self.__isDeviceBusy(device): + if self.__is_device_busy(device): self.hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy" return None return device - def __isDeviceValid(self, device): + def __is_device_valid(self, device): + """Check if the device is valid and allowed. + """ if not device: return False - if not self.cbox.isDeviceAllowed(device): + if not self.cbox.is_device_allowed(device): return False - if not device in cbxTools.getParentBlockDevices(): + if not device in cbxTools.get_parent_blockdevices(): return False return True - def __isDeviceBusy(self, device): - """check if the device (or one of its partitions) is mounted""" + def __is_device_busy(self, device): + """check if the device (or one of its partitions) is mounted + """ # the config partition is ignored, as it will get unmounted if necessary import re - for c in self.cbox.getContainerList(): - if re.match(device + "\d*$", c.getDevice()): - if c.isMounted(): return True + for cont in self.cbox.get_container_list(): + if re.match(device + "\d*$", cont.get_device()): + if cont.is_mounted(): + return True return False - def __actionSelectDevice(self, args): + def __action_select_device(self): + """Show a form to select the device for partitioning. + """ block_devices = [e - for e in cbxTools.getParentBlockDevices() - if self.cbox.isDeviceAllowed(e)] + for e in cbxTools.get_parent_blockdevices() + if self.cbox.is_device_allowed(e)] counter = 0 - for a in block_devices: - self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = a - self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = cbxTools.getBlockDeviceSizeHumanly(a) - self.cbox.log.debug("found a suitable block device: %s" % a) + for dev in block_devices: + self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = dev + self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = \ + cbxTools.get_blockdevice_size_humanly(dev) + self.cbox.log.debug("found a suitable block device: %s" % dev) counter += 1 - if self.withConfigPartition: + if self.with_config_partition: self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" ## there is no disk available if not block_devices: @@ -133,59 +160,72 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): return "select_device" - def __actionAddPartition(self, args): + def __action_add_partition(self, args): + """Add a selected partition to the currently proposed partition table. + """ self.hdf[self.hdf_prefix + "Device"] = self.device - self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize - parts = self.__getPartitionsFromArgs(args) - self.__setPartitionData(parts) + self.hdf[self.hdf_prefix + "Device.Size"] = self.device_size + parts = self.__get_partitions_from_args(args) + self.__set_partition_data(parts) return "set_partitions" - def __actionDelPartition(self, args): + def __action_del_partition(self, args): + """Remove a partition from the proposed partition table. + """ try: part_num = int(args["del_num"]) except (TypeError,KeyError): - return self.__actionAddPartition(args) + return self.__action_add_partition(args) self.hdf[self.hdf_prefix + "Device"] = self.device - self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize - parts = self.__getPartitionsFromArgs(args) + self.hdf[self.hdf_prefix + "Device.Size"] = self.device_size + parts = self.__get_partitions_from_args(args) ## valid partition number to be deleted? if part_num < len(parts): del parts[part_num] else: - return self.__actionAddPartition(args) - self.__setPartitionData(parts) + return self.__action_add_partition(args) + self.__set_partition_data(parts) return "set_partitions" - def __actionFinish(self, args): - parts = self.__getPartitionsFromArgs(args) + def __action_finish(self, args): + """Write the partition table. + """ + parts = self.__get_partitions_from_args(args) if parts: - self.__setPartitionData(parts) - if cbxTools.isPartOfBlockDevice(self.device, self.cbox.prefs.getActivePartition()): - self.cbox.prefs.umountPartition() - if not self.__runFDisk(parts): + self.__set_partition_data(parts) + if cbxTools.is_part_of_blockdevice(self.device, + self.cbox.prefs.get_active_partition()): + self.cbox.prefs.umount_partition() + if not self.__run_fdisk(parts): self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" - self.cbox.log.warn("partition: failed to partition device: %s" % self.device) - return self.__actionAddPartition(args) + self.cbox.log.warn( + "partition: failed to partition device: %s" % self.device) + return self.__action_add_partition(args) else: - """ - tricky problem: if the device was partitioned, then a created config partition is still part of the containerlist, as the label is not checked again - very ugly!!! So we will call reReadContainerList after formatting the last partition - see below - """ - self.cbox.reReadContainerList() + ## tricky problem: if the device was partitioned, then a created config + ## partition is still part of the containerlist, as the label is not + ## checked again - very ugly!!! So we will call reReadContainerList + ## after formatting the last partition - see below + self.cbox.reread_container_list() def result_generator(): + """Generate the results of formatting - may be threaded. + """ counter = 0 ## initialize the generator - formatPart_gen = self.__formatPartitions(parts) + format_part_gen = self.__format_partitions(parts) while counter < len(parts): ## first part: get the device name - yield formatPart_gen.next() + yield format_part_gen.next() counter += 1 ## second part: do the real formatting of a partition - result = formatPart_gen.next() - ## after the first partiton, we can reRead the containerList (as the possible config partition was already created) - if self.withConfigPartition and (counter == 1): - ## important: reRead the containerList - but somehow it breaks the flow (hanging process) + result = format_part_gen.next() + ## after the first partiton, we can reRead the containerList + ## (as the possible config partition was already created) + if self.with_config_partition and (counter == 1): + ## important: reRead the containerList - but somehow it + ## breaks the flow (hanging process) #self.cbox.reReadContainerList() ## write config data self.cbox.prefs.mountPartition() @@ -200,24 +240,28 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): "template": "show_format_progress", "generator": result_generator} else: - return self.__actionAddPartition(args) + return self.__action_add_partition(args) - def __actionEasySetup(self, args): + def __action_easy_setup(self): + """Do automatic partitioning (create only one big partition). + """ import types ## we do not have to take special care for a possible config partition - parts = [ { "size": self.deviceSize, "type": "windows" } ] + parts = [ { "size": self.device_size, "type": "windows" } ] ## umount partition if necessary - if cbxTools.isPartOfBlockDevice(self.device, self.cbox.prefs.getActivePartition()): - self.cbox.prefs.umountPartition() + if cbxTools.is_part_of_blockdevice(self.device, + self.cbox.prefs.get_active_partition()): + self.cbox.prefs.umount_partition() ## partition it - if not self.__runFDisk(parts): + if not self.__run_fdisk(parts): self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" return None ## "formatPartitions" is a generator, returning device names and bolean values - result = [e for e in self.__formatPartitions(parts) if type(e) == types.BooleanType] - if self.withConfigPartition: - self.cbox.prefs.mountPartition() + result = [e for e in self.__format_partitions(parts) + if type(e) == types.BooleanType] + if self.with_config_partition: + self.cbox.prefs.mount_partition() self.cbox.prefs.write() ## check if there is a "False" return value if False in result: @@ -229,50 +273,59 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): ## operation was successful self.hdf["Data.Success"] = "Plugins.partition.EasySetup" self.cbox.log.info("easy partitioning succeeded") - ## do not show the disk overview immediately - it does not get updated that fast + ## do not show the disk overview immediately + ## it does not get updated that fast return { "plugin":"system_preferences", "values":[] } - def __setPartitionData(self, parts): - availSize = self.deviceSize + def __set_partition_data(self, parts): + """Set some hdf values for the currently proposed partition table. + """ + avail_size = self.device_size i = 0 for part in parts: self.cbox.log.debug(part) self.hdf[self.hdf_prefix + "Parts.%d.Size" % i] = part["size"] self.hdf[self.hdf_prefix + "Parts.%d.Type" % i] = part["type"] - availSize -= part["size"] + avail_size -= part["size"] i += 1 - self.hdf[self.hdf_prefix + "availSize"] = availSize - if self.withConfigPartition: + self.hdf[self.hdf_prefix + "availSize"] = avail_size + if self.with_config_partition: self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" - for t in self.PartTypes.keys(): - self.hdf[self.hdf_prefix + "Types.%s" % t] = t + for ptype in PARTTYPES.keys(): + self.hdf[self.hdf_prefix + "Types.%s" % ptype] = ptype ## store the currently existing partitions of the choosen block device - current_containers = [ e for e in self.cbox.getContainerList() if cbxTools.isPartOfBlockDevice(self.device, e.getDevice()) ] - for (index, t) in enumerate(current_containers): - self.hdf[self.hdf_prefix + "ExistingContainers.%d.name" % index] = t.getName() - self.hdf[self.hdf_prefix + "ExistingContainers.%d.size" % index] = cbxTools.getBlockDeviceSizeHumanly(t.getDevice()) + current_containers = [ e for e in self.cbox.get_container_list() + if cbxTools.is_part_of_blockdevice(self.device, e.get_device()) ] + for (index, cont) in enumerate(current_containers): + self.hdf[self.hdf_prefix + "ExistingContainers.%d.name" % index] = \ + cont.get_name() + self.hdf[self.hdf_prefix + "ExistingContainers.%d.size" % index] = \ + cbxTools.get_blockdevice_size_humanly(cont.get_device()) - def __getPartitionsFromArgs(self, args): + def __get_partitions_from_args(self, args): + """Filter the given arguments and construct a partition table. + """ parts = [] done = False - availSize = self.deviceSize + avail_size = self.device_size i = -1 while not done: i += 1 try: size = int(args["part%d_size" % i]) - partType = args["part%d_type" % i] - if int(size) > availSize: + part_type = args["part%d_type" % i] + if int(size) > avail_size: self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooBig" continue if int(size) < 10: self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooSmall" continue - if not partType in self.PartTypes.keys(): continue - parts.append({"size":size, "type":partType}) - availSize -= size + if not part_type in PARTTYPES.keys(): + continue + parts.append({"size":size, "type":part_type}) + avail_size -= size except TypeError: pass except KeyError: @@ -280,33 +333,40 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): return parts - def __getAvailableDeviceSize(self, device): + def __get_available_device_size(self, device): """calculate the available size (MB) of the device - also consider a (possible) configuration partition""" - deviceSize = cbxTools.getBlockDeviceSize(device) - if deviceSize < 0: return 0 - if self.withConfigPartition: - deviceSize -= self.ConfigPartition["size"] - return deviceSize + also consider a (possible) configuration partition + """ + device_size = cbxTools.get_blockdevice_size(device) + if device_size < 0: + return 0 + if self.with_config_partition: + device_size -= CONFIGPARTITION["size"] + return device_size - def __isWithConfigPartition(self): - """check if we have to create a configuration partition""" - if self.cbox.prefs.requiresPartition(): - active = self.cbox.prefs.getActivePartition() + def __is_with_config_partition(self): + """check if we have to create a configuration partition + """ + if self.cbox.prefs.requires_partition(): + active = self.cbox.prefs.get_active_partition() ## we need a partition, if there is no active one - if not active: return True + if not active: + return True ## check if the active one is part of the current device - return cbxTools.isPartOfBlockDevice(self.device, active) + return cbxTools.is_part_of_blockdevice(self.device, active) return False - def __runFDisk(self, parts): + def __run_fdisk(self, parts): + """Call fdisk to partition the device. + """ ## check if the device is completely filled (to avoid some empty last blocks) - avail_size = self.deviceSize - for d in parts: avail_size -= d["size"] + avail_size = self.device_size + for one_part in parts: + avail_size -= one_part["size"] self.cbox.log.debug("remaining size: %d" % avail_size) - isFilled = avail_size == 0 + is_filled = avail_size == 0 proc = subprocess.Popen( shell = False, stdin = subprocess.PIPE, @@ -316,81 +376,91 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "plugin", - os.path.join(self.pluginDir, "root_action.py"), + os.path.join(self.plugin_dir, "root_action.py"), "partition", self.device]) - for line in self.__getSFDiskLayout(parts, isFilled): + for line in self.__get_sfdisk_layout(parts, is_filled): proc.stdin.write(line + "\n") (output, error) = proc.communicate() - if proc.returncode != 0: self.cbox.log.debug("partitioning failed: %s" % error) + if proc.returncode != 0: + self.cbox.log.debug("partitioning failed: %s" % error) return proc.returncode == 0 - def __getSFDiskLayout(self, paramParts, isFilled): - """this generator returns the input lines for sfdisk""" - parts = paramParts[:] + def __get_sfdisk_layout(self, param_parts, is_filled): + """this generator returns the input lines for sfdisk + """ + parts = param_parts[:] ## first a (possible) configuration partition - so it will be reusable - if self.withConfigPartition: + if self.with_config_partition: ## fill the main table (including a config partition) - yield ",%d,%s" % (self.ConfigPartition["size"], self.ConfigPartition["type"]) + yield ",%d,%s" % (CONFIGPARTITION["size"], CONFIGPARTITION["type"]) ## one primary partition - if isFilled and (len(parts) == 1): + if is_filled and (len(parts) == 1): ## fill the rest of the device - yield ",,%s,*" % self.PartTypes[parts[0]["type"]][0] + yield ",,%s,*" % PARTTYPES[parts[0]["type"]][0] else: ## only use the specified size - yield ",%d,%s,*" % (parts[0]["size"], self.PartTypes[parts[0]["type"]][0]) + yield ",%d,%s,*" % (parts[0]["size"], PARTTYPES[parts[0]["type"]][0]) del parts[0] ## no extended partition, if there is only one disk - if not parts: return + if not parts: + return ## an extended container for the rest yield ",,E" ## an empty partition in main table yield ";" ## maybe another empty partition if there is no config partition - if not self.withConfigPartition: yield ";" + if not self.with_config_partition: + yield ";" while parts: - if isFilled and (len(parts) == 1): - yield ",,%s" % (self.PartTypes[parts[0]["type"]][0],) + if is_filled and (len(parts) == 1): + yield ",,%s" % (PARTTYPES[parts[0]["type"]][0],) else: - yield ",%d,%s" % (parts[0]["size"], self.PartTypes[parts[0]["type"]][0]) + yield ",%d,%s" % (parts[0]["size"], PARTTYPES[parts[0]["type"]][0]) del parts[0] - def __formatPartitions(self, paramParts): - parts = paramParts[:] + def __format_partitions(self, param_parts): + """Format all partitions of the device. + """ + parts = param_parts[:] part_num = 1 ## maybe a config partition? - if self.withConfigPartition: + if self.with_config_partition: dev_name = self.device + str(part_num) self.cbox.log.info("formatting config partition (%s)" % dev_name) - if self.__formatOnePartition(dev_name, self.ConfigPartition["fs"]): - self.__setLabelOfPartition(dev_name, self.cbox.prefs["Main"]["ConfigVolumeLabel"]) + if self.__format_one_partition(dev_name, CONFIGPARTITION["fs"]): + self.__set_label_of_partition(dev_name, + self.cbox.prefs["Main"]["ConfigVolumeLabel"]) part_num += 1 ## the first data partition dev_name = self.device + str(part_num) - partType = self.PartTypes[parts[0]["type"]][1] - self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType)) + part_type = PARTTYPES[parts[0]["type"]][1] + self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, part_type)) yield dev_name - yield self.__formatOnePartition(dev_name, partType) + yield self.__format_one_partition(dev_name, part_type) del parts[0] ## other data partitions part_num = 5 while parts: dev_name = self.device + str(part_num) - partType = self.PartTypes[parts[0]["type"]][1] - self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType)) + part_type = PARTTYPES[parts[0]["type"]][1] + self.cbox.log.info("formatting partition (%s) as '%s'" % \ + (dev_name, part_type)) yield dev_name - yield self.__formatOnePartition(dev_name, partType) + yield self.__format_one_partition(dev_name, part_type) part_num += 1 del parts[0] return - def __formatOnePartition(self, dev_name, type): + def __format_one_partition(self, dev_name, fs_type): + """Format a single partition + """ ## first: retrieve UUID - it can be removed from the database afterwards - volDB = self.cbox.prefs.volumesDB - prev_name = [e.getName() for e in self.cbox.getContainerList() if e.getDevice() == dev_name] + prev_name = [e.get_name() for e in self.cbox.get_container_list() + if e.get_device() == dev_name] ## call "mkfs" proc = subprocess.Popen( shell = False, @@ -398,10 +468,10 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "plugin", - os.path.join(self.pluginDir, "root_action.py"), + os.path.join(self.plugin_dir, "root_action.py"), "format", dev_name, - type]) + fs_type]) (output, error) = proc.communicate() if proc.returncode != 0: self.cbox.log.warn("failed to create filesystem on %s: %s" % (dev_name, error)) @@ -409,11 +479,13 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): else: ## remove unused volume entry if prev_name: - self.cbox.prefs.volumesDB[prev_name[0]] + del self.cbox.prefs.volumes_db[prev_name[0]] return True - def __setLabelOfPartition(self, dev_name, label): + def __set_label_of_partition(self, dev_name, label): + """Set the label of a partition - useful for the config partition. + """ proc = subprocess.Popen( shell = False, stdout = subprocess.PIPE, @@ -422,7 +494,7 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "plugin", - os.path.join(self.pluginDir, "root_action.py"), + os.path.join(self.plugin_dir, "root_action.py"), "label", dev_name, label]) diff --git a/plugins/partition/root_action.py b/plugins/partition/root_action.py index 0b54124..f7fe5b5 100755 --- a/plugins/partition/root_action.py +++ b/plugins/partition/root_action.py @@ -19,6 +19,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + ## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script PLUGIN_TYPE = "cryptobox" diff --git a/plugins/partition/unittests.py b/plugins/partition/unittests.py index 696c67a..36ba4c9 100644 --- a/plugins/partition/unittests.py +++ b/plugins/partition/unittests.py @@ -18,12 +18,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - url = self.URL + "partition?weblang=en" + url = self.url + "partition?weblang=en" self.register_auth(url) self.cmd.go(url) self.cmd.find('VERY careful') diff --git a/plugins/plugin-interface.txt b/plugins/plugin-interface.txt index a90f379..321415c 100644 --- a/plugins/plugin-interface.txt +++ b/plugins/plugin-interface.txt @@ -7,7 +7,7 @@ The following directory structure is required: Python code interface: - create a class with the same name as the plugin - it must inherit CryptoBoxPlugin - - function "doAction": + - function "do_action": - this function will get called whenever this plugins is involved in a request - all arguments should be optional (e.g. for displaying a form without previous input values) - the argument "store" should be used to process a form submission (just a recommendation) @@ -27,15 +27,15 @@ Python code interface: * values: a dictionary of variables that should be defined for this plugin - an empty (e.g. None) return value can be used to go to the default page ("disks" or "volume_mount" (for volume plugins)) - - function "getStatus": + - function "get_status": - returns a string, that describes a state connected to this plugin (e.g. the current date and time (for the "date" plugin)) - - the class variable "pluginCapabilities" must be an array of strings (supported: "system" and + - the class variable "plugin_capabilities" must be an array of strings (supported: "system" and "volume") - - the class variable "pluginVisibility" may contain one or more of the following items: - menu/preferences/volume. This obviously should fit to the 'pluginCapabilities' variable. - An empty list is interpreted as a disabled plugin. - - the class variable "requestAuth" is boolean and defines, if admin authentication is necessary + - the class variable "plugin_visibility" may contain one or more of the following items: + menu/preferences/volume. This should fit to the 'plugin_capabilities' variable. + An empty list is interpreted as an invisible plugin. + - the class variable "request_auth" is boolean and defines, if admin authentication is necessary for this plugin - the class variable "rank" is an integer in the range of 0..100 - it determines the order of plugins in listings (lower value -> higher priority) diff --git a/plugins/plugin_manager/plugin_manager.py b/plugins/plugin_manager/plugin_manager.py index bb5a0e7..61b918a 100644 --- a/plugins/plugin_manager/plugin_manager.py +++ b/plugins/plugin_manager/plugin_manager.py @@ -18,27 +18,31 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base import cryptobox.plugins.manage class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "system" ] - pluginVisibility = [ "preferences" ] - requestAuth = True + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = True rank = 90 - def doAction(self, store=None, action=None, plugin_name=None, **args): + def do_action(self, store=None, action=None, plugin_name=None, **args): import re if plugin_name: ## check for invalid characters if re.search(u'\W', plugin_name): return "plugin_list" - pluginList = cryptobox.plugins.manage.PluginManager(self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) - plugin = pluginList.getPlugin(plugin_name) + plugin_manager = cryptobox.plugins.manage.PluginManager( + self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) + plugin = plugin_manager.get_plugin(plugin_name) if not plugin: return "plugin_list" ## take only plugins, that are of the same type as the choosen one - self.plugins = [e for e in pluginList.getPlugins() if e.pluginCapabilities == plugin.pluginCapabilities] + self.plugins = [e for e in plugin_manager.get_plugins() + if e.plugin_capabilities == plugin.plugin_capabilities] if action == "up": self.__move_up(plugin) elif action == "down": @@ -50,44 +54,48 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin): if not re.search(u'\W',key): self.__setConfig(key[:-7], args) else: - self.cbox.log.info("plugin_manager: invalid plugin name (%s)" % str(key[:-7])) + self.cbox.log.info("plugin_manager: invalid plugin name (%s)" % \ + str(key[:-7])) try: - self.cbox.prefs.pluginConf.write() + self.cbox.prefs.plugin_conf.write() except IOError: self.cbox.log.warn("failed to write plugin configuration") return "plugin_list" - def getStatus(self): + def get_status(self): return "no status" - def __sortPlugins(self): + def __sort_plugins(self): """sort all plugins in the list according to their rank""" def cmp_func(x,y): - xRank = x.getRank() - yRank = y.getRank() - if xRank < yRank: return -1 - elif xRank == yRank: return 0 - else: return 1 + x_rank = x.get_rank() + y_rank = y.get_rank() + if x_rank < y_rank: + return -1 + elif x_rank == y_rank: + return 0 + else: + return 1 self.plugins.sort(cmp = cmp_func) - def __distributeRanks(self): + def __distribute_ranks(self): """evenly distribute the 'rank' values according to the current order of the list""" dist = 100/len(self.plugins) for index,pl in enumerate(self.plugins): try: - self.cbox.prefs.pluginConf[pl.getName()]["rank"] = dist*index + self.cbox.prefs.plugin_conf[pl.get_name()]["rank"] = dist*index except KeyError: - self.cbox.prefs.pluginConf[pl.getName()] = {} - self.cbox.prefs.pluginConf[pl.getName()]["rank"] = dist*index - self.cbox.prefs.pluginConf.write() + self.cbox.prefs.plugin_conf[pl.get_name()] = {} + self.cbox.prefs.plugin_conf[pl.get_name()]["rank"] = dist*index + self.cbox.prefs.plugin_conf.write() def __move_up(self, plugin): - self.__sortPlugins() + self.__sort_plugins() try: index = self.plugins.index(plugin) ## first elements may not move up @@ -97,11 +105,11 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin): return self.plugins.remove(plugin) self.plugins.insert(index-1, plugin) - self.__distributeRanks() + self.__distribute_ranks() def __move_down(self, plugin): - self.__sortPlugins() + self.__sort_plugins() try: index = self.plugins.index(plugin) ## last elements may not move down @@ -111,7 +119,7 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin): return self.plugins.remove(plugin) self.plugins.insert(index+1, plugin) - self.__distributeRanks() + self.__distribute_ranks() def __setConfig(self, name, args): @@ -137,5 +145,5 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin): setting["requestAuth"] = True except (KeyError, ValueError): pass - self.cbox.prefs.pluginConf[name] = setting + self.cbox.prefs.plugin_conf[name] = setting diff --git a/plugins/plugin_manager/unittests.py b/plugins/plugin_manager/unittests.py index 8252461..4738fa5 100644 --- a/plugins/plugin_manager/unittests.py +++ b/plugins/plugin_manager/unittests.py @@ -18,19 +18,21 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - url = self.URL + "plugin_manager?weblang=en" + url = self.url + "plugin_manager?weblang=en" self.register_auth(url) self.cmd.go(url) self.cmd.find('Plugin Manager') def test_set_options(self): - url = self.URL + "plugin_manager" + url = self.url + "plugin_manager" self.register_auth(url) self.cmd.go(url + u"?plugin_name=t/-!") self.cmd.find('Plugin Manager') @@ -55,8 +57,8 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_move_up(self): - ## TODO: if we want to be perfect, then we should check the change of the rank - url = self.URL + "plugin_manager" + #TODO: if we want to be perfect, then we should check the change of the rank + url = self.url + "plugin_manager" self.register_auth(url) self.cmd.go(url + u"?plugin_name=disks&action=up") self.cmd.find('Plugin Manager') @@ -68,7 +70,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_move_down(self): ## TODO: if we want to be perfect, then we should check the change of the rank - url = self.URL + "plugin_manager" + url = self.url + "plugin_manager" self.register_auth(url) self.cmd.go(url + u"?plugin_name=disks&action=down") self.cmd.find('Plugin Manager') diff --git a/plugins/shutdown/root_action.py b/plugins/shutdown/root_action.py index 5259d75..25809eb 100755 --- a/plugins/shutdown/root_action.py +++ b/plugins/shutdown/root_action.py @@ -19,6 +19,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + ## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script PLUGIN_TYPE = "cryptobox" diff --git a/plugins/shutdown/shutdown.py b/plugins/shutdown/shutdown.py index f240cc7..7a3cebe 100644 --- a/plugins/shutdown/shutdown.py +++ b/plugins/shutdown/shutdown.py @@ -18,29 +18,31 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base REDIRECT_DELAY = 180 class shutdown(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "system" ] - pluginVisibility = [ "preferences", "menu" ] - requestAuth = False + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences", "menu" ] + request_auth = False rank = 90 - def doAction(self, type=None): + def do_action(self, type=None): if not type: return "form_shutdown" elif type == "shutdown": - if self.__doShutdown("shutdown"): + if self.__do_shutdown("shutdown"): self.hdf["Data.Success"] = "Plugins.shutdown.Shutdown" return "progress_shutdown" else: self.hdf["Data.Warning"] = "Plugins.shutdown.ShutdownFailed" return "form_shutdown" elif type == "reboot": - if self.__doShutdown("reboot"): + if self.__do_shutdown("reboot"): self.hdf["Data.Success"] = "Plugins.shutdown.Reboot" self.hdf["Data.Redirect.URL"] = "" self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY @@ -52,11 +54,11 @@ class shutdown(cryptobox.plugins.base.CryptoBoxPlugin): return "form_shutdown" - def getStatus(self): + def get_status(self): return "the box is up'n'running" - def __doShutdown(self, action): + def __do_shutdown(self, action): import subprocess import os proc = subprocess.Popen( @@ -65,7 +67,7 @@ class shutdown(cryptobox.plugins.base.CryptoBoxPlugin): self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "plugin", - os.path.join(self.pluginDir, "root_action.py"), + os.path.join(self.plugin_dir, "root_action.py"), action]) proc.wait() return proc.returncode == 0 diff --git a/plugins/shutdown/unittests.py b/plugins/shutdown/unittests.py index 2d506f9..bfc2105 100644 --- a/plugins/shutdown/unittests.py +++ b/plugins/shutdown/unittests.py @@ -18,13 +18,15 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): """just read the form - I do not know of a way how to check success""" - url = self.URL + "shutdown" + url = self.url + "shutdown" self.register_auth(url) self.cmd.go(url) self.cmd.find('shutdown') diff --git a/plugins/system_preferences/system_preferences.py b/plugins/system_preferences/system_preferences.py index 6cd0da8..0d1aadf 100644 --- a/plugins/system_preferences/system_preferences.py +++ b/plugins/system_preferences/system_preferences.py @@ -18,20 +18,23 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base class system_preferences(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "system" ] - pluginVisibility = [ "menu" ] - requestAuth = False + plugin_capabilities = [ "system" ] + plugin_visibility = [ "menu" ] + request_auth = False rank = 20 - def doAction(self): + def do_action(self): return "show_plugins" - def getStatus(self): - return ":".join([p.getName() for p in self.site.pluginList.getPlugins()]) - + def get_status(self): + plugin_manager = cryptobox.plugins.manage.PluginManager( + self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) + return ":".join([p.get_name() for p in plugin_manager.get_plugins()]) diff --git a/plugins/system_preferences/unittests.py b/plugins/system_preferences/unittests.py index b904f19..716975c 100644 --- a/plugins/system_preferences/unittests.py +++ b/plugins/system_preferences/unittests.py @@ -18,17 +18,19 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - self.cmd.go(self.URL + "system_preferences") + self.cmd.go(self.url + "system_preferences") self.cmd.find("Preferences") def test_check_plugins(self): - self.cmd.go(self.URL + "system_preferences") + self.cmd.go(self.url + "system_preferences") self.cmd.find(u'Data.Status.Plugins.system_preferences=(.*)$', "m") plugins = self.locals["__match__"].split(":") self.assertTrue(len(plugins) > 1) diff --git a/plugins/user_manager/unittests.py b/plugins/user_manager/unittests.py index 86f6023..eb9d821 100644 --- a/plugins/user_manager/unittests.py +++ b/plugins/user_manager/unittests.py @@ -18,6 +18,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass ## this user may not be removed @@ -35,7 +37,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_test_wrong_credentials(self): """check if the user_manager is protected""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url,"foo","bar") self.cmd.go(url) self.cmd.notfind("Manage users") @@ -43,7 +45,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_add_existing_user(self): """adding an existing user should fail""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self._add_user("admin","foo","foo") self.cmd.find("The choosen username does already exist") @@ -51,7 +53,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_add_invalid_username(self): """adding an invalid username should fail""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self._add_user("foo/bar","foo","foo") self.cmd.find("Invalid username") @@ -60,7 +62,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_add_without_password(self): """adding a user without password should fail""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self.assertFalse("foo" in self._getUsers()) self._add_user("foo","","foo") @@ -70,7 +72,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_add_with_different_passwords(self): """adding a user with different passwords should fail""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self.assertFalse("foo" in self._getUsers()) self._add_user("foo","bar","foo") @@ -80,7 +82,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_change_pw_for_invalid_user(self): """changing a password of a non existing user should fail""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self.assertFalse("barfoo" in self._getUsers()) self.cmd.go(url + "?store=change_password&user=foobar&new_pw=foo&new_pw2=foo") @@ -89,7 +91,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_change_pw_without_password(self): """changing a password without a new password should fail""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self.assertFalse("foo" in self._getUsers()) self._add_user("foo","bar","bar") @@ -102,7 +104,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_change_pw_wit_different_passwords(self): """changing a password while supplying different passwords should fail""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self.assertFalse("foo" in self._getUsers()) self._add_user("foo","bar","bar") @@ -115,7 +117,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def _remove_reserved_user(self): """removing a reserved user should fail""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self.assertTrue("admin" in self._getUsers()) self._del_user("admin") @@ -125,7 +127,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def _remove_non_existing_user(self): """removing a non-existing user should fail""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self.assertFalse("barfoo" in self._getUsers()) self._del_user("barfoo") @@ -134,7 +136,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_manage_users(self): """add a new user, change its password and remove the user afterwards""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) ## remove the user that should be added - just in case a previous run was unclean ## check its existence before @@ -158,13 +160,13 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_invalid_input(self): """check all combinations of invalid input""" - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self.cmd.go(url + "?store=foobar") def _add_user(self, username, pw, pw2): - self.cmd.go(self.URL + "user_manager") + self.cmd.go(self.url + "user_manager") self.cmd.formvalue("add_user","user",username) self.cmd.formvalue("add_user","new_pw",pw) self.cmd.formvalue("add_user","new_pw2",pw2) @@ -172,13 +174,13 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def _del_user(self, username): - self.cmd.go(self.URL + "user_manager") + self.cmd.go(self.url + "user_manager") self.cmd.formvalue("del_user","user",username) self.cmd.submit() def _change_password(self, username, pw, pw2): - self.cmd.go(self.URL + "user_manager") + self.cmd.go(self.url + "user_manager") self.cmd.formvalue("change_password","user",username) self.cmd.formvalue("change_password","new_pw",pw) self.cmd.formvalue("change_password","new_pw2",pw2) @@ -186,7 +188,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def _getUsers(self): - url = self.URL + "user_manager" + url = self.url + "user_manager" self.register_auth(url) self.cmd.go(url) self.cmd.find("Data.Status.Plugins.user_manager=([\w:]+)") diff --git a/plugins/user_manager/user_manager.py b/plugins/user_manager/user_manager.py index 556d241..f4c2420 100644 --- a/plugins/user_manager/user_manager.py +++ b/plugins/user_manager/user_manager.py @@ -18,21 +18,23 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base RESERVED_USERS = [ "admin" ] class user_manager(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "system" ] - pluginVisibility = [ "preferences" ] - requestAuth = True + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = True rank = 45 - def doAction(self, store=None, user=None, new_pw=None, new_pw2=None): + def do_action(self, store=None, user=None, new_pw=None, new_pw2=None): import re - adminDict = self.cbox.prefs.userDB["admins"] - self.__cleanHDF() + admin_dict = self.cbox.prefs.user_db["admins"] + self.__clean_hdf() if store is None: pass elif store == "add_user": @@ -42,13 +44,13 @@ class user_manager(cryptobox.plugins.base.CryptoBoxPlugin): self.hdf["Data.Warning"] = "EmptyNewPassword" elif new_pw != new_pw2: self.hdf["Data.Warning"] = "DifferentPasswords" - elif user in adminDict.keys(): + elif user in admin_dict.keys(): self.hdf["Data.Warning"] = "Plugins.user_manager.UserAlreadyExists" else: - adminDict[user] = self.cbox.prefs.userDB.getDigest(new_pw) + admin_dict[user] = self.cbox.prefs.user_db.get_digest(new_pw) self.hdf["Data.Success"] = "Plugins.user_manager.UserAdded" try: - self.cbox.prefs.userDB.write() + self.cbox.prefs.user_db.write() except IOError: self.cbox.log.warn("failed to write user database") elif store == "change_password": @@ -56,11 +58,11 @@ class user_manager(cryptobox.plugins.base.CryptoBoxPlugin): self.hdf["Data.Warning"] = "EmptyNewPassword" elif new_pw != new_pw2: self.hdf["Data.Warning"] = "DifferentPasswords" - elif user in adminDict.keys(): - adminDict[user] = self.cbox.prefs.userDB.getDigest(new_pw) + elif user in admin_dict.keys(): + admin_dict[user] = self.cbox.prefs.user_db.get_digest(new_pw) self.hdf["Data.Success"] = "Plugins.user_manager.PasswordChanged" try: - self.cbox.prefs.userDB.write() + self.cbox.prefs.user_db.write() except IOError: self.cbox.log.warn("failed to write user database") else: @@ -69,31 +71,31 @@ class user_manager(cryptobox.plugins.base.CryptoBoxPlugin): if user in RESERVED_USERS: self.cbox.log.info("user_manager: tried to remove reserved user (%s)" % user) self.hdf["Data.Warning"] = "NeverRemoveReservedUser" - elif user in adminDict.keys(): - del adminDict[user] + elif user in admin_dict.keys(): + del admin_dict[user] self.hdf["Data.Success"] = "Plugins.user_manager.UserRemoved" try: - self.cbox.prefs.userDB.write() + self.cbox.prefs.user_db.write() except IOError: self.cbox.log.warn("failed to write user database") else: self.cbox.log.info("user_manager: tried to remove non-existing user (%s)" % str(user)) else: self.cbox.log.info("user_manager: invalid value of 'store' (%s)" % store) - self.__prepareHDF(adminDict) + self.__prepare_hdf(admin_dict) return "user_list" - def getStatus(self): - return ":".join(self.cbox.prefs.userDB["admins"].keys()) + def get_status(self): + return ":".join(self.cbox.prefs.user_db["admins"].keys()) - def __cleanHDF(self): + def __clean_hdf(self): for key in self.hdf.keys(): del self.hdf[key] - def __prepareHDF(self, dict): + def __prepare_hdf(self, dict): ## sort by name users = dict.keys() users.sort() diff --git a/plugins/volume_automount/unittests.py b/plugins/volume_automount/unittests.py index f4645f5..6701cd1 100644 --- a/plugins/volume_automount/unittests.py +++ b/plugins/volume_automount/unittests.py @@ -18,13 +18,15 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): """try to read automount form""" - url = self.URL + "volume_automount?weblang=en&device=%2Fdev%2F" + self.device + "1" + url = self.url + "volume_automount?weblang=en&device=%2Fdev%2F" + self.device self.register_auth(url) self.cmd.go(url) self.cmd.find('Activate during startup') @@ -32,21 +34,21 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_toggle(self): """try to toggle automount property""" - url = self.URL + "volume_automount" + url = self.url + "volume_automount" self.register_auth(url) - self.cmd.go(url + "?device=%%2Fdev%%2F%s1&action=disable" % self.device) + self.cmd.go(url + "?device=%%2Fdev%%2F%s&action=disable" % self.device) self.cmd.find("Automatic activation disabled") self.cmd.find("is disabled") - self.cmd.go(url + "?device=%%2Fdev%%2F%s1&action=enable" % self.device) + self.cmd.go(url + "?device=%%2Fdev%%2F%s&action=enable" % self.device) self.cmd.find("Automatic activation enabled") self.cmd.notfind("is disabled") def test_invalid_input(self): """check invalid inputs""" - url = self.URL + "volume_automount" + url = self.url + "volume_automount" self.register_auth(url) - self.cmd.go(url + "?device=%%2Fdev%%2F%s1&action=foobar" % self.device) + self.cmd.go(url + "?device=%%2Fdev%%2F%s&action=foobar" % self.device) self.cmd.notfind("Automatic activation disabled") self.cmd.notfind("Automatic activation enabled") diff --git a/plugins/volume_automount/volume_automount.py b/plugins/volume_automount/volume_automount.py index c0df4f2..6a87c14 100644 --- a/plugins/volume_automount/volume_automount.py +++ b/plugins/volume_automount/volume_automount.py @@ -18,58 +18,60 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base from cryptobox.core.exceptions import * class volume_automount(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "volume" ] - pluginVisibility = [ "properties" ] - requestAuth = False + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "properties" ] + request_auth = False rank = 80 - trueString = "yes" - falseString = "no" + true_string = "yes" + false_string = "no" - def doAction(self, action=None): - container = self.cbox.getContainer(self.device) + def do_action(self, action=None): + container = self.cbox.get_container(self.device) if action is None: pass elif action == "enable": - container.attributes["automount"] = self.trueString + container.attributes["automount"] = self.true_string self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountEnabled" self.cbox.log.info("volume_automount: enabled for device '%s'" % self.device) - self.cbox.prefs.volumesDB.write() + self.cbox.prefs.volumes_db.write() elif action == "disable": - container.attributes["automount"] = self.falseString + container.attributes["automount"] = self.false_string self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountDisabled" self.cbox.log.info("volume_automount: disabled for device '%s'" % self.device) - self.cbox.prefs.volumesDB.write() + self.cbox.prefs.volumes_db.write() else: self.cbox.log.info("volume_automount: invalid action (%s)" % str(action)) - self.__prepareHDF() + self.__prepare_hdf() return "volume_automount" - def getStatus(self): - return str(self.__isAutoMount()) + def get_status(self): + return str(self.__is_auto_mount()) - def __prepareHDF(self): - if self.__isAutoMount(): + def __prepare_hdf(self): + if self.__is_auto_mount(): self.hdf[self.hdf_prefix + "automount_setting"] = "1" else: self.hdf[self.hdf_prefix + "automount_setting"] = "0" - def __isAutoMount(self): - container = self.cbox.getContainer(self.device) + def __is_auto_mount(self): + container = self.cbox.get_container(self.device) if not container: return False if container.attributes.has_key("automount"): - return container.attributes["automount"] == self.trueString + return container.attributes["automount"] == self.true_string else: return False diff --git a/plugins/volume_chpasswd/unittests.py b/plugins/volume_chpasswd/unittests.py index 55871d9..5ad7f7a 100644 --- a/plugins/volume_chpasswd/unittests.py +++ b/plugins/volume_chpasswd/unittests.py @@ -18,12 +18,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - url = self.URL + "volume_chpasswd?weblang=en&device=%2Fdev%2F" + self.device + "1" + url = self.url + "volume_chpasswd?weblang=en&device=%2Fdev%2F" + self.device self.register_auth(url) self.cmd.go(url) self.cmd.find('hange') diff --git a/plugins/volume_chpasswd/volume_chpasswd.py b/plugins/volume_chpasswd/volume_chpasswd.py index f307368..8482950 100644 --- a/plugins/volume_chpasswd/volume_chpasswd.py +++ b/plugins/volume_chpasswd/volume_chpasswd.py @@ -18,24 +18,26 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base from cryptobox.core.exceptions import * class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "volume" ] - pluginVisibility = [ "properties" ] - requestAuth = False + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "properties" ] + request_auth = False rank = 70 - def doAction(self, store=None, old_pw=None, new_pw=None, new_pw2=None): - self.container = self.cbox.getContainer(self.device) + def do_action(self, store=None, old_pw=None, new_pw=None, new_pw2=None): + self.container = self.cbox.get_container(self.device) if not self.container: return None elif store == "change_pw": - return self.__changePassword(old_pw, new_pw, new_pw2) + return self.__change_password(old_pw, new_pw, new_pw2) elif not store: return "volume_chpasswd" else: @@ -43,11 +45,11 @@ class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin): return "volume_chpasswd" - def getStatus(self): + def get_status(self): return "TODO" - def __changePassword(self, old_pw, new_pw, new_pw2): + def __change_password(self, old_pw, new_pw, new_pw2): if not old_pw: self.hdf["Data.Warning"] = "EmptyPassword" elif not new_pw: @@ -59,13 +61,13 @@ class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin): pass else: try: - self.container.changePassword(old_pw, new_pw) - except CBInvalidType, errMsg: - self.cbox.log.info("plugin 'volume_chpasswd' - cannot change passphrase for non-encrypted container (%s): %s" % (self.device, errMsg)) + self.container.change_password(old_pw, new_pw) + except CBInvalidType, err_msg: + self.cbox.log.info("plugin 'volume_chpasswd' - cannot change passphrase for non-encrypted container (%s): %s" % (self.device, err_msg)) except CBVolumeIsActive: self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" - except CBChangePasswordError, errMsg: - self.cbox.log.warn("plugin 'volume_chpasswd' - cannot change password for device (%s): %s" % (self.device, errMsg)) + except CBChangePasswordError, err_msg: + self.cbox.log.warn("plugin 'volume_chpasswd' - cannot change password for device (%s): %s" % (self.device, err_msg)) self.hdf["Data.Warning"] = "Plugins.volume_chpasswd.PasswordChange" else: self.hdf["Data.Success"] = "Plugins.volume_chpasswd.PasswordChange" diff --git a/plugins/volume_details/unittests.py b/plugins/volume_details/unittests.py index 8835883..cad34fe 100644 --- a/plugins/volume_details/unittests.py +++ b/plugins/volume_details/unittests.py @@ -18,12 +18,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - url = self.URL + "volume_details?weblang=en&device=%2Fdev%2F" + self.device + "1" + url = self.url + "volume_details?weblang=en&device=%2Fdev%2F" + self.device self.register_auth(url) self.cmd.go(url) self.cmd.find('Technical details') diff --git a/plugins/volume_details/volume_details.py b/plugins/volume_details/volume_details.py index 2694a6e..4bfbdde 100644 --- a/plugins/volume_details/volume_details.py +++ b/plugins/volume_details/volume_details.py @@ -18,22 +18,24 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base class volume_details(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "volume" ] - pluginVisibility = [ "volume" ] - requestAuth = False + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "volume" ] + request_auth = False rank = 100 - def doAction(self): + def do_action(self): ## all variables are already set somewhere else return "volume_details" - def getStatus(self): + def get_status(self): return "no status" diff --git a/plugins/volume_format_fs/unittests.py b/plugins/volume_format_fs/unittests.py index 12d3938..c58738b 100644 --- a/plugins/volume_format_fs/unittests.py +++ b/plugins/volume_format_fs/unittests.py @@ -18,12 +18,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - url = self.URL + "volume_format_fs?weblang=en&device=%2Fdev%2F" + self.device + "1" + url = self.url + "volume_format_fs?weblang=en&device=%2Fdev%2F" + self.device self.register_auth(url) self.cmd.go(url) self.cmd.find('Initializing filesystem') diff --git a/plugins/volume_format_fs/volume_format_fs.py b/plugins/volume_format_fs/volume_format_fs.py index f7e1364..4f3ffbd 100644 --- a/plugins/volume_format_fs/volume_format_fs.py +++ b/plugins/volume_format_fs/volume_format_fs.py @@ -18,32 +18,33 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base from cryptobox.core.exceptions import * -import cryptobox.core.container as cbxContainer +import cryptobox.core.container as cbx_container + +## map filesystem types to the appropriate arguments for 'mkfs' +FSTYPES = { + "windows": "vfat", + "linux": "ext3" } + class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "volume" ] - pluginVisibility = [ "volume" ] - requestAuth = True + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "volume" ] + request_auth = True rank = 60 - ## map filesystem types to the appropriate arguments for 'mkfs' - fsTypes = { - "windows": "vfat", - "linux": "ext3" } - - containerTypes = [ "luks", "plain" ] - - def doAction(self, store=None, fs_type="windows", container_type="luks", crypto_password=None, crypto_password2=None, confirm=None): - if not fs_type in self.fsTypes.keys(): + def do_action(self, store=None, fs_type="windows", container_type="luks", crypto_password=None, crypto_password2=None, confirm=None): + if not fs_type in FSTYPES.keys(): self.cbox.info return "format_volume" self.hdf[self.hdf_prefix + "fs_type"] = fs_type self.hdf[self.hdf_prefix + "container_type"] = container_type - for t in self.fsTypes.keys(): + for t in FSTYPES.keys(): self.hdf[self.hdf_prefix + "fs_types." + t] = t if store == "step1": if not confirm: @@ -67,14 +68,14 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin): return "volume_format" - def getStatus(self): + def get_status(self): return "no status" def __format_plain(self, fsType): try: - container = self.cbox.getContainer(self.device) - container.create(cbxContainer.ContainerTypes["plain"]) + container = self.cbox.get_container(self.device) + container.create(cbx_container.CONTAINERTYPES["plain"]) except CBVolumeIsActive: self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device) @@ -98,9 +99,9 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin): self.hdf["Data.Warning"] = "DifferentPasswords" self.cbox.log.warn("the crypto password was not repeated correctly for initialization of device '%s'" % self.device) return "volume_format" - container = self.cbox.getContainer(self.device) + container = self.cbox.get_container(self.device) try: - container.create(cbxContainer.ContainerTypes["luks"], pw) + container.create(cbx_container.CONTAINERTYPES["luks"], pw) except CBVolumeIsActive: self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device) diff --git a/plugins/volume_mount/unittests.py b/plugins/volume_mount/unittests.py index 609258f..2aa68f5 100644 --- a/plugins/volume_mount/unittests.py +++ b/plugins/volume_mount/unittests.py @@ -18,12 +18,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - url = self.URL + "volume_mount?weblang=en&device=%2Fdev%2F" + self.device + "1" + url = self.url + "volume_mount?weblang=en&device=%2Fdev%2F" + self.device self.register_auth(url) self.cmd.go(url) self.cmd.find('ctivate volume') diff --git a/plugins/volume_mount/volume_mount.py b/plugins/volume_mount/volume_mount.py index 1136348..97e4c6d 100644 --- a/plugins/volume_mount/volume_mount.py +++ b/plugins/volume_mount/volume_mount.py @@ -18,6 +18,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base from cryptobox.core.exceptions import * import cryptobox.core.container as cbxContainer @@ -25,20 +27,20 @@ import cryptobox.core.container as cbxContainer class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "volume" ] - pluginVisibility = [ "volume" ] - requestAuth = False + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "volume" ] + request_auth = False rank = 0 - def doAction(self, action=None, pw=None): - self.container = self.cbox.getContainer(self.device) + def do_action(self, action=None, pw=None): + self.container = self.cbox.get_container(self.device) if action == "mount_plain": - return self.__doMountPlain() + return self.__do_mount_plain() elif action == "mount_luks": - return self.__doMountLuks(pw) + return self.__do_mount_luks(pw) elif action == "umount": - return self.__doUmount() + return self.__do_umount() elif not action: return "volume_status" else: @@ -46,43 +48,43 @@ class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin): return None - def getStatus(self): - container = self.cbox.getContainer(self.device) + def get_status(self): + container = self.cbox.get_container(self.device) if not self.container: return "invalid device" - if container.isMounted(): + if container.is_mounted(): return "active" else: return "passive" - def __doMountPlain(self): - if self.container.isMounted(): + def __do_mount_plain(self): + if self.container.is_mounted(): self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" self.cbox.log.info("the device (%s) is already mounted" % self.device) return "volume_status" - if self.container.getType() != cbxContainer.ContainerTypes["plain"]: + if self.container.get_type() != cbxContainer.CONTAINERTYPES["plain"]: ## not a plain container self.cbox.log.info("plugin 'volume_mount' - invalid container type") self.hdf["Data.Warning"] = "Plugins.volume_mount.InvalidContainerType" return "volume_status" try: self.container.mount() - except CBMountError, errMsg: + except CBMountError, err_msg: self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" - self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg)) + self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, err_msg)) return "volume_status" - except CBContainerError, errMsg: + except CBContainerError, err_msg: self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" - self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg)) + self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, err_msg)) return "volume_status" self.cbox.log.info("successfully mounted the volume: %s" % self.device) self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" return "volume_status" - def __doMountLuks(self, pw): - if self.container.isMounted(): + def __do_mount_luks(self, pw): + if self.container.is_mounted(): self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" self.cbox.log.info("the device (%s) is already mounted" % self.device) return "volume_status" @@ -90,35 +92,35 @@ class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin): self.dataset["Data.Warning"] = "EmptyPassword" self.log.info("no password was supplied for mounting of device: '%s'" % self.device) return "volume_status" - if self.container.getType() != cbxContainer.ContainerTypes["luks"]: + if self.container.get_type() != cbxContainer.CONTAINERTYPES["luks"]: ## not a luks container - fail silently self.cbox.log.info("plugin 'volume_mount' - invalid container type") return "volume_status" try: self.container.mount(pw) - except CBMountError, errMsg: + except CBMountError, err_msg: self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" - self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg)) + self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, err_msg)) return "volume_status" - except CBContainerError, errMsg: + except CBContainerError, err_msg: self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" - self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, errMsg)) + self.cbox.log.warn("failed to mount the device (%s): %s" % (self.device, err_msg)) return "volume_status" self.cbox.log.info("successfully mounted the volume: %s" % self.device) self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" return "volume_status" - def __doUmount(self): - if not self.container.isMounted(): + def __do_umount(self): + if not self.container.is_mounted(): self.hdf["Data.Warning"] = "Plugins.volume_mount.IsNotMounted" self.cbox.log.info("the device (%s) is currently not mounted" % self.device) return "volume_status" try: self.container.umount() - except CBUmountError, errMsg: + except CBUmountError, err_msg: self.hdf["Data.Warning"] = "UmountFailed" - self.cbox.log.warn("could not umount the volume (%s): %s" % (self.device, errMsg)) + self.cbox.log.warn("could not umount the volume (%s): %s" % (self.device, err_msg)) return "volume_status" self.cbox.log.info("successfully unmounted the container: %s" % self.device) self.hdf["Data.Success"] = "Plugins.volume_mount.UmountDone" diff --git a/plugins/volume_props/unittests.py b/plugins/volume_props/unittests.py index 1f8d802..f2c7281 100644 --- a/plugins/volume_props/unittests.py +++ b/plugins/volume_props/unittests.py @@ -18,12 +18,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - url = self.URL + "volume_props?weblang=en&device=%2Fdev%2F" + self.device + "1" + url = self.url + "volume_props?weblang=en&device=%2Fdev%2F" + self.device self.register_auth(url) self.cmd.go(url) self.cmd.find('Properties') diff --git a/plugins/volume_props/volume_props.py b/plugins/volume_props/volume_props.py index 9e9a7af..9e9e937 100644 --- a/plugins/volume_props/volume_props.py +++ b/plugins/volume_props/volume_props.py @@ -18,6 +18,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base import cryptobox.plugins.manage from cryptobox.core.exceptions import * @@ -25,51 +27,54 @@ from cryptobox.core.exceptions import * class volume_props(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "volume" ] - pluginVisibility = [ "volume" ] - requestAuth = False + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "volume" ] + request_auth = False rank = 30 - def doAction(self, **args): + def do_action(self, **args): import os - self.props_plugins = [e for e in cryptobox.plugins.manage.PluginManager(self.cbox, self.cbox.prefs["Locations"]["PluginDir"]).getPlugins() if "properties" in e.getVisibility()] + self.props_plugins = [e for e in cryptobox.plugins.manage.PluginManager(self.cbox, + self.cbox.prefs["Locations"]["PluginDir"]).get_plugins() + if "properties" in e.get_visibility()] ## sort plugins by rank - self.props_plugins.sort(cmp = self.__cmpPluginsRank) + self.props_plugins.sort(cmp = self.__cmp_plugins_rank) ## set the name of the templates for every plugin - loadString = "" + load_string = "" for p in self.props_plugins: p.device = self.device - plfname = os.path.join(p.pluginDir, str(p.doAction(**args)) + ".cs") - loadString += "" % plfname + plfname = os.path.join(p.plugin_dir, str(p.do_action(**args)) + ".cs") + load_string += "" % plfname ## this is a little bit ugly: as it is not possible, to load cs files via ## 'linclude' (see clearsilver doc) if they use previously defined macros (see ## clearsilver mailing list thread 'linclude file which calls a macro' - 27th ## December 02005) ## our workaround: define the appropriate "include" (not 'linclude') commands ## as a hdf variable - then we can include it via 'evar' - self.hdf[self.hdf_prefix + 'includePlugins'] = loadString + self.hdf[self.hdf_prefix + 'includePlugins'] = load_string return "volume_properties" - def getStatus(self): + def get_status(self): return "TODO" - def loadDataSet(self, hdf): + def load_dataset(self, hdf): """override the parent's function we have to get the data from all included plugins""" - for p in self.props_plugins: - p.loadDataSet(hdf) + for plugin in self.props_plugins: + plugin.load_dataset(hdf) ## call our parent's method - cryptobox.plugins.base.CryptoBoxPlugin.loadDataSet(self, hdf) + cryptobox.plugins.base.CryptoBoxPlugin.load_dataset(self, hdf) - def __cmpPluginsRank(self, p1, p2): - order = p1.getRank() - p2.getRank() + def __cmp_plugins_rank(self, p1, p2): + order = p1.get_rank() - p2.get_rank() if order < 0: return -1 elif order == 0: return 0 else: return 1 + diff --git a/plugins/volume_rename/unittests.py b/plugins/volume_rename/unittests.py index 97b5a72..dd83de6 100644 --- a/plugins/volume_rename/unittests.py +++ b/plugins/volume_rename/unittests.py @@ -18,12 +18,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.web.testclass class unittests(cryptobox.web.testclass.WebInterfaceTestClass): def test_read_form(self): - url = self.URL + "volume_rename?weblang=en&device=%2Fdev%2F" + self.device + "1" + url = self.url + "volume_rename?weblang=en&device=%2Fdev%2F" + self.device self.register_auth(url) self.cmd.go(url) self.cmd.find('name') diff --git a/plugins/volume_rename/volume_rename.py b/plugins/volume_rename/volume_rename.py index 60fc65f..5a0d036 100644 --- a/plugins/volume_rename/volume_rename.py +++ b/plugins/volume_rename/volume_rename.py @@ -18,49 +18,51 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +__revision__ = "$Id" + import cryptobox.plugins.base from cryptobox.core.exceptions import * class volume_rename(cryptobox.plugins.base.CryptoBoxPlugin): - pluginCapabilities = [ "volume" ] - pluginVisibility = [ "properties" ] - requestAuth = False + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "properties" ] + request_auth = False rank = 60 - def doAction(self, store=None, vol_name=None): - self.container = self.cbox.getContainer(self.device) + def do_action(self, store=None, vol_name=None): + self.container = self.cbox.get_container(self.device) if not self.container: return None - self.__prepareHDF() + self.__prepare_hdf() if store and vol_name: - return self.__setVolumeName(vol_name) + return self.__set_volume_name(vol_name) else: return "volume_rename" - def getStatus(self): - self.container = self.cbox.getContainer(self.device) + def get_status(self): + self.container = self.cbox.get_container(self.device) if not self.container: return "invalid device" - return "name=%s" % self.container.getName() + return "name=%s" % self.container.get_name() - def __prepareHDF(self): - self.hdf[self.hdf_prefix + "vol_name"] = self.container.getName() + def __prepare_hdf(self): + self.hdf[self.hdf_prefix + "vol_name"] = self.container.get_name() - def __setVolumeName(self, vol_name): + def __set_volume_name(self, vol_name): if not vol_name: self.hdf["Data.Warning"] = "Plugins.volume_rename.InvalidVolumeName" return "volume_rename" - if vol_name == self.container.getName(): + if vol_name == self.container.get_name(): ## nothing has to be done return "volume_rename" try: - self.container.setName(vol_name) + self.container.set_name(vol_name) self.hdf["Data.Success"] = "Plugins.volume_rename.VolumeNameChanged" except CBVolumeIsActive: self.hdf["Data.Warning"] = "Plugins.volume_rename.NoRenameIfActive" @@ -71,6 +73,6 @@ class volume_rename(cryptobox.plugins.base.CryptoBoxPlugin): except CBContainerError: self.hdf["Data.Warning"] = "Plugins.volume_rename.SetVolumeNameFailed" ## reread the volume name - self.__prepareHDF() + self.__prepare_hdf() return "volume_rename" diff --git a/src/cryptobox/__init__.py b/src/cryptobox/__init__.py index 161c961..c261d92 100644 --- a/src/cryptobox/__init__.py +++ b/src/cryptobox/__init__.py @@ -1 +1,13 @@ -__all__ = ['core','web','plugins','tests'] +"""CryptoBox package + +The CryptoBox is a webserver. It enables you to control your encrypted +(cryptsetup-luks) and plaintext disks via an easy to use web interface. +The CryptoBox is especially suitable for non-desktop fileservers with +encrypted partitions. +""" + +__all__ = ['core', 'web', 'plugins', 'tests'] +__revision__ = "$Id$" + +__version__ = "0.2.99~1" + diff --git a/src/cryptobox/core/__init__.py b/src/cryptobox/core/__init__.py index e69de29..3b001bf 100644 --- a/src/cryptobox/core/__init__.py +++ b/src/cryptobox/core/__init__.py @@ -0,0 +1,7 @@ +"""Core management functions of the CryptoBox. +""" + +__revision__ = "$Id" + +__all__ = [ 'main', 'container', 'exceptions', 'tools', 'settings' ] + diff --git a/src/cryptobox/core/container.py b/src/cryptobox/core/container.py index 539b0fe..c2d4d6b 100644 --- a/src/cryptobox/core/container.py +++ b/src/cryptobox/core/container.py @@ -18,21 +18,18 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -## check python version -import sys -(ver_major, ver_minor, ver_sub, ver_desc, ver_subsub) = sys.version_info -if (ver_major < 2) or ((ver_major == 2) and (ver_minor < 4)): - sys.stderr.write("You need a python version >= 2.4\nCurrent version is:\n %s\n" % sys.version) - sys.exit(1) +"""Manage a single container of the CryptoBox +""" + +__revision__ = "$Id" import subprocess import os import re -import logging from cryptobox.core.exceptions import * -ContainerTypes = { +CONTAINERTYPES = { "unused":0, "plain":1, "luks":2, @@ -46,11 +43,12 @@ MOUNT_DIR_MARKER = '_cryptobox_mount_dir_' class CryptoBoxContainer: + """Manage a container of the CryptoBox + """ __fsTypes = { - "plain":["ext3", "ext2", "vfat", "reiser"], + "plain":["ext3", "ext2", "vfat", "reiserfs"], "swap":["swap"]} - # TODO: more filesystem types? / check 'reiser' __dmDir = "/dev/mapper" @@ -58,41 +56,58 @@ class CryptoBoxContainer: def __init__(self, device, cbox): self.device = device self.cbox = cbox - self.log = logging.getLogger("CryptoBox") - self.resetObject() + self.uuid = None + self.name = None + self.cont_type = None + self.mount = None + self.umount = None + self.attributes = None + self.reset_object() - def getName(self): + def get_name(self): + """Return a humanly readable name for the container. + """ return self.name - def __setAttributes(self): + def __set_attributes(self): + """Define the default attributes of a container. + + At least there should be a uuid. + Other attributes may be added by features (e.g. automount). + """ try: ## is there already an entry in the database? - self.attributes = self.cbox.prefs.volumesDB[self.getName()] + self.attributes = self.cbox.prefs.volumes_db[self.get_name()] self.attributes["uuid"] = self.uuid except KeyError: ## set default values self.attributes = { "uuid": self.uuid } - self.cbox.prefs.volumesDB[self.getName()] = self.attributes + self.cbox.prefs.volumes_db[self.get_name()] = self.attributes - def setName(self, new_name): - old_name = self.getName() - if new_name == self.name: return + def set_name(self, new_name): + """Define a humanly readable name of this container. + + this also manages the name database + """ + old_name = self.get_name() + if new_name == self.name: + return ## renaming is not possible, if the volume is active, as the mountpoint name ## is the same as the volume name - if self.isMounted(): + if self.is_mounted(): raise CBVolumeIsActive("the container must not be active during renaming") if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name): raise CBInvalidName("the supplied new name contains illegal characters") ## check for another partitions with the same name - if self.cbox.getContainerList(filterName=new_name): + if self.cbox.get_container_list(filter_name=new_name): raise CBNameIsInUse("the supplied new name is already in use for anonther partition") ## maybe there a is an entry in the volumes database (but the partition is not active try: ## remove possibly existing inactive database item - del self.cbox.prefs.volumesDB[new_name] + del self.cbox.prefs.volumes_db[new_name] except KeyError: ## no entry - so nothing happens pass @@ -100,99 +115,116 @@ class CryptoBoxContainer: self.name = new_name ## remove old database entry try: - del self.cbox.prefs.volumesDB[old_name] + del self.cbox.prefs.volumes_db[old_name] except KeyError: pass ## set new volumes database entry - self.cbox.prefs.volumesDB[new_name] = self.attributes - self.cbox.prefs.volumesDB.write() + self.cbox.prefs.volumes_db[new_name] = self.attributes + self.cbox.prefs.volumes_db.write() - def getDevice(self): + def get_device(self): + """Return the device name of the container + + e.g.: /dev/hdc1 + """ return self.device - def getType(self): - return self.type + def get_type(self): + """Return the type (int) of this container. + """ + return self.cont_type - def isMounted(self): - return os.path.ismount(self.__getMountPoint()) + def is_mounted(self): + """Check if the container is currently mounted. + """ + return os.path.ismount(self.__get_mount_point()) - def getCapacity(self): - """return the current capacity state of the volume + def get_capacity(self): + """Return the current capacity state of the volume. the volume may not be mounted the result is a tuple of values in megabyte: (size, available, used) """ - info = os.statvfs(self.__getMountPoint()) + info = os.statvfs(self.__get_mount_point()) return ( int(info.f_bsize*info.f_blocks/1024/1024), int(info.f_bsize*info.f_bavail/1024/1024), int(info.f_bsize*(info.f_blocks-info.f_bavail)/1024/1024)) - def getSize(self): + def get_size(self): """return the size of the block device (_not_ of the filesystem) the result is a value in megabyte an error is indicated by "-1" """ import cryptobox.core.tools as cbxtools - return cbxtools.getBlockDeviceSize(self.device) + return cbxtools.get_blockdevice_size(self.device) - def resetObject(self): + def reset_object(self): """ recheck the information about this container this is especially useful after changing the type via 'create' """ - self.uuid = self.__getUUID() - self.type = self.__getTypeOfPartition() - self.name = self.__getNameOfContainer() - self.__setAttributes() - if self.type == ContainerTypes["luks"]: - self.mount = self.__mountLuks - self.umount = self.__umountLuks - elif self.type == ContainerTypes["plain"]: - self.mount = self.__mountPlain - self.umount = self.__umountPlain + self.uuid = self.__get_uuid() + self.cont_type = self.__get_type_of_partition() + self.name = self.__get_name_of_container() + self.__set_attributes() + if self.cont_type == CONTAINERTYPES["luks"]: + self.mount = self.__mount_luks + self.umount = self.__umount_luks + elif self.cont_type == CONTAINERTYPES["plain"]: + self.mount = self.__mount_plain + self.umount = self.__umount_plain - def create(self, type, password=None): - old_name = self.getName() - if type == ContainerTypes["luks"]: - self.__createLuks(password) - elif type == ContainerTypes["plain"]: - self.__createPlain() + def create(self, cont_type, password=None): + """Format a container. + + Also set a password for encrypted container. + """ + old_name = self.get_name() + if cont_type == CONTAINERTYPES["luks"]: + self.__create_luks(password) + elif cont_type == CONTAINERTYPES["plain"]: + self.__create_plain() else: - raise CBInvalidType("invalid container type (%d) supplied" % (type, )) + raise CBInvalidType("invalid container type (%d) supplied" % (cont_type, )) ## no exception was raised during creation -> we can continue ## reset the properties (encryption state, ...) of the device - self.resetObject() - ## restore the old name (must be after resetObject) - self.setName(old_name) + self.reset_object() + ## restore the old name (must be after reset_object) + self.set_name(old_name) - def changePassword(self, oldpw, newpw): - if self.type != ContainerTypes["luks"]: + def change_password(self, oldpw, newpw): + """Change the password of an encrypted container. + + Raises an exception for plaintext container. + """ + if self.cont_type != CONTAINERTYPES["luks"]: raise CBInvalidType("changing of password is possible only for luks containers") if not oldpw: raise CBInvalidPassword("no old password supplied for password change") if not newpw: raise CBInvalidPassword("no new password supplied for password change") - "return if new and old passwords are the same" - if oldpw == newpw: return - if self.isMounted(): + ## return if new and old passwords are the same + if oldpw == newpw: + return + if self.is_mounted(): raise CBVolumeIsActive("this container is currently active") devnull = None try: devnull = open(os.devnull, "w") except IOError: - self.log.warn("Could not open %s" % (os.devnull, )) - "remove any potential open luks mapping" - self.__umountLuks() - "create the luks header" + self.cbox.log.warn("Could not open %s" % (os.devnull, )) + ## remove any potential open luks mapping + self.__umount_luks() + ## create the luks header proc = subprocess.Popen( shell = False, stdin = subprocess.PIPE, @@ -208,16 +240,17 @@ class CryptoBoxContainer: proc.stdin.write("%s\n%s" % (oldpw, newpw)) (output, errout) = proc.communicate() if proc.returncode != 0: - errorMsg = "Could not add a new luks key: %s - %s" % (output.strip(), errout.strip(), ) - self.log.error(errorMsg) - raise CBChangePasswordError(errorMsg) + error_msg = "Could not add a new luks key: %s - %s" \ + % (output.strip(), errout.strip(), ) + self.cbox.log.error(error_msg) + raise CBChangePasswordError(error_msg) ## retrieve the key slot we used for unlocking keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups() if keys_found: keyslot = int(keys_found[0]) else: raise CBChangePasswordError("could not get the old key slot") - "remove the old key" + ## remove the old key proc = subprocess.Popen( shell = False, stdin = None, @@ -231,40 +264,43 @@ class CryptoBoxContainer: "%d" % (keyslot, )]) proc.wait() if proc.returncode != 0: - errorMsg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), ) - self.log.error(errorMsg) - raise CBChangePasswordError(errorMsg) + error_msg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.error(error_msg) + raise CBChangePasswordError(error_msg) - " ****************** internal stuff ********************* " + ## ****************** internal stuff ********************* - def __getNameOfContainer(self): + def __get_name_of_container(self): """retrieve the name of the container by querying the database call this function only for the initial setup of the container object""" found_name = None - for key in self.cbox.prefs.volumesDB.keys(): - if self.cbox.prefs.volumesDB[key]["uuid"] == self.uuid: + for key in self.cbox.prefs.volumes_db.keys(): + if self.cbox.prefs.volumes_db[key]["uuid"] == self.uuid: found_name = key - if found_name: return found_name + if found_name: + return found_name ## there is no name defined for this uuid - we will propose a good one prefix = self.cbox.prefs["Main"]["DefaultVolumePrefix"] unused_found = False counter = 1 while not unused_found: guess = prefix + str(counter) - if self.cbox.prefs.volumesDB.has_key(guess): + if self.cbox.prefs.volumes_db.has_key(guess): counter += 1 else: unused_found = True return guess - def __getUUID(self): - if self.__getTypeOfPartition() == ContainerTypes["luks"]: - guess = self.__getLuksUUID() + def __get_uuid(self): + """Retrieve the uuid of the container device. + """ + if self.__get_type_of_partition() == CONTAINERTYPES["luks"]: + guess = self.__get_luks_uuid() else: - guess = self.__getNonLuksUUID() + guess = self.__get_non_luks_uuid() ## did we get a valid value? if guess: return guess @@ -273,7 +309,7 @@ class CryptoBoxContainer: return self.device.replace(os.path.sep, "_") - def __getLuksUUID(self): + def __get_luks_uuid(self): """get uuid for luks devices""" proc = subprocess.Popen( shell = False, @@ -284,17 +320,18 @@ class CryptoBoxContainer: self.device]) (stdout, stderr) = proc.communicate() if proc.returncode != 0: - self.cbox.log.info("could not retrieve luks uuid (%s): %s", (self.device, stderr.strip())) + self.cbox.log.info("could not retrieve luks uuid (%s): %s", + (self.device, stderr.strip())) return None return stdout.strip() - def __getNonLuksUUID(self): + def __get_non_luks_uuid(self): """return UUID for ext2/3 and vfat filesystems""" try: devnull = open(os.devnull, "w") except IOError: - self.warn("Could not open %s" % (os.devnull, )) + self.cbox.log.warn("Could not open %s" % (os.devnull, )) proc = subprocess.Popen( shell=False, stdout=subprocess.PIPE, @@ -309,30 +346,36 @@ class CryptoBoxContainer: devnull.close() ## execution failed? if proc.returncode != 0: - self.log.info("retrieving of partition type (%s) via 'blkid' failed: %s - maybe it is encrypted?" % (self.device, stderr.strip())) + self.cbox.log.info("retrieving of partition type (" + str(self.device) \ + + ") via 'blkid' failed: " + str(stderr.strip()) \ + + " - maybe it is encrypted?") return None ## return output of blkid return stdout.strip() - def __getTypeOfPartition(self): - "retrieve the type of the given partition (see cryptobox.core.container.ContainerTypes)" - if self.__isLuksPartition(): return ContainerTypes["luks"] - typeOfPartition = self.__getTypeIdOfPartition() - if typeOfPartition in self.__fsTypes["plain"]: - return ContainerTypes["plain"] - if typeOfPartition in self.__fsTypes["swap"]: - return ContainerTypes["swap"] - return ContainerTypes["unused"] + def __get_type_of_partition(self): + """Retrieve the type of the given partition. + + see cryptobox.core.container.CONTAINERTYPES + """ + if self.__is_luks_partition(): + return CONTAINERTYPES["luks"] + type_of_partition = self.__get_type_id_of_partition() + if type_of_partition in self.__fsTypes["plain"]: + return CONTAINERTYPES["plain"] + if type_of_partition in self.__fsTypes["swap"]: + return CONTAINERTYPES["swap"] + return CONTAINERTYPES["unused"] - def __getTypeIdOfPartition(self): + def __get_type_id_of_partition(self): "returns the type of the partition (see 'man blkid')" devnull = None try: devnull = open(os.devnull, "w") except IOError: - self.log.warn("Could not open %s" % (os.devnull, )) + self.cbox.log.warn("Could not open %s" % (os.devnull, )) proc = subprocess.Popen( shell=False, stdin=None, @@ -347,19 +390,20 @@ class CryptoBoxContainer: proc.wait() output = proc.stdout.read().strip() if proc.returncode != 0: - self.log.warn("retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), )) + self.cbox.log.warn("retrieving of partition type via 'blkid' failed: %s" % \ + (proc.stderr.read().strip(), )) return None devnull.close() return output - def __isLuksPartition(self): + def __is_luks_partition(self): "check if the given device is a luks partition" devnull = None try: devnull = open(os.devnull, "w") except IOError: - self.log.warn("Could not open %s" % (os.devnull, )) + self.cbox.log.warn("Could not open %s" % (os.devnull, )) proc = subprocess.Popen( shell = False, stdin = None, @@ -375,29 +419,30 @@ class CryptoBoxContainer: return proc.returncode == 0 - def __getMountPoint(self): + def __get_mount_point(self): "return the name of the mountpoint of this volume" return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], self.name) - def __mountLuks(self, password): + def __mount_luks(self, password): "mount a luks partition" if not password: raise CBInvalidPassword("no password supplied for luksOpen") - if self.isMounted(): raise CBVolumeIsActive("this container is already active") - self.__umountLuks() + if self.is_mounted(): + raise CBVolumeIsActive("this container is already active") + self.__umount_luks() try: devnull = open(os.devnull, "w") except IOError: - self.log.warn("Could not open %s" % (os.devnull, )) - self.__cleanMountDirs() - if not os.path.exists(self.__getMountPoint()): - self.__createMountDirectory(self.__getMountPoint()) - if not os.path.exists(self.__getMountPoint()): - errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), ) - self.log.error(errorMsg) - raise CBMountError(errorMsg) - self.cbox.sendEventNotification("premount", self.__getEventArgs()) + self.cbox.log.warn("Could not open %s" % (os.devnull, )) + self.__clean_mount_dirs() + if not os.path.exists(self.__get_mount_point()): + self.__create_mount_directory(self.__get_mount_point()) + if not os.path.exists(self.__get_mount_point()): + err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), ) + self.cbox.log.error(err_msg) + raise CBMountError(err_msg) + self.cbox.send_event_notification("premount", self.__get_event_args()) proc = subprocess.Popen( shell = False, stdin = subprocess.PIPE, @@ -414,9 +459,9 @@ class CryptoBoxContainer: proc.stdin.write(password) (output, errout) = proc.communicate() if proc.returncode != 0: - errorMsg = "Could not open the luks mapping: %s" % (errout.strip(), ) - self.log.warn(errorMsg) - raise CBMountError(errorMsg) + err_msg = "Could not open the luks mapping: %s" % (errout.strip(), ) + self.cbox.log.warn(err_msg) + raise CBMountError(err_msg) proc = subprocess.Popen( shell = False, stdin = None, @@ -427,25 +472,25 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "mount", os.path.join(self.__dmDir, self.name), - self.__getMountPoint()]) + self.__get_mount_point()]) proc.wait() if proc.returncode != 0: - errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) - self.log.warn(errorMsg) - raise CBMountError(errorMsg) + err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBMountError(err_msg) devnull.close() - self.cbox.sendEventNotification("postmount", self.__getEventArgs()) + self.cbox.send_event_notification("postmount", self.__get_event_args()) - def __umountLuks(self): + def __umount_luks(self): "umount a luks partition" devnull = None try: devnull = open(os.devnull, "w") except IOError: - self.log.warn("Could not open %s" % (os.devnull, )) - self.cbox.sendEventNotification("preumount", self.__getEventArgs()) - if self.isMounted(): + self.cbox.log.warn("Could not open %s" % (os.devnull, )) + self.cbox.send_event_notification("preumount", self.__get_event_args()) + if self.is_mounted(): proc = subprocess.Popen( shell = False, stdin = None, @@ -455,12 +500,12 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "umount", - self.__getMountPoint()]) + self.__get_mount_point()]) proc.wait() if proc.returncode != 0: - errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) - self.log.warn(errorMsg) - raise CBUmountError(errorMsg) + err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBUmountError(err_msg) if os.path.exists(os.path.join(self.__dmDir, self.name)): proc = subprocess.Popen( shell = False, @@ -476,29 +521,30 @@ class CryptoBoxContainer: "--batch-mode"]) proc.wait() if proc.returncode != 0: - errorMsg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), ) - self.log.warn(errorMsg) - raise CBUmountError(errorMsg) + err_msg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBUmountError(err_msg) devnull.close() - self.cbox.sendEventNotification("postumount", self.__getEventArgs()) + self.cbox.send_event_notification("postumount", self.__get_event_args()) - def __mountPlain(self): + def __mount_plain(self): "mount a plaintext partition" - if self.isMounted(): raise CBVolumeIsActive("this container is already active") + if self.is_mounted(): + raise CBVolumeIsActive("this container is already active") devnull = None try: devnull = open(os.devnull, "w") except IOError: - self.log.warn("Could not open %s" % (os.devnull, )) - self.__cleanMountDirs() - if not os.path.exists(self.__getMountPoint()): - self.__createMountDirectory(self.__getMountPoint()) - if not os.path.exists(self.__getMountPoint()): - errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), ) - self.log.error(errorMsg) - raise CBMountError(errorMsg) - self.cbox.sendEventNotification("premount", self.__getEventArgs()) + self.cbox.log.warn("Could not open %s" % (os.devnull, )) + self.__clean_mount_dirs() + if not os.path.exists(self.__get_mount_point()): + self.__create_mount_directory(self.__get_mount_point()) + if not os.path.exists(self.__get_mount_point()): + err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), ) + self.cbox.log.error(err_msg) + raise CBMountError(err_msg) + self.cbox.send_event_notification("premount", self.__get_event_args()) proc = subprocess.Popen( shell = False, stdin = None, @@ -509,27 +555,28 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "mount", self.device, - self.__getMountPoint()]) + self.__get_mount_point()]) proc.wait() if proc.returncode != 0: - errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) - self.log.warn(errorMsg) - raise CBMountError(errorMsg) + err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBMountError(err_msg) devnull.close() - self.cbox.sendEventNotification("postmount", self.__getEventArgs()) + self.cbox.send_event_notification("postmount", self.__get_event_args()) - def __umountPlain(self): + def __umount_plain(self): "umount a plaintext partition" - if not self.isMounted(): - self.cbox.log.info("trying to umount while volume (%s) is mounted" % self.getDevice()) + if not self.is_mounted(): + self.cbox.log.info("trying to umount while volume (%s) is mounted" % \ + self.get_device()) return devnull = None try: devnull = open(os.devnull, "w") except IOError: - self.log.warn("Could not open %s" % (os.devnull, )) - self.cbox.sendEventNotification("preumount", self.__getEventArgs()) + self.cbox.log.warn("Could not open %s" % (os.devnull, )) + self.cbox.send_event_notification("preumount", self.__get_event_args()) proc = subprocess.Popen( shell = False, stdin = None, @@ -539,25 +586,25 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"], "umount", - self.__getMountPoint()]) + self.__get_mount_point()]) proc.wait() if proc.returncode != 0: - errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) - self.log.warn(errorMsg) - raise CBUmountError(errorMsg) + err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBUmountError(err_msg) devnull.close() - self.cbox.sendEventNotification("postumount", self.__getEventArgs()) + self.cbox.send_event_notification("postumount", self.__get_event_args()) - def __createPlain(self): + def __create_plain(self): "make a plaintext partition" - if self.isMounted(): + if self.is_mounted(): raise CBVolumeIsActive("deactivate the partition before filesystem initialization") devnull = None try: devnull = open(os.devnull, "w") except IOError: - self.log.warn("Could not open %s" % (os.devnull, )) + self.cbox.log.warn("Could not open %s" % (os.devnull, )) proc = subprocess.Popen( shell = False, stdin = None, @@ -568,26 +615,27 @@ class CryptoBoxContainer: self.device]) proc.wait() if proc.returncode != 0: - errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) - self.log.error(errorMsg) - raise CBCreateError(errorMsg) + err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.error(err_msg) + raise CBCreateError(err_msg) devnull.close() - def __createLuks(self, password): - "make a luks partition" + def __create_luks(self, password): + """Create a luks partition. + """ if not password: raise CBInvalidPassword("no password supplied for new luks mapping") - if self.isMounted(): + if self.is_mounted(): raise CBVolumeIsActive("deactivate the partition before filesystem initialization") devnull = None try: devnull = open(os.devnull, "w") except IOError: - self.log.warn("Could not open %s" % (os.devnull, )) - "remove any potential open luks mapping" - self.__umountLuks() - "create the luks header" + self.cbox.log.warn("Could not open %s" % (os.devnull, )) + ## remove any potential open luks mapping + self.__umount_luks() + ## create the luks header proc = subprocess.Popen( shell = False, stdin = subprocess.PIPE, @@ -605,10 +653,10 @@ class CryptoBoxContainer: proc.stdin.write(password) (output, errout) = proc.communicate() if proc.returncode != 0: - errorMsg = "Could not create the luks header: %s" % (errout.strip(), ) - self.log.error(errorMsg) - raise CBCreateError(errorMsg) - "open the luks container for mkfs" + err_msg = "Could not create the luks header: %s" % (errout.strip(), ) + self.cbox.log.error(err_msg) + raise CBCreateError(err_msg) + ## open the luks container for mkfs proc = subprocess.Popen( shell = False, stdin = subprocess.PIPE, @@ -625,10 +673,10 @@ class CryptoBoxContainer: proc.stdin.write(password) (output, errout) = proc.communicate() if proc.returncode != 0: - errorMsg = "Could not open the new luks mapping: %s" % (errout.strip(), ) - self.log.error(errorMsg) - raise CBCreateError(errorMsg) - "make the filesystem" + err_msg = "Could not open the new luks mapping: %s" % (errout.strip(), ) + self.cbox.log.error(err_msg) + raise CBCreateError(err_msg) + ## make the filesystem proc = subprocess.Popen( shell = False, stdin = None, @@ -638,50 +686,54 @@ class CryptoBoxContainer: self.cbox.prefs["Programs"]["mkfs-data"], os.path.join(self.__dmDir, self.name)]) proc.wait() - "remove the mapping - for every exit status" - self.__umountLuks() + ## remove the mapping - for every exit status + self.__umount_luks() if proc.returncode != 0: - errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) - self.log.error(errorMsg) - "remove the luks mapping" - raise CBCreateError(errorMsg) + err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.error(err_msg) + ## remove the luks mapping + raise CBCreateError(err_msg) devnull.close() - def __cleanMountDirs(self): + def __clean_mount_dirs(self): """ remove all unnecessary subdirs of the mount parent directory this should be called for every (u)mount """ subdirs = os.listdir(self.cbox.prefs["Locations"]["MountParentDir"]) - for d in subdirs: - abs_dir = os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], d) + for one_dir in subdirs: + abs_dir = os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], one_dir) if (not os.path.islink(abs_dir)) \ and os.path.isdir(abs_dir) \ and (not os.path.ismount(abs_dir)) \ and (os.path.isfile(os.path.join(abs_dir,MOUNT_DIR_MARKER))) \ and (len(os.listdir(abs_dir)) == 1): try: - os.remove(os.path.join(abs_dir,MOUNT_DIR_MARKER)) + os.remove(os.path.join(abs_dir, MOUNT_DIR_MARKER)) os.rmdir(abs_dir) - except OSError,errMsg: + except OSError, err_msg: ## we do not care too much about unclean cleaning ... - self.log.info("failed to clean a mountpoint (%s): %s" % (abs_dir,str(errMsg))) + self.cbox.log.info("failed to clean a mountpoint (%s): %s" % \ + (abs_dir, str(err_msg))) - def __createMountDirectory(self,dirname): + def __create_mount_directory(self, dirname): """create and mark a mount directory this marking helps to remove old mountdirs safely""" os.mkdir(dirname) try: - f = file(os.path.join(dirname,MOUNT_DIR_MARKER),"w") - f.close() - except OSError,errMsg: + mark_file = file(os.path.join(dirname, MOUNT_DIR_MARKER), "w") + mark_file.close() + except OSError, err_msg: ## we do not care too much about the marking - self.log.info("failed to mark a mountpoint (%s): %s" % (dirname,str(errMsg))) + self.cbox.log.info("failed to mark a mountpoint (%s): %s" % (dirname, str(err_msg))) - def __getEventArgs(self): - """return an array of arguments for event scripts handling pre/post-mount/umount - events""" - typeText = [e for e in ContainerTypes.keys() if ContainerTypes[e] == self.getType()][0] - return [self.getDevice(), self.getName(), typeText, self.__getMountPoint()] + def __get_event_args(self): + """Return an array of arguments for event scripts. + + for now supported: pre/post-mount/umount events + """ + type_text = [e for e in CONTAINERTYPES.keys() + if CONTAINERTYPES[e] == self.get_type()][0] + return [self.get_device(), self.get_name(), type_text, self.__get_mount_point()] diff --git a/src/cryptobox/core/exceptions.py b/src/cryptobox/core/exceptions.py index c80b07d..c3f0343 100644 --- a/src/cryptobox/core/exceptions.py +++ b/src/cryptobox/core/exceptions.py @@ -22,6 +22,8 @@ exceptions of the cryptobox package """ +__revision__ = "$Id" + class CBError(Exception): """base class for exceptions of the cryptobox""" @@ -54,13 +56,16 @@ class CBConfigUndefinedError(CBConfigError): self.name = name def __str__(self): - # is it a settings or a section? + """Output the appropriate string: for a setting or a section. + """ if self.name: # setting - return "undefined configuration setting: [%s]->%s - please check your configuration file" % (self.section, self.name) + return "undefined configuration setting: [" + str(self.section) \ + + "]->" + str(self.name) + " - please check your configuration file" else: # section - return "undefined configuration section: [%s] - please check your configuration file" % (self.section, ) + return "undefined configuration section: [" + str(self.section) \ + + "] - please check your configuration file" @@ -73,8 +78,12 @@ class CBConfigInvalidValueError(CBConfigError): self.value = value self.reason = reason + def __str__(self): - return "invalid configuration setting [%s]->%s (%s): %s" % (self.section, self.name, self.value, self.reason) + """Return the error description. + """ + return "invalid configuration setting [%s]->%s (%s): %s" % \ + (self.section, self.name, self.value, self.reason) class CBEnvironmentError(CBError): @@ -86,42 +95,58 @@ class CBEnvironmentError(CBError): self.desc = desc def __str__(self): + """Return the error description. + """ return "misconfiguration detected: %s" % self.desc class CBContainerError(CBError): - """any error raised while manipulating a cryptobox container""" + """Any error raised while manipulating a cryptobox container. + """ + - def __init__(self, desc): - self.desc = desc - - def __str__(self): - return self.desc - class CBCreateError(CBContainerError): + """Raised if a container could not be created (formatted). + """ pass class CBVolumeIsActive(CBContainerError): + """Raised if a container was active even if it may not for a specific action. + """ pass class CBInvalidName(CBContainerError): + """Raised if someone tried to set an invalid container name. + """ pass class CBNameIsInUse(CBContainerError): + """Raised if the new name of a container is already in use. + """ pass class CBInvalidType(CBContainerError): + """Raised if a container is of an invalid type for a choosen action. + """ pass class CBInvalidPassword(CBContainerError): + """Someone tried to open an ecnrypted container with the wrong password. + """ pass class CBChangePasswordError(CBContainerError): + """Changing of the password of an encrypted container failed. + """ pass class CBMountError(CBContainerError): + """Failed to mount a container. + """ pass class CBUmountError(CBContainerError): + """Failed to umount a container. + """ pass diff --git a/src/cryptobox/core/main.py b/src/cryptobox/core/main.py index e0d6ba5..e0baf2a 100644 --- a/src/cryptobox/core/main.py +++ b/src/cryptobox/core/main.py @@ -22,23 +22,17 @@ This is the web interface for a fileserver managing encrypted filesystems. ''' -# check python version -import sys -(ver_major, ver_minor, ver_sub, ver_desc, ver_subsub) = sys.version_info -if (ver_major < 2) or ((ver_major == 2) and (ver_minor < 4)): - sys.stderr.write("You need a python version >= 2.4\nCurrent version is:\n %s\n" % sys.version) - sys.exit(1) +__revision__ = "$Id" +import sys import cryptobox.core.container as cbxContainer -from cryptobox.core.exceptions import * +from cryptobox.core.exceptions import CBEnvironmentError, CBConfigUndefinedError import re import os import cryptobox.core.tools as cbxTools import subprocess -VERSION = "0.3~1" - class CryptoBox: '''this class rules them all! @@ -48,14 +42,15 @@ class CryptoBox: def __init__(self, config_file=None): import cryptobox.core.settings as cbxSettings - self.log = self.__getStartupLogger() + self.log = self.__get_startup_logger() self.prefs = cbxSettings.CryptoBoxSettings(config_file) - self.__runTests() + self.__run_tests() + self.__containers = [] + self.reread_container_list() - def __getStartupLogger(self): - import logging - '''initialises the logging system + def __get_startup_logger(self): + """Initialize the configured logging facility of the CryptoBox. use it with: 'self.log.[debug|info|warning|error|critical](logmessage)' all classes should get the logging instance during __init__: @@ -63,13 +58,15 @@ class CryptoBox: first we output all warnings/errors to stderr as soon as we opened the config file successfully, we redirect debug output - to the configured destination''' + to the configured destination + """ + import logging ## basicConfig(...) needs python >= 2.4 try: log_handler = logging.getLogger("CryptoBox") logging.basicConfig( - format='%(asctime)s CryptoBox %(levelname)s: %(message)s', - stderr=sys.stderr) + format = '%(asctime)s CryptoBox %(levelname)s: %(message)s', + stderr = sys.stderr) log_handler.setLevel(logging.ERROR) log_handler.info("loggingsystem is up'n running") ## from now on everything can be logged via self.log... @@ -78,19 +75,15 @@ class CryptoBox: return log_handler - # do some initial checks - def __runTests(self): - #self.__runTestUID() - self.__runTestRootPriv() + def __run_tests(self): + """Do some initial tests. + """ + self.__run_test_root_priv() - def __runTestUID(self): - if os.geteuid() == 0: - raise CBEnvironmentError("you may not run the cryptobox as root") - - - def __runTestRootPriv(self): - """try to run 'super' with 'CryptoBoxRootActions'""" + def __run_test_root_priv(self): + """Try to run 'super' with 'CryptoBoxRootActions'. + """ try: devnull = open(os.devnull, "w") except IOError: @@ -110,46 +103,39 @@ class CryptoBox: stderr = devnull, args = [prog_super, prog_rootactions, "check"]) except OSError: - raise CBEnvironmentError("failed to execute 'super' (%s)" % self.prefs["Programs"]["super"]) + raise CBEnvironmentError( + "failed to execute 'super' (%s)" % self.prefs["Programs"]["super"]) proc.wait() if proc.returncode != 0: - raise CBEnvironmentError("failed to call CryptoBoxRootActions (%s) via 'super' - maybe you did not add the appropriate line to /etc/super.tab?" % prog_rootactions) + raise CBEnvironmentError("failed to call CryptoBoxRootActions (" + + prog_rootactions + ") via 'super' - maybe you did not add the " + + "appropriate line to '/etc/super.tab'?") - # this method just demonstrates inheritance effects - may be removed - def cbx_inheritance_test(self, string="you lucky widow"): - self.log.info(string) - + def reread_container_list(self): + """Reinitialize the list of available containers. -# RFC: why should CryptoBoxProps inherit CryptoBox? [l] -# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l] -class CryptoBoxProps(CryptoBox): - '''Get and set the properties of a CryptoBox - - This class contains all available devices that may be accessed. - All properties of the cryptobox can be accessed by this class. - ''' - - def __init__(self, config_file=None): - '''read config and fill class variables''' - CryptoBox.__init__(self, config_file) - self.reReadContainerList() - - - def reReadContainerList(self): + This should be called whenever the available containers may have changed. + E.g.: after partitioning and after device addition/removal + """ self.log.debug("rereading container list") - self.containers = [] - for device in cbxTools.getAvailablePartitions(): - if self.isDeviceAllowed(device) and not self.isConfigPartition(device): - self.containers.append(cbxContainer.CryptoBoxContainer(device, self)) + self.__containers = [] + for device in cbxTools.get_available_partitions(): + if self.is_device_allowed(device) and not self.is_config_partition(device): + self.__containers.append(cbxContainer.CryptoBoxContainer(device, self)) ## sort by container name - self.containers.sort(cmp = lambda x,y: x.getName() < y.getName() and -1 or 1) + self.__containers.sort(cmp = lambda x, y: x.get_name() < y.get_name() and -1 or 1) - def isConfigPartition(self, device): + def is_config_partition(self, device): + """Check if a given partition contains configuration informations. + + The check is done by comparing the label of the filesystem with a string. + """ proc = subprocess.Popen( shell = False, stdout = subprocess.PIPE, + stderr = subprocess.PIPE, args = [ self.prefs["Programs"]["blkid"], "-c", os.path.devnull, @@ -160,70 +146,77 @@ class CryptoBoxProps(CryptoBox): return output.strip() == self.prefs["Main"]["ConfigVolumeLabel"] - def isDeviceAllowed(self, devicename): + def is_device_allowed(self, devicename): "check if a device is white-listed for being used as cryptobox containers" import types allowed = self.prefs["Main"]["AllowedDevices"] - if type(allowed) == types.StringType: allowed = [allowed] + if type(allowed) == types.StringType: + allowed = [allowed] for a_dev in allowed: - "remove double dots and so on ..." + ## remove double dots and so on ... real_device = os.path.realpath(devicename) - if a_dev and re.search('^' + a_dev, real_device): return True + if a_dev and re.search('^' + a_dev, real_device): + return True return False - def getLogData(self, lines=None, maxSize=None): + def get_log_data(self, lines=None, max_size=None): """get the most recent log entries of the cryptobox - the maximum number and size of these entries can be limited by 'lines' and 'maxSize' + the maximum number and size of these entries can be limited by + 'lines' and 'max_size' """ # return nothing if the currently selected log output is not a file try: - if self.prefs["Log"]["Destination"].upper() != "FILE": return [] + if self.prefs["Log"]["Destination"].upper() != "FILE": + return [] log_file = self.prefs["Log"]["Details"] except KeyError: - self.log.error("could not evaluate one of the following config settings: [Log]->Destination or [Log]->Details") + self.log.error("could not evaluate one of the following config settings: " + + "[Log]->Destination or [Log]->Details") return [] try: - fd = open(log_file, "r") - if maxSize: fd.seek(-maxSize, 2) # seek relative to the end of the file - content = fd.readlines() - fd.close() + fdesc = open(log_file, "r") + if max_size: + fdesc.seek(-max_size, 2) # seek relative to the end of the file + content = fdesc.readlines() + fdesc.close() except IOError: self.log.warn("failed to read the log file (%s)" % log_file) return [] - if lines: content = content[-lines:] + if lines: + content = content[-lines:] content.reverse() return content - def getContainerList(self, filterType=None, filterName=None): + def get_container_list(self, filter_type=None, filter_name=None): "retrieve the list of all containers of this cryptobox" try: - result = self.containers[:] - if filterType != None: - if filterType in range(len(cbxContainer.ContainerTypes)): - return [e for e in self.containers if e.getType() == filterType] + result = self.__containers[:] + if filter_type != None: + if filter_type in range(len(cbxContainer.CONTAINERTYPES)): + return [e for e in self.__containers if e.get_type() == filter_type] else: - self.log.info("invalid filterType (%d)" % filterType) + self.log.info("invalid filter_type (%d)" % filter_type) result.clear() - if filterName != None: - result = [e for e in self.containers if e.getName() == filterName] + if filter_name != None: + result = [e for e in self.__containers if e.get_name() == filter_name] return result except AttributeError: return [] - def getContainer(self, device): + def get_container(self, device): "retrieve the container element for this device" - all = [e for e in self.getContainerList() if e.device == device] + all = [e for e in self.get_container_list() if e.device == device] if all: return all[0] else: return None - def sendEventNotification(self, event, event_infos): + def send_event_notification(self, event, event_infos): """call all available scripts in the event directory with some event information""" event_dir = self.prefs["Locations"]["EventDir"] for fname in os.listdir(event_dir): @@ -240,11 +233,14 @@ class CryptoBoxProps(CryptoBox): args = cmd_args) (stdout, stderr) = proc.communicate() if proc.returncode != 0: - self.log.warn("an event script (%s) failed (exitcode=%d) to handle an event (%s): %s" % (real_fname, proc.returncode, event, stderr.strip())) + self.log.warn( + "an event script (%s) failed (exitcode=%d) to handle an event (%s): %s" % + (real_fname, proc.returncode, event, stderr.strip())) else: - self.log.info("event handler (%s) finished successfully: %s" % (real_fname, event)) + self.log.info("event handler (%s) finished successfully: %s" % + (real_fname, event)) if __name__ == "__main__": - cb = CryptoBoxProps() + CryptoBox() diff --git a/src/cryptobox/core/settings.py b/src/cryptobox/core/settings.py index 6162cdf..f519ea0 100644 --- a/src/cryptobox/core/settings.py +++ b/src/cryptobox/core/settings.py @@ -18,22 +18,21 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""Manage the configuration of a CryptoBox +""" + +__revision__ = "$Id" + from cryptobox.core.exceptions import * import logging -try: - import validate -except: - raise CBEnvironmentError("couldn't import 'validate'! Try 'apt-get install python-formencode'.") -import os import subprocess -try: - import configobj ## needed for reading and writing of the config file -except: - raise CBEnvironmentError("couldn't import 'configobj'! Try 'apt-get install python-configobj'.") - +import os +import configobj, validate class CryptoBoxSettings: + """Manage the various configuration files of the CryptoBox + """ CONF_LOCATIONS = [ "./cryptobox.conf", @@ -47,76 +46,85 @@ class CryptoBoxSettings: def __init__(self, config_file=None): self.log = logging.getLogger("CryptoBox") - config_file = self.__getConfigFileName(config_file) + config_file = self.__get_config_filename(config_file) self.log.info("loading config file: %s" % config_file) - self.prefs = self.__getPreferences(config_file) - self.__validateConfig() - self.__configureLogHandler() - self.__checkUnknownPreferences() - self.preparePartition() - self.volumesDB = self.__getVolumesDatabase() - self.pluginConf = self.__getPluginConfig() - self.userDB = self.__getUserDB() - self.misc_files = self.__getMiscFiles() + self.prefs = self.__get_preferences(config_file) + self.__validate_config() + self.__configure_log_handler() + self.__check_unknown_preferences() + self.prepare_partition() + self.volumes_db = self.__get_volumes_database() + self.plugin_conf = self.__get_plugin_config() + self.user_db = self.__get_user_db() + self.misc_files = self.__get_misc_files() def write(self): """ write all local setting files including the content of the "misc" subdirectory """ - ok = True + status = True try: - self.volumesDB.write() + self.volumes_db.write() except IOError: self.log.warn("could not save the volume database") - ok = False + status = False try: - self.pluginConf.write() + self.plugin_conf.write() except IOError: self.log.warn("could not save the plugin configuration") - ok = False + status = False try: - self.userDB.write() + self.user_db.write() except IOError: self.log.warn("could not save the user database") - ok = False + status = False for misc_file in self.misc_files: if not misc_file.save(): self.log.warn("could not save a misc setting file (%s)" % misc_file.filename) - ok = False - return ok + status = False + return status - def requiresPartition(self): + def requires_partition(self): return bool(self.prefs["Main"]["UseConfigPartition"]) - def getActivePartition(self): + def get_active_partition(self): + """Return the currently active cnfiguration partition. + """ settings_dir = self.prefs["Locations"]["SettingsDir"] - if not os.path.ismount(settings_dir): return None + if not os.path.ismount(settings_dir): + return None for line in file("/proc/mounts"): fields = line.split(" ") mount_dir = fields[1] try: - if os.path.samefile(mount_dir, settings_dir): return fields[0] + if os.path.samefile(mount_dir, settings_dir): + return fields[0] except OSError: pass ## no matching entry found return None - def mountPartition(self): + def mount_partition(self): + """Mount a config partition. + """ self.log.debug("trying to mount configuration partition") - if not self.requiresPartition(): - self.log.warn("mountConfigPartition: configuration partition is not required - mounting anyway") - if self.getActivePartition(): - self.log.warn("mountConfigPartition: configuration partition already mounted - not mounting again") + if not self.requires_partition(): + self.log.warn("mountConfigPartition: configuration partition is " + + "not required - mounting anyway") + if self.get_active_partition(): + self.log.warn("mountConfigPartition: configuration partition already " + + "mounted - not mounting again") return False - confPartitions = self.getAvailablePartitions() - if not confPartitions: - self.log.error("no configuration partitions found - you have to create it first") + conf_partitions = self.get_available_partitions() + if not conf_partitions: + self.log.error("no configuration partition found - you have to create " + + "it first") return False - partition = confPartitions[0] + partition = conf_partitions[0] proc = subprocess.Popen( shell = False, stdout = subprocess.PIPE, @@ -136,8 +144,10 @@ class CryptoBoxSettings: return True - def umountPartition(self): - if not self.getActivePartition(): + def umount_partition(self): + """Umount the currently active configuration partition. + """ + if not self.get_active_partition(): self.log.warn("umountConfigPartition: no configuration partition mounted") return False proc = subprocess.Popen( @@ -158,7 +168,7 @@ class CryptoBoxSettings: return True - def getAvailablePartitions(self): + def get_available_partitions(self): """returns a sequence of found config partitions""" proc = subprocess.Popen( shell = False, @@ -169,14 +179,16 @@ class CryptoBoxSettings: "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ]) (output, error) = proc.communicate() if output: - return [e.strip().split(":",1)[0] for e in output.splitlines()] + return [e.strip().split(":", 1)[0] for e in output.splitlines()] else: return [] - def preparePartition(self): - if self.requiresPartition() and not self.getActivePartition(): - self.mountPartition() + def prepare_partition(self): + """Mount a config partition if necessary. + """ + if self.requires_partition() and not self.get_active_partition(): + self.mount_partition() def __getitem__(self, key): @@ -184,7 +196,9 @@ class CryptoBoxSettings: return self.prefs[key] - def __getPreferences(self, config_file): + def __get_preferences(self, config_file): + """Load the CryptoBox configuration. + """ import StringIO config_rules = StringIO.StringIO(self.validation_spec) try: @@ -192,86 +206,111 @@ class CryptoBoxSettings: if prefs: self.log.info("found config: %s" % prefs.items()) else: - raise CBConfigUnavailableError("failed to load the config file: %s" % config_file) + raise CBConfigUnavailableError( + "failed to load the config file: %s" % config_file) except IOError: - raise CBConfigUnavailableError("unable to open the config file: %s" % config_file) + raise CBConfigUnavailableError( + "unable to open the config file: %s" % config_file) return prefs - def __validateConfig(self): + def __validate_config(self): + """Check the configuration settings and cast value types. + """ result = self.prefs.validate(CryptoBoxSettingsValidator(), preserve_errors=True) error_list = configobj.flatten_errors(self.prefs, result) - if not error_list: return - errorMsgs = [] + if not error_list: + return + error_msgs = [] for sections, key, text in error_list: section_name = "->".join(sections) if not text: - errorMsg = "undefined configuration value (%s) in section '%s'" % (key, section_name) + error_msg = "undefined configuration value (%s) in section '%s'" % \ + (key, section_name) else: - errorMsg = "invalid configuration value (%s) in section '%s': %s" % (key, section_name, text) - errorMsgs.append(errorMsg) - raise CBConfigError, "\n".join(errorMsgs) + error_msg = "invalid configuration value (%s) in section '%s': %s" % \ + (key, section_name, text) + error_msgs.append(error_msg) + raise CBConfigError, "\n".join(error_msgs) - def __checkUnknownPreferences(self): + def __check_unknown_preferences(self): + """Check the configuration file for unknown settings to avoid spelling mistakes. + """ import StringIO - config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec), list_values=False) - self.__recursiveConfigSectionCheck("", self.prefs, config_rules) + config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec), + list_values=False) + self.__recursive_section_check("", self.prefs, config_rules) - def __recursiveConfigSectionCheck(self, section_path, section_config, section_rules): - """should be called by '__checkUnknownPreferences' for every section + def __recursive_section_check(self, section_path, section_config, section_rules): + """should be called by '__check_unknown_preferences' for every section sends a warning message to the logger for every undefined (see validation_spec) configuration setting """ - for e in section_config.keys(): - element_path = section_path + e - if e in section_rules.keys(): - if isinstance(section_config[e], configobj.Section): - if isinstance(section_rules[e], configobj.Section): - self.__recursiveConfigSectionCheck(element_path + "->", section_config[e], section_rules[e]) + for section in section_config.keys(): + element_path = section_path + section + if section in section_rules.keys(): + if isinstance(section_config[section], configobj.Section): + if isinstance(section_rules[section], configobj.Section): + self.__recursive_section_check(element_path + "->", + section_config[section], section_rules[section]) else: - self.log.warn("configuration setting should be a value instead of a section name: %s" % element_path) + self.log.warn("configuration setting should be a value " + + "instead of a section name: %s" % element_path) else: - if not isinstance(section_rules[e], configobj.Section): + if not isinstance(section_rules[section], configobj.Section): pass # good - the setting is valid else: - self.log.warn("configuration setting should be a section name instead of a value: %s" % element_path) + self.log.warn("configuration setting should be a section " + + "name instead of a value: %s" % element_path) else: self.log.warn("unknown configuration setting: %s" % element_path) - def __getPluginConfig(self): + def __get_plugin_config(self): + """Load the plugin configuration file if it exists. + """ import StringIO plugin_rules = StringIO.StringIO(self.pluginValidationSpec) try: try: - pluginConf_file = os.path.join(self.prefs["Locations"]["SettingsDir"], self.PLUGINCONF_FILE) + plugin_conf_file = os.path.join( + self.prefs["Locations"]["SettingsDir"], self.PLUGINCONF_FILE) except KeyError: raise CBConfigUndefinedError("Locations", "SettingsDir") except SyntaxError: - raise CBConfigInvalidValueError("Locations", "SettingsDir", pluginConf_file, "failed to interprete the filename of the plugin config file correctly (%s)" % pluginConf_file) - ## create pluginConf_file if necessary - if os.path.exists(pluginConf_file): - pluginConf = configobj.ConfigObj(pluginConf_file, configspec=plugin_rules) + raise CBConfigInvalidValueError("Locations", "SettingsDir", plugin_conf_file, + "failed to interprete the filename of the plugin config file " + + "correctly (%s)" % plugin_conf_file) + ## create plugin_conf_file if necessary + if os.path.exists(plugin_conf_file): + plugin_conf = configobj.ConfigObj(plugin_conf_file, configspec=plugin_rules) else: - pluginConf = configobj.ConfigObj(pluginConf_file, configspec=plugin_rules, create_empty=True) + plugin_conf = configobj.ConfigObj(plugin_conf_file, configspec=plugin_rules, + create_empty=True) ## validate and convert values according to the spec - pluginConf.validate(validate.Validator()) - ## check if pluginConf_file file was created successfully? - if not os.path.exists(pluginConf_file): - raise CBEnvironmentError("failed to create plugin configuration file (%s)" % pluginConf_file) - return pluginConf + plugin_conf.validate(validate.Validator()) + ## check if plugin_conf_file file was created successfully? + if not os.path.exists(plugin_conf_file): + raise CBEnvironmentError( + "failed to create plugin configuration file (%s)" % plugin_conf_file) + return plugin_conf - def __getVolumesDatabase(self): + def __get_volumes_database(self): + """Load the volume database file if it exists. + """ try: try: - conf_file = os.path.join(self.prefs["Locations"]["SettingsDir"], self.VOLUMESDB_FILE) + conf_file = os.path.join( + self.prefs["Locations"]["SettingsDir"], self.VOLUMESDB_FILE) except KeyError: raise CBConfigUndefinedError("Locations", "SettingsDir") except SyntaxError: - raise CBConfigInvalidValueError("Locations", "SettingsDir", conf_file, "failed to interprete the filename of the volume database correctly (%s)" % conf_file) + raise CBConfigInvalidValueError("Locations", "SettingsDir", conf_file, + "failed to interprete the filename of the volume database " + + "correctly (%s)" % conf_file) ## create conf_file if necessary if os.path.exists(conf_file): conf = configobj.ConfigObj(conf_file) @@ -279,36 +318,48 @@ class CryptoBoxSettings: conf = configobj.ConfigObj(conf_file, create_empty=True) ## check if conf_file file was created successfully? if not os.path.exists(conf_file): - raise CBEnvironmentError("failed to create volume database file (%s)" % conf_file) + raise CBEnvironmentError( + "failed to create volume database file (%s)" % conf_file) return conf - def __getUserDB(self): + def __get_user_db(self): + """Load the user database file if it exists. + """ import StringIO, sha - userDB_rules = StringIO.StringIO(self.userDatabaseSpec) + user_db_rules = StringIO.StringIO(self.userDatabaseSpec) try: try: - userDB_file = os.path.join(self.prefs["Locations"]["SettingsDir"], self.USERDB_FILE) + user_db_file = os.path.join( + self.prefs["Locations"]["SettingsDir"], self.USERDB_FILE) except KeyError: raise CBConfigUndefinedError("Locations", "SettingsDir") except SyntaxError: - raise CBConfigInvalidValueError("Locations", "SettingsDir", userDB_file, "failed to interprete the filename of the users database file correctly (%s)" % userDB_file) - ## create userDB_file if necessary - if os.path.exists(userDB_file): - userDB = configobj.ConfigObj(userDB_file, configspec=userDB_rules) + raise CBConfigInvalidValueError("Locations", "SettingsDir", user_db_file, + "failed to interprete the filename of the users database file " + + "correctly (%s)" % user_db_file) + ## create user_db_file if necessary + if os.path.exists(user_db_file): + user_db = configobj.ConfigObj(user_db_file, configspec=user_db_rules) else: - userDB = configobj.ConfigObj(userDB_file, configspec=userDB_rules, create_empty=True) + user_db = configobj.ConfigObj(user_db_file, configspec=user_db_rules, + create_empty=True) ## validate and set default value for "admin" user - userDB.validate(validate.Validator()) - ## check if userDB file was created successfully? - if not os.path.exists(userDB_file): - raise CBEnvironmentError("failed to create user database file (%s)" % userDB_file) + user_db.validate(validate.Validator()) + ## check if user_db file was created successfully? + if not os.path.exists(user_db_file): + raise CBEnvironmentError( + "failed to create user database file (%s)" % user_db_file) ## define password hash function - never use "sha" directly - SPOT - userDB.getDigest = lambda password: sha.new(password).hexdigest() - return userDB + user_db.get_digest = lambda password: sha.new(password).hexdigest() + return user_db - def __getMiscFiles(self): + def __get_misc_files(self): + """Load miscelleanous configuration files. + + e.g.: an ssl certificate, ... + """ misc_dir = os.path.join(self.prefs["Locations"]["SettingsDir"], "misc") if (not os.path.isdir(misc_dir)) or (not os.access(misc_dir, os.X_OK)): return [] @@ -317,8 +368,9 @@ class CryptoBoxSettings: if os.path.isfile(os.path.join(misc_dir, f))] - def __getConfigFileName(self, config_file): - # search for the configuration file + def __get_config_filename(self, config_file): + """Search for the configuration file. + """ import types if config_file is None: # no config file was specified - we will look for it in the ususal locations @@ -332,13 +384,17 @@ class CryptoBoxSettings: else: # a config file was specified (e.g. via command line) if type(config_file) != types.StringType: - raise CBConfigUnavailableError("invalid config file specified: %s" % config_file) + raise CBConfigUnavailableError( + "invalid config file specified: %s" % config_file) if not os.path.exists(config_file): - raise CBConfigUnavailableError("could not find the specified configuration file (%s)" % config_file) + raise CBConfigUnavailableError( + "could not find the specified configuration file (%s)" % config_file) return config_file - def __configureLogHandler(self): + def __configure_log_handler(self): + """Configure the log handler of the CryptoBox according to the config. + """ try: log_level = self.prefs["Log"]["Level"].upper() log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"] @@ -347,15 +403,18 @@ class CryptoBoxSettings: except KeyError: raise CBConfigUndefinedError("Log", "Level") except TypeError: - raise CBConfigInvalidValueError("Log", "Level", log_level, "invalid log level: only %s are allowed" % log_level_avail) + raise CBConfigInvalidValueError("Log", "Level", log_level, + "invalid log level: only %s are allowed" % log_level_avail) try: try: log_handler = logging.FileHandler(self.prefs["Log"]["Details"]) except KeyError: raise CBConfigUndefinedError("Log", "Details") except IOError: - raise CBEnvironmentError("could not create the log file (%s)" % self.prefs["Log"]["Details"]) - log_handler.setFormatter(logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s')) + raise CBEnvironmentError("could not create the log file (%s)" % \ + self.prefs["Log"]["Details"]) + log_handler.setFormatter( + logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s')) cbox_log = logging.getLogger("CryptoBox") ## remove previous handlers cbox_log.handlers = [] @@ -364,7 +423,7 @@ class CryptoBoxSettings: ## do not call parent's handlers cbox_log.propagate = False ## 'log_level' is a string -> use 'getattr' - cbox_log.setLevel(getattr(logging,log_level)) + cbox_log.setLevel(getattr(logging, log_level)) ## the logger named "CryptoBox" is configured now @@ -420,16 +479,20 @@ admin = string(default=d033e22ae348aeb5660fc2140aec35850c4da997) class CryptoBoxSettingsValidator(validate.Validator): + """Some custom configuration check functions. + """ def __init__(self): validate.Validator.__init__(self) - self.functions["directoryExists"] = self.check_directoryExists - self.functions["fileExecutable"] = self.check_fileExecutable - self.functions["fileWriteable"] = self.check_fileWriteable - self.functions["listOfExistingDirectories"] = self.check_listOfExistingDirectories + self.functions["directoryExists"] = self.check_directory_exists + self.functions["fileExecutable"] = self.check_file_executable + self.functions["fileWriteable"] = self.check_file_writeable + self.functions["listOfExistingDirectories"] = self.check_existing_directories - def check_directoryExists(self, value): + def check_directory_exists(self, value): + """Is the directory accessible? + """ dir_path = os.path.abspath(value) if not os.path.isdir(dir_path): raise validate.VdtValueError("%s (not found)" % value) @@ -438,7 +501,9 @@ class CryptoBoxSettingsValidator(validate.Validator): return dir_path - def check_fileExecutable(self, value): + def check_file_executable(self, value): + """Is the file executable? + """ file_path = os.path.abspath(value) if not os.path.isfile(file_path): raise validate.VdtValueError("%s (not found)" % value) @@ -447,7 +512,9 @@ class CryptoBoxSettingsValidator(validate.Validator): return file_path - def check_fileWriteable(self, value): + def check_file_writeable(self, value): + """Is the file writeable? + """ file_path = os.path.abspath(value) if os.path.isfile(file_path): if not os.access(file_path, os.W_OK): @@ -460,18 +527,22 @@ class CryptoBoxSettingsValidator(validate.Validator): return file_path - def check_listOfExistingDirectories(self, value): + def check_existing_directories(self, value): + """Are these directories accessible? + """ if not value: raise validate.VdtValueError("no plugin directory specified") - if not isinstance(value,list): + if not isinstance(value, list): value = [value] result = [] - for d in value: - dir_path = os.path.abspath(d) + for one_dir in value: + dir_path = os.path.abspath(one_dir) if not os.path.isdir(dir_path): - raise validate.VdtValueError("%s (plugin directory not found)" % d) + raise validate.VdtValueError( + "%s (plugin directory not found)" % one_dir) if not os.access(dir_path, os.X_OK): - raise validate.VdtValueError("%s (access denied for plugin directory)" % d) + raise validate.VdtValueError( + "%s (access denied for plugin directory)" % one_dir) result.append(dir_path) return result @@ -484,19 +555,25 @@ class MiscConfigFile: def __init__(self, filename, logger): self.filename = filename self.log = logger + self.content = None self.load() def load(self): - fd = open(self.filename, "rb") + """Load a configuration file into memory. + """ + fdesc = open(self.filename, "rb") ## limit the maximum size - self.content = fd.read(self.maxSize) - if fd.tell() == self.maxSize: - self.log.warn("file in misc settings directory (%s) is bigger than allowed (%s)" % (self.filename, self.maxSize)) - fd.close() + self.content = fdesc.read(self.maxSize) + if fdesc.tell() == self.maxSize: + self.log.warn("file in misc settings directory (" + str(self.filename) \ + + ") is bigger than allowed (" + str(self.maxSize) + ")") + fdesc.close() def save(self): + """Save a configuration file to disk. + """ save_dir = os.path.dirname(self.filename) ## create the directory, if necessary if not os.path.isdir(save_dir): @@ -506,14 +583,14 @@ class MiscConfigFile: return False ## save the content of the file try: - fd = open(self.filename, "wb") + fdesc = open(self.filename, "wb") except IOError: return False try: - fd.write(self.content) - fd.close() + fdesc.write(self.content) + fdesc.close() return True except IOError: - fd.close() + fdesc.close() return False diff --git a/src/cryptobox/core/tools.py b/src/cryptobox/core/tools.py index a7b8676..e574f59 100644 --- a/src/cryptobox/core/tools.py +++ b/src/cryptobox/core/tools.py @@ -18,137 +18,169 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""Some useful functions for the CryptoBox. +""" + +__revision__ = "$Id" + import logging import os import re -logger = logging.getLogger("CryptoBox") +LOGGER = logging.getLogger("CryptoBox") -def getAvailablePartitions(): +def get_available_partitions(): "retrieve a list of all available containers" ret_list = [] try: - "the following reads all lines of /proc/partitions and adds the mentioned devices" + ## the following reads all lines of /proc/partitions and adds the mentioned devices fpart = open("/proc/partitions", "r") try: line = fpart.readline() while line: p_details = line.split() if (len(p_details) == 4): - "the following code prevents double entries like /dev/hda and /dev/hda1" + ## the following code prevents double entries like /dev/hda and /dev/hda1 (p_major, p_minor, p_size, p_device) = p_details ## ignore lines with: invalid minor/major or extend partitions (size=1) - if re.search('^[0-9]*$', p_major) and re.search('^[0-9]*$', p_minor) and (p_size != "1"): + if re.search('^[0-9]*$', p_major) and \ + re.search('^[0-9]*$', p_minor) and (p_size != "1"): p_parent = re.sub('[1-9]?[0-9]$', '', p_device) if p_parent == p_device: - if [e for e in ret_list if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]: - "major partition - its children are already in the list" + if [e for e in ret_list + if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]: + ## major partition - its children are already in the list pass else: - "major partition - but there are no children for now" + ## major partition - but there are no children for now ret_list.append(p_device) else: - "minor partition - remove parent if necessary" - if p_parent in ret_list: ret_list.remove(p_parent) + ## minor partition - remove parent if necessary + if p_parent in ret_list: + ret_list.remove(p_parent) ret_list.append(p_device) line = fpart.readline() finally: fpart.close() - return map(getAbsoluteDeviceName, ret_list) + return [ get_absolute_devicename(e) for e in ret_list ] except IOError: - logger.warning("Could not read /proc/partitions") + LOGGER.warning("Could not read /proc/partitions") return [] -def getAbsoluteDeviceName(shortname): +def get_absolute_devicename(shortname): """ returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1") this does also work for device mapper devices - if the result is non-unique, one arbitrary value is returned""" - if re.search('^/', shortname): return shortname + if the result is non-unique, one arbitrary value is returned + """ + if re.search('^/', shortname): + return shortname default = os.path.join("/dev", shortname) - if os.path.exists(default): return default - result = findMajorMinorOfDevice(shortname) - "if no valid major/minor was found -> exit" - if not result: return default + if os.path.exists(default): + return default + result = find_major_minor_of_device(shortname) + ## if no valid major/minor was found -> exit + if not result: + return default (major, minor) = result - "for device-mapper devices (major == 254) ..." + ## for device-mapper devices (major == 254) ... if major == 254: - result = findMajorMinorDeviceName("/dev/mapper", major, minor) - if result: return result[0] - "now check all files in /dev" - result = findMajorMinorDeviceName("/dev", major, minor) - if result: return result[0] + result = find_major_minor_device("/dev/mapper", major, minor) + if result: + return result[0] + ## now check all files in /dev + result = find_major_minor_device("/dev", major, minor) + if result: + return result[0] return default -def findMajorMinorOfDevice(device): - "return the major/minor numbers of a block device" - if re.match("/", device) or not os.path.exists(os.path.join(os.path.sep,"sys","block",device)): +def find_major_minor_of_device(device): + """Return the major/minor numbers of a block device. + """ + if re.match("/", device) or \ + not os.path.exists(os.path.join(os.path.sep, "sys", "block", device)): ## maybe it is an absolute device name - if not os.path.exists(device): return None + if not os.path.exists(device): + return None ## okay - it seems to to a device node rdev = os.stat(device).st_rdev return (os.major(rdev), os.minor(rdev)) - blockdev_info_file = os.path.join(os.path.join(os.path.sep,"sys","block", device), "dev") + blockdev_info_file = os.path.join(os.path.join( + os.path.sep,"sys","block", device), "dev") try: f_blockdev_info = open(blockdev_info_file, "r") blockdev_info = f_blockdev_info.read() f_blockdev_info.close() (str_major, str_minor) = blockdev_info.split(":") - "numeric conversion" + ## numeric conversion try: major = int(str_major) minor = int(str_minor) return (major, minor) except ValueError: - "unknown device numbers -> stop guessing" + ## unknown device numbers -> stop guessing return None except IOError: pass return None -def findMajorMinorDeviceName(dir, major, minor): - "returns the names of devices with the specified major and minor number" +def find_major_minor_device(dirpath, major, minor): + """Returns the names of devices with the specified major and minor number. + """ collected = [] try: - subdirs = [os.path.join(dir, e) for e in os.listdir(dir) if (not os.path.islink(os.path.join(dir, e))) and os.path.isdir(os.path.join(dir, e))] - "do a recursive call to parse the directory tree" + subdirs = [os.path.join(dirpath, e) for e in os.listdir(dirpath) + if (not os.path.islink(os.path.join(dirpath, e))) and \ + os.path.isdir(os.path.join(dirpath, e))] + ## do a recursive call to parse the directory tree for dirs in subdirs: - collected.extend(findMajorMinorDeviceName(dirs, major, minor)) - "filter all device inodes in this directory" - collected.extend([os.path.realpath(os.path.join(dir, e)) for e in os.listdir(dir) if (os.major(os.stat(os.path.join(dir, e)).st_rdev) == major) and (os.minor(os.stat(os.path.join(dir, e)).st_rdev) == minor)]) + collected.extend(find_major_minor_device(dirs, major, minor)) + ## filter all device inodes in this directory + collected.extend([os.path.realpath(os.path.join(dirpath, e)) + for e in os.listdir(dirpath) + if (os.major(os.stat(os.path.join(dirpath, e)).st_rdev) == major) \ + and (os.minor(os.stat(os.path.join(dirpath, e)).st_rdev) == minor)]) ## remove double entries result = [] - for e in collected: - if e not in result: result.append(e) + for item in collected: + if item not in result: + result.append(item) return result except OSError: return [] -def getParentBlockDevices(): +def get_parent_blockdevices(): + """Return a list of all block devices that contain other devices. + """ devs = [] for line in file("/proc/partitions"): p_details = line.split() ## we expect four values - otherwise continue with next iteration - if len(p_details) != 4: continue + if len(p_details) != 4: + continue (p_major, p_minor, p_size, p_device) = p_details ## we expect numeric values in the first two columns - if re.search(u'\D',p_major) or re.search(u'\D',p_minor): continue + if re.search(u'\D', p_major) or re.search(u'\D', p_minor): + continue ## now let us check, if it is a (parent) block device or a partition - if not os.path.isdir(os.path.join(os.path.sep, "sys", "block", p_device)): continue + if not os.path.isdir(os.path.join(os.path.sep, "sys", "block", p_device)): + continue devs.append(p_device) - return map(getAbsoluteDeviceName, devs) + return [ get_absolute_devicename(e) for e in devs ] -def isPartOfBlockDevice(parent, subdevice): - """check if the given block device is a parent of 'subdevice' - e.g. for checking if a partition belongs to a block device""" +def is_part_of_blockdevice(parent, subdevice): + """Check if the given block device is a parent of 'subdevice'. + + e.g. for checking if a partition belongs to a block device + """ try: - (par_major, par_minor) = findMajorMinorOfDevice(parent) - (sub_major, sub_minor) = findMajorMinorOfDevice(subdevice) + (par_major, par_minor) = find_major_minor_of_device(parent) + (sub_major, sub_minor) = find_major_minor_of_device(subdevice) except TypeError: ## at least one of these devices did not return a valid major/minor combination return False @@ -158,7 +190,8 @@ def isPartOfBlockDevice(parent, subdevice): blpath = os.path.join(root, bldev, 'dev') if os.access(blpath, os.R_OK): try: - if (str(par_major), str(par_minor)) == tuple([e for e in file(blpath)][0].strip().split(":",1)): + if (str(par_major), str(par_minor)) == tuple([e + for e in file(blpath)][0].strip().split(":",1)): parent_path = os.path.join(root, bldev) break except (IndexError, OSError): @@ -170,7 +203,8 @@ def isPartOfBlockDevice(parent, subdevice): subblpath = os.path.join(parent_path, subbldev, "dev") if os.access(subblpath, os.R_OK): try: - if (str(sub_major), str(sub_minor)) == tuple([e for e in file(subblpath)][0].strip().split(":",1)): + if (str(sub_major), str(sub_minor)) == tuple([e + for e in file(subblpath)][0].strip().split(":",1)): ## the name of the subdevice node is not important - we found it! return True except (IndexError, OSError): @@ -178,18 +212,22 @@ def isPartOfBlockDevice(parent, subdevice): return False -def getBlockDeviceSize(device): - if not device: return -1 +def get_blockdevice_size(device): + """Return the size of a blockdevice. + """ + if not device: + return -1 try: rdev = os.stat(device).st_rdev except OSError: return -1 minor = os.minor(rdev) major = os.major(rdev) - for f in file("/proc/partitions"): + for line in file("/proc/partitions"): try: - elements = f.split() - if len(elements) != 4: continue + elements = line.split() + if len(elements) != 4: + continue if (int(elements[0]) == major) and (int(elements[1]) == minor): return int(elements[2])/1024 except ValueError: @@ -197,8 +235,10 @@ def getBlockDeviceSize(device): return -1 -def getBlockDeviceSizeHumanly(device): - size = getBlockDeviceSize(device) +def get_blockdevice_size_humanly(device): + """Return a human readable size of a blockdevice. + """ + size = get_blockdevice_size(device) if size > 5120: return "%sGB" % size/1024 else: diff --git a/src/cryptobox/plugins/__init__.py b/src/cryptobox/plugins/__init__.py index e69de29..eedeafc 100644 --- a/src/cryptobox/plugins/__init__.py +++ b/src/cryptobox/plugins/__init__.py @@ -0,0 +1,7 @@ +"""Features may be easily added to the CryptoBox. +""" + +__revision__ = "$Id" + +__all__ = [ 'base', 'manage' ] + diff --git a/src/cryptobox/plugins/base.py b/src/cryptobox/plugins/base.py index 65f5f91..5d50965 100644 --- a/src/cryptobox/plugins/base.py +++ b/src/cryptobox/plugins/base.py @@ -21,161 +21,189 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""All features should inherit from this class. +""" + +__revision__ = "$Id" import os import cherrypy class CryptoBoxPlugin: + """The base class of all features. + """ ## default capability is "system" - the other supported capability is: "volume" - pluginCapabilities = [ "system" ] + plugin_capabilities = [ "system" ] ## where should the plugin be visible by default? - pluginVisibility = [ "preferences" ] + plugin_visibility = [ "preferences" ] ## does this plugin require admin authentification? - requestAuth = False + request_auth = False ## default rank (0..100) of the plugin in listings (lower value means higher priority) rank = 80 ## default icon of this plugin (relative path) - defaultIconFileName = "plugin_icon.gif" + default_icon_filename = "plugin_icon.gif" ## fallback icon file (in the common plugin directory) - fallbackIconFileName = "plugin_icon_unknown.gif" + fallback_icon_filename = "plugin_icon_unknown.gif" - def __init__(self, cbox, pluginDir, siteClass=None): + def __init__(self, cbox, plugin_dir, site_class=None): self.cbox = cbox self.hdf = {} - self.pluginDir = pluginDir - self.hdf_prefix = "Data.Plugins.%s." % self.getName() - self.site = siteClass + self.plugin_dir = plugin_dir + self.hdf_prefix = "Data.Plugins.%s." % self.get_name() + self.site = site_class - def doAction(self, **args): - """override doAction with your plugin code""" - raise Exception, "undefined action handler ('doAction') in plugin '%'" % self.getName() + def do_action(self, **args): + """Override do_action with your plugin code + """ + raise Exception, \ + "undefined action handler ('do_action') in plugin '%s'" % self.get_name() - def getStatus(self): - """you should override this, to supply useful state information""" - raise Exception, "undefined state handler ('getStatus') in plugin '%'" % self.getName() + def get_status(self): + """you should override this, to supply useful state information + """ + raise Exception, \ + "undefined state handler ('get_status') in plugin '%s'" % self.get_name() - def getName(self): - """the name of the python file (module) should be the name of the plugin""" + def get_name(self): + """the name of the python file (module) should be the name of the plugin + """ return self.__module__ @cherrypy.expose - def getIcon(self, image=None, **kargs): + def get_icon(self, image=None, **kargs): """return the image data of the icon of the plugin the parameter 'image' may be used for alternative image locations (relative to the directory of the plugin) - '**kargs' is necessary, as a 'weblang' attribute may be specified (and ignored)""" - import cherrypy, re - if (image is None): # or (re.search(u'[\w-\.]', image)): - plugin_icon_file = os.path.join(self.pluginDir, self.defaultIconFileName) + '**kargs' is necessary, as a 'weblang' attribute may be specified (and ignored) + """ + import re + if (image is None) or (not re.match(u'[\w\-\.]*$', image)): + plugin_icon_file = os.path.join(self.plugin_dir, self.default_icon_filename) else: - plugin_icon_file = os.path.join(self.pluginDir, image) + plugin_icon_file = os.path.join(self.plugin_dir, image) if not os.access(plugin_icon_file, os.R_OK): - plugin_icon_file = os.path.join(self.cbox.prefs["Locations"]["PluginDir"], self.fallbackIconFileName) + plugin_icon_file = os.path.join( + self.cbox.prefs["Locations"]["PluginDir"], self.fallback_icon_filename) return cherrypy.lib.cptools.serveFile(plugin_icon_file) - def getTemplateFileName(self, template_name): + def get_template_filename(self, template_name): """return the filename of the template, if it is part of this plugin use this function to check, if the plugin provides the specified template """ - result_file = os.path.join(self.pluginDir, template_name + ".cs") + result_file = os.path.join(self.plugin_dir, template_name + ".cs") if os.access(result_file, os.R_OK) and os.path.isfile(result_file): return result_file else: return None - def getLanguageData(self): + def get_language_data(self): + """Retrieve the language data of the feature. + + Typically this is the content of the language.hdf file as a HDF object. + """ import neo_cgi, neo_util lang_hdf = neo_util.HDF() - langFile = os.path.join(self.pluginDir, 'language.hdf') + lang_file = os.path.join(self.plugin_dir, 'language.hdf') try: - lang_hdf.readFile(langFile) - except (neo_util.Error, neo_util.ParseError), errMsg: - self.cbox.log.error("failed to load language file (%s) of plugin (%s):" % (langFile,self.getName())) + lang_hdf.readFile(lang_file) + except (neo_util.Error, neo_util.ParseError): + self.cbox.log.error("failed to load language file (%s) of plugin (%s):" % \ + (lang_file, self.get_name())) return lang_hdf - def loadDataSet(self, hdf): + def load_dataset(self, hdf): + """Add the local values of the feature to the hdf dataset. + """ for (key, value) in self.hdf.items(): hdf.setValue(key, str(value)) - def isAuthRequired(self): + def is_auth_required(self): """check if this plugin requires authentication first step: check plugin configuration - second step: check default value of plugin""" + second step: check default value of plugin + """ try: - if self.cbox.prefs.pluginConf[self.getName()]["requestAuth"] is None: - return self.requestAuth - if self.cbox.prefs.pluginConf[self.getName()]["requestAuth"]: + if self.cbox.prefs.plugin_conf[self.get_name()]["requestAuth"] is None: + return self.request_auth + if self.cbox.prefs.plugin_conf[self.get_name()]["requestAuth"]: return True else: return False except KeyError: - return self.requestAuth + return self.request_auth - def isEnabled(self): + def is_enabled(self): """check if this plugin is enabled first step: check plugin configuration - second step: check default value of plugin""" - fallback = bool(self.pluginVisibility) + second step: check default value of plugin + """ + fallback = bool(self.plugin_visibility) try: - if self.cbox.prefs.pluginConf[self.getName()]["visibility"] is None: + if self.cbox.prefs.plugin_conf[self.get_name()]["visibility"] is None: return fallback - return bool(self.cbox.prefs.pluginConf[self.getName()]["visibility"]) + return bool(self.cbox.prefs.plugin_conf[self.get_name()]["visibility"]) except KeyError: return fallback - def getRank(self): + def get_rank(self): """check the rank of this plugin first step: check plugin configuration - second step: check default value of plugin""" + second step: check default value of plugin + """ try: - if self.cbox.prefs.pluginConf[self.getName()]["rank"] is None: + if self.cbox.prefs.plugin_conf[self.get_name()]["rank"] is None: return self.rank - return int(self.cbox.prefs.pluginConf[self.getName()]["rank"]) + return int(self.cbox.prefs.plugin_conf[self.get_name()]["rank"]) except (KeyError, TypeError): return self.rank - def getVisibility(self): + def get_visibility(self): + """Check which visibility flags of the feature are set. + """ try: - if self.cbox.prefs.pluginConf[self.getName()]["visibility"] is None: - return self.pluginVisibility[:] - return self.cbox.prefs.pluginConf[self.getName()]["visibility"] + if self.cbox.prefs.plugin_conf[self.get_name()]["visibility"] is None: + return self.plugin_visibility[:] + return self.cbox.prefs.plugin_conf[self.get_name()]["visibility"] except KeyError: - return self.pluginVisibility + return self.plugin_visibility - def getTestClass(self): + def get_test_class(self): + """Return the unittest class of the feature. + """ import imp - pl_file = os.path.join(self.pluginDir, "unittests.py") + pl_file = os.path.join(self.plugin_dir, "unittests.py") if os.access(pl_file, os.R_OK) and os.path.isfile(pl_file): try: - return getattr(imp.load_source("unittests_%s" % self.getName(), pl_file), "unittests") + return imp.load_source("unittests_%s" % self.get_name(), pl_file).unittests except AttributeError: pass try: - self.cbox.log.info("could not load unittests for plugin: %s" % self.getName()) + self.cbox.log.info("could not load unittests for plugin: %s" % \ + self.get_name()) except AttributeError: pass return None diff --git a/src/cryptobox/plugins/manage.py b/src/cryptobox/plugins/manage.py index bfe0067..78c7916 100644 --- a/src/cryptobox/plugins/manage.py +++ b/src/cryptobox/plugins/manage.py @@ -19,46 +19,56 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""Manages the pluggable features of the CryptoBox. +""" + +__revision__ = "$Id" -import imp import os -import logging class PluginManager: """manage available plugins""" - def __init__(self, cbox, plugin_dirs=".", siteClass=None): + def __init__(self, cbox, plugin_dirs=".", site_class=None): self.cbox = cbox - self.log = logging.getLogger("CryptoBox") - self.site = siteClass + self.site = site_class if hasattr(plugin_dirs, "__iter__"): - self.plugin_dirs = [os.path.abspath(dir) for dir in plugin_dirs] + self.plugin_dirs = [os.path.abspath(d) for d in plugin_dirs] else: self.plugin_dirs = [os.path.abspath(plugin_dirs)] - self.pluginList = self.__getAllPlugins() + self.plugin_list = self.__get_all_plugins() - def getPlugins(self): - return self.pluginList[:] + def get_plugins(self): + """Return a list of all feature instances. + """ + return self.plugin_list[:] - def getPlugin(self, name): - for p in self.pluginList[:]: - if p.getName() == name: - return p + def get_plugin(self, name): + """Return the specified feature as an instance. + """ + for plugin in self.plugin_list[:]: + if plugin.get_name() == name: + return plugin return None - def __getAllPlugins(self): - list = [] - for plfile in self.__getPluginFiles(): - list.append(self.__getPluginClass(os.path.basename(plfile)[:-3])) - return list + def __get_all_plugins(self): + """Return all available features as instances. + """ + plist = [] + for plfile in self.__get_plugin_files(): + plist.append(self.__get_plugin_class(os.path.basename(plfile)[:-3])) + return plist - def __getPluginClass(self, name): - for plfile in self.__getPluginFiles(): + def __get_plugin_class(self, name): + """Return a instance object of the give feature. + """ + import imp + for plfile in self.__get_plugin_files(): if name == os.path.basename(plfile)[:-3]: try: pl_class = getattr(imp.load_source(name, plfile), name) @@ -69,18 +79,23 @@ class PluginManager: return None - def __getPluginFiles(self): + def __get_plugin_files(self): + """Retrieve all python files that may potentially be a feature. + """ result = [] if self.cbox and self.cbox.prefs["Main"]["DisabledPlugins"]: disabled = self.cbox.prefs["Main"]["DisabledPlugins"] else: disabled = [] - for dir in [os.path.abspath(e) for e in self.plugin_dirs if os.access(e, os.R_OK) and os.path.isdir(e)]: - for plname in [f for f in os.listdir(dir)]: + for pdir in [os.path.abspath(e) for e in self.plugin_dirs + if os.access(e, os.R_OK) and os.path.isdir(e)]: + for plname in [f for f in os.listdir(pdir)]: if plname in disabled: - if self.cbox: self.cbox.log.info("skipped plugin '%s' (disabled via config)" % plname) + if self.cbox: + self.cbox.log.info( + "skipped plugin '%s' (disabled via config)" % plname) continue - pldir = os.path.join(dir, plname) + pldir = os.path.join(pdir, plname) plfile = os.path.join(pldir, plname + ".py") if os.path.isfile(plfile) and os.access(plfile, os.R_OK): result.append(plfile) @@ -88,8 +103,8 @@ class PluginManager: if __name__ == "__main__": - x = PluginManager(None, "../plugins") - for a in x.getPlugins(): - if not a is None: - print "Plugin: %s" % a.getName() + MANAGER = PluginManager(None, "../plugins") + for one_plugin in MANAGER.get_plugins(): + if not one_plugin is None: + print "Plugin: %s" % one_plugin.get_name() diff --git a/src/cryptobox/tests/__init__.py b/src/cryptobox/tests/__init__.py index e69de29..afc7ca5 100644 --- a/src/cryptobox/tests/__init__.py +++ b/src/cryptobox/tests/__init__.py @@ -0,0 +1,7 @@ +"""Some unittests for the CryptoBox. +""" + +__revision__ = "$Id" + +__all__ = [ 'test.cryptobox', 'test.cryptoboxtools', 'test.plugins', 'test.websites' ] + diff --git a/src/cryptobox/tests/test.cryptobox.py b/src/cryptobox/tests/test.cryptobox.py index 07ffc21..c0e722e 100755 --- a/src/cryptobox/tests/test.cryptobox.py +++ b/src/cryptobox/tests/test.cryptobox.py @@ -19,33 +19,41 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""Some unittests for the core CryptoBox modules. +""" + +__revision__ = "$Id" + import unittest import sys import cryptobox.core.main from cryptobox.core.exceptions import * import cryptobox.core.settings +import os -class CryptoBoxPropsDeviceTests(unittest.TestCase): - cb = cryptobox.core.main.CryptoBoxProps() +class CryptoBoxDeviceTests(unittest.TestCase): + """Some unittests for the CryptoBox + """ + + cb = cryptobox.core.main.CryptoBox() - def testAllowedDevices(self): - '''isDeviceAllowed should accept permitted devices''' - self.assertTrue(self.cb.isDeviceAllowed("/dev/loop")) - self.assertTrue(self.cb.isDeviceAllowed("/dev/loop1")) - self.assertTrue(self.cb.isDeviceAllowed("/dev/loop/urgd")) - self.assertTrue(self.cb.isDeviceAllowed("/dev/usb/../loop1")) + def test_allowed_devices(self): + '''is_device_allowed should accept permitted devices''' + self.assertTrue(self.cb.is_device_allowed("/dev/loop")) + self.assertTrue(self.cb.is_device_allowed("/dev/loop1")) + self.assertTrue(self.cb.is_device_allowed("/dev/loop/urgd")) + self.assertTrue(self.cb.is_device_allowed("/dev/usb/../loop1")) - def testDeniedDevices(self): - '''isDeviceAllowed should fail with not explicitly allowed devices''' - self.assertFalse(self.cb.isDeviceAllowed("/dev/hda")) - self.assertFalse(self.cb.isDeviceAllowed("/dev/loopa/../hda")) - self.assertFalse(self.cb.isDeviceAllowed("/")) + def test_denied_devices(self): + '''is_device_allowed should fail with not explicitly allowed devices''' + self.assertFalse(self.cb.is_device_allowed("/dev/hda")) + self.assertFalse(self.cb.is_device_allowed("/dev/loopa/../hda")) + self.assertFalse(self.cb.is_device_allowed("/")) -class CryptoBoxPropsConfigTests(unittest.TestCase): +class CryptoBoxConfigTests(unittest.TestCase): '''test here if everything with the config turns right''' - import os files = { "configFileOK" : "cbox-test_ok.conf", "configFileBroken" : "cbox-test_broken.conf", @@ -86,71 +94,89 @@ CryptoBoxRootActions = CryptoBoxRootActions def setUp(self): '''generate all files in tmp and remember the names''' import tempfile - os = self.os self.tmpdirname = tempfile.mkdtemp(prefix="cbox-") - for file in self.files.keys(): - self.filenames[file] = os.path.join(self.tmpdirname, self.files[file]) - self.writeConfig() + for tfile in self.files.keys(): + self.filenames[tfile] = os.path.join(self.tmpdirname, self.files[tfile]) + self.write_config() def tearDown(self): '''remove the created tmpfiles''' - os = self.os # remove temp files - for file in self.filenames.values(): - compl_name = os.path.join(self.tmpdirname, file) + for tfile in self.filenames.values(): + compl_name = os.path.join(self.tmpdirname, tfile) if os.path.exists(compl_name): os.remove(compl_name) # remove temp dir os.rmdir(self.tmpdirname) - def testConfigInit(self): + def test_config_init(self): '''Check various branches of config file loading''' - import os - self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps,"/invalid/path/to/config/file") - self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps,"/etc/shadow") - """ check one of the following things: - 1) are we successfully using an existing config file? - 2) do we break, if no config file is there? - depending on the existence of a config file, only one of these conditions - can be checked - hints for more comprehensive tests are appreciated :) """ - for a in ['cryptobox.conf']: - if os.path.exists(a): - cryptobox.core.main.CryptoBoxProps() + self.assertRaises(CBConfigUnavailableError, + cryptobox.core.main.CryptoBox,"/invalid/path/to/config/file") + self.assertRaises(CBConfigUnavailableError, + cryptobox.core.main.CryptoBox,"/etc/shadow") + ## check one of the following things: + ## 1) are we successfully using an existing config file? + ## 2) do we break, if no config file is there? + ## depending on the existence of a config file, only one of these conditions + ## can be checked - hints for more comprehensive tests are appreciated :) + for cfile in ['cryptobox.conf']: + if os.path.exists(cfile): + cryptobox.core.main.CryptoBox() break # this skips the 'else' clause - else: self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps) - self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps,[]) + else: + self.assertRaises(CBConfigUnavailableError, + cryptobox.core.main.CryptoBox) + self.assertRaises(CBConfigUnavailableError, + cryptobox.core.main.CryptoBox,[]) - def testBrokenConfigs(self): - """Check various broken configurations""" - self.writeConfig("SettingsDir", "SettingsDir=/foo/bar", filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) - self.writeConfig("Level", "Level = ho", filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) - self.writeConfig("Details", "#out", filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) - self.writeConfig("super", "super=/bin/invalid/no", filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) - self.writeConfig("CryptoBoxRootActions", "#not here", filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) - self.writeConfig("CryptoBoxRootActions", "CryptoBoxRootActions = /bin/false", filename=self.filenames["configFileBroken"]) - self.assertRaises(CBEnvironmentError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) + def test_broken_configs(self): + """Check various broken configurations + """ + self.write_config("SettingsDir", "SettingsDir=/foo/bar", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("Level", "Level = ho", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("Details", "#out", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("super", "super=/bin/invalid/no", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("CryptoBoxRootActions", "#not here", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("CryptoBoxRootActions", "CryptoBoxRootActions = /bin/false", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBEnvironmentError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) - def writeConfig(self, replace=None, newline=None, filename=None): + def write_config(self, replace=None, newline=None, filename=None): """write a config file and (optional) replace a line in it""" import re - if not filename: filename = self.filenames["configFileOK"] - content = self.configContentOK % (self.tmpdirname, self.tmpdirname, self.tmpdirname) + if not filename: + filename = self.filenames["configFileOK"] + content = self.configContentOK % \ + (self.tmpdirname, self.tmpdirname, self.tmpdirname) if replace: pattern = re.compile('^' + replace + '\\s*=.*$', flags=re.M) content = re.sub(pattern, newline, content) - cf = open(filename, "w") - cf.write(content) - cf.close() + cfile = open(filename, "w") + cfile.write(content) + cfile.close() if __name__ == "__main__": unittest.main() + diff --git a/src/cryptobox/tests/test.cryptoboxtools.py b/src/cryptobox/tests/test.cryptoboxtools.py index 02e460a..f8eb101 100755 --- a/src/cryptobox/tests/test.cryptoboxtools.py +++ b/src/cryptobox/tests/test.cryptoboxtools.py @@ -19,9 +19,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""Unittests for cryptobox.core.tools +""" + +__revision__ = "$Id" + import unittest -import cryptobox.core.tools as cbxTools +import cryptobox.core.tools as cbx_tools import os ## use /dev/ubd? if possible - otherwise /dev/hd? @@ -36,30 +41,40 @@ else: class CryptoBoxToolsTests(unittest.TestCase): + """All unittests for cryptoboxtools + """ - def testGetAbsoluteDeviceName(self): - func = cbxTools.getAbsoluteDeviceName + def test_get_absolute_devicename(self): + """check the get_absolute_devicename function + """ + func = cbx_tools.get_absolute_devicename self.assertTrue(func(device) == "/dev/%s" % device) self.assertTrue(func("loop0") == "/dev/loop0") self.assertTrue(func(os.path.devnull) == os.path.devnull) - def testFindMajorMinorOfDevice(self): - func = cbxTools.findMajorMinorOfDevice - self.assertTrue(func(os.path.devnull) == (1,3)) + def test_find_major_minor_of_device(self): + """check the find_major_minor_of_device function + """ + func = cbx_tools.find_major_minor_of_device + self.assertTrue(func(os.path.devnull) == (1, 3)) self.assertTrue(func("/dev/nothere") is None) - def testFindMajorMinorDeviceName(self): - func = cbxTools.findMajorMinorDeviceName - dir = os.path.join(os.path.sep, "dev") - self.assertTrue(os.path.devnull in func(dir,1,3)) - self.assertFalse(os.path.devnull in func(dir,2,3)) - self.assertFalse(None in func(dir,17,23)) + def test_find_major_minor_device(self): + """check the find_major_minor_device function + """ + func = cbx_tools.find_major_minor_device + path = os.path.join(os.path.sep, "dev") + self.assertTrue(os.path.devnull in func(path, 1, 3)) + self.assertFalse(os.path.devnull in func(path, 2, 3)) + self.assertFalse(None in func(path, 17, 23)) - def testIsPartOfBlockDevice(self): - func = cbxTools.isPartOfBlockDevice + def test_is_part_of_blockdevice(self): + """check the is_part_of_blockdevice function + """ + func = cbx_tools.is_part_of_blockdevice self.assertTrue(func("/dev/%s" % device, "/dev/%s1" % device)) self.assertFalse(func("/dev/%s" % device, "/dev/%s" % device)) self.assertFalse(func("/dev/%s1" % device, "/dev/%s" % device)) diff --git a/src/cryptobox/tests/test.plugins.py b/src/cryptobox/tests/test.plugins.py index eeb9a5a..d9172fa 100755 --- a/src/cryptobox/tests/test.plugins.py +++ b/src/cryptobox/tests/test.plugins.py @@ -19,6 +19,10 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""This module handles the unittests of all features. +""" + +__revision__ = "$Id" import unittest import cryptobox.plugins.manage @@ -28,20 +32,21 @@ class CheckForUndefinedTestCases(unittest.TestCase): def create_testcases(): - - plugins = cryptobox.plugins.manage.PluginManager(None, "../plugins").getPlugins() + """Create functions that execute unittests for all features. + """ + plugins = cryptobox.plugins.manage.PluginManager(None, "../plugins").get_plugins() glob_dict = globals() loc_dict = locals() - for pl in plugins: - test_class = pl.getTestClass() + for plugin in plugins: + test_class = plugin.get_test_class() if test_class: ## add the testclass to the global dictionary - glob_dict["unittest" + pl.getName()] = test_class + glob_dict["unittest" + plugin.get_name()] = test_class else: - subname = "test_existence_%s" % pl.getName() + subname = "test_existence_%s" % plugin.get_name() def test_existence(self): - """check if the plugin (%s) contains tests""" % pl.getName() - self.fail("no tests defined for plugin: %s" % pl.getName()) + """check if the plugin (%s) contains tests""" % plugin.get_name() + self.fail("no tests defined for plugin: %s" % plugin.get_name()) ## add this function to the class above setattr(CheckForUndefinedTestCases, subname, test_existence) #FIXME: the failure output always contains the same name for all plugins diff --git a/src/cryptobox/tests/test.websites.py b/src/cryptobox/tests/test.websites.py index 5dbc75b..3a6ab6f 100755 --- a/src/cryptobox/tests/test.websites.py +++ b/src/cryptobox/tests/test.websites.py @@ -19,6 +19,12 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""Base class for all unittests involving the webserver. + +This class uses twill. +""" + +__revision__ = "$Id" import unittest @@ -32,28 +38,32 @@ import cryptobox.web.testclass class WebServer(cryptobox.web.testclass.WebInterfaceTestClass): + """Basic tests for the webserver. + """ def test_is_server_running(self): '''the server should run under given name and port''' - self.register_auth(self.URL) - self.cmd.go(self.URL) + self.register_auth(self.url) + self.cmd.go(self.url) self.cmd.find("CBOX-STATUS") ## other URLs must not be checked, as we do not know, if they are valid class BuiltinPages(cryptobox.web.testclass.WebInterfaceTestClass): + """Basic test of builtin pages (no features). + """ def test_goto_index(self): '''display all devices''' - self.register_auth(self.URL) - self.cmd.go(self.URL) + self.register_auth(self.url) + self.cmd.go(self.url) self.cmd.find("The CryptoBox") - self.cmd.go(self.URL + "?weblang=de") + self.cmd.go(self.url + "?weblang=de") self.cmd.find("Die CryptoBox") - self.cmd.go(self.URL + "?weblang=sl") + self.cmd.go(self.url + "?weblang=sl") self.cmd.find("Privatnost v vsako vas") - self.cmd.go(self.URL + "?weblang=fr") + self.cmd.go(self.url + "?weblang=fr") self.cmd.find("La CryptoBox") diff --git a/src/cryptobox/web/__init__.py b/src/cryptobox/web/__init__.py index e69de29..f524565 100644 --- a/src/cryptobox/web/__init__.py +++ b/src/cryptobox/web/__init__.py @@ -0,0 +1,7 @@ +"""The webinterface of the CryptoBox. +""" + +__revision__ = "$Id" + +__all__ = [ 'dataset', 'languages', 'sites', 'testclass' ] + diff --git a/src/cryptobox/web/dataset.py b/src/cryptobox/web/dataset.py index 7ba2865..c04cf17 100644 --- a/src/cryptobox/web/dataset.py +++ b/src/cryptobox/web/dataset.py @@ -18,8 +18,12 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +"""Manage the hdf dataset of the cryptobox web sites. +""" + +__revision__ = "$Id" + import os -from cryptobox.core.exceptions import * import cryptobox.core.container as cbxContainer import cryptobox.core.tools as cbxTools @@ -30,20 +34,24 @@ class WebInterfaceDataset(dict): """ def __init__(self, cbox, prefs, plugins): + super(WebInterfaceDataset, self).__init__() self.prefs = prefs self.cbox = cbox - self.__setConfigValues() + self.__set_config_values() self.plugins = plugins - self.setCryptoBoxState() - self.setPluginData() - self.setContainersState() + self.set_crypto_box_state() + self.set_plugin_data() + self.set_containers_state() - def setCryptoBoxState(self): + def set_crypto_box_state(self): + """Set some hdf values according to the cryptobox as a whole. + """ import cherrypy import cryptobox.core.main import cryptobox.web.languages - self["Data.Version"] = cryptobox.core.main.VERSION + import cryptobox + self["Data.Version"] = cryptobox.__version__ langs = self.cbox.prefs["WebSettings"]["Languages"][:] langs.sort() for (index, lang) in enumerate(langs): @@ -58,10 +66,11 @@ class WebInterfaceDataset(dict): try: self["Data.ScriptURL.Prot"] = cherrypy.request.scheme host = cherrypy.request.headers["Host"] - self["Data.ScriptURL.Host"] = host.split(":",1)[0] - complete_url = "%s://%s" % (self["Data.ScriptURL.Prot"], self["Data.ScriptURL.Host"]) + self["Data.ScriptURL.Host"] = host.split(":", 1)[0] + complete_url = "%s://%s" % \ + (self["Data.ScriptURL.Prot"], self["Data.ScriptURL.Host"]) try: - port = int(host.split(":",1)[1]) + port = int(host.split(":", 1)[1]) complete_url += ":%s" % port except (IndexError, ValueError): if cherrypy.request.scheme == "http": @@ -70,7 +79,8 @@ class WebInterfaceDataset(dict): port = 443 else: ## unknown scheme -> port 0 - self.cbox.log.info("unknown protocol scheme used: %s" % (cherrypy.request.scheme,)) + self.cbox.log.info( + "unknown protocol scheme used: %s" % (cherrypy.request.scheme,)) port = 0 self["Data.ScriptURL.Port"] = port ## retrieve the relative address of the CGI (or the cherrypy base address) @@ -83,20 +93,25 @@ class WebInterfaceDataset(dict): self["Data.ScriptURL"] = "" - def setCurrentDiskState(self, device): - for container in self.cbox.getContainerList(): - if container.getDevice() == device: - isEncrypted = (container.getType() == cbxContainer.ContainerTypes["luks"]) and 1 or 0 - isPlain = (container.getType() == cbxContainer.ContainerTypes["plain"]) and 1 or 0 - isMounted = container.isMounted() and 1 or 0 - self["Data.CurrentDisk.device"] = container.getDevice() - self["Data.CurrentDisk.name"] = container.getName() - self["Data.CurrentDisk.encryption"] = isEncrypted - self["Data.CurrentDisk.plaintext"] = isPlain - self["Data.CurrentDisk.active"] = isMounted - self["Data.CurrentDisk.size"] = cbxTools.getBlockDeviceSizeHumanly(container.getDevice()) - if isMounted: - (size, avail, used) = container.getCapacity() + def set_current_disk_state(self, device): + """Set some hdf values according to the currently active disk. + """ + for container in self.cbox.get_container_list(): + if container.get_device() == device: + is_encrypted = (container.get_type() == \ + cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0 + is_plain = (container.get_type() == \ + cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 + is_mounted = container.is_mounted() and 1 or 0 + self["Data.CurrentDisk.device"] = container.get_device() + self["Data.CurrentDisk.name"] = container.get_name() + self["Data.CurrentDisk.encryption"] = is_encrypted + self["Data.CurrentDisk.plaintext"] = is_plain + self["Data.CurrentDisk.active"] = is_mounted + self["Data.CurrentDisk.size"] = cbxTools.get_blockdevice_size_humanly( + container.get_device()) + if is_mounted: + (size, avail, used) = container.get_capacity() percent = used / size self["Data.CurrentDisk.capacity.used"] = used self["Data.CurrentDisk.capacity.free"] = avail @@ -105,51 +120,63 @@ class WebInterfaceDataset(dict): self["Settings.LinkAttrs.device"] = device - def setContainersState(self): + def set_containers_state(self): + """Set some hdf values according to the list of available containers. + """ avail_counter = 0 active_counter = 0 - for container in self.cbox.getContainerList(): + for container in self.cbox.get_container_list(): ## useful if the container was changed during an action - container.resetObject() - isEncrypted = (container.getType() == cbxContainer.ContainerTypes["luks"]) and 1 or 0 - isPlain = (container.getType() == cbxContainer.ContainerTypes["plain"]) and 1 or 0 - isMounted = container.isMounted() and 1 or 0 - self["Data.Disks.%d.device" % avail_counter] = container.getDevice() - self["Data.Disks.%d.name" % avail_counter] = container.getName() - self["Data.Disks.%d.encryption" % avail_counter] = isEncrypted - self["Data.Disks.%d.plaintext" % avail_counter] = isPlain - self["Data.Disks.%d.active" % avail_counter] = isMounted - self["Data.Disks.%d.size" % avail_counter] = cbxTools.getBlockDeviceSizeHumanly(container.getDevice()) - if isMounted: active_counter += 1 + container.reset_object() + is_encrypted = (container.get_type() == \ + cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0 + is_plain = (container.get_type() == \ + cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 + is_mounted = container.is_mounted() and 1 or 0 + self["Data.Disks.%d.device" % avail_counter] = container.get_device() + self["Data.Disks.%d.name" % avail_counter] = container.get_name() + self["Data.Disks.%d.encryption" % avail_counter] = is_encrypted + self["Data.Disks.%d.plaintext" % avail_counter] = is_plain + self["Data.Disks.%d.active" % avail_counter] = is_mounted + self["Data.Disks.%d.size" % avail_counter] = \ + cbxTools.get_blockdevice_size_humanly(container.get_device()) + if is_mounted: + active_counter += 1 avail_counter += 1 self["Data.activeDisksCount"] = active_counter - def setPluginData(self): - for p in self.plugins: - entryName = "Settings.PluginList." + p.getName() + def set_plugin_data(self): + """Set some hdf values according to the available features. + """ + for plugin in self.plugins: + entry_name = "Settings.PluginList." + plugin.get_name() ## first: remove all existing settings of this plugin for key in self.keys(): - if key.startswith(entryName): del self[key] - lang_data = p.getLanguageData() - self[entryName] = p.getName() - self[entryName + ".Name"] = lang_data.getValue("Name", p.getName()) - self[entryName + ".Link"] = lang_data.getValue("Link", p.getName()) - self[entryName + ".Rank"] = p.getRank() - self[entryName + ".RequestAuth"] = p.isAuthRequired() and "1" or "0" - for a in p.pluginCapabilities: - self[entryName + ".Types." + a] = "1" - for a in p.getVisibility(): - self[entryName + ".Visible." + a] = "1" + if key.startswith(entry_name): + del self[key] + lang_data = plugin.get_language_data() + self[entry_name] = plugin.get_name() + self[entry_name + ".Name"] = lang_data.getValue("Name", plugin.get_name()) + self[entry_name + ".Link"] = lang_data.getValue("Link", plugin.get_name()) + self[entry_name + ".Rank"] = plugin.get_rank() + self[entry_name + ".RequestAuth"] = plugin.is_auth_required() and "1" or "0" + for capy in plugin.plugin_capabilities: + self[entry_name + ".Types." + capy] = "1" + for visi in plugin.get_visibility(): + self[entry_name + ".Visible." + visi] = "1" - def __setConfigValues(self): - self["Settings.TemplateDir"] = os.path.abspath(self.prefs["Locations"]["TemplateDir"]) + def __set_config_values(self): + """Set some hdf values according to configuration settings. + """ + self["Settings.TemplateDir"] = os.path.abspath( + self.prefs["Locations"]["TemplateDir"]) self["Settings.DocDir"] = os.path.abspath(self.prefs["Locations"]["DocDir"]) self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"] self["Settings.Language"] = self.prefs["WebSettings"]["Languages"][0] - for num,d in enumerate(self.prefs["Locations"]["PluginDir"]): - self["Settings.PluginDir.%d" % num] = d + for (num, dpath) in enumerate(self.prefs["Locations"]["PluginDir"]): + self["Settings.PluginDir.%d" % num] = dpath ## store the first directory in this settings variable - backward compatibility self["Settings.PluginDir"] = self.prefs["Locations"]["PluginDir"][0] self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"] diff --git a/src/cryptobox/web/languages.py b/src/cryptobox/web/languages.py index e0e857c..51f0a29 100644 --- a/src/cryptobox/web/languages.py +++ b/src/cryptobox/web/languages.py @@ -22,23 +22,28 @@ """supply information about existing languages """ +__revision__ = "$Id" + ## every language information should contain (name, pluralformat) LANGUAGE_INFO = { - "cs": ('Český', ('3', '(n==1) ? 0 : (n>=2 && n< =4) ? 1 : 2')), - "da": ('Dansk', ('2', '(n != 1)')), - "de": ('Deutsch', ('2', '(n != 1)')), - "en": ('English', ('2', '(n != 1)')), - "es": ('Español', ('2', '(n != 1)')), - "fi": ('Suomi', ('2', '(n != 1)')), - "fr": ('Français', ('2', '(n != 1)')), - "hu": ('Magyar', ('1', '0')), - "it": ('Italiano', ('2', '(n != 1)')), - "ja": ('日本語', ('1', '0')), - "nl": ('Nederlands', ('2', '(n != 1)')), - "pl": ('Polski', ('3', '(n==1 ? 0 : n%10>=2 && n%10< =4 && (n%100<10 || n%100>=20) ? 1 : 2)')), - "pt": ('Português', ('2', '(n != 1)')), - "ru": ('Русский', ('3', '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10< =4 && (n%100<10 || n%100>=20) ? 1 : 2)')), - "sl": ('Slovensko', ('4', '(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3)')), - "sv": ('Svenska', ('2', '(n != 1)')), - } + "cs": ('Český', ('3', '(n==1) ? 0 : (n>=2 && n< =4) ? 1 : 2')), + "da": ('Dansk', ('2', '(n != 1)')), + "de": ('Deutsch', ('2', '(n != 1)')), + "en": ('English', ('2', '(n != 1)')), + "es": ('Español', ('2', '(n != 1)')), + "fi": ('Suomi', ('2', '(n != 1)')), + "fr": ('Français', ('2', '(n != 1)')), + "hu": ('Magyar', ('1', '0')), + "it": ('Italiano', ('2', '(n != 1)')), + "ja": ('日本語', ('1', '0')), + "nl": ('Nederlands', ('2', '(n != 1)')), + "pl": ('Polski', ('3', '(n==1 ? 0 : n%10>=2 && n%10< =4 ' + + '&& (n%100<10 || n%100>=20) ? 1 : 2)')), + "pt": ('Português', ('2', '(n != 1)')), + "ru": ('Русский', ('3', '(n%10==1 && n%100!=11 ? 0 : ' + + 'n%10>=2 && n%10< =4 && (n%100<10 || n%100>=20) ? 1 : 2)')), + "sl": ('Slovensko', ('4', '(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || ' + + 'n%100==4 ? 2 : 3)')), + "sv": ('Svenska', ('2', '(n != 1)')), + } diff --git a/src/cryptobox/web/sites.py b/src/cryptobox/web/sites.py index 7347b82..f9eb04b 100644 --- a/src/cryptobox/web/sites.py +++ b/src/cryptobox/web/sites.py @@ -17,103 +17,150 @@ # along with the CryptoBox; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +""" this module handles all http requests and renders a website """ + +__revision__ = "$Id$" import cryptobox.core.main import cryptobox.web.dataset import cryptobox.plugins.manage -from cryptobox.core.exceptions import * +import cryptobox.core.exceptions import re import cherrypy -import types import os +import sys try: import neo_cgi, neo_util, neo_cs except ImportError: - errorMsg = "Could not import clearsilver module. Try 'apt-get install python-clearsilver'." - self.log.error(errorMsg) - sys.stderr.write(errorMsg) - raise ImportError, errorMsg + _ERRMSG = "Could not import clearsilver module. \ + Try 'apt-get install python-clearsilver'." + sys.stderr.write(_ERRMSG) + raise ImportError, _ERRMSG GETTEXT_DOMAIN = 'cryptobox-server' class PluginIconHandler: + """deliver the icons of available plugins via cherrypy""" def __init__(self, plugins): - for plugin in plugins.getPlugins(): - if not plugin: continue - plname = plugin.getName() - ## expose the getIcon function of this plugin - setattr(self, plname, plugin.getIcon) + for plugin in plugins.get_plugins(): + if not plugin: + continue + plname = plugin.get_name() + ## expose the get_icon function of this plugin + setattr(self, plname, plugin.get_icon) class WebInterfaceSites: - ''' - ''' + """handle all http requests and render pages + + this includes: + - filtering common arguments + - calling feature actions + - translating content + + all available features are dynamically exposed + """ ## this template is used under strange circumstances defaultTemplate = "empty" def __init__(self, conf_file=None): - import logging,sys - self.cbox = cryptobox.core.main.CryptoBoxProps(conf_file) - self.log = logging.getLogger("CryptoBox") - self.prefs = self.cbox.prefs - self.__resetDataset() + ## we should only use variables preceded by "__" to avoid name conflicts + ## when loading features + self.cbox = cryptobox.core.main.CryptoBox(conf_file) + self.__cached_language_data = None + self.__dataset = None + self.icons = None + self.__plugin_manager = None + self.__reset_dataset() ## store the original http error handler - self._cp_on_http_error = self.newHTTPErrorHandler + self._cp_on_http_error = self.new_http_error_handler ## set initial language order - self.langOrder = self.cbox.prefs["WebSettings"]["Languages"][:] + self.lang_order = self.cbox.prefs["WebSettings"]["Languages"][:] - def __resetDataset(self): + def __reset_dataset(self): """this method has to be called at the beginning of every "site" action important: only at the beginning of an action (to not loose information) important: for _every_ "site" action (cherrypy is stateful) also take care for the plugins, as they also contain datasets """ - self.__loadPlugins() - self.dataset = cryptobox.web.dataset.WebInterfaceDataset(self.cbox, self.prefs, self.pluginList.getPlugins()) + self.__load_plugins() + self.__dataset = cryptobox.web.dataset.WebInterfaceDataset( + self.cbox, self.cbox.prefs, self.__plugin_manager.get_plugins()) ## publish plugin icons - self.icons = PluginIconHandler(self.pluginList) + self.icons = PluginIconHandler(self.__plugin_manager) self.icons.exposed = True ## check, if a configuration partition has become available - self.cbox.prefs.preparePartition() + self.cbox.prefs.prepare_partition() - def __loadPlugins(self): - self.pluginList = cryptobox.plugins.manage.PluginManager(self.cbox, self.prefs["Locations"]["PluginDir"], self) - for plugin in self.pluginList.getPlugins(): - if not plugin: continue - plname = plugin.getName() - if plugin.isEnabled(): - self.cbox.log.info("Plugin '%s' loaded" % plname) - ## this should be the "easiest" way to expose all plugins as URLs - setattr(self, plname, self.return_plugin_action(plugin)) - setattr(getattr(self, plname), "exposed", True) - # TODO: check, if this really works - for now the "stream_response" feature seems to be broken - #setattr(getattr(self, plname), "stream_respones", True) - else: - self.cbox.log.info("Plugin '%s' is disabled" % plname) - ## remove the plugin, if it was active before - setattr(self, plname, None) + def __load_plugins(self): + """reinitialize the list of available plugins + + this includes the following: + - reload all plugins and check their state (disabled or not) + - reinitilize the datasets of all plugins + """ + self.__plugin_manager = cryptobox.plugins.manage.PluginManager( + self.cbox, self.cbox.prefs["Locations"]["PluginDir"], self) + for plugin in self.__plugin_manager.get_plugins(): + if not plugin: + continue + plname = plugin.get_name() + ## check if there are name conflicts: e.g. a local variable has the + ## same name as a plugin to be loaded -> skip these plugins + ## if we do not check this here, nasty side effects may occour ... + try: + prev_obj = getattr(self, plname) + if not callable(prev_obj) \ + or not prev_obj.exposed: + self.cbox.log.error("Skipped feature (%s) as its name" + + " conflicts with a local variable - see" + + " module cryptobox.web.sites" % plname) + ## skip this plugin + continue + except (NameError, AttributeError): + ## an attribute with the same name does not exist -> ok + if plugin.is_enabled(): + self.cbox.log.info("Plugin '%s' loaded" % plname) + ## expose all features as URLs + setattr(self, plname, self.return_plugin_action(plugin)) + getattr(self, plname).exposed = True + #TODO: check, if this really works + #for now the "stream_response" feature seems to be broken + #setattr(getattr(self, plname), "stream_respones", True) + else: + self.cbox.log.info("Plugin '%s' is disabled" % plname) + ## remove the plugin, if it was active before + setattr(self, plname, None) - ## this is a function decorator to check authentication - ## it has to be defined before any page definition requiring authentification - def __requestAuth(self=None): + ## sub pages requiring authentication may not be defined above + def __request_auth(self=None): + """ this is a function decorator to check authentication + """ def check_credentials(site): + """ see description of _inner_wrapper - please simplify this! + """ def _inner_wrapper(self, *args, **kargs): + """this function was necessary while trying around with the + function decorator - if someone can implement the decorator + with less effort, then any suggestions are welcome! + """ import base64 ## define a "non-allowed" function user, password = None, None try: - resp = cherrypy.request.headers["Authorization"][6:] # ignore "Basic " - (user, password) = base64.b64decode(resp).split(":",1) + ## ignore the "Basic " (first six letters) part + resp = cherrypy.request.headers["Authorization"][6:] + (user, password) = base64.b64decode(resp).split(":", 1) except KeyError: ## no "authorization" header was sent pass @@ -123,18 +170,20 @@ class WebInterfaceSites: except AttributeError: ## no cherrypy response header defined pass - authDict = self.cbox.prefs.userDB["admins"] - if user in authDict.keys(): - if self.cbox.prefs.userDB.getDigest(password) == authDict[user]: + auth_dict = self.cbox.prefs.user_db["admins"] + if user in auth_dict.keys(): + if self.cbox.prefs.user_db.get_digest(password) == auth_dict[user]: ## ok: return the choosen page self.cbox.log.info("access granted for: %s" % user) return site(self, *args, **kargs) else: - self.cbox.log.info("wrong password supplied for: %s" % user) + self.cbox.log.info( + "wrong password supplied for: %s" % user) else: self.cbox.log.info("unknown user: %s" % str(user)) ## wrong credentials: return "access denied" - cherrypy.response.headers["WWW-Authenticate"] = '''Basic realm="CryptoBox"''' + cherrypy.response.headers["WWW-Authenticate"] = \ + '''Basic realm="CryptoBox"''' cherrypy.response.status = 401 return self.__render("access_denied") return _inner_wrapper @@ -147,15 +196,18 @@ class WebInterfaceSites: @cherrypy.expose def index(self, weblang=""): - self.__resetDataset() - self.__setWebLang(weblang) - self.__checkEnvironment() + """the default page on startup - we show the list of available disks + """ + self.__reset_dataset() + self.__set_web_lang(weblang) + self.__check_environment() ## do not forget the language! param_dict = {"weblang":weblang} ## render "disks" plugin by default - return self.return_plugin_action(self.pluginList.getPlugin("disks"))(**param_dict) + return self.return_plugin_action( + self.__plugin_manager.get_plugin("disks"))(**param_dict) - def newHTTPErrorHandler(self, errorCode, message): + def new_http_error_handler(self, error_code, message): """handle http errors gracefully 404 - not found errors: ignored if url is below /cryptobox-misc/ @@ -163,148 +215,164 @@ class WebInterfaceSites: 500 - runtime errors: return "ok" exit code and show a polite excuse others: are there any other possible http errors? """ - import traceback, sys + import traceback ## we ignore uninteresting not-found errors - if (errorCode == 404) and \ + if (error_code == 404) and \ (cherrypy.request.path.startswith("/cryptobox-misc/") or \ cherrypy.request.path in ['/robots.txt','/favicon.ico']): - cherrypy.response.status = errorCode + cherrypy.response.status = error_code return ## an invalid action was requested - if errorCode == 404: + if error_code == 404: ## we send a not-found error (with the usual interface) - cherrypy.response.status = errorCode - self.dataset["Data.Warning"] = "InvalidAction" + cherrypy.response.status = error_code + self.__dataset["Data.Warning"] = "InvalidAction" cherrypy.response.body = self.__render("empty") return ## are there still bugs in the code? - if errorCode == 500: + if error_code == 500: ## we fix the error code (200 is "OK") cherrypy.response.status = 200 - self.cbox.log.error("HTTP-ERROR[500] - a runtime error occoured: %s" % str(message)) + self.cbox.log.error( + "HTTP-ERROR[500] - runtime error: %s" % str(message)) ## add a traceback and exception information to the lo - for a in traceback.format_exception(*sys.exc_info()): - self.cbox.log.error("\t%s" % a) - self.dataset["Data.Warning"] = "RuntimeError" + for log_line in traceback.format_exception(*sys.exc_info()): + self.cbox.log.error("\t%s" % log_line) + self.__dataset["Data.Warning"] = "RuntimeError" cherrypy.response.body = self.__render("empty") return ## unknown error type - cherrypy.response.status = errorCode - self.cbox.log.warn("HTTP-ERROR[%d] - an unknown error occoured: %s" % (errorCode, message)) + cherrypy.response.status = error_code + self.cbox.log.warn("HTTP-ERROR[%d] - an unknown error occoured: %s" \ + % (error_code, message)) cherrypy.response.body = self.__render("empty") def return_plugin_action(self, plugin): + """ returns a function that is suitable for handling a cherrypy + page request + """ def handler(self, **args): - self.__resetDataset() - self.__checkEnvironment() - args_orig = dict(args) + """this function handles a cherrypy page request + """ + self.__reset_dataset() + self.__check_environment() ## set web interface language try: - self.__setWebLang(args["weblang"]) + self.__set_web_lang(args["weblang"]) del args["weblang"] except KeyError: - self.__setWebLang("") - ## we always read the "device" setting - otherwise volume-plugin links - ## would not work easily (see "volume_props" linking to "volume_format_fs") + self.__set_web_lang("") + ## we always read the "device" setting - otherwise volume-plugin + ## links would not work easily + ## (see "volume_props" linking to "volume_format_fs") ## it will get ignored for non-volume plugins try: plugin.device = None - if self.__setDevice(args["device"]): + if self.__set_device(args["device"]): plugin.device = args["device"] del args["device"] except KeyError: - pass + plugin.device = None ## check the device argument of volume plugins - if "volume" in plugin.pluginCapabilities: + if "volume" in plugin.plugin_capabilities: ## initialize the dataset of the selected device if necessary if plugin.device: - self.dataset.setCurrentDiskState(plugin.device) + self.__dataset.set_current_disk_state(plugin.device) else: ## invalid (or missing) device setting return self.__render(self.defaultTemplate) - ## check if there is a "redirect" setting - this will override the return - ## value of the doAction function (e.g. useful for umount-before-format) + ## check if there is a "redirect" setting - this will override + ## the return value of the do_action function + ## (e.g. useful for umount-before-format) try: if args["redirect"]: - override_nextTemplate = { "plugin":args["redirect"] } - if "volume" in plugin.pluginCapabilities: - override_nextTemplate["values"] = {"device":plugin.device} + override_next_template = { "plugin":args["redirect"] } + if "volume" in plugin.plugin_capabilities: + override_next_template["values"] = {"device":plugin.device} del args["redirect"] except KeyError: - override_nextTemplate = None + override_next_template = None ## check for information to be kept after the last call try: keep_values = args["message_keep"] del args["message_keep"] for key, value in keep_values["dataset"].items(): - self.dataset[key] = value + self.__dataset[key] = value except KeyError: keep_values = None ## call the plugin handler - nextTemplate = plugin.doAction(**args) + next_template = plugin.do_action(**args) ## for 'volume' plugins: reread the dataset of the current disk ## additionally: set the default template for plugins - if "volume" in plugin.pluginCapabilities: + if "volume" in plugin.plugin_capabilities: ## maybe the state of the current volume was changed? - self.dataset.setCurrentDiskState(plugin.device) - if not nextTemplate: nextTemplate = { "plugin":"volume_mount", "values":{"device":plugin.device}} + self.__dataset.set_current_disk_state(plugin.device) + if not next_template: + next_template = { "plugin":"volume_mount", + "values":{"device":plugin.device}} else: - ## maybe a non-volume plugin changed some plugin settings (e.g. plugin_manager) - self.dataset.setPluginData() - ## update the container hdf-dataset (maybe a plugin changed the state of a container) - self.dataset.setContainersState() + ## some non-volume plugins change the internal state of other + ## plugins - e.g.: plugin_manager + self.__dataset.set_plugin_data() + ## some non-volume plugins may change the state of containers + self.__dataset.set_containers_state() ## default page for non-volume plugins is the disk selection - if not nextTemplate: nextTemplate = { "plugin":"disks", "values":{} } + if not next_template: + next_template = { "plugin":"disks", "values":{} } ## was a redirect requested? - if override_nextTemplate: - nextTemplate = override_nextTemplate - ## if another plugins was choosen for 'nextTemplate', then do it! - if isinstance(nextTemplate, types.DictType) \ - and "plugin" in nextTemplate.keys() \ - and "values" in nextTemplate.keys() \ - and self.pluginList.getPlugin(nextTemplate["plugin"]): - valueDict = dict(nextTemplate["values"]) + if override_next_template: + next_template = override_next_template + ## if another plugins was choosen for 'next_template', then do it! + if isinstance(next_template, dict) \ + and "plugin" in next_template.keys() \ + and "values" in next_template.keys() \ + and self.__plugin_manager.get_plugin(next_template["plugin"]): + value_dict = dict(next_template["values"]) ## force the current weblang attribute - otherwise it gets lost - valueDict["weblang"] = self.dataset["Settings.Language"] + value_dict["weblang"] = self.__dataset["Settings.Language"] ## check for warnings/success messages, that should be kept - if "Data.Warning" in plugin.hdf.keys() \ - or "Data.Success" in plugin.hdf.keys(): - self.cbox.log.info("keep warning message") - valueDict["message_keep"] = { "plugin":plugin, "dataset":{}} + if "Data.Success" in plugin.hdf.keys() \ + or "Data.Warning" in plugin.hdf.keys(): + value_dict["message_keep"] = {"plugin":plugin, "dataset":{}} for keep_key in ("Data.Warning", "Data.Success"): if keep_key in plugin.hdf.keys(): - valueDict["message_keep"]["dataset"][keep_key] = plugin.hdf[keep_key] - new_plugin = self.pluginList.getPlugin(nextTemplate["plugin"]) - return self.return_plugin_action(new_plugin)(**valueDict) + self.cbox.log.info("keeping message: %s" % \ + plugin.hdf[keep_key]) + value_dict["message_keep"]["dataset"][keep_key] = \ + plugin.hdf[keep_key] + new_plugin = self.__plugin_manager.get_plugin(next_template["plugin"]) + return self.return_plugin_action(new_plugin)(**value_dict) ## save the currently active plugin name - self.dataset["Data.ActivePlugin"] = plugin.getName() - return self.__render(nextTemplate, plugin) + self.__dataset["Data.ActivePlugin"] = plugin.get_name() + return self.__render(next_template, plugin) ## apply authentication? - if plugin.isAuthRequired(): - return lambda **args: self.__requestAuth()(handler)(self, **args) + if plugin.is_auth_required(): + return lambda **args: self.__request_auth()(handler)(self, **args) else: return lambda **args: handler(self, **args) - ## test authentication @cherrypy.expose - @__requestAuth + @__request_auth def test(self, weblang=""): - self.__resetDataset() - self.__setWebLang(weblang) - self.__checkEnvironment() + """test authentication - this function may be safely removed + """ + self.__reset_dataset() + self.__set_web_lang(weblang) + self.__check_environment() return "test passed" @cherrypy.expose def test_stream(self): """just for testing purposes - to check if the "stream_response" feature - actually works - for now (September 02006) it does not seem to be ok""" + actually works - for now (September 02006) it does not seem to be ok + """ import time yield "neu

" @@ -312,69 +380,77 @@ class WebInterfaceSites: ##################### input checker ########################## - def __checkEnvironment(self): - """here we should place all interesting checks to inform the user of problems + def __check_environment(self): + """inform the user of suspicious environmental problems examples are: non-https, readonly-config, ... """ - ## this check is done _after_ "resetDataSet" -> a possible config partition was - ## loaded before - if self.cbox.prefs.requiresPartition() and not self.cbox.prefs.getActivePartition(): - self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig" - # TODO: turn this on soon (add "not") - for now it is annoying - if self.__checkHTTPS(): - self.dataset["Data.EnvironmentWarning"] = "NoSSL" + ## this check is done _after_ "reset_dataset" -> if there is + ## a config partition, then it was loaded before + if self.cbox.prefs.requires_partition() \ + and not self.cbox.prefs.get_active_partition(): + self.__dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig" + #TODO: turn this on soon (add "not") - for now it is annoying + if self.__check_https(): + self.__dataset["Data.EnvironmentWarning"] = "NoSSL" - def __checkHTTPS(self): - ## check the request scheme - if cherrypy.request.scheme == "https": return True + def __check_https(self): + """check the request scheme + """ + if cherrypy.request.scheme == "https": + return True ## check an environment setting - this is quite common behind proxies try: - if os.environ["HTTPS"]: return True + if os.environ["HTTPS"]: + return True except KeyError: - pass - ## check http header TODO (check pound for the name) - try: - if cherrypy.request.headers["TODO"]: return True - except KeyError: - pass - ## the connection seems to be unencrypted - return False + ## check http header for ssl information + #TODO: (check pound for the name) + try: + if cherrypy.request.headers["XXX"]: + return True + except KeyError: + ## the connection seems to be unencrypted + return False - def __setWebLang(self, value): - """set the preferred priority of languages according to the following order: + def __set_web_lang(self, value): + """set the preferred priority of languages according to this order: 1. language selected via web interface 2. preferred browser language setting 3. languages defined in the config file """ ## start with the configured language order - langOrder = self.cbox.prefs["WebSettings"]["Languages"][:] - self.cbox.log.debug("updating language preferences (default: %s)" % str(langOrder)) + lang_order = self.cbox.prefs["WebSettings"]["Languages"][:] + self.cbox.log.debug( + "updating language preferences (default: %s)" % str(lang_order)) ## put the preferred browser language in front - guess = self.__getPreferredBrowserLanguage(langOrder) + guess = self.__get_browser_language(lang_order) if guess: - langOrder.remove(guess) - langOrder.insert(0,guess) - self.cbox.log.debug("raised priority of preferred browser language: %s" % guess) + lang_order.remove(guess) + lang_order.insert(0, guess) + self.cbox.log.debug( + "raised priority of preferred browser language: %s" % guess) ## is the chosen language (via web interface) valid? - put it in front - if value and (value in langOrder) and (not re.search(u'\W',value)): - langOrder.remove(value) - langOrder.insert(0,value) - self.cbox.log.debug("raised priority of selected language: %s" % value) + if value and (value in lang_order) and (not re.search(u'\W', value)): + lang_order.remove(value) + lang_order.insert(0, value) + self.cbox.log.debug( + "raised priority of selected language: %s" % value) elif value: self.cbox.log.info("invalid language selected: %s" % value) ## store current language setting - self.cbox.log.debug("current language preference: %s" % str(langOrder)) - self.langOrder = langOrder - self.dataset["Settings.Language"] = langOrder[0] - self.dataset["Settings.LinkAttrs.weblang"] = langOrder[0] + self.cbox.log.debug( + "current language preference: %s" % str(lang_order)) + self.lang_order = lang_order + self.__dataset["Settings.Language"] = lang_order[0] + self.__dataset["Settings.LinkAttrs.weblang"] = lang_order[0] - def __getPreferredBrowserLanguage(self, availLangs): + def __get_browser_language(self, avail_langs): """guess the preferred language of the user (as sent by the browser) - take the first language, that is part of 'availLangs' + take the first language, that is part of 'avail_langs' """ try: pref_lang_header = cherrypy.request.headers["Accept-Language"] @@ -382,56 +458,64 @@ class WebInterfaceSites: ## no language header was specified return None ## this could be a typical 'Accept-Language' header: - ## de-de,de;q=0.8,en-us;q=0.5,en;q=0.3 + ## de-de,de;q=0.8,en-us;q=0.5,en;q=0.3 regex = re.compile(u"\w+(-\w+)?(;q=[\d\.]+)?$") - pref_langs = [e.split(";",1)[0] + pref_langs = [e.split(";", 1)[0] for e in pref_lang_header.split(",") if regex.match(e)] ## is one of these preferred languages available? for lang in pref_langs: - if lang in availLangs: return lang + if lang in avail_langs: + return lang ## we try to be nice: also look for "de" if "de-de" was specified ... for lang in pref_langs: ## use only the first part of the language - short_lang = lang.split("-",1)[0] - if short_lang in availLangs: return short_lang + short_lang = lang.split("-", 1)[0] + if short_lang in avail_langs: + return short_lang ## we give up return None - def __setDevice(self, device): + def __set_device(self, device): """check a device name that was chosen via the web interface issue a warning if the device is invalid""" - if device and re.match(u'[\w /\-]+$', device) and self.cbox.getContainer(device): - self.log.debug("select device: %s" % device) + if device and re.match(u'[\w /\-]+$', device) \ + and self.cbox.get_container(device): + self.cbox.log.debug("select device: %s" % device) return True else: - self.log.warn("invalid device: %s" % device) - self.dataset["Data.Warning"] = "InvalidDevice" + self.cbox.log.warn("invalid device: %s" % device) + self.__dataset["Data.Warning"] = "InvalidDevice" return False - def __substituteGettext(self, languages, textDomain, hdf): + def __substitute_gettext(self, languages, text_domain, hdf): """substitute all texts in the hdf dataset with their translated counterparts as returned by gettext """ import gettext try: - translator = gettext.translation(textDomain, languages=languages) - except IOError, errMsg: + translator = gettext.translation(text_domain, languages=languages) + except IOError, err_msg: ## no translation found - self.cbox.log.warn("unable to load language file: %s" % errMsg) + self.cbox.log.warn("unable to load language file: %s" % err_msg) return hdf def walk_tree(hdf_node): + """iterate through all nodes""" def translate_node(node): - for (key,value) in node.attrs(): - if key == 'LINK': return + """turn one single string into unicode""" + for (key, value) in node.attrs(): + if key == "LINK": + return try: - node.setValue("",translator.ugettext(node.value())) - except UnicodeEncodeError, errMsg: - self.cbox.log.info("Failed unicode encoding for gettext: %s - %s" % (node.value(),errMsg)) + node.setValue("", translator.ugettext(node.value())) + except UnicodeEncodeError, err_msg: + self.cbox.log.info( + "Failed unicode encoding for gettext: %s - %s" \ + % (node.value(),err_msg)) ## fallback to default encoding - node.setValue("",translator.gettext(node.value())) + node.setValue("", translator.gettext(node.value())) while hdf_node: translate_node(hdf_node) walk_tree(hdf_node.child()) @@ -439,86 +523,95 @@ class WebInterfaceSites: walk_tree(hdf) - def __getLanguageData(self): + def __get_language_data(self): """return the hdf dataset of the main interface and all plugins - translations are done according to self.langOrder + translations are done according to self.lang_order """ - ## check if the language setting was changed - use cached data if possible - try: - if self.cachedLanguageData["langOrder"] == self.langOrder: - self.cbox.log.debug("using cached language data: %s" % str(self.langOrder)) - return self.cachedLanguageData["hdf"] - except AttributeError: - pass + ## check if the language setting has changed - use cache if possible + if self.__cached_language_data and \ + self.__cached_language_data["lang_order"] == self.lang_order: + self.cbox.log.debug( + "using cached language data: %s" % str(self.lang_order)) + return self.__cached_language_data["hdf"] self.cbox.log.debug("generating language data") hdf = neo_util.HDF() - hdf.readFile(os.path.join(self.prefs["Locations"]["TemplateDir"],"language.hdf")) - self.__substituteGettext(self.langOrder, GETTEXT_DOMAIN, hdf) + hdf.readFile(os.path.join( + self.cbox.prefs["Locations"]["TemplateDir"],"language.hdf")) + self.__substitute_gettext(self.lang_order, GETTEXT_DOMAIN, hdf) ## load the language data of all plugins - for p in self.pluginList.getPlugins(): - pl_lang = p.getLanguageData() - self.__substituteGettext(self.langOrder, "%s-feature-%s" % (GETTEXT_DOMAIN, p.getName()), pl_lang) - hdf.copy("Plugins.%s" % p.getName(), pl_lang) - self.cbox.log.debug("language data for plugin loaded: %s" % p.getName()) + for plugin in self.__plugin_manager.get_plugins(): + pl_lang = plugin.get_language_data() + self.__substitute_gettext(self.lang_order, "%s-feature-%s" % \ + (GETTEXT_DOMAIN, plugin.get_name()), pl_lang) + hdf.copy("Plugins.%s" % plugin.get_name(), pl_lang) + self.cbox.log.debug( + "language data for plugin loaded: %s" % plugin.get_name()) ## cache result for later retrieval - self.cachedLanguageData = {"langOrder": self.langOrder, "hdf": hdf} + self.__cached_language_data = \ + {"lang_order": self.lang_order, "hdf": hdf} return hdf - def __render(self, renderInfo, plugin=None): + def __render(self, render_info, plugin=None): '''renders from clearsilver templates and returns the resulting html ''' - ## is renderInfo a string (filename of the template) or a dictionary? - if type(renderInfo) == types.DictType: - template = renderInfo["template"] - if renderInfo.has_key("generator"): - generator = renderInfo["generator"] + ## is render_info a string (filename of the template) or a dictionary? + if isinstance(render_info, dict): + template = render_info["template"] + if render_info.has_key("generator"): + generator = render_info["generator"] else: generator = None else: - (template, generator) = (renderInfo, None) + (template, generator) = (render_info, None) ## load the language data hdf = neo_util.HDF() - hdf.copy("Lang", self.__getLanguageData()) + hdf.copy("Lang", self.__get_language_data()) - ## first: assume, that the template file is in the global template directory - self.dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], template + ".cs")) + ## first: assume, that the template file is in the global + ## template directory + self.__dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join( + self.cbox.prefs["Locations"]["TemplateDir"], + template + ".cs")) if plugin: ## check, if the plugin provides the template file -> overriding - plugin_cs_file = plugin.getTemplateFileName(template) + plugin_cs_file = plugin.get_template_filename(template) if plugin_cs_file: - self.dataset["Settings.TemplateFile"] = plugin_cs_file + self.__dataset["Settings.TemplateFile"] = plugin_cs_file ## add the current state of the plugins to the hdf dataset - self.dataset["Data.Status.Plugins.%s" % plugin.getName()] = plugin.getStatus() + self.__dataset["Data.Status.Plugins.%s" % plugin.get_name()] = \ + plugin.get_status() ## load the dataset of the plugin - plugin.loadDataSet(hdf) + plugin.load_dataset(hdf) - self.log.info("rendering site: " + template) + self.cbox.log.info("rendering site: " + template) - cs_path = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], "main.cs")) + cs_path = os.path.abspath(os.path.join( + self.cbox.prefs["Locations"]["TemplateDir"], "main.cs")) if not os.access(cs_path, os.R_OK): - log.error("Couldn't read clearsilver file: %s" % cs_path) + self.cbox.log.error( + "Couldn't read clearsilver file: %s" % cs_path) yield "Couldn't read clearsilver file: %s" % cs_path return - self.log.debug(self.dataset) - for key in self.dataset.keys(): - hdf.setValue(key,str(self.dataset[key])) - cs = neo_cs.CS(hdf) - cs.parseFile(cs_path) + self.cbox.log.debug(self.__dataset) + for key in self.__dataset.keys(): + hdf.setValue(key, str(self.__dataset[key])) + cs_data = neo_cs.CS(hdf) + cs_data.parseFile(cs_path) ## is there a generator containing additional information? if not generator: ## all content in one flush - yield cs.render() + yield cs_data.render() else: content_generate = generator() dummy_line = """""" ## now we do it linewise - checking for the content marker - for line in cs.render().splitlines(): + for line in cs_data.render().splitlines(): if line.find(dummy_line) != -1: yield line.replace(dummy_line, content_generate.next()) else: diff --git a/src/cryptobox/web/testclass.py b/src/cryptobox/web/testclass.py index 291220d..6dc3bce 100644 --- a/src/cryptobox/web/testclass.py +++ b/src/cryptobox/web/testclass.py @@ -24,6 +24,8 @@ super class of all web interface unittests for the cryptobox just inherit this class and add some test functions """ +__revision__ = "$Id" + import unittest import twill import cherrypy @@ -34,10 +36,12 @@ import os ## we do the following, for easy surfing ## e.g. use: cbx.go(your_url) ## commands api: http://twill.idyll.org/commands.html -CBXHOST="localhost" -CBXPORT=8081 -CBX_URL="http://%s:%d/" % (CBXHOST, CBXPORT) -LOG_FILE="/tmp/twill.log" +CBXHOST = "localhost" +CBXPORT = 8081 +CBX_URL = "http://%s:%d/" % (CBXHOST, CBXPORT) +LOG_FILE = "/tmp/cryptobox-twill.log" +WEBLOG_FILE = "/tmp/cryptobox-cherrypy.log" + class WebInterfaceTestClass(unittest.TestCase): '''this class checks the webserver, using "twill" @@ -62,9 +66,12 @@ class WebInterfaceTestClass(unittest.TestCase): 'server.logToScreen' : False, 'autoreload.on': False, 'server.threadPool': 1, - 'server.environment': 'production', + 'server.environment': 'development', + 'server.log_tracebacks': True, + 'server.log_file': WEBLOG_FILE, }) - cherrypy.root = cryptobox.web.sites.WebInterfaceSites("cryptobox-unittests.conf") + cherrypy.root = cryptobox.web.sites.WebInterfaceSites( + "cryptobox-unittests.conf") cherrypy.server.start(initOnly=True, serverClass=None) from cherrypy._cpwsgi import wsgiApp @@ -74,19 +81,13 @@ class WebInterfaceTestClass(unittest.TestCase): self.output = open(LOG_FILE,"a") twill.set_output(self.output) self.cmd = twill.commands - self.URL = CBX_URL + self.url = CBX_URL self.cbox = cherrypy.root.cbox self.globals, self.locals = twill.namespaces.get_twill_glocals() ## search for a usable block device ## use /dev/ubd? if possible - otherwise /dev/hd? ## so it will be possible to use these tests inside of an uml - for d in ["ubdb", "loop", "ubda", "udbc", "ubdd", "hdb", "hda", "hdc", "hdd"]: - if os.path.exists("/dev/%s1" % d): - device = d - break - else: - device = "hda" - self.device = device + self.blockdevice, self.device = self.__find_test_device() @@ -103,6 +104,34 @@ class WebInterfaceTestClass(unittest.TestCase): browser = twill.commands.get_browser() soup = BeautifulSoup(browser.get_html()) return soup + + + def __find_test_device(self): + """Search for a valid test device - the data will get lost ... + """ + for dev in ["ubdb", "loop", "ubda", "udbc", "ubdd"]: + if os.path.exists("/dev/%s1" % dev) \ + and not self.__is_config_partition("/dev/%s1" % dev): + return (dev, dev + "1") + if os.path.exists("/dev/%s2" % dev) \ + and not self.__is_config_partition("/dev/%s2" % dev): + return (dev, dev + "2") + else: + raise Exception, "no valid device for testing found" + + + def __is_config_partition(self, device): + """Check if the device is a configuration partition. + """ + import subprocess + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ '/sbin/e2label', + device ]) + (stdout, stderr) = proc.communicate() + return stdout.strip() == "cbox_config" def register_auth(self, url, user="admin", password="admin"): diff --git a/src/pylintrc b/src/pylintrc new file mode 100644 index 0000000..bdcf546 --- /dev/null +++ b/src/pylintrc @@ -0,0 +1,44 @@ +# this is a local configuration file for pylint to be used for checking the +# quality of the CryptoBox code +# +# just run: +# bin/do_pylint.sh cryptobox.core.main +# to check the module cryptobox.core.main + + +[MASTER] +# Add to the black list. It should be a base name, not a +# path. You may set this option multiple times. +ignore=CVS +ignore=.svn + + +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes=__revision__ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=88 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string='\t' + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO