changed coding style according to pylint

This commit is contained in:
lars 2006-12-05 12:24:47 +00:00
parent f6295a4b2d
commit fe69eb38ae
66 changed files with 2355 additions and 1515 deletions

View file

@ -31,6 +31,8 @@ Syntax:
this script will always return with an exitcode 0 (true), if "check" is the only argument this script will always return with an exitcode 0 (true), if "check" is the only argument
""" """
__revision__ = "$Id"
import os import os
import sys import sys
import subprocess import subprocess

View file

@ -24,16 +24,42 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import os, sys import os, sys
import cryptobox.web.sites import cryptobox.web.sites
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
from optparse import OptionParser 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: try:
import cherrypy import cherrypy
except: 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) sys.exit(1)
@ -129,17 +155,22 @@ def close_open_files():
def write_pid_file(pid_file): 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: try:
pidf = open(pid_file,"w") pidf = open(pid_file,"w")
pidf.write(str(os.getpid())) pidf.write(str(os.getpid()))
pidf.close() pidf.close()
except (IOError, OSError), errMsg: 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 ## it is just a warning - no need to break
def parseOptions(): def parseOptions():
version = "%prog" + cryptobox.core.main.VERSION import cryptobox
version = "%prog" + cryptobox.__version__
parser = OptionParser(version=version) parser = OptionParser(version=version)
parser.set_defaults(conffile="/etc/cryptobox-server/cryptobox.conf", parser.set_defaults(conffile="/etc/cryptobox-server/cryptobox.conf",
pidfile="/var/run/cryptobox-server/webserver.pid", pidfile="/var/run/cryptobox-server/webserver.pid",

33
bin/do_pylint.sh Executable file
View file

@ -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

View file

@ -16,7 +16,6 @@
# Short-Description: start CryptoBox webserver # Short-Description: start CryptoBox webserver
### END INIT INFO ### END INIT INFO
# read the default setting file, if it exists # read the default setting file, if it exists
[ -r /etc/default/cryptobox-server ] && source /etc/default/cryptobox-server [ -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 [ "$NO_START" = "1" ] && exit 0
DAEMON=/usr/sbin/CryptoBoxWebserver DAEMON=/usr/sbin/CryptoBoxWebserver
PYTHON_EXEC=/usr/bin/python
PIDFILE=/var/run/cryptobox-server/webserver.pid PIDFILE=/var/run/cryptobox-server/webserver.pid
DESC="CryptoBox Daemon (webinterface)" DESC="CryptoBox Daemon (webinterface)"
OPTIONS="-B --pidfile=$PIDFILE --config=$CONF_FILE --logfile=$LOGFILE --host=$HOST --port=$PORT $SERVER_OPTS" 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 case "$1" in
start ) start )
# TODO: mount config dir # create the directory of the pid file if necessary
# 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
PIDDIR=$(dirname "$PIDFILE") PIDDIR=$(dirname "$PIDFILE")
if [ -d "$PIDDIR" ] if [ -d "$PIDDIR" ]
then mkdir -p "$PIDDIR" then mkdir -p "$PIDDIR"
@ -60,14 +56,21 @@ case "$1" in
if start-stop-daemon \ if start-stop-daemon \
--chuid $RUNAS: --quiet --start \ --chuid $RUNAS: --quiet --start \
--user $RUNAS --pidfile "$PIDFILE" \ --user $RUNAS --pidfile "$PIDFILE" \
--exec /usr/bin/python --startas "$DAEMON" -- $OPTIONS --startas "$PYTHON_EXEC" -- "$DAEMON" $OPTIONS
then log_end_msg 0 then log_end_msg 0
else log_end_msg 1 else log_end_msg 1
fi fi
;; ;;
stop ) stop )
log_daemon_msg "Stopping cryptobox webserver" "$DESC" 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" \ --pidfile "$PIDFILE" \
--user "$RUNAS" --user "$RUNAS"
then test -e "$PIDFILE" && rm "$PIDFILE" then test -e "$PIDFILE" && rm "$PIDFILE"

View file

@ -18,29 +18,39 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
"""Change date and time.
"""
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
class date(cryptobox.plugins.base.CryptoBoxPlugin): class date(cryptobox.plugins.base.CryptoBoxPlugin):
"""The date feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "preferences" ] plugin_visibility = [ "preferences" ]
requestAuth = False request_auth = False
rank = 10 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 import datetime
if store: if store:
try: try:
year, month, day = int(year), int(month), int(day) year, month, day = int(year), int(month), int(day)
hour, minute = int(hour), int(minute) 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: except ValueError:
self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" self.hdf["Data.Warning"] = "Plugins.date.InvalidDate"
self.__prepareFormData() self.__prepare_form_data()
return "form_date" return "form_date"
date = "%02d%02d%02d%02d%d" % (month, day, hour, minute, year) 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.cbox.log.info("changed date to: %s" % date)
self.hdf["Data.Success"] = "Plugins.date.DateChanged" self.hdf["Data.Success"] = "Plugins.date.DateChanged"
return None 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) ## 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.cbox.log.info("failed to set date: %s" % date)
self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" self.hdf["Data.Warning"] = "Plugins.date.InvalidDate"
self.__prepareFormData() self.__prepare_form_data()
return "form_date" return "form_date"
else: else:
self.__prepareFormData() self.__prepare_form_data()
return "form_date" return "form_date"
def getStatus(self): def get_status(self):
now = self.__getCurrentDate() """Retrieve the status of the feature.
return "%d/%d/%d/%d/%d/%d" % (now.year, now.month, now.day, now.hour, now.minute, now.second) """
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): def __prepare_form_data(self):
date = self.__getCurrentDate() """Set some hdf values.
self.hdf[self.hdf_prefix + "year"] = date.year """
self.hdf[self.hdf_prefix + "month"] = date.month cur_date = self.__get_current_date()
self.hdf[self.hdf_prefix + "day"] = date.day self.hdf[self.hdf_prefix + "year"] = cur_date.year
self.hdf[self.hdf_prefix + "hour"] = date.hour self.hdf[self.hdf_prefix + "month"] = cur_date.month
self.hdf[self.hdf_prefix + "minute"] = date.minute 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 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 subprocess
import os import os
proc = subprocess.Popen( proc = subprocess.Popen(
@ -83,7 +102,7 @@ class date(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin", "plugin",
os.path.join(self.pluginDir, "root_action.py"), os.path.join(self.plugin_dir, "root_action.py"),
date]) date])
proc.wait() proc.wait()
return proc.returncode == 0 return proc.returncode == 0

View file

@ -19,6 +19,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script ## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script
PLUGIN_TYPE = "cryptobox" PLUGIN_TYPE = "cryptobox"

View file

@ -18,18 +18,20 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_get_date(self): def test_get_date(self):
"""retrieve the current date""" """retrieve the current date"""
date = self._getCurrentDate() date = self._get_current_date()
def test_change_date(self): def test_change_date(self):
"""set the date back and forth""" """set the date back and forth"""
now = self._getCurrentDate() now = self._get_current_date()
## copy current time ## copy current time
new_date = dict(now) new_date = dict(now)
## move three minutes forward (more is not nice because of screensavers) ## 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) new_date["hour"] = now["hour"] + ((now["minute"] + 3) / 60)
## move forward ... ## move forward ...
self._setDate(new_date) self._setDate(new_date)
self.assertEquals(new_date, self._getCurrentDate()) self.assertEquals(new_date, self._get_current_date())
## ... and backward ## ... and backward
self._setDate(now) self._setDate(now)
self.assertEquals(now, self._getCurrentDate()) self.assertEquals(now, self._get_current_date())
def test_try_broken_date(self): def test_try_broken_date(self):
@ -54,8 +56,8 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.cmd.find("invalid value for date") self.cmd.find("invalid value for date")
def _getCurrentDate(self): def _get_current_date(self):
date_url = self.URL + "date" date_url = self.url + "date"
self.register_auth(date_url) self.register_auth(date_url)
self.cmd.go(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") 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): def _setDate(self, date):
"""for now we have to use this function instead of the one below""" """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["year"]), str(date["month"]), str(date["day"]),
str(date["hour"]), str(date["minute"])) str(date["hour"]), str(date["minute"]))
self.register_auth(date_url) self.register_auth(date_url)
@ -83,7 +85,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _setDateBroken(self, date): def _setDateBroken(self, date):
"""this should work, but the parsing of twill seems to be broken """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""" 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.register_auth(date_url)
self.cmd.go(date_url) self.cmd.go(date_url)
self.cmd.formvalue("set_date", "year", str(date["year"])) self.cmd.formvalue("set_date", "year", str(date["year"]))

View file

@ -18,21 +18,31 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
"""The disks feature of the CryptoBox.
"""
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
class disks(cryptobox.plugins.base.CryptoBoxPlugin): class disks(cryptobox.plugins.base.CryptoBoxPlugin):
"""The disk feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "menu" ] plugin_visibility = [ "menu" ]
requestAuth = False request_auth = False
rank = 10 rank = 10
def doAction(self): def do_action(self):
self.cbox.reReadContainerList() """The action handler.
"""
self.cbox.reread_container_list()
return "disks" return "disks"
def getStatus(self): def get_status(self):
return ":".join([e.getDevice() for e in self.cbox.getContainerList()]) """Retrieve the current status of the feature.
"""
return ":".join([e.get_device() for e in self.cbox.get_container_list()])

View file

@ -18,24 +18,26 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): def test_read_form(self):
'''display all devices''' '''display all devices'''
self.register_auth(self.URL) self.register_auth(self.url)
self.cmd.go(self.URL + "disks?weblang=en") self.cmd.go(self.url + "disks?weblang=en")
self.cmd.find("Available disks") self.cmd.find("Available disks")
def test_is_device_in_list(self): def test_is_device_in_list(self):
"""check if the device-under-test is in the device list""" """check if the device-under-test is in the device list"""
self.register_auth(self.URL) self.register_auth(self.url)
self.cmd.go(self.URL + "disks?weblang=en") self.cmd.go(self.url + "disks?weblang=en")
self.cmd.find("Available disks") self.cmd.find("Available disks")
self.cmd.find(u'Data.Status.Plugins.disks=(.*)$', "m") self.cmd.find(u'Data.Status.Plugins.disks=(.*)$', "m")
devices = self.locals["__match__"].split(":") devices = self.locals["__match__"].split(":")
self.assertTrue(len(devices)>0) 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)

View file

@ -18,26 +18,34 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
"""The help feature of the CryptoBox.
"""
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
class help(cryptobox.plugins.base.CryptoBoxPlugin): class help(cryptobox.plugins.base.CryptoBoxPlugin):
"""The help feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "menu" ] plugin_visibility = [ "menu" ]
requestAuth = False request_auth = False
rank = 80 rank = 80
default_lang = 'en' default_lang = 'en'
default_page = "CryptoBoxUser" default_page = "CryptoBoxUser"
def doAction(self, page=""): def do_action(self, page=""):
'''prints the offline wikipage '''prints the offline wikipage
''' '''
import re,os import re, os
## check for invalid characters and if the page exists in the default language ## check for invalid characters and if the page exists in the default language
if page and \ if page and \
not re.search(u'\W', 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 ## everything is ok
pass pass
else: else:
@ -49,20 +57,23 @@ class help(cryptobox.plugins.base.CryptoBoxPlugin):
## store the name of the page ## store the name of the page
self.hdf[self.hdf_prefix + "Page"] = page self.hdf[self.hdf_prefix + "Page"] = page
## choose the right language ## choose the right language
for l in self.site.langOrder: for lang in self.site.lang_order:
if os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], l, page + '.html')): if os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"],
lang = l lang, page + '.html')):
doc_lang = lang
break break
else: else:
lang = self.default_lang doc_lang = self.default_lang
self.hdf[self.hdf_prefix + "Language"] = lang self.hdf[self.hdf_prefix + "Language"] = doc_lang
## store the current setting for a later "getStatus" call ## store the current setting for a later "getStatus" call
self.current_lang = lang self.current_lang = doc_lang
self.current_page = page self.current_page = page
return "doc" 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) return "%s:%s" % (self.current_lang, self.current_page)

View file

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
from twill.errors import * from twill.errors import *
@ -27,7 +29,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
'''help pages should be available in different languages''' '''help pages should be available in different languages'''
## check english help pages ## 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("Table of Contents")
self.cmd.find("Getting started") self.cmd.find("Getting started")
(lang,page) = self._getHelpStatus() (lang,page) = self._getHelpStatus()
@ -35,7 +37,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.assertTrue(page == "CryptoBoxUser") self.assertTrue(page == "CryptoBoxUser")
## check german help pages ## 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("Table of Contents")
self.cmd.find("Wie geht es los") self.cmd.find("Wie geht es los")
(lang,page) = self._getHelpStatus() (lang,page) = self._getHelpStatus()
@ -43,7 +45,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.assertTrue(page == "CryptoBoxUser") self.assertTrue(page == "CryptoBoxUser")
## check slovene help pages ## 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") self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents")
## add a slovene text here, as soon as the help is translated ## add a slovene text here, as soon as the help is translated
(lang,page) = self._getHelpStatus() (lang,page) = self._getHelpStatus()
@ -52,7 +54,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.assertTrue(page == "CryptoBoxUser") self.assertTrue(page == "CryptoBoxUser")
## check french help pages ## 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") self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents")
## add a french text here, as soon as the help is translated ## add a french text here, as soon as the help is translated
(lang,page) = self._getHelpStatus() (lang,page) = self._getHelpStatus()
@ -61,7 +63,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
self.assertTrue(page == "CryptoBoxUser") self.assertTrue(page == "CryptoBoxUser")
## test a random language - it should fall back to english ## 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") self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents")
(lang,page) = self._getHelpStatus() (lang,page) = self._getHelpStatus()
self.assertTrue(lang == "en") self.assertTrue(lang == "en")
@ -70,18 +72,18 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_help_pages(self): def test_help_pages(self):
"""check invalid page requests""" """check invalid page requests"""
self.cmd.go(self.URL + "help?page=foobar") self.cmd.go(self.url + "help?page=foobar")
(lang,page) = self._getHelpStatus() (lang,page) = self._getHelpStatus()
self.assertTrue(page == "CryptoBoxUser") self.assertTrue(page == "CryptoBoxUser")
self.cmd.go(self.URL + "help?page=CryptoBoxUser") self.cmd.go(self.url + "help?page=CryptoBoxUser")
(lang,page) = self._getHelpStatus() (lang,page) = self._getHelpStatus()
self.assertTrue(page == "CryptoBoxUser") self.assertTrue(page == "CryptoBoxUser")
def test_help_default_languages(self): def test_help_default_languages(self):
"""check invalid page requests""" """check invalid page requests"""
self.cmd.go(self.URL + "help?weblang=foobar") self.cmd.go(self.url + "help?weblang=foobar")
(lang,page) = self._getHelpStatus() (lang,page) = self._getHelpStatus()
self.assertTrue(lang == "en") self.assertTrue(lang == "en")

View file

@ -18,20 +18,31 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 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 import cryptobox.plugins.base
class language_selection(cryptobox.plugins.base.CryptoBoxPlugin): class language_selection(cryptobox.plugins.base.CryptoBoxPlugin):
"""The language_selection feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "menu", "preferences" ] plugin_visibility = [ "menu", "preferences" ]
requestAuth = False request_auth = False
rank = 60 rank = 60
def doAction(self): def do_action(self):
"""Show all available languages.
"""
return "language_selection" return "language_selection"
def getStatus(self): def get_status(self):
return ":".join(self.site.langOrder) """The current status of the feature is defined as the current language.
"""
return ":".join(self.site.lang_order)

View file

@ -18,19 +18,21 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): def test_read_form(self):
url = self.URL + "language_selection?weblang=en" url = self.url + "language_selection?weblang=en"
self.register_auth(url) self.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('hoose an interface language') self.cmd.find('hoose an interface language')
def test_check_language_list(self): def test_check_language_list(self):
url = self.URL + "language_selection" url = self.url + "language_selection"
self.register_auth(url) self.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find(u'Data.Status.Plugins.language_selection=(.*)$', "m") self.cmd.find(u'Data.Status.Plugins.language_selection=(.*)$', "m")

View file

@ -18,56 +18,76 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 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 cryptobox.plugins.base
import os import os
class logs(cryptobox.plugins.base.CryptoBoxPlugin): class logs(cryptobox.plugins.base.CryptoBoxPlugin):
"""The logs feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "preferences" ] plugin_visibility = [ "preferences" ]
requestAuth = False request_auth = False
rank = 90 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 import re
## filter input ## filter input
try: try:
lines = int(lines) lines = int(lines)
if lines <= 0: raise(ValueError) if lines <= 0:
raise(ValueError)
except ValueError: except ValueError:
lines = 50 lines = 50
try: try:
size = int(size) size = int(size)
if size <= 0: raise(ValueError) if size <= 0:
raise(ValueError)
except ValueError: except ValueError:
size = 3000 size = 3000
if not pattern is None: if not pattern is None:
pattern = str(pattern) pattern = str(pattern)
if re.search(u'\W', pattern): pattern = None if re.search(u'\W', pattern):
self.hdf[self.hdf_prefix + "Content"] = self.__getLogContent(lines, size, pattern) pattern = None
self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.abspath(os.path.join(self.pluginDir, "logs.css")) 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" return "show_log"
def getStatus(self): def get_status(self):
"""The current status includes the log configuration details.
"""
return "%s:%s:%s" % ( return "%s:%s:%s" % (
self.cbox.prefs["Log"]["Level"], self.cbox.prefs["Log"]["Level"],
self.cbox.prefs["Log"]["Destination"], self.cbox.prefs["Log"]["Destination"],
self.cbox.prefs["Log"]["Details"]) self.cbox.prefs["Log"]["Details"])
def __getLogContent(self, lines, maxSize, pattern): def __get_log_content(self, lines, max_size, pattern):
import re """Filter, sort and shorten the log content.
"""
if pattern: if pattern:
content = [] content = []
current_length = 0 current_length = 0
for line in self.cbox.getLogData(): for line in self.cbox.get_log_data():
if line.find(pattern) != -1: if line.find(pattern) != -1:
content.append(line) content.append(line)
current_length += len(line) current_length += len(line)
if lines and len(content) >=lines: break if lines and len(content) >= lines:
if maxSize and current_length >=maxSize: break break
if max_size and current_length >= max_size:
break
else: else:
content = self.cbox.getLogData(lines, maxSize) content = self.cbox.get_log_data(lines, max_size)
return "<br/>".join(content) return "<br/>".join(content)

View file

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_logs(self): def test_read_logs(self):
log_url = self.URL + "logs" log_url = self.url + "logs"
self.register_auth(log_url) self.register_auth(log_url)
self.cmd.go(log_url) self.cmd.go(log_url)
self.cmd.find('class="console"') self.cmd.find('class="console"')
@ -31,13 +33,13 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_write_logs(self): def test_write_logs(self):
log_text = "unittest - just a marker - please ignore" log_text = "unittest - just a marker - please ignore"
self.cbox.log.error(log_text) self.cbox.log.error(log_text)
log_url = self.URL + "logs" log_url = self.url + "logs"
self.register_auth(log_url) self.register_auth(log_url)
self.cmd.go(log_url + "?pattern=ERROR") self.cmd.go(log_url + "?pattern=ERROR")
self.cmd.find(log_text) self.cmd.find(log_text)
def test_invalid_args(self): def test_invalid_args(self):
log_url = self.URL + "logs" log_url = self.url + "logs"
self.cmd.go(log_url + "?lines=10") self.cmd.go(log_url + "?lines=10")
self.cmd.find('class="console"') self.cmd.find('class="console"')
self.cmd.go(log_url + "?lines=0") self.cmd.go(log_url + "?lines=0")

View file

@ -18,23 +18,32 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
"""The network feature of the CryptoBox.
"""
__revision__ = "$Id"
import subprocess import subprocess
import os import os
import cryptobox.plugins.base import cryptobox.plugins.base
## specify (in seconds), how long we should wait before redirecting and ip change ## specify (in seconds), how long we should wait before redirecting and ip change
REDIRECT_DELAY=20 REDIRECT_DELAY = 10
CHANGE_IP_DELAY=1 CHANGE_IP_DELAY = 2
class network(cryptobox.plugins.base.CryptoBoxPlugin): class network(cryptobox.plugins.base.CryptoBoxPlugin):
"""The network feature of the CryptoBox.
"""
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "preferences" ] plugin_visibility = [ "preferences" ]
requestAuth = True request_auth = True
rank = 30 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 ## if we were redirected, then we should display the default page
self.cbox.log.debug("executing network plugin") self.cbox.log.debug("executing network plugin")
if redirected == "1": if redirected == "1":
@ -46,58 +55,68 @@ class network(cryptobox.plugins.base.CryptoBoxPlugin):
try: try:
for ip_in in (ip1, ip2, ip3, ip4): for ip_in in (ip1, ip2, ip3, ip4):
if (int(ip_in) < 0) or (int(ip_in) > 255): 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 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: except ValueError:
self.hdf["Data.Warning"] = "Plugins.network.InvalidIP" self.hdf["Data.Warning"] = "Plugins.network.InvalidIP"
self.__prepareFormData() self.__prepare_form_data()
return "form_network" return "form_network"
if self.__setIP(ip): if self.__set_ip(new_ip):
self.cbox.log.info("the IP was successfully changed: %s" % ip) self.cbox.log.info("the IP was successfully changed: %s" % new_ip)
self.hdf["Data.Success"] = "Plugins.network.IPChanged" 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 self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY
return None return None
else: 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.hdf["Data.Warning"] = "Plugins.network.InvalidIP"
self.__prepareFormData() self.__prepare_form_data()
return "form_network" return "form_network"
else: else:
self.cbox.log.debug("network plugin: show form") self.cbox.log.debug("network plugin: show form")
## just show the form ## just show the form
self.__prepareFormData() self.__prepare_form_data()
return "form_network" return "form_network"
def getStatus(self): def get_status(self):
return "%d.%d.%d.%d" % self.__getCurrentIP() """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 import cherrypy
req = cherrypy.request req = cherrypy.request
base_parts = req.base.split(":") base_parts = req.base.split(":")
dest = "%s:%s" % (base_parts[0], ip) dest = "%s://%s" % (base_parts[0], ip)
if len(base_parts) == 3: if len(base_parts) == 3:
dest += ":%s" % base_parts[2] dest += ":%s" % base_parts[2]
return dest return dest
def __prepareFormData(self): def __prepare_form_data(self):
(oc1, oc2, oc3, oc4) = self.__getCurrentIP() """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.oc1"] = oc1
self.hdf[self.hdf_prefix + "ip.oc2"] = oc2 self.hdf[self.hdf_prefix + "ip.oc2"] = oc2
self.hdf[self.hdf_prefix + "ip.oc3"] = oc3 self.hdf[self.hdf_prefix + "ip.oc3"] = oc3
self.hdf[self.hdf_prefix + "ip.oc4"] = oc4 self.hdf[self.hdf_prefix + "ip.oc4"] = oc4
def __getCurrentIP(self): def __get_current_ip(self):
"""Retrieve the current IP.
"""
import re import re
import imp import imp
## load some values from the root_action.py script ## 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 ## get the current IP of the network interface
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
@ -106,20 +125,25 @@ class network(cryptobox.plugins.base.CryptoBoxPlugin):
root_action_plug.IFCONFIG_BIN, root_action_plug.IFCONFIG_BIN,
root_action_plug.IFACE]) root_action_plug.IFACE])
(stdout, stderr) = proc.communicate() (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 ## 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) match = re.search(u'inet [\w]+:(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s', stdout)
if match: if match:
## use the previously matched numbers ## use the previously matched numbers
return tuple([int(e) for e in match.groups()]) return tuple([int(e) for e in match.groups()])
else: 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 import threading
## call the root_action script after some seconds - so we can deliver the page before ## 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 import time
time.sleep(CHANGE_IP_DELAY) time.sleep(CHANGE_IP_DELAY)
proc = subprocess.Popen( proc = subprocess.Popen(
@ -129,19 +153,17 @@ class network(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin", "plugin",
os.path.join(self.pluginDir, "root_action.py"), os.path.join(self.plugin_dir, "root_action.py"),
ip]) new_ip])
proc.wait() proc.wait()
if proc.returncode != 0: 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())) self.cbox.log.warn("error output: %s" % str(proc.stderr.read()))
return return
thread = threading.Thread() thread = threading.Thread()
thread.run = delayedIPchange thread.run = delayed_ip_change
thread.setDaemon(True) thread.setDaemon(True)
thread.start() thread.start()
# TODO: how could we guess, if it failed? # TODO: how could we guess, if it failed?
return True return True

View file

@ -19,6 +19,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
#TODO: add netmask and gateway #TODO: add netmask and gateway

View file

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
from network import CHANGE_IP_DELAY from network import CHANGE_IP_DELAY
@ -28,43 +30,47 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
'''change of network address''' '''change of network address'''
## the time module is necessary for the CHANGE_IP_DELAY ## the time module is necessary for the CHANGE_IP_DELAY
import time import time
self.register_auth(self.URL + "network") self.register_auth(self.url + "network")
self.cmd.go(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 ## extract the current IP from the network plugin output
def getCurrentIP(): def get_current_ip():
self.cmd.go(self.URL + "network") self.register_auth(self.url + "network")
self.cmd.go(self.url + "network")
self.cmd.find(u'Data.Status.Plugins.network=([0-9\.]*)$', "m") self.cmd.find(u'Data.Status.Plugins.network=([0-9\.]*)$', "m")
return self.locals["__match__"] return self.locals["__match__"]
origIPtext = getCurrentIP() orig_ip_text = get_current_ip()
origIPocts = origIPtext.split(".") orig_ip_octs = orig_ip_text.split(".")
## check, if the original IP is valid (contains four octets) ## check, if the original IP is valid (contains four octets)
self.assertEquals(4, len(origIPocts)) self.assertEquals(4, len(orig_ip_octs))
wrongIP = "192.168.123.321" def set_ip((ip1, ip2, ip3, ip4)):
def setIP((ip1, ip2, ip3, ip4)): self.cmd.go(self.url + "network")
self.cmd.go(self.URL + "network")
self.cmd.formvalue("set_ip", "ip1", str(ip1)) self.cmd.formvalue("set_ip", "ip1", str(ip1))
self.cmd.formvalue("set_ip", "ip2", str(ip2)) self.cmd.formvalue("set_ip", "ip2", str(ip2))
self.cmd.formvalue("set_ip", "ip3", str(ip3)) self.cmd.formvalue("set_ip", "ip3", str(ip3))
self.cmd.formvalue("set_ip", "ip4", str(ip4)) self.cmd.formvalue("set_ip", "ip4", str(ip4))
self.cmd.submit() self.cmd.submit()
## sleep a little bit longer than the delay necessary for ip-change ## sleep a little bit longer than the delay necessary for ip-change
time.sleep(CHANGE_IP_DELAY + 0.2) time.sleep(CHANGE_IP_DELAY + 3)
setIP([1,-2,0,1]) set_ip([1,-2,0,1])
self.assertEquals(origIPtext, getCurrentIP()) self.assertEquals(orig_ip_text, get_current_ip())
setIP([1,0,0,256]) set_ip([1,0,0,256])
self.assertEquals(origIPtext, getCurrentIP()) self.assertEquals(orig_ip_text, get_current_ip())
setIP([1,"foo",0,1]) set_ip([1,"foo",0,1])
self.assertEquals(origIPtext, getCurrentIP()) self.assertEquals(orig_ip_text, get_current_ip())
setIP([10,12,0,2]) new_ip = orig_ip_octs[:]
self.assertEquals("10.12.0.2", getCurrentIP()) new_ip[3] = str((int(orig_ip_octs[3]) + 128) % 256)
setIP(origIPocts) set_ip(new_ip)
self.assertEquals(origIPtext, getCurrentIP()) 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): def test_inputs(self):
self.register_auth(self.URL + "network") self.register_auth(self.url + "network")
self.cmd.go(self.URL + "network" + "?redirected=1") self.cmd.go(self.url + "network" + "?redirected=1")
self.cmd.notfind("problem") 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") self.cmd.find("invalid network address")

View file

@ -18,114 +18,141 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
"""The partition feature of the CryptoBox.
"""
__revision__ = "$Id"
import subprocess import subprocess
import os import os
import logging import logging
import cryptobox.core.tools as cbxTools import cryptobox.core.tools as cbxTools
import cryptobox.plugins.base import cryptobox.plugins.base
class partition(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ] PARTTYPES = {
pluginVisibility = [ "preferences" ] "windows" : ["0xC", "vfat"],
requestAuth = True "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 rank = 80
PartTypes = { def do_action(self, **args):
"windows" : ["0xC", "vfat"], """Show the partitioning form and execute the requested action.
"linux" : ["L", "ext3"]} """
ConfigPartition = {
"size" : 5, # size of configuration partition (if necessary) in MB
"type" : "L",
"fs" : "ext2"}
def doAction(self, **args):
## load default hdf values ## load default hdf values
self.__prepareDataset() self.__prepare_dataset()
## retrieve some values from 'args' - defaults are empty ## retrieve some values from 'args' - defaults are empty
self.device = self.__getSelectedDevice(args) self.device = self.__get_selected_device(args)
self.withConfigPartition = self.__isWithConfigPartition() self.with_config_partition = self.__is_with_config_partition()
self.cbox.log.debug("partition plugin: selected device=%s" % str(self.device)) 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: try:
step = args["step"] step = args["step"]
del args["step"] del args["step"]
except KeyError: except KeyError:
step = "select_device" step = "select_device"
try: try:
## this way of selecting the easy setup is necessary: see select_device.py for details ## this way of selecting the easy setup is necessary:
if args["easy"]: step = "easy" ## see select_device.cs for details (button values for ie)
if args["easy"]:
step = "easy"
except KeyError: except KeyError:
pass pass
## no (or invalid) device was supplied ## no (or invalid) device was supplied
if not self.device: if not self.device:
step = "select_device" step = "select_device"
if step == "add_partition": if step == "add_partition":
return self.__actionAddPartition(args) return self.__action_add_partition(args)
elif step == "del_partition": elif step == "del_partition":
return self.__actionDelPartition(args) return self.__action_del_partition(args)
elif step == "finish": elif step == "finish":
return self.__actionFinish(args) return self.__action_finish(args)
elif step == "easy": elif step == "easy":
return self.__actionEasySetup(args) return self.__action_easy_setup()
else: # for "select_device" and for invalid targets else: # for "select_device" and for invalid targets
return self.__actionSelectDevice(args) return self.__action_select_device()
def getStatus(self): def get_status(self):
return "%s / %s / %s" % (self.device, self.deviceSize, self.withConfigPartition) """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): def __prepare_dataset(self):
self.hdf[self.hdf_prefix + "StyleSheetFile"] = os.path.join(self.pluginDir, "partition.css") """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: try:
device = args["block_device"] device = args["block_device"]
except KeyError: except KeyError:
return None return None
if not self.__isDeviceValid(device): if not self.__is_device_valid(device):
return None return None
if self.__isDeviceBusy(device): if self.__is_device_busy(device):
self.hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy" self.hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy"
return None return None
return device return device
def __isDeviceValid(self, device): def __is_device_valid(self, device):
"""Check if the device is valid and allowed.
"""
if not device: if not device:
return False return False
if not self.cbox.isDeviceAllowed(device): if not self.cbox.is_device_allowed(device):
return False return False
if not device in cbxTools.getParentBlockDevices(): if not device in cbxTools.get_parent_blockdevices():
return False return False
return True return True
def __isDeviceBusy(self, device): def __is_device_busy(self, device):
"""check if the device (or one of its partitions) is mounted""" """check if the device (or one of its partitions) is mounted
"""
# the config partition is ignored, as it will get unmounted if necessary # the config partition is ignored, as it will get unmounted if necessary
import re import re
for c in self.cbox.getContainerList(): for cont in self.cbox.get_container_list():
if re.match(device + "\d*$", c.getDevice()): if re.match(device + "\d*$", cont.get_device()):
if c.isMounted(): return True if cont.is_mounted():
return True
return False return False
def __actionSelectDevice(self, args): def __action_select_device(self):
"""Show a form to select the device for partitioning.
"""
block_devices = [e block_devices = [e
for e in cbxTools.getParentBlockDevices() for e in cbxTools.get_parent_blockdevices()
if self.cbox.isDeviceAllowed(e)] if self.cbox.is_device_allowed(e)]
counter = 0 counter = 0
for a in block_devices: for dev in block_devices:
self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = a self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = dev
self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = cbxTools.getBlockDeviceSizeHumanly(a) self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = \
self.cbox.log.debug("found a suitable block device: %s" % a) cbxTools.get_blockdevice_size_humanly(dev)
self.cbox.log.debug("found a suitable block device: %s" % dev)
counter += 1 counter += 1
if self.withConfigPartition: if self.with_config_partition:
self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1"
## there is no disk available ## there is no disk available
if not block_devices: if not block_devices:
@ -133,59 +160,72 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
return "select_device" 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"] = self.device
self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize self.hdf[self.hdf_prefix + "Device.Size"] = self.device_size
parts = self.__getPartitionsFromArgs(args) parts = self.__get_partitions_from_args(args)
self.__setPartitionData(parts) self.__set_partition_data(parts)
return "set_partitions" return "set_partitions"
def __actionDelPartition(self, args): def __action_del_partition(self, args):
"""Remove a partition from the proposed partition table.
"""
try: try:
part_num = int(args["del_num"]) part_num = int(args["del_num"])
except (TypeError,KeyError): 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"] = self.device
self.hdf[self.hdf_prefix + "Device.Size"] = self.deviceSize self.hdf[self.hdf_prefix + "Device.Size"] = self.device_size
parts = self.__getPartitionsFromArgs(args) parts = self.__get_partitions_from_args(args)
## valid partition number to be deleted? ## valid partition number to be deleted?
if part_num < len(parts): if part_num < len(parts):
del parts[part_num] del parts[part_num]
else: else:
return self.__actionAddPartition(args) return self.__action_add_partition(args)
self.__setPartitionData(parts) self.__set_partition_data(parts)
return "set_partitions" return "set_partitions"
def __actionFinish(self, args): def __action_finish(self, args):
parts = self.__getPartitionsFromArgs(args) """Write the partition table.
"""
parts = self.__get_partitions_from_args(args)
if parts: if parts:
self.__setPartitionData(parts) self.__set_partition_data(parts)
if cbxTools.isPartOfBlockDevice(self.device, self.cbox.prefs.getActivePartition()): if cbxTools.is_part_of_blockdevice(self.device,
self.cbox.prefs.umountPartition() self.cbox.prefs.get_active_partition()):
if not self.__runFDisk(parts): self.cbox.prefs.umount_partition()
if not self.__run_fdisk(parts):
self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed"
self.cbox.log.warn("partition: failed to partition device: %s" % self.device) self.cbox.log.warn(
return self.__actionAddPartition(args) "partition: failed to partition device: %s" % self.device)
return self.__action_add_partition(args)
else: else:
""" ## tricky problem: if the device was partitioned, then a created config
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 ## partition is still part of the containerlist, as the label is not
""" ## checked again - very ugly!!! So we will call reReadContainerList
self.cbox.reReadContainerList() ## after formatting the last partition - see below
self.cbox.reread_container_list()
def result_generator(): def result_generator():
"""Generate the results of formatting - may be threaded.
"""
counter = 0 counter = 0
## initialize the generator ## initialize the generator
formatPart_gen = self.__formatPartitions(parts) format_part_gen = self.__format_partitions(parts)
while counter < len(parts): while counter < len(parts):
## first part: get the device name ## first part: get the device name
yield formatPart_gen.next() yield format_part_gen.next()
counter += 1 counter += 1
## second part: do the real formatting of a partition ## second part: do the real formatting of a partition
result = formatPart_gen.next() result = format_part_gen.next()
## after the first partiton, we can reRead the containerList (as the possible config partition was already created) ## after the first partiton, we can reRead the containerList
if self.withConfigPartition and (counter == 1): ## (as the possible config partition was already created)
## important: reRead the containerList - but somehow it breaks the flow (hanging process) if self.with_config_partition and (counter == 1):
## important: reRead the containerList - but somehow it
## breaks the flow (hanging process)
#self.cbox.reReadContainerList() #self.cbox.reReadContainerList()
## write config data ## write config data
self.cbox.prefs.mountPartition() self.cbox.prefs.mountPartition()
@ -200,24 +240,28 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
"template": "show_format_progress", "template": "show_format_progress",
"generator": result_generator} "generator": result_generator}
else: 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 import types
## we do not have to take special care for a possible config partition ## 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 ## umount partition if necessary
if cbxTools.isPartOfBlockDevice(self.device, self.cbox.prefs.getActivePartition()): if cbxTools.is_part_of_blockdevice(self.device,
self.cbox.prefs.umountPartition() self.cbox.prefs.get_active_partition()):
self.cbox.prefs.umount_partition()
## partition it ## partition it
if not self.__runFDisk(parts): if not self.__run_fdisk(parts):
self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed"
return None return None
## "formatPartitions" is a generator, returning device names and bolean values ## "formatPartitions" is a generator, returning device names and bolean values
result = [e for e in self.__formatPartitions(parts) if type(e) == types.BooleanType] result = [e for e in self.__format_partitions(parts)
if self.withConfigPartition: if type(e) == types.BooleanType]
self.cbox.prefs.mountPartition() if self.with_config_partition:
self.cbox.prefs.mount_partition()
self.cbox.prefs.write() self.cbox.prefs.write()
## check if there is a "False" return value ## check if there is a "False" return value
if False in result: if False in result:
@ -229,50 +273,59 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
## operation was successful ## operation was successful
self.hdf["Data.Success"] = "Plugins.partition.EasySetup" self.hdf["Data.Success"] = "Plugins.partition.EasySetup"
self.cbox.log.info("easy partitioning succeeded") 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":[] } return { "plugin":"system_preferences", "values":[] }
def __setPartitionData(self, parts): def __set_partition_data(self, parts):
availSize = self.deviceSize """Set some hdf values for the currently proposed partition table.
"""
avail_size = self.device_size
i = 0 i = 0
for part in parts: for part in parts:
self.cbox.log.debug(part) self.cbox.log.debug(part)
self.hdf[self.hdf_prefix + "Parts.%d.Size" % i] = part["size"] self.hdf[self.hdf_prefix + "Parts.%d.Size" % i] = part["size"]
self.hdf[self.hdf_prefix + "Parts.%d.Type" % i] = part["type"] self.hdf[self.hdf_prefix + "Parts.%d.Type" % i] = part["type"]
availSize -= part["size"] avail_size -= part["size"]
i += 1 i += 1
self.hdf[self.hdf_prefix + "availSize"] = availSize self.hdf[self.hdf_prefix + "availSize"] = avail_size
if self.withConfigPartition: if self.with_config_partition:
self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1"
for t in self.PartTypes.keys(): for ptype in PARTTYPES.keys():
self.hdf[self.hdf_prefix + "Types.%s" % t] = t self.hdf[self.hdf_prefix + "Types.%s" % ptype] = ptype
## store the currently existing partitions of the choosen block device ## 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()) ] current_containers = [ e for e in self.cbox.get_container_list()
for (index, t) in enumerate(current_containers): if cbxTools.is_part_of_blockdevice(self.device, e.get_device()) ]
self.hdf[self.hdf_prefix + "ExistingContainers.%d.name" % index] = t.getName() for (index, cont) in enumerate(current_containers):
self.hdf[self.hdf_prefix + "ExistingContainers.%d.size" % index] = cbxTools.getBlockDeviceSizeHumanly(t.getDevice()) 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 = [] parts = []
done = False done = False
availSize = self.deviceSize avail_size = self.device_size
i = -1 i = -1
while not done: while not done:
i += 1 i += 1
try: try:
size = int(args["part%d_size" % i]) size = int(args["part%d_size" % i])
partType = args["part%d_type" % i] part_type = args["part%d_type" % i]
if int(size) > availSize: if int(size) > avail_size:
self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooBig" self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooBig"
continue continue
if int(size) < 10: if int(size) < 10:
self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooSmall" self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooSmall"
continue continue
if not partType in self.PartTypes.keys(): continue if not part_type in PARTTYPES.keys():
parts.append({"size":size, "type":partType}) continue
availSize -= size parts.append({"size":size, "type":part_type})
avail_size -= size
except TypeError: except TypeError:
pass pass
except KeyError: except KeyError:
@ -280,33 +333,40 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
return parts return parts
def __getAvailableDeviceSize(self, device): def __get_available_device_size(self, device):
"""calculate the available size (MB) of the device """calculate the available size (MB) of the device
also consider a (possible) configuration partition""" also consider a (possible) configuration partition
deviceSize = cbxTools.getBlockDeviceSize(device) """
if deviceSize < 0: return 0 device_size = cbxTools.get_blockdevice_size(device)
if self.withConfigPartition: if device_size < 0:
deviceSize -= self.ConfigPartition["size"] return 0
return deviceSize if self.with_config_partition:
device_size -= CONFIGPARTITION["size"]
return device_size
def __isWithConfigPartition(self): def __is_with_config_partition(self):
"""check if we have to create a configuration partition""" """check if we have to create a configuration partition
if self.cbox.prefs.requiresPartition(): """
active = self.cbox.prefs.getActivePartition() if self.cbox.prefs.requires_partition():
active = self.cbox.prefs.get_active_partition()
## we need a partition, if there is no active one ## 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 ## 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 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) ## check if the device is completely filled (to avoid some empty last blocks)
avail_size = self.deviceSize avail_size = self.device_size
for d in parts: avail_size -= d["size"] for one_part in parts:
avail_size -= one_part["size"]
self.cbox.log.debug("remaining size: %d" % avail_size) self.cbox.log.debug("remaining size: %d" % avail_size)
isFilled = avail_size == 0 is_filled = avail_size == 0
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -316,81 +376,91 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin", "plugin",
os.path.join(self.pluginDir, "root_action.py"), os.path.join(self.plugin_dir, "root_action.py"),
"partition", "partition",
self.device]) self.device])
for line in self.__getSFDiskLayout(parts, isFilled): for line in self.__get_sfdisk_layout(parts, is_filled):
proc.stdin.write(line + "\n") proc.stdin.write(line + "\n")
(output, error) = proc.communicate() (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 return proc.returncode == 0
def __getSFDiskLayout(self, paramParts, isFilled): def __get_sfdisk_layout(self, param_parts, is_filled):
"""this generator returns the input lines for sfdisk""" """this generator returns the input lines for sfdisk
parts = paramParts[:] """
parts = param_parts[:]
## first a (possible) configuration partition - so it will be reusable ## 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) ## 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 ## one primary partition
if isFilled and (len(parts) == 1): if is_filled and (len(parts) == 1):
## fill the rest of the device ## fill the rest of the device
yield ",,%s,*" % self.PartTypes[parts[0]["type"]][0] yield ",,%s,*" % PARTTYPES[parts[0]["type"]][0]
else: else:
## only use the specified size ## 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] del parts[0]
## no extended partition, if there is only one disk ## no extended partition, if there is only one disk
if not parts: return if not parts:
return
## an extended container for the rest ## an extended container for the rest
yield ",,E" yield ",,E"
## an empty partition in main table ## an empty partition in main table
yield ";" yield ";"
## maybe another empty partition if there is no config partition ## maybe another empty partition if there is no config partition
if not self.withConfigPartition: yield ";" if not self.with_config_partition:
yield ";"
while parts: while parts:
if isFilled and (len(parts) == 1): if is_filled and (len(parts) == 1):
yield ",,%s" % (self.PartTypes[parts[0]["type"]][0],) yield ",,%s" % (PARTTYPES[parts[0]["type"]][0],)
else: 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] del parts[0]
def __formatPartitions(self, paramParts): def __format_partitions(self, param_parts):
parts = paramParts[:] """Format all partitions of the device.
"""
parts = param_parts[:]
part_num = 1 part_num = 1
## maybe a config partition? ## maybe a config partition?
if self.withConfigPartition: if self.with_config_partition:
dev_name = self.device + str(part_num) dev_name = self.device + str(part_num)
self.cbox.log.info("formatting config partition (%s)" % dev_name) self.cbox.log.info("formatting config partition (%s)" % dev_name)
if self.__formatOnePartition(dev_name, self.ConfigPartition["fs"]): if self.__format_one_partition(dev_name, CONFIGPARTITION["fs"]):
self.__setLabelOfPartition(dev_name, self.cbox.prefs["Main"]["ConfigVolumeLabel"]) self.__set_label_of_partition(dev_name,
self.cbox.prefs["Main"]["ConfigVolumeLabel"])
part_num += 1 part_num += 1
## the first data partition ## the first data partition
dev_name = self.device + str(part_num) dev_name = self.device + str(part_num)
partType = self.PartTypes[parts[0]["type"]][1] part_type = PARTTYPES[parts[0]["type"]][1]
self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType)) self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, part_type))
yield dev_name yield dev_name
yield self.__formatOnePartition(dev_name, partType) yield self.__format_one_partition(dev_name, part_type)
del parts[0] del parts[0]
## other data partitions ## other data partitions
part_num = 5 part_num = 5
while parts: while parts:
dev_name = self.device + str(part_num) dev_name = self.device + str(part_num)
partType = self.PartTypes[parts[0]["type"]][1] part_type = PARTTYPES[parts[0]["type"]][1]
self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, partType)) self.cbox.log.info("formatting partition (%s) as '%s'" % \
(dev_name, part_type))
yield dev_name yield dev_name
yield self.__formatOnePartition(dev_name, partType) yield self.__format_one_partition(dev_name, part_type)
part_num += 1 part_num += 1
del parts[0] del parts[0]
return 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 ## first: retrieve UUID - it can be removed from the database afterwards
volDB = self.cbox.prefs.volumesDB prev_name = [e.get_name() for e in self.cbox.get_container_list()
prev_name = [e.getName() for e in self.cbox.getContainerList() if e.getDevice() == dev_name] if e.get_device() == dev_name]
## call "mkfs" ## call "mkfs"
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
@ -398,10 +468,10 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin", "plugin",
os.path.join(self.pluginDir, "root_action.py"), os.path.join(self.plugin_dir, "root_action.py"),
"format", "format",
dev_name, dev_name,
type]) fs_type])
(output, error) = proc.communicate() (output, error) = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
self.cbox.log.warn("failed to create filesystem on %s: %s" % (dev_name, error)) 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: else:
## remove unused volume entry ## remove unused volume entry
if prev_name: if prev_name:
self.cbox.prefs.volumesDB[prev_name[0]] del self.cbox.prefs.volumes_db[prev_name[0]]
return True 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( proc = subprocess.Popen(
shell = False, shell = False,
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
@ -422,7 +494,7 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin", "plugin",
os.path.join(self.pluginDir, "root_action.py"), os.path.join(self.plugin_dir, "root_action.py"),
"label", "label",
dev_name, dev_name,
label]) label])

View file

@ -19,6 +19,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script ## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script
PLUGIN_TYPE = "cryptobox" PLUGIN_TYPE = "cryptobox"

View file

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): def test_read_form(self):
url = self.URL + "partition?weblang=en" url = self.url + "partition?weblang=en"
self.register_auth(url) self.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('VERY careful') self.cmd.find('VERY careful')

View file

@ -7,7 +7,7 @@ The following directory structure is required:
Python code interface: Python code interface:
- create a class with the same name as the plugin - it must inherit CryptoBoxPlugin - 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 - 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) - 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) - 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 * 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" - an empty (e.g. None) return value can be used to go to the default page ("disks"
or "volume_mount" (for volume plugins)) 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 - returns a string, that describes a state connected to this plugin (e.g. the current date and
time (for the "date" plugin)) 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") "volume")
- the class variable "pluginVisibility" may contain one or more of the following items: - the class variable "plugin_visibility" may contain one or more of the following items:
menu/preferences/volume. This obviously should fit to the 'pluginCapabilities' variable. menu/preferences/volume. This should fit to the 'plugin_capabilities' variable.
An empty list is interpreted as a disabled plugin. An empty list is interpreted as an invisible plugin.
- the class variable "requestAuth" is boolean and defines, if admin authentication is necessary - the class variable "request_auth" is boolean and defines, if admin authentication is necessary
for this plugin for this plugin
- the class variable "rank" is an integer in the range of 0..100 - it determines the order - 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) of plugins in listings (lower value -> higher priority)

View file

@ -18,27 +18,31 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
import cryptobox.plugins.manage import cryptobox.plugins.manage
class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin): class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "preferences" ] plugin_visibility = [ "preferences" ]
requestAuth = True request_auth = True
rank = 90 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 import re
if plugin_name: if plugin_name:
## check for invalid characters ## check for invalid characters
if re.search(u'\W', plugin_name): return "plugin_list" if re.search(u'\W', plugin_name): return "plugin_list"
pluginList = cryptobox.plugins.manage.PluginManager(self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) plugin_manager = cryptobox.plugins.manage.PluginManager(
plugin = pluginList.getPlugin(plugin_name) self.cbox, self.cbox.prefs["Locations"]["PluginDir"])
plugin = plugin_manager.get_plugin(plugin_name)
if not plugin: return "plugin_list" if not plugin: return "plugin_list"
## take only plugins, that are of the same type as the choosen one ## 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": if action == "up":
self.__move_up(plugin) self.__move_up(plugin)
elif action == "down": elif action == "down":
@ -50,44 +54,48 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
if not re.search(u'\W',key): if not re.search(u'\W',key):
self.__setConfig(key[:-7], args) self.__setConfig(key[:-7], args)
else: 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: try:
self.cbox.prefs.pluginConf.write() self.cbox.prefs.plugin_conf.write()
except IOError: except IOError:
self.cbox.log.warn("failed to write plugin configuration") self.cbox.log.warn("failed to write plugin configuration")
return "plugin_list" return "plugin_list"
def getStatus(self): def get_status(self):
return "no status" return "no status"
def __sortPlugins(self): def __sort_plugins(self):
"""sort all plugins in the list according to their rank""" """sort all plugins in the list according to their rank"""
def cmp_func(x,y): def cmp_func(x,y):
xRank = x.getRank() x_rank = x.get_rank()
yRank = y.getRank() y_rank = y.get_rank()
if xRank < yRank: return -1 if x_rank < y_rank:
elif xRank == yRank: return 0 return -1
else: return 1 elif x_rank == y_rank:
return 0
else:
return 1
self.plugins.sort(cmp = cmp_func) self.plugins.sort(cmp = cmp_func)
def __distributeRanks(self): def __distribute_ranks(self):
"""evenly distribute the 'rank' values according to the current order of """evenly distribute the 'rank' values according to the current order of
the list""" the list"""
dist = 100/len(self.plugins) dist = 100/len(self.plugins)
for index,pl in enumerate(self.plugins): for index,pl in enumerate(self.plugins):
try: try:
self.cbox.prefs.pluginConf[pl.getName()]["rank"] = dist*index self.cbox.prefs.plugin_conf[pl.get_name()]["rank"] = dist*index
except KeyError: except KeyError:
self.cbox.prefs.pluginConf[pl.getName()] = {} self.cbox.prefs.plugin_conf[pl.get_name()] = {}
self.cbox.prefs.pluginConf[pl.getName()]["rank"] = dist*index self.cbox.prefs.plugin_conf[pl.get_name()]["rank"] = dist*index
self.cbox.prefs.pluginConf.write() self.cbox.prefs.plugin_conf.write()
def __move_up(self, plugin): def __move_up(self, plugin):
self.__sortPlugins() self.__sort_plugins()
try: try:
index = self.plugins.index(plugin) index = self.plugins.index(plugin)
## first elements may not move up ## first elements may not move up
@ -97,11 +105,11 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
return return
self.plugins.remove(plugin) self.plugins.remove(plugin)
self.plugins.insert(index-1, plugin) self.plugins.insert(index-1, plugin)
self.__distributeRanks() self.__distribute_ranks()
def __move_down(self, plugin): def __move_down(self, plugin):
self.__sortPlugins() self.__sort_plugins()
try: try:
index = self.plugins.index(plugin) index = self.plugins.index(plugin)
## last elements may not move down ## last elements may not move down
@ -111,7 +119,7 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
return return
self.plugins.remove(plugin) self.plugins.remove(plugin)
self.plugins.insert(index+1, plugin) self.plugins.insert(index+1, plugin)
self.__distributeRanks() self.__distribute_ranks()
def __setConfig(self, name, args): def __setConfig(self, name, args):
@ -137,5 +145,5 @@ class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin):
setting["requestAuth"] = True setting["requestAuth"] = True
except (KeyError, ValueError): except (KeyError, ValueError):
pass pass
self.cbox.prefs.pluginConf[name] = setting self.cbox.prefs.plugin_conf[name] = setting

View file

@ -18,19 +18,21 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): def test_read_form(self):
url = self.URL + "plugin_manager?weblang=en" url = self.url + "plugin_manager?weblang=en"
self.register_auth(url) self.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('Plugin Manager') self.cmd.find('Plugin Manager')
def test_set_options(self): def test_set_options(self):
url = self.URL + "plugin_manager" url = self.url + "plugin_manager"
self.register_auth(url) self.register_auth(url)
self.cmd.go(url + u"?plugin_name=t/-!") self.cmd.go(url + u"?plugin_name=t/-!")
self.cmd.find('Plugin Manager') self.cmd.find('Plugin Manager')
@ -55,8 +57,8 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_move_up(self): def test_move_up(self):
## TODO: if we want to be perfect, then we should check the change of the rank #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.register_auth(url)
self.cmd.go(url + u"?plugin_name=disks&action=up") self.cmd.go(url + u"?plugin_name=disks&action=up")
self.cmd.find('Plugin Manager') self.cmd.find('Plugin Manager')
@ -68,7 +70,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_move_down(self): def test_move_down(self):
## TODO: if we want to be perfect, then we should check the change of the rank ## 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.register_auth(url)
self.cmd.go(url + u"?plugin_name=disks&action=down") self.cmd.go(url + u"?plugin_name=disks&action=down")
self.cmd.find('Plugin Manager') self.cmd.find('Plugin Manager')

View file

@ -19,6 +19,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script ## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script
PLUGIN_TYPE = "cryptobox" PLUGIN_TYPE = "cryptobox"

View file

@ -18,29 +18,31 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
REDIRECT_DELAY = 180 REDIRECT_DELAY = 180
class shutdown(cryptobox.plugins.base.CryptoBoxPlugin): class shutdown(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "preferences", "menu" ] plugin_visibility = [ "preferences", "menu" ]
requestAuth = False request_auth = False
rank = 90 rank = 90
def doAction(self, type=None): def do_action(self, type=None):
if not type: if not type:
return "form_shutdown" return "form_shutdown"
elif type == "shutdown": elif type == "shutdown":
if self.__doShutdown("shutdown"): if self.__do_shutdown("shutdown"):
self.hdf["Data.Success"] = "Plugins.shutdown.Shutdown" self.hdf["Data.Success"] = "Plugins.shutdown.Shutdown"
return "progress_shutdown" return "progress_shutdown"
else: else:
self.hdf["Data.Warning"] = "Plugins.shutdown.ShutdownFailed" self.hdf["Data.Warning"] = "Plugins.shutdown.ShutdownFailed"
return "form_shutdown" return "form_shutdown"
elif type == "reboot": elif type == "reboot":
if self.__doShutdown("reboot"): if self.__do_shutdown("reboot"):
self.hdf["Data.Success"] = "Plugins.shutdown.Reboot" self.hdf["Data.Success"] = "Plugins.shutdown.Reboot"
self.hdf["Data.Redirect.URL"] = "" self.hdf["Data.Redirect.URL"] = ""
self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY
@ -52,11 +54,11 @@ class shutdown(cryptobox.plugins.base.CryptoBoxPlugin):
return "form_shutdown" return "form_shutdown"
def getStatus(self): def get_status(self):
return "the box is up'n'running" return "the box is up'n'running"
def __doShutdown(self, action): def __do_shutdown(self, action):
import subprocess import subprocess
import os import os
proc = subprocess.Popen( proc = subprocess.Popen(
@ -65,7 +67,7 @@ class shutdown(cryptobox.plugins.base.CryptoBoxPlugin):
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"plugin", "plugin",
os.path.join(self.pluginDir, "root_action.py"), os.path.join(self.plugin_dir, "root_action.py"),
action]) action])
proc.wait() proc.wait()
return proc.returncode == 0 return proc.returncode == 0

View file

@ -18,13 +18,15 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): def test_read_form(self):
"""just read the form - I do not know of a way how to check success""" """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.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('shutdown') self.cmd.find('shutdown')

View file

@ -18,20 +18,23 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
class system_preferences(cryptobox.plugins.base.CryptoBoxPlugin): class system_preferences(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "menu" ] plugin_visibility = [ "menu" ]
requestAuth = False request_auth = False
rank = 20 rank = 20
def doAction(self): def do_action(self):
return "show_plugins" return "show_plugins"
def getStatus(self): def get_status(self):
return ":".join([p.getName() for p in self.site.pluginList.getPlugins()]) 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()])

View file

@ -18,17 +18,19 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): def test_read_form(self):
self.cmd.go(self.URL + "system_preferences") self.cmd.go(self.url + "system_preferences")
self.cmd.find("Preferences") self.cmd.find("Preferences")
def test_check_plugins(self): 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") self.cmd.find(u'Data.Status.Plugins.system_preferences=(.*)$', "m")
plugins = self.locals["__match__"].split(":") plugins = self.locals["__match__"].split(":")
self.assertTrue(len(plugins) > 1) self.assertTrue(len(plugins) > 1)

View file

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
## this user may not be removed ## this user may not be removed
@ -35,7 +37,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_test_wrong_credentials(self): def test_test_wrong_credentials(self):
"""check if the user_manager is protected""" """check if the user_manager is protected"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url,"foo","bar") self.register_auth(url,"foo","bar")
self.cmd.go(url) self.cmd.go(url)
self.cmd.notfind("Manage users") self.cmd.notfind("Manage users")
@ -43,7 +45,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_add_existing_user(self): def test_add_existing_user(self):
"""adding an existing user should fail""" """adding an existing user should fail"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self._add_user("admin","foo","foo") self._add_user("admin","foo","foo")
self.cmd.find("The choosen username does already exist") 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): def test_add_invalid_username(self):
"""adding an invalid username should fail""" """adding an invalid username should fail"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self._add_user("foo/bar","foo","foo") self._add_user("foo/bar","foo","foo")
self.cmd.find("Invalid username") self.cmd.find("Invalid username")
@ -60,7 +62,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_add_without_password(self): def test_add_without_password(self):
"""adding a user without password should fail""" """adding a user without password should fail"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self.assertFalse("foo" in self._getUsers()) self.assertFalse("foo" in self._getUsers())
self._add_user("foo","","foo") self._add_user("foo","","foo")
@ -70,7 +72,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_add_with_different_passwords(self): def test_add_with_different_passwords(self):
"""adding a user with different passwords should fail""" """adding a user with different passwords should fail"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self.assertFalse("foo" in self._getUsers()) self.assertFalse("foo" in self._getUsers())
self._add_user("foo","bar","foo") self._add_user("foo","bar","foo")
@ -80,7 +82,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_change_pw_for_invalid_user(self): def test_change_pw_for_invalid_user(self):
"""changing a password of a non existing user should fail""" """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.register_auth(url)
self.assertFalse("barfoo" in self._getUsers()) self.assertFalse("barfoo" in self._getUsers())
self.cmd.go(url + "?store=change_password&user=foobar&new_pw=foo&new_pw2=foo") 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): def test_change_pw_without_password(self):
"""changing a password without a new password should fail""" """changing a password without a new password should fail"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self.assertFalse("foo" in self._getUsers()) self.assertFalse("foo" in self._getUsers())
self._add_user("foo","bar","bar") self._add_user("foo","bar","bar")
@ -102,7 +104,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_change_pw_wit_different_passwords(self): def test_change_pw_wit_different_passwords(self):
"""changing a password while supplying different passwords should fail""" """changing a password while supplying different passwords should fail"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self.assertFalse("foo" in self._getUsers()) self.assertFalse("foo" in self._getUsers())
self._add_user("foo","bar","bar") self._add_user("foo","bar","bar")
@ -115,7 +117,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _remove_reserved_user(self): def _remove_reserved_user(self):
"""removing a reserved user should fail""" """removing a reserved user should fail"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self.assertTrue("admin" in self._getUsers()) self.assertTrue("admin" in self._getUsers())
self._del_user("admin") self._del_user("admin")
@ -125,7 +127,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _remove_non_existing_user(self): def _remove_non_existing_user(self):
"""removing a non-existing user should fail""" """removing a non-existing user should fail"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self.assertFalse("barfoo" in self._getUsers()) self.assertFalse("barfoo" in self._getUsers())
self._del_user("barfoo") self._del_user("barfoo")
@ -134,7 +136,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_manage_users(self): def test_manage_users(self):
"""add a new user, change its password and remove the user afterwards""" """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) self.register_auth(url)
## remove the user that should be added - just in case a previous run was unclean ## remove the user that should be added - just in case a previous run was unclean
## check its existence before ## check its existence before
@ -158,13 +160,13 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_invalid_input(self): def test_invalid_input(self):
"""check all combinations of invalid input""" """check all combinations of invalid input"""
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self.cmd.go(url + "?store=foobar") self.cmd.go(url + "?store=foobar")
def _add_user(self, username, pw, pw2): 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","user",username)
self.cmd.formvalue("add_user","new_pw",pw) self.cmd.formvalue("add_user","new_pw",pw)
self.cmd.formvalue("add_user","new_pw2",pw2) self.cmd.formvalue("add_user","new_pw2",pw2)
@ -172,13 +174,13 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _del_user(self, username): 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.formvalue("del_user","user",username)
self.cmd.submit() self.cmd.submit()
def _change_password(self, username, pw, pw2): 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","user",username)
self.cmd.formvalue("change_password","new_pw",pw) self.cmd.formvalue("change_password","new_pw",pw)
self.cmd.formvalue("change_password","new_pw2",pw2) self.cmd.formvalue("change_password","new_pw2",pw2)
@ -186,7 +188,7 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def _getUsers(self): def _getUsers(self):
url = self.URL + "user_manager" url = self.url + "user_manager"
self.register_auth(url) self.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find("Data.Status.Plugins.user_manager=([\w:]+)") self.cmd.find("Data.Status.Plugins.user_manager=([\w:]+)")

View file

@ -18,21 +18,23 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
RESERVED_USERS = [ "admin" ] RESERVED_USERS = [ "admin" ]
class user_manager(cryptobox.plugins.base.CryptoBoxPlugin): class user_manager(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
pluginVisibility = [ "preferences" ] plugin_visibility = [ "preferences" ]
requestAuth = True request_auth = True
rank = 45 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 import re
adminDict = self.cbox.prefs.userDB["admins"] admin_dict = self.cbox.prefs.user_db["admins"]
self.__cleanHDF() self.__clean_hdf()
if store is None: if store is None:
pass pass
elif store == "add_user": elif store == "add_user":
@ -42,13 +44,13 @@ class user_manager(cryptobox.plugins.base.CryptoBoxPlugin):
self.hdf["Data.Warning"] = "EmptyNewPassword" self.hdf["Data.Warning"] = "EmptyNewPassword"
elif new_pw != new_pw2: elif new_pw != new_pw2:
self.hdf["Data.Warning"] = "DifferentPasswords" self.hdf["Data.Warning"] = "DifferentPasswords"
elif user in adminDict.keys(): elif user in admin_dict.keys():
self.hdf["Data.Warning"] = "Plugins.user_manager.UserAlreadyExists" self.hdf["Data.Warning"] = "Plugins.user_manager.UserAlreadyExists"
else: 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" self.hdf["Data.Success"] = "Plugins.user_manager.UserAdded"
try: try:
self.cbox.prefs.userDB.write() self.cbox.prefs.user_db.write()
except IOError: except IOError:
self.cbox.log.warn("failed to write user database") self.cbox.log.warn("failed to write user database")
elif store == "change_password": elif store == "change_password":
@ -56,11 +58,11 @@ class user_manager(cryptobox.plugins.base.CryptoBoxPlugin):
self.hdf["Data.Warning"] = "EmptyNewPassword" self.hdf["Data.Warning"] = "EmptyNewPassword"
elif new_pw != new_pw2: elif new_pw != new_pw2:
self.hdf["Data.Warning"] = "DifferentPasswords" self.hdf["Data.Warning"] = "DifferentPasswords"
elif user in adminDict.keys(): elif user in admin_dict.keys():
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.PasswordChanged" self.hdf["Data.Success"] = "Plugins.user_manager.PasswordChanged"
try: try:
self.cbox.prefs.userDB.write() self.cbox.prefs.user_db.write()
except IOError: except IOError:
self.cbox.log.warn("failed to write user database") self.cbox.log.warn("failed to write user database")
else: else:
@ -69,31 +71,31 @@ class user_manager(cryptobox.plugins.base.CryptoBoxPlugin):
if user in RESERVED_USERS: if user in RESERVED_USERS:
self.cbox.log.info("user_manager: tried to remove reserved user (%s)" % user) self.cbox.log.info("user_manager: tried to remove reserved user (%s)" % user)
self.hdf["Data.Warning"] = "NeverRemoveReservedUser" self.hdf["Data.Warning"] = "NeverRemoveReservedUser"
elif user in adminDict.keys(): elif user in admin_dict.keys():
del adminDict[user] del admin_dict[user]
self.hdf["Data.Success"] = "Plugins.user_manager.UserRemoved" self.hdf["Data.Success"] = "Plugins.user_manager.UserRemoved"
try: try:
self.cbox.prefs.userDB.write() self.cbox.prefs.user_db.write()
except IOError: except IOError:
self.cbox.log.warn("failed to write user database") self.cbox.log.warn("failed to write user database")
else: else:
self.cbox.log.info("user_manager: tried to remove non-existing user (%s)" % str(user)) self.cbox.log.info("user_manager: tried to remove non-existing user (%s)" % str(user))
else: else:
self.cbox.log.info("user_manager: invalid value of 'store' (%s)" % store) self.cbox.log.info("user_manager: invalid value of 'store' (%s)" % store)
self.__prepareHDF(adminDict) self.__prepare_hdf(admin_dict)
return "user_list" return "user_list"
def getStatus(self): def get_status(self):
return ":".join(self.cbox.prefs.userDB["admins"].keys()) return ":".join(self.cbox.prefs.user_db["admins"].keys())
def __cleanHDF(self): def __clean_hdf(self):
for key in self.hdf.keys(): for key in self.hdf.keys():
del self.hdf[key] del self.hdf[key]
def __prepareHDF(self, dict): def __prepare_hdf(self, dict):
## sort by name ## sort by name
users = dict.keys() users = dict.keys()
users.sort() users.sort()

View file

@ -18,13 +18,15 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): def test_read_form(self):
"""try to read automount form""" """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.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('Activate during startup') self.cmd.find('Activate during startup')
@ -32,21 +34,21 @@ class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_toggle(self): def test_toggle(self):
"""try to toggle automount property""" """try to toggle automount property"""
url = self.URL + "volume_automount" url = self.url + "volume_automount"
self.register_auth(url) 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("Automatic activation disabled")
self.cmd.find("is 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.find("Automatic activation enabled")
self.cmd.notfind("is disabled") self.cmd.notfind("is disabled")
def test_invalid_input(self): def test_invalid_input(self):
"""check invalid inputs""" """check invalid inputs"""
url = self.URL + "volume_automount" url = self.url + "volume_automount"
self.register_auth(url) 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 disabled")
self.cmd.notfind("Automatic activation enabled") self.cmd.notfind("Automatic activation enabled")

View file

@ -18,58 +18,60 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
class volume_automount(cryptobox.plugins.base.CryptoBoxPlugin): class volume_automount(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ] plugin_capabilities = [ "volume" ]
pluginVisibility = [ "properties" ] plugin_visibility = [ "properties" ]
requestAuth = False request_auth = False
rank = 80 rank = 80
trueString = "yes" true_string = "yes"
falseString = "no" false_string = "no"
def doAction(self, action=None): def do_action(self, action=None):
container = self.cbox.getContainer(self.device) container = self.cbox.get_container(self.device)
if action is None: if action is None:
pass pass
elif action == "enable": elif action == "enable":
container.attributes["automount"] = self.trueString container.attributes["automount"] = self.true_string
self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountEnabled" self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountEnabled"
self.cbox.log.info("volume_automount: enabled for device '%s'" % self.device) 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": elif action == "disable":
container.attributes["automount"] = self.falseString container.attributes["automount"] = self.false_string
self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountDisabled" self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountDisabled"
self.cbox.log.info("volume_automount: disabled for device '%s'" % self.device) self.cbox.log.info("volume_automount: disabled for device '%s'" % self.device)
self.cbox.prefs.volumesDB.write() self.cbox.prefs.volumes_db.write()
else: else:
self.cbox.log.info("volume_automount: invalid action (%s)" % str(action)) self.cbox.log.info("volume_automount: invalid action (%s)" % str(action))
self.__prepareHDF() self.__prepare_hdf()
return "volume_automount" return "volume_automount"
def getStatus(self): def get_status(self):
return str(self.__isAutoMount()) return str(self.__is_auto_mount())
def __prepareHDF(self): def __prepare_hdf(self):
if self.__isAutoMount(): if self.__is_auto_mount():
self.hdf[self.hdf_prefix + "automount_setting"] = "1" self.hdf[self.hdf_prefix + "automount_setting"] = "1"
else: else:
self.hdf[self.hdf_prefix + "automount_setting"] = "0" self.hdf[self.hdf_prefix + "automount_setting"] = "0"
def __isAutoMount(self): def __is_auto_mount(self):
container = self.cbox.getContainer(self.device) container = self.cbox.get_container(self.device)
if not container: if not container:
return False return False
if container.attributes.has_key("automount"): if container.attributes.has_key("automount"):
return container.attributes["automount"] == self.trueString return container.attributes["automount"] == self.true_string
else: else:
return False return False

View file

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): 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.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('hange') self.cmd.find('hange')

View file

@ -18,24 +18,26 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin): class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ] plugin_capabilities = [ "volume" ]
pluginVisibility = [ "properties" ] plugin_visibility = [ "properties" ]
requestAuth = False request_auth = False
rank = 70 rank = 70
def doAction(self, store=None, old_pw=None, new_pw=None, new_pw2=None): def do_action(self, store=None, old_pw=None, new_pw=None, new_pw2=None):
self.container = self.cbox.getContainer(self.device) self.container = self.cbox.get_container(self.device)
if not self.container: if not self.container:
return None return None
elif store == "change_pw": 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: elif not store:
return "volume_chpasswd" return "volume_chpasswd"
else: else:
@ -43,11 +45,11 @@ class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin):
return "volume_chpasswd" return "volume_chpasswd"
def getStatus(self): def get_status(self):
return "TODO" 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: if not old_pw:
self.hdf["Data.Warning"] = "EmptyPassword" self.hdf["Data.Warning"] = "EmptyPassword"
elif not new_pw: elif not new_pw:
@ -59,13 +61,13 @@ class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin):
pass pass
else: else:
try: try:
self.container.changePassword(old_pw, new_pw) self.container.change_password(old_pw, new_pw)
except CBInvalidType, errMsg: except CBInvalidType, err_msg:
self.cbox.log.info("plugin 'volume_chpasswd' - cannot change passphrase for non-encrypted container (%s): %s" % (self.device, errMsg)) self.cbox.log.info("plugin 'volume_chpasswd' - cannot change passphrase for non-encrypted container (%s): %s" % (self.device, err_msg))
except CBVolumeIsActive: except CBVolumeIsActive:
self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" self.hdf["Data.Warning"] = "VolumeMayNotBeMounted"
except CBChangePasswordError, errMsg: except CBChangePasswordError, err_msg:
self.cbox.log.warn("plugin 'volume_chpasswd' - cannot change password for device (%s): %s" % (self.device, errMsg)) 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" self.hdf["Data.Warning"] = "Plugins.volume_chpasswd.PasswordChange"
else: else:
self.hdf["Data.Success"] = "Plugins.volume_chpasswd.PasswordChange" self.hdf["Data.Success"] = "Plugins.volume_chpasswd.PasswordChange"

View file

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): 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.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('Technical details') self.cmd.find('Technical details')

View file

@ -18,22 +18,24 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
class volume_details(cryptobox.plugins.base.CryptoBoxPlugin): class volume_details(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ] plugin_capabilities = [ "volume" ]
pluginVisibility = [ "volume" ] plugin_visibility = [ "volume" ]
requestAuth = False request_auth = False
rank = 100 rank = 100
def doAction(self): def do_action(self):
## all variables are already set somewhere else ## all variables are already set somewhere else
return "volume_details" return "volume_details"
def getStatus(self): def get_status(self):
return "no status" return "no status"

View file

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): 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.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('Initializing filesystem') self.cmd.find('Initializing filesystem')

View file

@ -18,32 +18,33 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
from cryptobox.core.exceptions import * 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): class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ] plugin_capabilities = [ "volume" ]
pluginVisibility = [ "volume" ] plugin_visibility = [ "volume" ]
requestAuth = True request_auth = True
rank = 60 rank = 60
## map filesystem types to the appropriate arguments for 'mkfs'
fsTypes = {
"windows": "vfat",
"linux": "ext3" }
containerTypes = [ "luks", "plain" ]
def do_action(self, store=None, fs_type="windows", container_type="luks", crypto_password=None, crypto_password2=None, confirm=None):
def doAction(self, store=None, fs_type="windows", container_type="luks", crypto_password=None, crypto_password2=None, confirm=None): if not fs_type in FSTYPES.keys():
if not fs_type in self.fsTypes.keys():
self.cbox.info self.cbox.info
return "format_volume" return "format_volume"
self.hdf[self.hdf_prefix + "fs_type"] = fs_type self.hdf[self.hdf_prefix + "fs_type"] = fs_type
self.hdf[self.hdf_prefix + "container_type"] = container_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 self.hdf[self.hdf_prefix + "fs_types." + t] = t
if store == "step1": if store == "step1":
if not confirm: if not confirm:
@ -67,14 +68,14 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin):
return "volume_format" return "volume_format"
def getStatus(self): def get_status(self):
return "no status" return "no status"
def __format_plain(self, fsType): def __format_plain(self, fsType):
try: try:
container = self.cbox.getContainer(self.device) container = self.cbox.get_container(self.device)
container.create(cbxContainer.ContainerTypes["plain"]) container.create(cbx_container.CONTAINERTYPES["plain"])
except CBVolumeIsActive: except CBVolumeIsActive:
self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" self.hdf["Data.Warning"] = "VolumeMayNotBeMounted"
self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device) 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.hdf["Data.Warning"] = "DifferentPasswords"
self.cbox.log.warn("the crypto password was not repeated correctly for initialization of device '%s'" % self.device) self.cbox.log.warn("the crypto password was not repeated correctly for initialization of device '%s'" % self.device)
return "volume_format" return "volume_format"
container = self.cbox.getContainer(self.device) container = self.cbox.get_container(self.device)
try: try:
container.create(cbxContainer.ContainerTypes["luks"], pw) container.create(cbx_container.CONTAINERTYPES["luks"], pw)
except CBVolumeIsActive: except CBVolumeIsActive:
self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" self.hdf["Data.Warning"] = "VolumeMayNotBeMounted"
self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device) self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device)

View file

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): 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.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('ctivate volume') self.cmd.find('ctivate volume')

View file

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
import cryptobox.core.container as cbxContainer import cryptobox.core.container as cbxContainer
@ -25,20 +27,20 @@ import cryptobox.core.container as cbxContainer
class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin): class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ] plugin_capabilities = [ "volume" ]
pluginVisibility = [ "volume" ] plugin_visibility = [ "volume" ]
requestAuth = False request_auth = False
rank = 0 rank = 0
def doAction(self, action=None, pw=None): def do_action(self, action=None, pw=None):
self.container = self.cbox.getContainer(self.device) self.container = self.cbox.get_container(self.device)
if action == "mount_plain": if action == "mount_plain":
return self.__doMountPlain() return self.__do_mount_plain()
elif action == "mount_luks": elif action == "mount_luks":
return self.__doMountLuks(pw) return self.__do_mount_luks(pw)
elif action == "umount": elif action == "umount":
return self.__doUmount() return self.__do_umount()
elif not action: elif not action:
return "volume_status" return "volume_status"
else: else:
@ -46,43 +48,43 @@ class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin):
return None return None
def getStatus(self): def get_status(self):
container = self.cbox.getContainer(self.device) container = self.cbox.get_container(self.device)
if not self.container: if not self.container:
return "invalid device" return "invalid device"
if container.isMounted(): if container.is_mounted():
return "active" return "active"
else: else:
return "passive" return "passive"
def __doMountPlain(self): def __do_mount_plain(self):
if self.container.isMounted(): if self.container.is_mounted():
self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted"
self.cbox.log.info("the device (%s) is already mounted" % self.device) self.cbox.log.info("the device (%s) is already mounted" % self.device)
return "volume_status" return "volume_status"
if self.container.getType() != cbxContainer.ContainerTypes["plain"]: if self.container.get_type() != cbxContainer.CONTAINERTYPES["plain"]:
## not a plain container ## not a plain container
self.cbox.log.info("plugin 'volume_mount' - invalid container type") self.cbox.log.info("plugin 'volume_mount' - invalid container type")
self.hdf["Data.Warning"] = "Plugins.volume_mount.InvalidContainerType" self.hdf["Data.Warning"] = "Plugins.volume_mount.InvalidContainerType"
return "volume_status" return "volume_status"
try: try:
self.container.mount() self.container.mount()
except CBMountError, errMsg: except CBMountError, err_msg:
self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" 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" return "volume_status"
except CBContainerError, errMsg: except CBContainerError, err_msg:
self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" 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" return "volume_status"
self.cbox.log.info("successfully mounted the volume: %s" % self.device) self.cbox.log.info("successfully mounted the volume: %s" % self.device)
self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone"
return "volume_status" return "volume_status"
def __doMountLuks(self, pw): def __do_mount_luks(self, pw):
if self.container.isMounted(): if self.container.is_mounted():
self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted"
self.cbox.log.info("the device (%s) is already mounted" % self.device) self.cbox.log.info("the device (%s) is already mounted" % self.device)
return "volume_status" return "volume_status"
@ -90,35 +92,35 @@ class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin):
self.dataset["Data.Warning"] = "EmptyPassword" self.dataset["Data.Warning"] = "EmptyPassword"
self.log.info("no password was supplied for mounting of device: '%s'" % self.device) self.log.info("no password was supplied for mounting of device: '%s'" % self.device)
return "volume_status" return "volume_status"
if self.container.getType() != cbxContainer.ContainerTypes["luks"]: if self.container.get_type() != cbxContainer.CONTAINERTYPES["luks"]:
## not a luks container - fail silently ## not a luks container - fail silently
self.cbox.log.info("plugin 'volume_mount' - invalid container type") self.cbox.log.info("plugin 'volume_mount' - invalid container type")
return "volume_status" return "volume_status"
try: try:
self.container.mount(pw) self.container.mount(pw)
except CBMountError, errMsg: except CBMountError, err_msg:
self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" 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" return "volume_status"
except CBContainerError, errMsg: except CBContainerError, err_msg:
self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" 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" return "volume_status"
self.cbox.log.info("successfully mounted the volume: %s" % self.device) self.cbox.log.info("successfully mounted the volume: %s" % self.device)
self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone"
return "volume_status" return "volume_status"
def __doUmount(self): def __do_umount(self):
if not self.container.isMounted(): if not self.container.is_mounted():
self.hdf["Data.Warning"] = "Plugins.volume_mount.IsNotMounted" self.hdf["Data.Warning"] = "Plugins.volume_mount.IsNotMounted"
self.cbox.log.info("the device (%s) is currently not mounted" % self.device) self.cbox.log.info("the device (%s) is currently not mounted" % self.device)
return "volume_status" return "volume_status"
try: try:
self.container.umount() self.container.umount()
except CBUmountError, errMsg: except CBUmountError, err_msg:
self.hdf["Data.Warning"] = "UmountFailed" 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" return "volume_status"
self.cbox.log.info("successfully unmounted the container: %s" % self.device) self.cbox.log.info("successfully unmounted the container: %s" % self.device)
self.hdf["Data.Success"] = "Plugins.volume_mount.UmountDone" self.hdf["Data.Success"] = "Plugins.volume_mount.UmountDone"

View file

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): 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.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('Properties') self.cmd.find('Properties')

View file

@ -18,6 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
import cryptobox.plugins.manage import cryptobox.plugins.manage
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
@ -25,51 +27,54 @@ from cryptobox.core.exceptions import *
class volume_props(cryptobox.plugins.base.CryptoBoxPlugin): class volume_props(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ] plugin_capabilities = [ "volume" ]
pluginVisibility = [ "volume" ] plugin_visibility = [ "volume" ]
requestAuth = False request_auth = False
rank = 30 rank = 30
def doAction(self, **args): def do_action(self, **args):
import os 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 ## 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 ## set the name of the templates for every plugin
loadString = "" load_string = ""
for p in self.props_plugins: for p in self.props_plugins:
p.device = self.device p.device = self.device
plfname = os.path.join(p.pluginDir, str(p.doAction(**args)) + ".cs") plfname = os.path.join(p.plugin_dir, str(p.do_action(**args)) + ".cs")
loadString += "<?cs include:'%s' ?>" % plfname load_string += "<?cs include:'%s' ?>" % plfname
## this is a little bit ugly: as it is not possible, to load cs files via ## 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 ## 'linclude' (see clearsilver doc) if they use previously defined macros (see
## clearsilver mailing list thread 'linclude file which calls a macro' - 27th ## clearsilver mailing list thread 'linclude file which calls a macro' - 27th
## December 02005) ## December 02005)
## our workaround: define the appropriate "include" (not 'linclude') commands ## our workaround: define the appropriate "include" (not 'linclude') commands
## as a hdf variable - then we can include it via 'evar' ## 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" return "volume_properties"
def getStatus(self): def get_status(self):
return "TODO" return "TODO"
def loadDataSet(self, hdf): def load_dataset(self, hdf):
"""override the parent's function """override the parent's function
we have to get the data from all included plugins""" we have to get the data from all included plugins"""
for p in self.props_plugins: for plugin in self.props_plugins:
p.loadDataSet(hdf) plugin.load_dataset(hdf)
## call our parent's method ## 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): def __cmp_plugins_rank(self, p1, p2):
order = p1.getRank() - p2.getRank() order = p1.get_rank() - p2.get_rank()
if order < 0: if order < 0:
return -1 return -1
elif order == 0: elif order == 0:
return 0 return 0
else: else:
return 1 return 1

View file

@ -18,12 +18,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.web.testclass import cryptobox.web.testclass
class unittests(cryptobox.web.testclass.WebInterfaceTestClass): class unittests(cryptobox.web.testclass.WebInterfaceTestClass):
def test_read_form(self): 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.register_auth(url)
self.cmd.go(url) self.cmd.go(url)
self.cmd.find('name') self.cmd.find('name')

View file

@ -18,49 +18,51 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
__revision__ = "$Id"
import cryptobox.plugins.base import cryptobox.plugins.base
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
class volume_rename(cryptobox.plugins.base.CryptoBoxPlugin): class volume_rename(cryptobox.plugins.base.CryptoBoxPlugin):
pluginCapabilities = [ "volume" ] plugin_capabilities = [ "volume" ]
pluginVisibility = [ "properties" ] plugin_visibility = [ "properties" ]
requestAuth = False request_auth = False
rank = 60 rank = 60
def doAction(self, store=None, vol_name=None): def do_action(self, store=None, vol_name=None):
self.container = self.cbox.getContainer(self.device) self.container = self.cbox.get_container(self.device)
if not self.container: if not self.container:
return None return None
self.__prepareHDF() self.__prepare_hdf()
if store and vol_name: if store and vol_name:
return self.__setVolumeName(vol_name) return self.__set_volume_name(vol_name)
else: else:
return "volume_rename" return "volume_rename"
def getStatus(self): def get_status(self):
self.container = self.cbox.getContainer(self.device) self.container = self.cbox.get_container(self.device)
if not self.container: if not self.container:
return "invalid device" return "invalid device"
return "name=%s" % self.container.getName() return "name=%s" % self.container.get_name()
def __prepareHDF(self): def __prepare_hdf(self):
self.hdf[self.hdf_prefix + "vol_name"] = self.container.getName() 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: if not vol_name:
self.hdf["Data.Warning"] = "Plugins.volume_rename.InvalidVolumeName" self.hdf["Data.Warning"] = "Plugins.volume_rename.InvalidVolumeName"
return "volume_rename" return "volume_rename"
if vol_name == self.container.getName(): if vol_name == self.container.get_name():
## nothing has to be done ## nothing has to be done
return "volume_rename" return "volume_rename"
try: try:
self.container.setName(vol_name) self.container.set_name(vol_name)
self.hdf["Data.Success"] = "Plugins.volume_rename.VolumeNameChanged" self.hdf["Data.Success"] = "Plugins.volume_rename.VolumeNameChanged"
except CBVolumeIsActive: except CBVolumeIsActive:
self.hdf["Data.Warning"] = "Plugins.volume_rename.NoRenameIfActive" self.hdf["Data.Warning"] = "Plugins.volume_rename.NoRenameIfActive"
@ -71,6 +73,6 @@ class volume_rename(cryptobox.plugins.base.CryptoBoxPlugin):
except CBContainerError: except CBContainerError:
self.hdf["Data.Warning"] = "Plugins.volume_rename.SetVolumeNameFailed" self.hdf["Data.Warning"] = "Plugins.volume_rename.SetVolumeNameFailed"
## reread the volume name ## reread the volume name
self.__prepareHDF() self.__prepare_hdf()
return "volume_rename" return "volume_rename"

View file

@ -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"

View file

@ -0,0 +1,7 @@
"""Core management functions of the CryptoBox.
"""
__revision__ = "$Id"
__all__ = [ 'main', 'container', 'exceptions', 'tools', 'settings' ]

View file

@ -18,21 +18,18 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
## check python version """Manage a single container of the CryptoBox
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)): __revision__ = "$Id"
sys.stderr.write("You need a python version >= 2.4\nCurrent version is:\n %s\n" % sys.version)
sys.exit(1)
import subprocess import subprocess
import os import os
import re import re
import logging
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
ContainerTypes = { CONTAINERTYPES = {
"unused":0, "unused":0,
"plain":1, "plain":1,
"luks":2, "luks":2,
@ -46,11 +43,12 @@ MOUNT_DIR_MARKER = '_cryptobox_mount_dir_'
class CryptoBoxContainer: class CryptoBoxContainer:
"""Manage a container of the CryptoBox
"""
__fsTypes = { __fsTypes = {
"plain":["ext3", "ext2", "vfat", "reiser"], "plain":["ext3", "ext2", "vfat", "reiserfs"],
"swap":["swap"]} "swap":["swap"]}
# TODO: more filesystem types? / check 'reiser'
__dmDir = "/dev/mapper" __dmDir = "/dev/mapper"
@ -58,41 +56,58 @@ class CryptoBoxContainer:
def __init__(self, device, cbox): def __init__(self, device, cbox):
self.device = device self.device = device
self.cbox = cbox self.cbox = cbox
self.log = logging.getLogger("CryptoBox") self.uuid = None
self.resetObject() 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 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: try:
## is there already an entry in the database? ## 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 self.attributes["uuid"] = self.uuid
except KeyError: except KeyError:
## set default values ## set default values
self.attributes = { "uuid": self.uuid } 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): def set_name(self, new_name):
old_name = self.getName() """Define a humanly readable name of this container.
if new_name == self.name: return
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 ## renaming is not possible, if the volume is active, as the mountpoint name
## is the same as the volume 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") raise CBVolumeIsActive("the container must not be active during renaming")
if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name): if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name):
raise CBInvalidName("the supplied new name contains illegal characters") raise CBInvalidName("the supplied new name contains illegal characters")
## check for another partitions with the same name ## 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") 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 ## maybe there a is an entry in the volumes database (but the partition is not active
try: try:
## remove possibly existing inactive database item ## remove possibly existing inactive database item
del self.cbox.prefs.volumesDB[new_name] del self.cbox.prefs.volumes_db[new_name]
except KeyError: except KeyError:
## no entry - so nothing happens ## no entry - so nothing happens
pass pass
@ -100,99 +115,116 @@ class CryptoBoxContainer:
self.name = new_name self.name = new_name
## remove old database entry ## remove old database entry
try: try:
del self.cbox.prefs.volumesDB[old_name] del self.cbox.prefs.volumes_db[old_name]
except KeyError: except KeyError:
pass pass
## set new volumes database entry ## set new volumes database entry
self.cbox.prefs.volumesDB[new_name] = self.attributes self.cbox.prefs.volumes_db[new_name] = self.attributes
self.cbox.prefs.volumesDB.write() 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 return self.device
def getType(self): def get_type(self):
return self.type """Return the type (int) of this container.
"""
return self.cont_type
def isMounted(self): def is_mounted(self):
return os.path.ismount(self.__getMountPoint()) """Check if the container is currently mounted.
"""
return os.path.ismount(self.__get_mount_point())
def getCapacity(self): def get_capacity(self):
"""return the current capacity state of the volume """Return the current capacity state of the volume.
the volume may not be mounted the volume may not be mounted
the result is a tuple of values in megabyte: the result is a tuple of values in megabyte:
(size, available, used) (size, available, used)
""" """
info = os.statvfs(self.__getMountPoint()) info = os.statvfs(self.__get_mount_point())
return ( return (
int(info.f_bsize*info.f_blocks/1024/1024), int(info.f_bsize*info.f_blocks/1024/1024),
int(info.f_bsize*info.f_bavail/1024/1024), int(info.f_bsize*info.f_bavail/1024/1024),
int(info.f_bsize*(info.f_blocks-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) """return the size of the block device (_not_ of the filesystem)
the result is a value in megabyte the result is a value in megabyte
an error is indicated by "-1" an error is indicated by "-1"
""" """
import cryptobox.core.tools as cbxtools 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 """ recheck the information about this container
this is especially useful after changing the type via 'create' """ this is especially useful after changing the type via 'create' """
self.uuid = self.__getUUID() self.uuid = self.__get_uuid()
self.type = self.__getTypeOfPartition() self.cont_type = self.__get_type_of_partition()
self.name = self.__getNameOfContainer() self.name = self.__get_name_of_container()
self.__setAttributes() self.__set_attributes()
if self.type == ContainerTypes["luks"]: if self.cont_type == CONTAINERTYPES["luks"]:
self.mount = self.__mountLuks self.mount = self.__mount_luks
self.umount = self.__umountLuks self.umount = self.__umount_luks
elif self.type == ContainerTypes["plain"]: elif self.cont_type == CONTAINERTYPES["plain"]:
self.mount = self.__mountPlain self.mount = self.__mount_plain
self.umount = self.__umountPlain self.umount = self.__umount_plain
def create(self, type, password=None): def create(self, cont_type, password=None):
old_name = self.getName() """Format a container.
if type == ContainerTypes["luks"]:
self.__createLuks(password) Also set a password for encrypted container.
elif type == ContainerTypes["plain"]: """
self.__createPlain() old_name = self.get_name()
if cont_type == CONTAINERTYPES["luks"]:
self.__create_luks(password)
elif cont_type == CONTAINERTYPES["plain"]:
self.__create_plain()
else: 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 ## no exception was raised during creation -> we can continue
## reset the properties (encryption state, ...) of the device ## reset the properties (encryption state, ...) of the device
self.resetObject() self.reset_object()
## restore the old name (must be after resetObject) ## restore the old name (must be after reset_object)
self.setName(old_name) self.set_name(old_name)
def changePassword(self, oldpw, newpw): def change_password(self, oldpw, newpw):
if self.type != ContainerTypes["luks"]: """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") raise CBInvalidType("changing of password is possible only for luks containers")
if not oldpw: if not oldpw:
raise CBInvalidPassword("no old password supplied for password change") raise CBInvalidPassword("no old password supplied for password change")
if not newpw: if not newpw:
raise CBInvalidPassword("no new password supplied for password change") raise CBInvalidPassword("no new password supplied for password change")
"return if new and old passwords are the same" ## return if new and old passwords are the same
if oldpw == newpw: return if oldpw == newpw:
if self.isMounted(): return
if self.is_mounted():
raise CBVolumeIsActive("this container is currently active") raise CBVolumeIsActive("this container is currently active")
devnull = None devnull = None
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
"remove any potential open luks mapping" ## remove any potential open luks mapping
self.__umountLuks() self.__umount_luks()
"create the luks header" ## create the luks header
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -208,16 +240,17 @@ class CryptoBoxContainer:
proc.stdin.write("%s\n%s" % (oldpw, newpw)) proc.stdin.write("%s\n%s" % (oldpw, newpw))
(output, errout) = proc.communicate() (output, errout) = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not add a new luks key: %s - %s" % (output.strip(), errout.strip(), ) error_msg = "Could not add a new luks key: %s - %s" \
self.log.error(errorMsg) % (output.strip(), errout.strip(), )
raise CBChangePasswordError(errorMsg) self.cbox.log.error(error_msg)
raise CBChangePasswordError(error_msg)
## retrieve the key slot we used for unlocking ## retrieve the key slot we used for unlocking
keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups() keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups()
if keys_found: if keys_found:
keyslot = int(keys_found[0]) keyslot = int(keys_found[0])
else: else:
raise CBChangePasswordError("could not get the old key slot") raise CBChangePasswordError("could not get the old key slot")
"remove the old key" ## remove the old key
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -231,40 +264,43 @@ class CryptoBoxContainer:
"%d" % (keyslot, )]) "%d" % (keyslot, )])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), ) error_msg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), )
self.log.error(errorMsg) self.cbox.log.error(error_msg)
raise CBChangePasswordError(errorMsg) 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 """retrieve the name of the container by querying the database
call this function only for the initial setup of the container object""" call this function only for the initial setup of the container object"""
found_name = None found_name = None
for key in self.cbox.prefs.volumesDB.keys(): for key in self.cbox.prefs.volumes_db.keys():
if self.cbox.prefs.volumesDB[key]["uuid"] == self.uuid: if self.cbox.prefs.volumes_db[key]["uuid"] == self.uuid:
found_name = key 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 ## there is no name defined for this uuid - we will propose a good one
prefix = self.cbox.prefs["Main"]["DefaultVolumePrefix"] prefix = self.cbox.prefs["Main"]["DefaultVolumePrefix"]
unused_found = False unused_found = False
counter = 1 counter = 1
while not unused_found: while not unused_found:
guess = prefix + str(counter) guess = prefix + str(counter)
if self.cbox.prefs.volumesDB.has_key(guess): if self.cbox.prefs.volumes_db.has_key(guess):
counter += 1 counter += 1
else: else:
unused_found = True unused_found = True
return guess return guess
def __getUUID(self): def __get_uuid(self):
if self.__getTypeOfPartition() == ContainerTypes["luks"]: """Retrieve the uuid of the container device.
guess = self.__getLuksUUID() """
if self.__get_type_of_partition() == CONTAINERTYPES["luks"]:
guess = self.__get_luks_uuid()
else: else:
guess = self.__getNonLuksUUID() guess = self.__get_non_luks_uuid()
## did we get a valid value? ## did we get a valid value?
if guess: if guess:
return guess return guess
@ -273,7 +309,7 @@ class CryptoBoxContainer:
return self.device.replace(os.path.sep, "_") return self.device.replace(os.path.sep, "_")
def __getLuksUUID(self): def __get_luks_uuid(self):
"""get uuid for luks devices""" """get uuid for luks devices"""
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
@ -284,17 +320,18 @@ class CryptoBoxContainer:
self.device]) self.device])
(stdout, stderr) = proc.communicate() (stdout, stderr) = proc.communicate()
if proc.returncode != 0: 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 None
return stdout.strip() return stdout.strip()
def __getNonLuksUUID(self): def __get_non_luks_uuid(self):
"""return UUID for ext2/3 and vfat filesystems""" """return UUID for ext2/3 and vfat filesystems"""
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
proc = subprocess.Popen( proc = subprocess.Popen(
shell=False, shell=False,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -309,30 +346,36 @@ class CryptoBoxContainer:
devnull.close() devnull.close()
## execution failed? ## execution failed?
if proc.returncode != 0: 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 None
## return output of blkid ## return output of blkid
return stdout.strip() return stdout.strip()
def __getTypeOfPartition(self): def __get_type_of_partition(self):
"retrieve the type of the given partition (see cryptobox.core.container.ContainerTypes)" """Retrieve the type of the given partition.
if self.__isLuksPartition(): return ContainerTypes["luks"]
typeOfPartition = self.__getTypeIdOfPartition() see cryptobox.core.container.CONTAINERTYPES
if typeOfPartition in self.__fsTypes["plain"]: """
return ContainerTypes["plain"] if self.__is_luks_partition():
if typeOfPartition in self.__fsTypes["swap"]: return CONTAINERTYPES["luks"]
return ContainerTypes["swap"] type_of_partition = self.__get_type_id_of_partition()
return ContainerTypes["unused"] 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')" "returns the type of the partition (see 'man blkid')"
devnull = None devnull = None
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
proc = subprocess.Popen( proc = subprocess.Popen(
shell=False, shell=False,
stdin=None, stdin=None,
@ -347,19 +390,20 @@ class CryptoBoxContainer:
proc.wait() proc.wait()
output = proc.stdout.read().strip() output = proc.stdout.read().strip()
if proc.returncode != 0: 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 return None
devnull.close() devnull.close()
return output return output
def __isLuksPartition(self): def __is_luks_partition(self):
"check if the given device is a luks partition" "check if the given device is a luks partition"
devnull = None devnull = None
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -375,29 +419,30 @@ class CryptoBoxContainer:
return proc.returncode == 0 return proc.returncode == 0
def __getMountPoint(self): def __get_mount_point(self):
"return the name of the mountpoint of this volume" "return the name of the mountpoint of this volume"
return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], self.name) return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], self.name)
def __mountLuks(self, password): def __mount_luks(self, password):
"mount a luks partition" "mount a luks partition"
if not password: if not password:
raise CBInvalidPassword("no password supplied for luksOpen") raise CBInvalidPassword("no password supplied for luksOpen")
if self.isMounted(): raise CBVolumeIsActive("this container is already active") if self.is_mounted():
self.__umountLuks() raise CBVolumeIsActive("this container is already active")
self.__umount_luks()
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
self.__cleanMountDirs() self.__clean_mount_dirs()
if not os.path.exists(self.__getMountPoint()): if not os.path.exists(self.__get_mount_point()):
self.__createMountDirectory(self.__getMountPoint()) self.__create_mount_directory(self.__get_mount_point())
if not os.path.exists(self.__getMountPoint()): if not os.path.exists(self.__get_mount_point()):
errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), ) err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), )
self.log.error(errorMsg) self.cbox.log.error(err_msg)
raise CBMountError(errorMsg) raise CBMountError(err_msg)
self.cbox.sendEventNotification("premount", self.__getEventArgs()) self.cbox.send_event_notification("premount", self.__get_event_args())
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -414,9 +459,9 @@ class CryptoBoxContainer:
proc.stdin.write(password) proc.stdin.write(password)
(output, errout) = proc.communicate() (output, errout) = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not open the luks mapping: %s" % (errout.strip(), ) err_msg = "Could not open the luks mapping: %s" % (errout.strip(), )
self.log.warn(errorMsg) self.cbox.log.warn(err_msg)
raise CBMountError(errorMsg) raise CBMountError(err_msg)
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -427,25 +472,25 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"mount", "mount",
os.path.join(self.__dmDir, self.name), os.path.join(self.__dmDir, self.name),
self.__getMountPoint()]) self.__get_mount_point()])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg) self.cbox.log.warn(err_msg)
raise CBMountError(errorMsg) raise CBMountError(err_msg)
devnull.close() 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" "umount a luks partition"
devnull = None devnull = None
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
self.cbox.sendEventNotification("preumount", self.__getEventArgs()) self.cbox.send_event_notification("preumount", self.__get_event_args())
if self.isMounted(): if self.is_mounted():
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -455,12 +500,12 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"umount", "umount",
self.__getMountPoint()]) self.__get_mount_point()])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg) self.cbox.log.warn(err_msg)
raise CBUmountError(errorMsg) raise CBUmountError(err_msg)
if os.path.exists(os.path.join(self.__dmDir, self.name)): if os.path.exists(os.path.join(self.__dmDir, self.name)):
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
@ -476,29 +521,30 @@ class CryptoBoxContainer:
"--batch-mode"]) "--batch-mode"])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg) self.cbox.log.warn(err_msg)
raise CBUmountError(errorMsg) raise CBUmountError(err_msg)
devnull.close() 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" "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 devnull = None
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
self.__cleanMountDirs() self.__clean_mount_dirs()
if not os.path.exists(self.__getMountPoint()): if not os.path.exists(self.__get_mount_point()):
self.__createMountDirectory(self.__getMountPoint()) self.__create_mount_directory(self.__get_mount_point())
if not os.path.exists(self.__getMountPoint()): if not os.path.exists(self.__get_mount_point()):
errorMsg = "Could not create mountpoint (%s)" % (self.__getMountPoint(), ) err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), )
self.log.error(errorMsg) self.cbox.log.error(err_msg)
raise CBMountError(errorMsg) raise CBMountError(err_msg)
self.cbox.sendEventNotification("premount", self.__getEventArgs()) self.cbox.send_event_notification("premount", self.__get_event_args())
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -509,27 +555,28 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"mount", "mount",
self.device, self.device,
self.__getMountPoint()]) self.__get_mount_point()])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg) self.cbox.log.warn(err_msg)
raise CBMountError(errorMsg) raise CBMountError(err_msg)
devnull.close() 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" "umount a plaintext partition"
if not self.isMounted(): if not self.is_mounted():
self.cbox.log.info("trying to umount while volume (%s) is mounted" % self.getDevice()) self.cbox.log.info("trying to umount while volume (%s) is mounted" % \
self.get_device())
return return
devnull = None devnull = None
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
self.cbox.sendEventNotification("preumount", self.__getEventArgs()) self.cbox.send_event_notification("preumount", self.__get_event_args())
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -539,25 +586,25 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["super"], self.cbox.prefs["Programs"]["super"],
self.cbox.prefs["Programs"]["CryptoBoxRootActions"], self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
"umount", "umount",
self.__getMountPoint()]) self.__get_mount_point()])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.warn(errorMsg) self.cbox.log.warn(err_msg)
raise CBUmountError(errorMsg) raise CBUmountError(err_msg)
devnull.close() 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" "make a plaintext partition"
if self.isMounted(): if self.is_mounted():
raise CBVolumeIsActive("deactivate the partition before filesystem initialization") raise CBVolumeIsActive("deactivate the partition before filesystem initialization")
devnull = None devnull = None
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -568,26 +615,27 @@ class CryptoBoxContainer:
self.device]) self.device])
proc.wait() proc.wait()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.error(errorMsg) self.cbox.log.error(err_msg)
raise CBCreateError(errorMsg) raise CBCreateError(err_msg)
devnull.close() devnull.close()
def __createLuks(self, password): def __create_luks(self, password):
"make a luks partition" """Create a luks partition.
"""
if not password: if not password:
raise CBInvalidPassword("no password supplied for new luks mapping") raise CBInvalidPassword("no password supplied for new luks mapping")
if self.isMounted(): if self.is_mounted():
raise CBVolumeIsActive("deactivate the partition before filesystem initialization") raise CBVolumeIsActive("deactivate the partition before filesystem initialization")
devnull = None devnull = None
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.warn("Could not open %s" % (os.devnull, )) self.cbox.log.warn("Could not open %s" % (os.devnull, ))
"remove any potential open luks mapping" ## remove any potential open luks mapping
self.__umountLuks() self.__umount_luks()
"create the luks header" ## create the luks header
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -605,10 +653,10 @@ class CryptoBoxContainer:
proc.stdin.write(password) proc.stdin.write(password)
(output, errout) = proc.communicate() (output, errout) = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not create the luks header: %s" % (errout.strip(), ) err_msg = "Could not create the luks header: %s" % (errout.strip(), )
self.log.error(errorMsg) self.cbox.log.error(err_msg)
raise CBCreateError(errorMsg) raise CBCreateError(err_msg)
"open the luks container for mkfs" ## open the luks container for mkfs
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
@ -625,10 +673,10 @@ class CryptoBoxContainer:
proc.stdin.write(password) proc.stdin.write(password)
(output, errout) = proc.communicate() (output, errout) = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not open the new luks mapping: %s" % (errout.strip(), ) err_msg = "Could not open the new luks mapping: %s" % (errout.strip(), )
self.log.error(errorMsg) self.cbox.log.error(err_msg)
raise CBCreateError(errorMsg) raise CBCreateError(err_msg)
"make the filesystem" ## make the filesystem
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdin = None, stdin = None,
@ -638,50 +686,54 @@ class CryptoBoxContainer:
self.cbox.prefs["Programs"]["mkfs-data"], self.cbox.prefs["Programs"]["mkfs-data"],
os.path.join(self.__dmDir, self.name)]) os.path.join(self.__dmDir, self.name)])
proc.wait() proc.wait()
"remove the mapping - for every exit status" ## remove the mapping - for every exit status
self.__umountLuks() self.__umount_luks()
if proc.returncode != 0: if proc.returncode != 0:
errorMsg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), )
self.log.error(errorMsg) self.cbox.log.error(err_msg)
"remove the luks mapping" ## remove the luks mapping
raise CBCreateError(errorMsg) raise CBCreateError(err_msg)
devnull.close() devnull.close()
def __cleanMountDirs(self): def __clean_mount_dirs(self):
""" remove all unnecessary subdirs of the mount parent directory """ remove all unnecessary subdirs of the mount parent directory
this should be called for every (u)mount """ this should be called for every (u)mount """
subdirs = os.listdir(self.cbox.prefs["Locations"]["MountParentDir"]) subdirs = os.listdir(self.cbox.prefs["Locations"]["MountParentDir"])
for d in subdirs: for one_dir in subdirs:
abs_dir = os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], d) abs_dir = os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], one_dir)
if (not os.path.islink(abs_dir)) \ if (not os.path.islink(abs_dir)) \
and os.path.isdir(abs_dir) \ and os.path.isdir(abs_dir) \
and (not os.path.ismount(abs_dir)) \ and (not os.path.ismount(abs_dir)) \
and (os.path.isfile(os.path.join(abs_dir,MOUNT_DIR_MARKER))) \ and (os.path.isfile(os.path.join(abs_dir,MOUNT_DIR_MARKER))) \
and (len(os.listdir(abs_dir)) == 1): and (len(os.listdir(abs_dir)) == 1):
try: 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) os.rmdir(abs_dir)
except OSError,errMsg: except OSError, err_msg:
## we do not care too much about unclean cleaning ... ## 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 """create and mark a mount directory
this marking helps to remove old mountdirs safely""" this marking helps to remove old mountdirs safely"""
os.mkdir(dirname) os.mkdir(dirname)
try: try:
f = file(os.path.join(dirname,MOUNT_DIR_MARKER),"w") mark_file = file(os.path.join(dirname, MOUNT_DIR_MARKER), "w")
f.close() mark_file.close()
except OSError,errMsg: except OSError, err_msg:
## we do not care too much about the marking ## 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): def __get_event_args(self):
"""return an array of arguments for event scripts handling pre/post-mount/umount """Return an array of arguments for event scripts.
events"""
typeText = [e for e in ContainerTypes.keys() if ContainerTypes[e] == self.getType()][0] for now supported: pre/post-mount/umount events
return [self.getDevice(), self.getName(), typeText, self.__getMountPoint()] """
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()]

View file

@ -22,6 +22,8 @@
exceptions of the cryptobox package exceptions of the cryptobox package
""" """
__revision__ = "$Id"
class CBError(Exception): class CBError(Exception):
"""base class for exceptions of the cryptobox""" """base class for exceptions of the cryptobox"""
@ -54,13 +56,16 @@ class CBConfigUndefinedError(CBConfigError):
self.name = name self.name = name
def __str__(self): def __str__(self):
# is it a settings or a section? """Output the appropriate string: for a setting or a section.
"""
if self.name: if self.name:
# setting # 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: else:
# section # 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.value = value
self.reason = reason self.reason = reason
def __str__(self): 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): class CBEnvironmentError(CBError):
@ -86,42 +95,58 @@ class CBEnvironmentError(CBError):
self.desc = desc self.desc = desc
def __str__(self): def __str__(self):
"""Return the error description.
"""
return "misconfiguration detected: %s" % self.desc return "misconfiguration detected: %s" % self.desc
class CBContainerError(CBError): 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): class CBCreateError(CBContainerError):
"""Raised if a container could not be created (formatted).
"""
pass pass
class CBVolumeIsActive(CBContainerError): class CBVolumeIsActive(CBContainerError):
"""Raised if a container was active even if it may not for a specific action.
"""
pass pass
class CBInvalidName(CBContainerError): class CBInvalidName(CBContainerError):
"""Raised if someone tried to set an invalid container name.
"""
pass pass
class CBNameIsInUse(CBContainerError): class CBNameIsInUse(CBContainerError):
"""Raised if the new name of a container is already in use.
"""
pass pass
class CBInvalidType(CBContainerError): class CBInvalidType(CBContainerError):
"""Raised if a container is of an invalid type for a choosen action.
"""
pass pass
class CBInvalidPassword(CBContainerError): class CBInvalidPassword(CBContainerError):
"""Someone tried to open an ecnrypted container with the wrong password.
"""
pass pass
class CBChangePasswordError(CBContainerError): class CBChangePasswordError(CBContainerError):
"""Changing of the password of an encrypted container failed.
"""
pass pass
class CBMountError(CBContainerError): class CBMountError(CBContainerError):
"""Failed to mount a container.
"""
pass pass
class CBUmountError(CBContainerError): class CBUmountError(CBContainerError):
"""Failed to umount a container.
"""
pass pass

View file

@ -22,23 +22,17 @@
This is the web interface for a fileserver managing encrypted filesystems. This is the web interface for a fileserver managing encrypted filesystems.
''' '''
# check python version __revision__ = "$Id"
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)
import sys
import cryptobox.core.container as cbxContainer import cryptobox.core.container as cbxContainer
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import CBEnvironmentError, CBConfigUndefinedError
import re import re
import os import os
import cryptobox.core.tools as cbxTools import cryptobox.core.tools as cbxTools
import subprocess import subprocess
VERSION = "0.3~1"
class CryptoBox: class CryptoBox:
'''this class rules them all! '''this class rules them all!
@ -48,14 +42,15 @@ class CryptoBox:
def __init__(self, config_file=None): def __init__(self, config_file=None):
import cryptobox.core.settings as cbxSettings import cryptobox.core.settings as cbxSettings
self.log = self.__getStartupLogger() self.log = self.__get_startup_logger()
self.prefs = cbxSettings.CryptoBoxSettings(config_file) self.prefs = cbxSettings.CryptoBoxSettings(config_file)
self.__runTests() self.__run_tests()
self.__containers = []
self.reread_container_list()
def __getStartupLogger(self): def __get_startup_logger(self):
import logging """Initialize the configured logging facility of the CryptoBox.
'''initialises the logging system
use it with: 'self.log.[debug|info|warning|error|critical](logmessage)' use it with: 'self.log.[debug|info|warning|error|critical](logmessage)'
all classes should get the logging instance during __init__: all classes should get the logging instance during __init__:
@ -63,13 +58,15 @@ class CryptoBox:
first we output all warnings/errors to stderr first we output all warnings/errors to stderr
as soon as we opened the config file successfully, we redirect debug output 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 ## basicConfig(...) needs python >= 2.4
try: try:
log_handler = logging.getLogger("CryptoBox") log_handler = logging.getLogger("CryptoBox")
logging.basicConfig( logging.basicConfig(
format='%(asctime)s CryptoBox %(levelname)s: %(message)s', format = '%(asctime)s CryptoBox %(levelname)s: %(message)s',
stderr=sys.stderr) stderr = sys.stderr)
log_handler.setLevel(logging.ERROR) log_handler.setLevel(logging.ERROR)
log_handler.info("loggingsystem is up'n running") log_handler.info("loggingsystem is up'n running")
## from now on everything can be logged via self.log... ## from now on everything can be logged via self.log...
@ -78,19 +75,15 @@ class CryptoBox:
return log_handler return log_handler
# do some initial checks def __run_tests(self):
def __runTests(self): """Do some initial tests.
#self.__runTestUID() """
self.__runTestRootPriv() self.__run_test_root_priv()
def __runTestUID(self): def __run_test_root_priv(self):
if os.geteuid() == 0: """Try to run 'super' with 'CryptoBoxRootActions'.
raise CBEnvironmentError("you may not run the cryptobox as root") """
def __runTestRootPriv(self):
"""try to run 'super' with 'CryptoBoxRootActions'"""
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
@ -110,46 +103,39 @@ class CryptoBox:
stderr = devnull, stderr = devnull,
args = [prog_super, prog_rootactions, "check"]) args = [prog_super, prog_rootactions, "check"])
except OSError: 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() proc.wait()
if proc.returncode != 0: 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 reread_container_list(self):
def cbx_inheritance_test(self, string="you lucky widow"): """Reinitialize the list of available containers.
self.log.info(string)
# RFC: why should CryptoBoxProps inherit CryptoBox? [l] This should be called whenever the available containers may have changed.
# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l] E.g.: after partitioning and after device addition/removal
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):
self.log.debug("rereading container list") self.log.debug("rereading container list")
self.containers = [] self.__containers = []
for device in cbxTools.getAvailablePartitions(): for device in cbxTools.get_available_partitions():
if self.isDeviceAllowed(device) and not self.isConfigPartition(device): if self.is_device_allowed(device) and not self.is_config_partition(device):
self.containers.append(cbxContainer.CryptoBoxContainer(device, self)) self.__containers.append(cbxContainer.CryptoBoxContainer(device, self))
## sort by container name ## 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( proc = subprocess.Popen(
shell = False, shell = False,
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
args = [ args = [
self.prefs["Programs"]["blkid"], self.prefs["Programs"]["blkid"],
"-c", os.path.devnull, "-c", os.path.devnull,
@ -160,70 +146,77 @@ class CryptoBoxProps(CryptoBox):
return output.strip() == self.prefs["Main"]["ConfigVolumeLabel"] 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" "check if a device is white-listed for being used as cryptobox containers"
import types import types
allowed = self.prefs["Main"]["AllowedDevices"] allowed = self.prefs["Main"]["AllowedDevices"]
if type(allowed) == types.StringType: allowed = [allowed] if type(allowed) == types.StringType:
allowed = [allowed]
for a_dev in allowed: for a_dev in allowed:
"remove double dots and so on ..." ## remove double dots and so on ...
real_device = os.path.realpath(devicename) 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 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 """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 # return nothing if the currently selected log output is not a file
try: try:
if self.prefs["Log"]["Destination"].upper() != "FILE": return [] if self.prefs["Log"]["Destination"].upper() != "FILE":
return []
log_file = self.prefs["Log"]["Details"] log_file = self.prefs["Log"]["Details"]
except KeyError: 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 [] return []
try: try:
fd = open(log_file, "r") fdesc = open(log_file, "r")
if maxSize: fd.seek(-maxSize, 2) # seek relative to the end of the file if max_size:
content = fd.readlines() fdesc.seek(-max_size, 2) # seek relative to the end of the file
fd.close() content = fdesc.readlines()
fdesc.close()
except IOError: except IOError:
self.log.warn("failed to read the log file (%s)" % log_file) self.log.warn("failed to read the log file (%s)" % log_file)
return [] return []
if lines: content = content[-lines:] if lines:
content = content[-lines:]
content.reverse() content.reverse()
return content 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" "retrieve the list of all containers of this cryptobox"
try: try:
result = self.containers[:] result = self.__containers[:]
if filterType != None: if filter_type != None:
if filterType in range(len(cbxContainer.ContainerTypes)): if filter_type in range(len(cbxContainer.CONTAINERTYPES)):
return [e for e in self.containers if e.getType() == filterType] return [e for e in self.__containers if e.get_type() == filter_type]
else: else:
self.log.info("invalid filterType (%d)" % filterType) self.log.info("invalid filter_type (%d)" % filter_type)
result.clear() result.clear()
if filterName != None: if filter_name != None:
result = [e for e in self.containers if e.getName() == filterName] result = [e for e in self.__containers if e.get_name() == filter_name]
return result return result
except AttributeError: except AttributeError:
return [] return []
def getContainer(self, device): def get_container(self, device):
"retrieve the container element for this 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: if all:
return all[0] return all[0]
else: else:
return None 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""" """call all available scripts in the event directory with some event information"""
event_dir = self.prefs["Locations"]["EventDir"] event_dir = self.prefs["Locations"]["EventDir"]
for fname in os.listdir(event_dir): for fname in os.listdir(event_dir):
@ -240,11 +233,14 @@ class CryptoBoxProps(CryptoBox):
args = cmd_args) args = cmd_args)
(stdout, stderr) = proc.communicate() (stdout, stderr) = proc.communicate()
if proc.returncode != 0: 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: 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__": if __name__ == "__main__":
cb = CryptoBoxProps() CryptoBox()

View file

@ -18,22 +18,21 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 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 * from cryptobox.core.exceptions import *
import logging import logging
try:
import validate
except:
raise CBEnvironmentError("couldn't import 'validate'! Try 'apt-get install python-formencode'.")
import os
import subprocess import subprocess
try: import os
import configobj ## needed for reading and writing of the config file import configobj, validate
except:
raise CBEnvironmentError("couldn't import 'configobj'! Try 'apt-get install python-configobj'.")
class CryptoBoxSettings: class CryptoBoxSettings:
"""Manage the various configuration files of the CryptoBox
"""
CONF_LOCATIONS = [ CONF_LOCATIONS = [
"./cryptobox.conf", "./cryptobox.conf",
@ -47,76 +46,85 @@ class CryptoBoxSettings:
def __init__(self, config_file=None): def __init__(self, config_file=None):
self.log = logging.getLogger("CryptoBox") 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.log.info("loading config file: %s" % config_file)
self.prefs = self.__getPreferences(config_file) self.prefs = self.__get_preferences(config_file)
self.__validateConfig() self.__validate_config()
self.__configureLogHandler() self.__configure_log_handler()
self.__checkUnknownPreferences() self.__check_unknown_preferences()
self.preparePartition() self.prepare_partition()
self.volumesDB = self.__getVolumesDatabase() self.volumes_db = self.__get_volumes_database()
self.pluginConf = self.__getPluginConfig() self.plugin_conf = self.__get_plugin_config()
self.userDB = self.__getUserDB() self.user_db = self.__get_user_db()
self.misc_files = self.__getMiscFiles() self.misc_files = self.__get_misc_files()
def write(self): def write(self):
""" """
write all local setting files including the content of the "misc" subdirectory write all local setting files including the content of the "misc" subdirectory
""" """
ok = True status = True
try: try:
self.volumesDB.write() self.volumes_db.write()
except IOError: except IOError:
self.log.warn("could not save the volume database") self.log.warn("could not save the volume database")
ok = False status = False
try: try:
self.pluginConf.write() self.plugin_conf.write()
except IOError: except IOError:
self.log.warn("could not save the plugin configuration") self.log.warn("could not save the plugin configuration")
ok = False status = False
try: try:
self.userDB.write() self.user_db.write()
except IOError: except IOError:
self.log.warn("could not save the user database") self.log.warn("could not save the user database")
ok = False status = False
for misc_file in self.misc_files: for misc_file in self.misc_files:
if not misc_file.save(): if not misc_file.save():
self.log.warn("could not save a misc setting file (%s)" % misc_file.filename) self.log.warn("could not save a misc setting file (%s)" % misc_file.filename)
ok = False status = False
return ok return status
def requiresPartition(self): def requires_partition(self):
return bool(self.prefs["Main"]["UseConfigPartition"]) 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"] 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"): for line in file("/proc/mounts"):
fields = line.split(" ") fields = line.split(" ")
mount_dir = fields[1] mount_dir = fields[1]
try: 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: except OSError:
pass pass
## no matching entry found ## no matching entry found
return None return None
def mountPartition(self): def mount_partition(self):
"""Mount a config partition.
"""
self.log.debug("trying to mount configuration partition") self.log.debug("trying to mount configuration partition")
if not self.requiresPartition(): if not self.requires_partition():
self.log.warn("mountConfigPartition: configuration partition is not required - mounting anyway") self.log.warn("mountConfigPartition: configuration partition is "
if self.getActivePartition(): + "not required - mounting anyway")
self.log.warn("mountConfigPartition: configuration partition already mounted - not mounting again") if self.get_active_partition():
self.log.warn("mountConfigPartition: configuration partition already "
+ "mounted - not mounting again")
return False return False
confPartitions = self.getAvailablePartitions() conf_partitions = self.get_available_partitions()
if not confPartitions: if not conf_partitions:
self.log.error("no configuration partitions found - you have to create it first") self.log.error("no configuration partition found - you have to create "
+ "it first")
return False return False
partition = confPartitions[0] partition = conf_partitions[0]
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
@ -136,8 +144,10 @@ class CryptoBoxSettings:
return True return True
def umountPartition(self): def umount_partition(self):
if not self.getActivePartition(): """Umount the currently active configuration partition.
"""
if not self.get_active_partition():
self.log.warn("umountConfigPartition: no configuration partition mounted") self.log.warn("umountConfigPartition: no configuration partition mounted")
return False return False
proc = subprocess.Popen( proc = subprocess.Popen(
@ -158,7 +168,7 @@ class CryptoBoxSettings:
return True return True
def getAvailablePartitions(self): def get_available_partitions(self):
"""returns a sequence of found config partitions""" """returns a sequence of found config partitions"""
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
@ -169,14 +179,16 @@ class CryptoBoxSettings:
"-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ]) "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ])
(output, error) = proc.communicate() (output, error) = proc.communicate()
if output: 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: else:
return [] return []
def preparePartition(self): def prepare_partition(self):
if self.requiresPartition() and not self.getActivePartition(): """Mount a config partition if necessary.
self.mountPartition() """
if self.requires_partition() and not self.get_active_partition():
self.mount_partition()
def __getitem__(self, key): def __getitem__(self, key):
@ -184,7 +196,9 @@ class CryptoBoxSettings:
return self.prefs[key] return self.prefs[key]
def __getPreferences(self, config_file): def __get_preferences(self, config_file):
"""Load the CryptoBox configuration.
"""
import StringIO import StringIO
config_rules = StringIO.StringIO(self.validation_spec) config_rules = StringIO.StringIO(self.validation_spec)
try: try:
@ -192,86 +206,111 @@ class CryptoBoxSettings:
if prefs: if prefs:
self.log.info("found config: %s" % prefs.items()) self.log.info("found config: %s" % prefs.items())
else: 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: 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 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) result = self.prefs.validate(CryptoBoxSettingsValidator(), preserve_errors=True)
error_list = configobj.flatten_errors(self.prefs, result) error_list = configobj.flatten_errors(self.prefs, result)
if not error_list: return if not error_list:
errorMsgs = [] return
error_msgs = []
for sections, key, text in error_list: for sections, key, text in error_list:
section_name = "->".join(sections) section_name = "->".join(sections)
if not text: 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: else:
errorMsg = "invalid configuration value (%s) in section '%s': %s" % (key, section_name, text) error_msg = "invalid configuration value (%s) in section '%s': %s" % \
errorMsgs.append(errorMsg) (key, section_name, text)
raise CBConfigError, "\n".join(errorMsgs) 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 import StringIO
config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec), list_values=False) config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec),
self.__recursiveConfigSectionCheck("", self.prefs, config_rules) list_values=False)
self.__recursive_section_check("", self.prefs, config_rules)
def __recursiveConfigSectionCheck(self, section_path, section_config, section_rules): def __recursive_section_check(self, section_path, section_config, section_rules):
"""should be called by '__checkUnknownPreferences' for every section """should be called by '__check_unknown_preferences' for every section
sends a warning message to the logger for every undefined (see validation_spec) sends a warning message to the logger for every undefined (see validation_spec)
configuration setting configuration setting
""" """
for e in section_config.keys(): for section in section_config.keys():
element_path = section_path + e element_path = section_path + section
if e in section_rules.keys(): if section in section_rules.keys():
if isinstance(section_config[e], configobj.Section): if isinstance(section_config[section], configobj.Section):
if isinstance(section_rules[e], configobj.Section): if isinstance(section_rules[section], configobj.Section):
self.__recursiveConfigSectionCheck(element_path + "->", section_config[e], section_rules[e]) self.__recursive_section_check(element_path + "->",
section_config[section], section_rules[section])
else: 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: else:
if not isinstance(section_rules[e], configobj.Section): if not isinstance(section_rules[section], configobj.Section):
pass # good - the setting is valid pass # good - the setting is valid
else: 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: else:
self.log.warn("unknown configuration setting: %s" % element_path) 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 import StringIO
plugin_rules = StringIO.StringIO(self.pluginValidationSpec) plugin_rules = StringIO.StringIO(self.pluginValidationSpec)
try: try:
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: except KeyError:
raise CBConfigUndefinedError("Locations", "SettingsDir") raise CBConfigUndefinedError("Locations", "SettingsDir")
except SyntaxError: except SyntaxError:
raise CBConfigInvalidValueError("Locations", "SettingsDir", pluginConf_file, "failed to interprete the filename of the plugin config file correctly (%s)" % pluginConf_file) raise CBConfigInvalidValueError("Locations", "SettingsDir", plugin_conf_file,
## create pluginConf_file if necessary "failed to interprete the filename of the plugin config file "
if os.path.exists(pluginConf_file): + "correctly (%s)" % plugin_conf_file)
pluginConf = configobj.ConfigObj(pluginConf_file, configspec=plugin_rules) ## create plugin_conf_file if necessary
if os.path.exists(plugin_conf_file):
plugin_conf = configobj.ConfigObj(plugin_conf_file, configspec=plugin_rules)
else: 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 ## validate and convert values according to the spec
pluginConf.validate(validate.Validator()) plugin_conf.validate(validate.Validator())
## check if pluginConf_file file was created successfully? ## check if plugin_conf_file file was created successfully?
if not os.path.exists(pluginConf_file): if not os.path.exists(plugin_conf_file):
raise CBEnvironmentError("failed to create plugin configuration file (%s)" % pluginConf_file) raise CBEnvironmentError(
return pluginConf "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:
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: except KeyError:
raise CBConfigUndefinedError("Locations", "SettingsDir") raise CBConfigUndefinedError("Locations", "SettingsDir")
except SyntaxError: 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 ## create conf_file if necessary
if os.path.exists(conf_file): if os.path.exists(conf_file):
conf = configobj.ConfigObj(conf_file) conf = configobj.ConfigObj(conf_file)
@ -279,36 +318,48 @@ class CryptoBoxSettings:
conf = configobj.ConfigObj(conf_file, create_empty=True) conf = configobj.ConfigObj(conf_file, create_empty=True)
## check if conf_file file was created successfully? ## check if conf_file file was created successfully?
if not os.path.exists(conf_file): 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 return conf
def __getUserDB(self): def __get_user_db(self):
"""Load the user database file if it exists.
"""
import StringIO, sha import StringIO, sha
userDB_rules = StringIO.StringIO(self.userDatabaseSpec) user_db_rules = StringIO.StringIO(self.userDatabaseSpec)
try: try:
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: except KeyError:
raise CBConfigUndefinedError("Locations", "SettingsDir") raise CBConfigUndefinedError("Locations", "SettingsDir")
except SyntaxError: except SyntaxError:
raise CBConfigInvalidValueError("Locations", "SettingsDir", userDB_file, "failed to interprete the filename of the users database file correctly (%s)" % userDB_file) raise CBConfigInvalidValueError("Locations", "SettingsDir", user_db_file,
## create userDB_file if necessary "failed to interprete the filename of the users database file "
if os.path.exists(userDB_file): + "correctly (%s)" % user_db_file)
userDB = configobj.ConfigObj(userDB_file, configspec=userDB_rules) ## 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: 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 ## validate and set default value for "admin" user
userDB.validate(validate.Validator()) user_db.validate(validate.Validator())
## check if userDB file was created successfully? ## check if user_db file was created successfully?
if not os.path.exists(userDB_file): if not os.path.exists(user_db_file):
raise CBEnvironmentError("failed to create user database file (%s)" % userDB_file) raise CBEnvironmentError(
"failed to create user database file (%s)" % user_db_file)
## define password hash function - never use "sha" directly - SPOT ## define password hash function - never use "sha" directly - SPOT
userDB.getDigest = lambda password: sha.new(password).hexdigest() user_db.get_digest = lambda password: sha.new(password).hexdigest()
return userDB 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") 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)): if (not os.path.isdir(misc_dir)) or (not os.access(misc_dir, os.X_OK)):
return [] return []
@ -317,8 +368,9 @@ class CryptoBoxSettings:
if os.path.isfile(os.path.join(misc_dir, f))] if os.path.isfile(os.path.join(misc_dir, f))]
def __getConfigFileName(self, config_file): def __get_config_filename(self, config_file):
# search for the configuration file """Search for the configuration file.
"""
import types import types
if config_file is None: if config_file is None:
# no config file was specified - we will look for it in the ususal locations # no config file was specified - we will look for it in the ususal locations
@ -332,13 +384,17 @@ class CryptoBoxSettings:
else: else:
# a config file was specified (e.g. via command line) # a config file was specified (e.g. via command line)
if type(config_file) != types.StringType: 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): 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 return config_file
def __configureLogHandler(self): def __configure_log_handler(self):
"""Configure the log handler of the CryptoBox according to the config.
"""
try: try:
log_level = self.prefs["Log"]["Level"].upper() log_level = self.prefs["Log"]["Level"].upper()
log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"] log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"]
@ -347,15 +403,18 @@ class CryptoBoxSettings:
except KeyError: except KeyError:
raise CBConfigUndefinedError("Log", "Level") raise CBConfigUndefinedError("Log", "Level")
except TypeError: 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:
try: try:
log_handler = logging.FileHandler(self.prefs["Log"]["Details"]) log_handler = logging.FileHandler(self.prefs["Log"]["Details"])
except KeyError: except KeyError:
raise CBConfigUndefinedError("Log", "Details") raise CBConfigUndefinedError("Log", "Details")
except IOError: except IOError:
raise CBEnvironmentError("could not create the log file (%s)" % self.prefs["Log"]["Details"]) raise CBEnvironmentError("could not create the log file (%s)" % \
log_handler.setFormatter(logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s')) self.prefs["Log"]["Details"])
log_handler.setFormatter(
logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s'))
cbox_log = logging.getLogger("CryptoBox") cbox_log = logging.getLogger("CryptoBox")
## remove previous handlers ## remove previous handlers
cbox_log.handlers = [] cbox_log.handlers = []
@ -364,7 +423,7 @@ class CryptoBoxSettings:
## do not call parent's handlers ## do not call parent's handlers
cbox_log.propagate = False cbox_log.propagate = False
## 'log_level' is a string -> use 'getattr' ## '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 ## the logger named "CryptoBox" is configured now
@ -420,16 +479,20 @@ admin = string(default=d033e22ae348aeb5660fc2140aec35850c4da997)
class CryptoBoxSettingsValidator(validate.Validator): class CryptoBoxSettingsValidator(validate.Validator):
"""Some custom configuration check functions.
"""
def __init__(self): def __init__(self):
validate.Validator.__init__(self) validate.Validator.__init__(self)
self.functions["directoryExists"] = self.check_directoryExists self.functions["directoryExists"] = self.check_directory_exists
self.functions["fileExecutable"] = self.check_fileExecutable self.functions["fileExecutable"] = self.check_file_executable
self.functions["fileWriteable"] = self.check_fileWriteable self.functions["fileWriteable"] = self.check_file_writeable
self.functions["listOfExistingDirectories"] = self.check_listOfExistingDirectories 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) dir_path = os.path.abspath(value)
if not os.path.isdir(dir_path): if not os.path.isdir(dir_path):
raise validate.VdtValueError("%s (not found)" % value) raise validate.VdtValueError("%s (not found)" % value)
@ -438,7 +501,9 @@ class CryptoBoxSettingsValidator(validate.Validator):
return dir_path return dir_path
def check_fileExecutable(self, value): def check_file_executable(self, value):
"""Is the file executable?
"""
file_path = os.path.abspath(value) file_path = os.path.abspath(value)
if not os.path.isfile(file_path): if not os.path.isfile(file_path):
raise validate.VdtValueError("%s (not found)" % value) raise validate.VdtValueError("%s (not found)" % value)
@ -447,7 +512,9 @@ class CryptoBoxSettingsValidator(validate.Validator):
return file_path return file_path
def check_fileWriteable(self, value): def check_file_writeable(self, value):
"""Is the file writeable?
"""
file_path = os.path.abspath(value) file_path = os.path.abspath(value)
if os.path.isfile(file_path): if os.path.isfile(file_path):
if not os.access(file_path, os.W_OK): if not os.access(file_path, os.W_OK):
@ -460,18 +527,22 @@ class CryptoBoxSettingsValidator(validate.Validator):
return file_path return file_path
def check_listOfExistingDirectories(self, value): def check_existing_directories(self, value):
"""Are these directories accessible?
"""
if not value: if not value:
raise validate.VdtValueError("no plugin directory specified") raise validate.VdtValueError("no plugin directory specified")
if not isinstance(value,list): if not isinstance(value, list):
value = [value] value = [value]
result = [] result = []
for d in value: for one_dir in value:
dir_path = os.path.abspath(d) dir_path = os.path.abspath(one_dir)
if not os.path.isdir(dir_path): 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): 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) result.append(dir_path)
return result return result
@ -484,19 +555,25 @@ class MiscConfigFile:
def __init__(self, filename, logger): def __init__(self, filename, logger):
self.filename = filename self.filename = filename
self.log = logger self.log = logger
self.content = None
self.load() self.load()
def load(self): def load(self):
fd = open(self.filename, "rb") """Load a configuration file into memory.
"""
fdesc = open(self.filename, "rb")
## limit the maximum size ## limit the maximum size
self.content = fd.read(self.maxSize) self.content = fdesc.read(self.maxSize)
if fd.tell() == self.maxSize: if fdesc.tell() == self.maxSize:
self.log.warn("file in misc settings directory (%s) is bigger than allowed (%s)" % (self.filename, self.maxSize)) self.log.warn("file in misc settings directory (" + str(self.filename) \
fd.close() + ") is bigger than allowed (" + str(self.maxSize) + ")")
fdesc.close()
def save(self): def save(self):
"""Save a configuration file to disk.
"""
save_dir = os.path.dirname(self.filename) save_dir = os.path.dirname(self.filename)
## create the directory, if necessary ## create the directory, if necessary
if not os.path.isdir(save_dir): if not os.path.isdir(save_dir):
@ -506,14 +583,14 @@ class MiscConfigFile:
return False return False
## save the content of the file ## save the content of the file
try: try:
fd = open(self.filename, "wb") fdesc = open(self.filename, "wb")
except IOError: except IOError:
return False return False
try: try:
fd.write(self.content) fdesc.write(self.content)
fd.close() fdesc.close()
return True return True
except IOError: except IOError:
fd.close() fdesc.close()
return False return False

View file

@ -18,137 +18,169 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
"""Some useful functions for the CryptoBox.
"""
__revision__ = "$Id"
import logging import logging
import os import os
import re import re
logger = logging.getLogger("CryptoBox") LOGGER = logging.getLogger("CryptoBox")
def getAvailablePartitions(): def get_available_partitions():
"retrieve a list of all available containers" "retrieve a list of all available containers"
ret_list = [] ret_list = []
try: 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") fpart = open("/proc/partitions", "r")
try: try:
line = fpart.readline() line = fpart.readline()
while line: while line:
p_details = line.split() p_details = line.split()
if (len(p_details) == 4): 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 (p_major, p_minor, p_size, p_device) = p_details
## ignore lines with: invalid minor/major or extend partitions (size=1) ## 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) p_parent = re.sub('[1-9]?[0-9]$', '', p_device)
if p_parent == p_device: if p_parent == p_device:
if [e for e in ret_list if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]: if [e for e in ret_list
"major partition - its children are already in the list" if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]:
## major partition - its children are already in the list
pass pass
else: else:
"major partition - but there are no children for now" ## major partition - but there are no children for now
ret_list.append(p_device) ret_list.append(p_device)
else: else:
"minor partition - remove parent if necessary" ## minor partition - remove parent if necessary
if p_parent in ret_list: ret_list.remove(p_parent) if p_parent in ret_list:
ret_list.remove(p_parent)
ret_list.append(p_device) ret_list.append(p_device)
line = fpart.readline() line = fpart.readline()
finally: finally:
fpart.close() fpart.close()
return map(getAbsoluteDeviceName, ret_list) return [ get_absolute_devicename(e) for e in ret_list ]
except IOError: except IOError:
logger.warning("Could not read /proc/partitions") LOGGER.warning("Could not read /proc/partitions")
return [] return []
def getAbsoluteDeviceName(shortname): def get_absolute_devicename(shortname):
""" returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1") """ returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1")
this does also work for device mapper devices this does also work for device mapper devices
if the result is non-unique, one arbitrary value is returned""" if the result is non-unique, one arbitrary value is returned
if re.search('^/', shortname): return shortname """
if re.search('^/', shortname):
return shortname
default = os.path.join("/dev", shortname) default = os.path.join("/dev", shortname)
if os.path.exists(default): return default if os.path.exists(default):
result = findMajorMinorOfDevice(shortname) return default
"if no valid major/minor was found -> exit" result = find_major_minor_of_device(shortname)
if not result: return default ## if no valid major/minor was found -> exit
if not result:
return default
(major, minor) = result (major, minor) = result
"for device-mapper devices (major == 254) ..." ## for device-mapper devices (major == 254) ...
if major == 254: if major == 254:
result = findMajorMinorDeviceName("/dev/mapper", major, minor) result = find_major_minor_device("/dev/mapper", major, minor)
if result: return result[0] if result:
"now check all files in /dev" return result[0]
result = findMajorMinorDeviceName("/dev", major, minor) ## now check all files in /dev
if result: return result[0] result = find_major_minor_device("/dev", major, minor)
if result:
return result[0]
return default return default
def findMajorMinorOfDevice(device): def find_major_minor_of_device(device):
"return the major/minor numbers of a block 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)): """
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 ## 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 ## okay - it seems to to a device node
rdev = os.stat(device).st_rdev rdev = os.stat(device).st_rdev
return (os.major(rdev), os.minor(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: try:
f_blockdev_info = open(blockdev_info_file, "r") f_blockdev_info = open(blockdev_info_file, "r")
blockdev_info = f_blockdev_info.read() blockdev_info = f_blockdev_info.read()
f_blockdev_info.close() f_blockdev_info.close()
(str_major, str_minor) = blockdev_info.split(":") (str_major, str_minor) = blockdev_info.split(":")
"numeric conversion" ## numeric conversion
try: try:
major = int(str_major) major = int(str_major)
minor = int(str_minor) minor = int(str_minor)
return (major, minor) return (major, minor)
except ValueError: except ValueError:
"unknown device numbers -> stop guessing" ## unknown device numbers -> stop guessing
return None return None
except IOError: except IOError:
pass pass
return None return None
def findMajorMinorDeviceName(dir, major, minor): def find_major_minor_device(dirpath, major, minor):
"returns the names of devices with the specified major and minor number" """Returns the names of devices with the specified major and minor number.
"""
collected = [] collected = []
try: 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))] subdirs = [os.path.join(dirpath, e) for e in os.listdir(dirpath)
"do a recursive call to parse the directory tree" 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: for dirs in subdirs:
collected.extend(findMajorMinorDeviceName(dirs, major, minor)) collected.extend(find_major_minor_device(dirs, major, minor))
"filter all device inodes in this directory" ## 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([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 ## remove double entries
result = [] result = []
for e in collected: for item in collected:
if e not in result: result.append(e) if item not in result:
result.append(item)
return result return result
except OSError: except OSError:
return [] return []
def getParentBlockDevices(): def get_parent_blockdevices():
"""Return a list of all block devices that contain other devices.
"""
devs = [] devs = []
for line in file("/proc/partitions"): for line in file("/proc/partitions"):
p_details = line.split() p_details = line.split()
## we expect four values - otherwise continue with next iteration ## 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 (p_major, p_minor, p_size, p_device) = p_details
## we expect numeric values in the first two columns ## 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 ## 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) devs.append(p_device)
return map(getAbsoluteDeviceName, devs) return [ get_absolute_devicename(e) for e in devs ]
def isPartOfBlockDevice(parent, subdevice): def is_part_of_blockdevice(parent, subdevice):
"""check if the given block device is a parent of 'subdevice' """Check if the given block device is a parent of 'subdevice'.
e.g. for checking if a partition belongs to a block device"""
e.g. for checking if a partition belongs to a block device
"""
try: try:
(par_major, par_minor) = findMajorMinorOfDevice(parent) (par_major, par_minor) = find_major_minor_of_device(parent)
(sub_major, sub_minor) = findMajorMinorOfDevice(subdevice) (sub_major, sub_minor) = find_major_minor_of_device(subdevice)
except TypeError: except TypeError:
## at least one of these devices did not return a valid major/minor combination ## at least one of these devices did not return a valid major/minor combination
return False return False
@ -158,7 +190,8 @@ def isPartOfBlockDevice(parent, subdevice):
blpath = os.path.join(root, bldev, 'dev') blpath = os.path.join(root, bldev, 'dev')
if os.access(blpath, os.R_OK): if os.access(blpath, os.R_OK):
try: 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) parent_path = os.path.join(root, bldev)
break break
except (IndexError, OSError): except (IndexError, OSError):
@ -170,7 +203,8 @@ def isPartOfBlockDevice(parent, subdevice):
subblpath = os.path.join(parent_path, subbldev, "dev") subblpath = os.path.join(parent_path, subbldev, "dev")
if os.access(subblpath, os.R_OK): if os.access(subblpath, os.R_OK):
try: 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! ## the name of the subdevice node is not important - we found it!
return True return True
except (IndexError, OSError): except (IndexError, OSError):
@ -178,18 +212,22 @@ def isPartOfBlockDevice(parent, subdevice):
return False return False
def getBlockDeviceSize(device): def get_blockdevice_size(device):
if not device: return -1 """Return the size of a blockdevice.
"""
if not device:
return -1
try: try:
rdev = os.stat(device).st_rdev rdev = os.stat(device).st_rdev
except OSError: except OSError:
return -1 return -1
minor = os.minor(rdev) minor = os.minor(rdev)
major = os.major(rdev) major = os.major(rdev)
for f in file("/proc/partitions"): for line in file("/proc/partitions"):
try: try:
elements = f.split() elements = line.split()
if len(elements) != 4: continue if len(elements) != 4:
continue
if (int(elements[0]) == major) and (int(elements[1]) == minor): if (int(elements[0]) == major) and (int(elements[1]) == minor):
return int(elements[2])/1024 return int(elements[2])/1024
except ValueError: except ValueError:
@ -197,8 +235,10 @@ def getBlockDeviceSize(device):
return -1 return -1
def getBlockDeviceSizeHumanly(device): def get_blockdevice_size_humanly(device):
size = getBlockDeviceSize(device) """Return a human readable size of a blockdevice.
"""
size = get_blockdevice_size(device)
if size > 5120: if size > 5120:
return "%sGB" % size/1024 return "%sGB" % size/1024
else: else:

View file

@ -0,0 +1,7 @@
"""Features may be easily added to the CryptoBox.
"""
__revision__ = "$Id"
__all__ = [ 'base', 'manage' ]

View file

@ -21,161 +21,189 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
"""All features should inherit from this class.
"""
__revision__ = "$Id"
import os import os
import cherrypy import cherrypy
class CryptoBoxPlugin: class CryptoBoxPlugin:
"""The base class of all features.
"""
## default capability is "system" - the other supported capability is: "volume" ## default capability is "system" - the other supported capability is: "volume"
pluginCapabilities = [ "system" ] plugin_capabilities = [ "system" ]
## where should the plugin be visible by default? ## where should the plugin be visible by default?
pluginVisibility = [ "preferences" ] plugin_visibility = [ "preferences" ]
## does this plugin require admin authentification? ## 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) ## default rank (0..100) of the plugin in listings (lower value means higher priority)
rank = 80 rank = 80
## default icon of this plugin (relative path) ## 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) ## 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.cbox = cbox
self.hdf = {} self.hdf = {}
self.pluginDir = pluginDir self.plugin_dir = plugin_dir
self.hdf_prefix = "Data.Plugins.%s." % self.getName() self.hdf_prefix = "Data.Plugins.%s." % self.get_name()
self.site = siteClass self.site = site_class
def doAction(self, **args): def do_action(self, **args):
"""override doAction with your plugin code""" """Override do_action with your plugin code
raise Exception, "undefined action handler ('doAction') in plugin '%'" % self.getName() """
raise Exception, \
"undefined action handler ('do_action') in plugin '%s'" % self.get_name()
def getStatus(self): def get_status(self):
"""you should override this, to supply useful state information""" """you should override this, to supply useful state information
raise Exception, "undefined state handler ('getStatus') in plugin '%'" % self.getName() """
raise Exception, \
"undefined state handler ('get_status') in plugin '%s'" % self.get_name()
def getName(self): def get_name(self):
"""the name of the python file (module) should be the name of the plugin""" """the name of the python file (module) should be the name of the plugin
"""
return self.__module__ return self.__module__
@cherrypy.expose @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 """return the image data of the icon of the plugin
the parameter 'image' may be used for alternative image locations (relative the parameter 'image' may be used for alternative image locations (relative
to the directory of the plugin) to the directory of the plugin)
'**kargs' is necessary, as a 'weblang' attribute may be specified (and ignored)""" '**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)): import re
plugin_icon_file = os.path.join(self.pluginDir, self.defaultIconFileName) 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: 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): 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) 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 """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 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): if os.access(result_file, os.R_OK) and os.path.isfile(result_file):
return result_file return result_file
else: else:
return None 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 import neo_cgi, neo_util
lang_hdf = neo_util.HDF() lang_hdf = neo_util.HDF()
langFile = os.path.join(self.pluginDir, 'language.hdf') lang_file = os.path.join(self.plugin_dir, 'language.hdf')
try: try:
lang_hdf.readFile(langFile) lang_hdf.readFile(lang_file)
except (neo_util.Error, neo_util.ParseError), errMsg: except (neo_util.Error, neo_util.ParseError):
self.cbox.log.error("failed to load language file (%s) of plugin (%s):" % (langFile,self.getName())) self.cbox.log.error("failed to load language file (%s) of plugin (%s):" % \
(lang_file, self.get_name()))
return lang_hdf 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(): for (key, value) in self.hdf.items():
hdf.setValue(key, str(value)) hdf.setValue(key, str(value))
def isAuthRequired(self): def is_auth_required(self):
"""check if this plugin requires authentication """check if this plugin requires authentication
first step: check plugin configuration first step: check plugin configuration
second step: check default value of plugin""" second step: check default value of plugin
"""
try: try:
if self.cbox.prefs.pluginConf[self.getName()]["requestAuth"] is None: if self.cbox.prefs.plugin_conf[self.get_name()]["requestAuth"] is None:
return self.requestAuth return self.request_auth
if self.cbox.prefs.pluginConf[self.getName()]["requestAuth"]: if self.cbox.prefs.plugin_conf[self.get_name()]["requestAuth"]:
return True return True
else: else:
return False return False
except KeyError: except KeyError:
return self.requestAuth return self.request_auth
def isEnabled(self): def is_enabled(self):
"""check if this plugin is enabled """check if this plugin is enabled
first step: check plugin configuration first step: check plugin configuration
second step: check default value of plugin""" second step: check default value of plugin
fallback = bool(self.pluginVisibility) """
fallback = bool(self.plugin_visibility)
try: 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 fallback
return bool(self.cbox.prefs.pluginConf[self.getName()]["visibility"]) return bool(self.cbox.prefs.plugin_conf[self.get_name()]["visibility"])
except KeyError: except KeyError:
return fallback return fallback
def getRank(self): def get_rank(self):
"""check the rank of this plugin """check the rank of this plugin
first step: check plugin configuration first step: check plugin configuration
second step: check default value of plugin""" second step: check default value of plugin
"""
try: 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 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): except (KeyError, TypeError):
return self.rank return self.rank
def getVisibility(self): def get_visibility(self):
"""Check which visibility flags of the feature are set.
"""
try: try:
if self.cbox.prefs.pluginConf[self.getName()]["visibility"] is None: if self.cbox.prefs.plugin_conf[self.get_name()]["visibility"] is None:
return self.pluginVisibility[:] return self.plugin_visibility[:]
return self.cbox.prefs.pluginConf[self.getName()]["visibility"] return self.cbox.prefs.plugin_conf[self.get_name()]["visibility"]
except KeyError: 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 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): if os.access(pl_file, os.R_OK) and os.path.isfile(pl_file):
try: 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: except AttributeError:
pass pass
try: 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: except AttributeError:
pass pass
return None return None

View file

@ -19,46 +19,56 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 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 os
import logging
class PluginManager: class PluginManager:
"""manage available plugins""" """manage available plugins"""
def __init__(self, cbox, plugin_dirs=".", siteClass=None): def __init__(self, cbox, plugin_dirs=".", site_class=None):
self.cbox = cbox self.cbox = cbox
self.log = logging.getLogger("CryptoBox") self.site = site_class
self.site = siteClass
if hasattr(plugin_dirs, "__iter__"): 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: else:
self.plugin_dirs = [os.path.abspath(plugin_dirs)] self.plugin_dirs = [os.path.abspath(plugin_dirs)]
self.pluginList = self.__getAllPlugins() self.plugin_list = self.__get_all_plugins()
def getPlugins(self): def get_plugins(self):
return self.pluginList[:] """Return a list of all feature instances.
"""
return self.plugin_list[:]
def getPlugin(self, name): def get_plugin(self, name):
for p in self.pluginList[:]: """Return the specified feature as an instance.
if p.getName() == name: """
return p for plugin in self.plugin_list[:]:
if plugin.get_name() == name:
return plugin
return None return None
def __getAllPlugins(self): def __get_all_plugins(self):
list = [] """Return all available features as instances.
for plfile in self.__getPluginFiles(): """
list.append(self.__getPluginClass(os.path.basename(plfile)[:-3])) plist = []
return list for plfile in self.__get_plugin_files():
plist.append(self.__get_plugin_class(os.path.basename(plfile)[:-3]))
return plist
def __getPluginClass(self, name): def __get_plugin_class(self, name):
for plfile in self.__getPluginFiles(): """Return a instance object of the give feature.
"""
import imp
for plfile in self.__get_plugin_files():
if name == os.path.basename(plfile)[:-3]: if name == os.path.basename(plfile)[:-3]:
try: try:
pl_class = getattr(imp.load_source(name, plfile), name) pl_class = getattr(imp.load_source(name, plfile), name)
@ -69,18 +79,23 @@ class PluginManager:
return None return None
def __getPluginFiles(self): def __get_plugin_files(self):
"""Retrieve all python files that may potentially be a feature.
"""
result = [] result = []
if self.cbox and self.cbox.prefs["Main"]["DisabledPlugins"]: if self.cbox and self.cbox.prefs["Main"]["DisabledPlugins"]:
disabled = self.cbox.prefs["Main"]["DisabledPlugins"] disabled = self.cbox.prefs["Main"]["DisabledPlugins"]
else: else:
disabled = [] 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 pdir in [os.path.abspath(e) for e in self.plugin_dirs
for plname in [f for f in os.listdir(dir)]: 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 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 continue
pldir = os.path.join(dir, plname) pldir = os.path.join(pdir, plname)
plfile = os.path.join(pldir, plname + ".py") plfile = os.path.join(pldir, plname + ".py")
if os.path.isfile(plfile) and os.access(plfile, os.R_OK): if os.path.isfile(plfile) and os.access(plfile, os.R_OK):
result.append(plfile) result.append(plfile)
@ -88,8 +103,8 @@ class PluginManager:
if __name__ == "__main__": if __name__ == "__main__":
x = PluginManager(None, "../plugins") MANAGER = PluginManager(None, "../plugins")
for a in x.getPlugins(): for one_plugin in MANAGER.get_plugins():
if not a is None: if not one_plugin is None:
print "Plugin: %s" % a.getName() print "Plugin: %s" % one_plugin.get_name()

View file

@ -0,0 +1,7 @@
"""Some unittests for the CryptoBox.
"""
__revision__ = "$Id"
__all__ = [ 'test.cryptobox', 'test.cryptoboxtools', 'test.plugins', 'test.websites' ]

View file

@ -19,33 +19,41 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
"""Some unittests for the core CryptoBox modules.
"""
__revision__ = "$Id"
import unittest import unittest
import sys import sys
import cryptobox.core.main import cryptobox.core.main
from cryptobox.core.exceptions import * from cryptobox.core.exceptions import *
import cryptobox.core.settings import cryptobox.core.settings
import os
class CryptoBoxPropsDeviceTests(unittest.TestCase): class CryptoBoxDeviceTests(unittest.TestCase):
cb = cryptobox.core.main.CryptoBoxProps() """Some unittests for the CryptoBox
"""
cb = cryptobox.core.main.CryptoBox()
def testAllowedDevices(self): def test_allowed_devices(self):
'''isDeviceAllowed should accept permitted devices''' '''is_device_allowed should accept permitted devices'''
self.assertTrue(self.cb.isDeviceAllowed("/dev/loop")) self.assertTrue(self.cb.is_device_allowed("/dev/loop"))
self.assertTrue(self.cb.isDeviceAllowed("/dev/loop1")) self.assertTrue(self.cb.is_device_allowed("/dev/loop1"))
self.assertTrue(self.cb.isDeviceAllowed("/dev/loop/urgd")) self.assertTrue(self.cb.is_device_allowed("/dev/loop/urgd"))
self.assertTrue(self.cb.isDeviceAllowed("/dev/usb/../loop1")) self.assertTrue(self.cb.is_device_allowed("/dev/usb/../loop1"))
def testDeniedDevices(self): def test_denied_devices(self):
'''isDeviceAllowed should fail with not explicitly allowed devices''' '''is_device_allowed should fail with not explicitly allowed devices'''
self.assertFalse(self.cb.isDeviceAllowed("/dev/hda")) self.assertFalse(self.cb.is_device_allowed("/dev/hda"))
self.assertFalse(self.cb.isDeviceAllowed("/dev/loopa/../hda")) self.assertFalse(self.cb.is_device_allowed("/dev/loopa/../hda"))
self.assertFalse(self.cb.isDeviceAllowed("/")) self.assertFalse(self.cb.is_device_allowed("/"))
class CryptoBoxPropsConfigTests(unittest.TestCase): class CryptoBoxConfigTests(unittest.TestCase):
'''test here if everything with the config turns right''' '''test here if everything with the config turns right'''
import os
files = { files = {
"configFileOK" : "cbox-test_ok.conf", "configFileOK" : "cbox-test_ok.conf",
"configFileBroken" : "cbox-test_broken.conf", "configFileBroken" : "cbox-test_broken.conf",
@ -86,71 +94,89 @@ CryptoBoxRootActions = CryptoBoxRootActions
def setUp(self): def setUp(self):
'''generate all files in tmp and remember the names''' '''generate all files in tmp and remember the names'''
import tempfile import tempfile
os = self.os
self.tmpdirname = tempfile.mkdtemp(prefix="cbox-") self.tmpdirname = tempfile.mkdtemp(prefix="cbox-")
for file in self.files.keys(): for tfile in self.files.keys():
self.filenames[file] = os.path.join(self.tmpdirname, self.files[file]) self.filenames[tfile] = os.path.join(self.tmpdirname, self.files[tfile])
self.writeConfig() self.write_config()
def tearDown(self): def tearDown(self):
'''remove the created tmpfiles''' '''remove the created tmpfiles'''
os = self.os
# remove temp files # remove temp files
for file in self.filenames.values(): for tfile in self.filenames.values():
compl_name = os.path.join(self.tmpdirname, file) compl_name = os.path.join(self.tmpdirname, tfile)
if os.path.exists(compl_name): if os.path.exists(compl_name):
os.remove(compl_name) os.remove(compl_name)
# remove temp dir # remove temp dir
os.rmdir(self.tmpdirname) os.rmdir(self.tmpdirname)
def testConfigInit(self): def test_config_init(self):
'''Check various branches of config file loading''' '''Check various branches of config file loading'''
import os self.assertRaises(CBConfigUnavailableError,
self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps,"/invalid/path/to/config/file") cryptobox.core.main.CryptoBox,"/invalid/path/to/config/file")
self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps,"/etc/shadow") self.assertRaises(CBConfigUnavailableError,
""" check one of the following things: cryptobox.core.main.CryptoBox,"/etc/shadow")
1) are we successfully using an existing config file? ## check one of the following things:
2) do we break, if no config file is there? ## 1) are we successfully using an existing config file?
depending on the existence of a config file, only one of these conditions ## 2) do we break, if no config file is there?
can be checked - hints for more comprehensive tests are appreciated :) """ ## depending on the existence of a config file, only one of these conditions
for a in ['cryptobox.conf']: ## can be checked - hints for more comprehensive tests are appreciated :)
if os.path.exists(a): for cfile in ['cryptobox.conf']:
cryptobox.core.main.CryptoBoxProps() if os.path.exists(cfile):
cryptobox.core.main.CryptoBox()
break # this skips the 'else' clause break # this skips the 'else' clause
else: self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps) else:
self.assertRaises(CBConfigUnavailableError, cryptobox.core.main.CryptoBoxProps,[]) self.assertRaises(CBConfigUnavailableError,
cryptobox.core.main.CryptoBox)
self.assertRaises(CBConfigUnavailableError,
cryptobox.core.main.CryptoBox,[])
def testBrokenConfigs(self): def test_broken_configs(self):
"""Check various broken configurations""" """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.write_config("SettingsDir", "SettingsDir=/foo/bar",
self.writeConfig("Level", "Level = ho", filename=self.filenames["configFileBroken"]) filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox,
self.writeConfig("Details", "#out", filename=self.filenames["configFileBroken"]) self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) self.write_config("Level", "Level = ho",
self.writeConfig("super", "super=/bin/invalid/no", filename=self.filenames["configFileBroken"]) filename=self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox,
self.writeConfig("CryptoBoxRootActions", "#not here", filename=self.filenames["configFileBroken"]) self.filenames["configFileBroken"])
self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBoxProps,self.filenames["configFileBroken"]) self.write_config("Details", "#out",
self.writeConfig("CryptoBoxRootActions", "CryptoBoxRootActions = /bin/false", filename=self.filenames["configFileBroken"]) filename=self.filenames["configFileBroken"])
self.assertRaises(CBEnvironmentError, cryptobox.core.main.CryptoBoxProps,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""" """write a config file and (optional) replace a line in it"""
import re import re
if not filename: filename = self.filenames["configFileOK"] if not filename:
content = self.configContentOK % (self.tmpdirname, self.tmpdirname, self.tmpdirname) filename = self.filenames["configFileOK"]
content = self.configContentOK % \
(self.tmpdirname, self.tmpdirname, self.tmpdirname)
if replace: if replace:
pattern = re.compile('^' + replace + '\\s*=.*$', flags=re.M) pattern = re.compile('^' + replace + '\\s*=.*$', flags=re.M)
content = re.sub(pattern, newline, content) content = re.sub(pattern, newline, content)
cf = open(filename, "w") cfile = open(filename, "w")
cf.write(content) cfile.write(content)
cf.close() cfile.close()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -19,9 +19,14 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
"""Unittests for cryptobox.core.tools
"""
__revision__ = "$Id"
import unittest import unittest
import cryptobox.core.tools as cbxTools import cryptobox.core.tools as cbx_tools
import os import os
## use /dev/ubd? if possible - otherwise /dev/hd? ## use /dev/ubd? if possible - otherwise /dev/hd?
@ -36,30 +41,40 @@ else:
class CryptoBoxToolsTests(unittest.TestCase): class CryptoBoxToolsTests(unittest.TestCase):
"""All unittests for cryptoboxtools
"""
def testGetAbsoluteDeviceName(self): def test_get_absolute_devicename(self):
func = cbxTools.getAbsoluteDeviceName """check the get_absolute_devicename function
"""
func = cbx_tools.get_absolute_devicename
self.assertTrue(func(device) == "/dev/%s" % device) self.assertTrue(func(device) == "/dev/%s" % device)
self.assertTrue(func("loop0") == "/dev/loop0") self.assertTrue(func("loop0") == "/dev/loop0")
self.assertTrue(func(os.path.devnull) == os.path.devnull) self.assertTrue(func(os.path.devnull) == os.path.devnull)
def testFindMajorMinorOfDevice(self): def test_find_major_minor_of_device(self):
func = cbxTools.findMajorMinorOfDevice """check the find_major_minor_of_device function
self.assertTrue(func(os.path.devnull) == (1,3)) """
func = cbx_tools.find_major_minor_of_device
self.assertTrue(func(os.path.devnull) == (1, 3))
self.assertTrue(func("/dev/nothere") is None) self.assertTrue(func("/dev/nothere") is None)
def testFindMajorMinorDeviceName(self): def test_find_major_minor_device(self):
func = cbxTools.findMajorMinorDeviceName """check the find_major_minor_device function
dir = os.path.join(os.path.sep, "dev") """
self.assertTrue(os.path.devnull in func(dir,1,3)) func = cbx_tools.find_major_minor_device
self.assertFalse(os.path.devnull in func(dir,2,3)) path = os.path.join(os.path.sep, "dev")
self.assertFalse(None in func(dir,17,23)) 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): def test_is_part_of_blockdevice(self):
func = cbxTools.isPartOfBlockDevice """check the is_part_of_blockdevice function
"""
func = cbx_tools.is_part_of_blockdevice
self.assertTrue(func("/dev/%s" % device, "/dev/%s1" % device)) self.assertTrue(func("/dev/%s" % device, "/dev/%s1" % device))
self.assertFalse(func("/dev/%s" % device, "/dev/%s" % device)) self.assertFalse(func("/dev/%s" % device, "/dev/%s" % device))
self.assertFalse(func("/dev/%s1" % device, "/dev/%s" % device)) self.assertFalse(func("/dev/%s1" % device, "/dev/%s" % device))

View file

@ -19,6 +19,10 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 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 unittest
import cryptobox.plugins.manage import cryptobox.plugins.manage
@ -28,20 +32,21 @@ class CheckForUndefinedTestCases(unittest.TestCase):
def create_testcases(): def create_testcases():
"""Create functions that execute unittests for all features.
plugins = cryptobox.plugins.manage.PluginManager(None, "../plugins").getPlugins() """
plugins = cryptobox.plugins.manage.PluginManager(None, "../plugins").get_plugins()
glob_dict = globals() glob_dict = globals()
loc_dict = locals() loc_dict = locals()
for pl in plugins: for plugin in plugins:
test_class = pl.getTestClass() test_class = plugin.get_test_class()
if test_class: if test_class:
## add the testclass to the global dictionary ## add the testclass to the global dictionary
glob_dict["unittest" + pl.getName()] = test_class glob_dict["unittest" + plugin.get_name()] = test_class
else: else:
subname = "test_existence_%s" % pl.getName() subname = "test_existence_%s" % plugin.get_name()
def test_existence(self): def test_existence(self):
"""check if the plugin (%s) contains tests""" % pl.getName() """check if the plugin (%s) contains tests""" % plugin.get_name()
self.fail("no tests defined for plugin: %s" % pl.getName()) self.fail("no tests defined for plugin: %s" % plugin.get_name())
## add this function to the class above ## add this function to the class above
setattr(CheckForUndefinedTestCases, subname, test_existence) setattr(CheckForUndefinedTestCases, subname, test_existence)
#FIXME: the failure output always contains the same name for all plugins #FIXME: the failure output always contains the same name for all plugins

View file

@ -19,6 +19,12 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 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 import unittest
@ -32,28 +38,32 @@ import cryptobox.web.testclass
class WebServer(cryptobox.web.testclass.WebInterfaceTestClass): class WebServer(cryptobox.web.testclass.WebInterfaceTestClass):
"""Basic tests for the webserver.
"""
def test_is_server_running(self): def test_is_server_running(self):
'''the server should run under given name and port''' '''the server should run under given name and port'''
self.register_auth(self.URL) self.register_auth(self.url)
self.cmd.go(self.URL) self.cmd.go(self.url)
self.cmd.find("CBOX-STATUS") self.cmd.find("CBOX-STATUS")
## other URLs must not be checked, as we do not know, if they are valid ## other URLs must not be checked, as we do not know, if they are valid
class BuiltinPages(cryptobox.web.testclass.WebInterfaceTestClass): class BuiltinPages(cryptobox.web.testclass.WebInterfaceTestClass):
"""Basic test of builtin pages (no features).
"""
def test_goto_index(self): def test_goto_index(self):
'''display all devices''' '''display all devices'''
self.register_auth(self.URL) self.register_auth(self.url)
self.cmd.go(self.URL) self.cmd.go(self.url)
self.cmd.find("The CryptoBox") 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.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.find("Privatnost v vsako vas")
self.cmd.go(self.URL + "?weblang=fr") self.cmd.go(self.url + "?weblang=fr")
self.cmd.find("La CryptoBox") self.cmd.find("La CryptoBox")

View file

@ -0,0 +1,7 @@
"""The webinterface of the CryptoBox.
"""
__revision__ = "$Id"
__all__ = [ 'dataset', 'languages', 'sites', 'testclass' ]

View file

@ -18,8 +18,12 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 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 import os
from cryptobox.core.exceptions import *
import cryptobox.core.container as cbxContainer import cryptobox.core.container as cbxContainer
import cryptobox.core.tools as cbxTools import cryptobox.core.tools as cbxTools
@ -30,20 +34,24 @@ class WebInterfaceDataset(dict):
""" """
def __init__(self, cbox, prefs, plugins): def __init__(self, cbox, prefs, plugins):
super(WebInterfaceDataset, self).__init__()
self.prefs = prefs self.prefs = prefs
self.cbox = cbox self.cbox = cbox
self.__setConfigValues() self.__set_config_values()
self.plugins = plugins self.plugins = plugins
self.setCryptoBoxState() self.set_crypto_box_state()
self.setPluginData() self.set_plugin_data()
self.setContainersState() 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 cherrypy
import cryptobox.core.main import cryptobox.core.main
import cryptobox.web.languages 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 = self.cbox.prefs["WebSettings"]["Languages"][:]
langs.sort() langs.sort()
for (index, lang) in enumerate(langs): for (index, lang) in enumerate(langs):
@ -58,10 +66,11 @@ class WebInterfaceDataset(dict):
try: try:
self["Data.ScriptURL.Prot"] = cherrypy.request.scheme self["Data.ScriptURL.Prot"] = cherrypy.request.scheme
host = cherrypy.request.headers["Host"] host = cherrypy.request.headers["Host"]
self["Data.ScriptURL.Host"] = host.split(":",1)[0] self["Data.ScriptURL.Host"] = host.split(":", 1)[0]
complete_url = "%s://%s" % (self["Data.ScriptURL.Prot"], self["Data.ScriptURL.Host"]) complete_url = "%s://%s" % \
(self["Data.ScriptURL.Prot"], self["Data.ScriptURL.Host"])
try: try:
port = int(host.split(":",1)[1]) port = int(host.split(":", 1)[1])
complete_url += ":%s" % port complete_url += ":%s" % port
except (IndexError, ValueError): except (IndexError, ValueError):
if cherrypy.request.scheme == "http": if cherrypy.request.scheme == "http":
@ -70,7 +79,8 @@ class WebInterfaceDataset(dict):
port = 443 port = 443
else: else:
## unknown scheme -> port 0 ## 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 port = 0
self["Data.ScriptURL.Port"] = port self["Data.ScriptURL.Port"] = port
## retrieve the relative address of the CGI (or the cherrypy base address) ## retrieve the relative address of the CGI (or the cherrypy base address)
@ -83,20 +93,25 @@ class WebInterfaceDataset(dict):
self["Data.ScriptURL"] = "" self["Data.ScriptURL"] = ""
def setCurrentDiskState(self, device): def set_current_disk_state(self, device):
for container in self.cbox.getContainerList(): """Set some hdf values according to the currently active disk.
if container.getDevice() == device: """
isEncrypted = (container.getType() == cbxContainer.ContainerTypes["luks"]) and 1 or 0 for container in self.cbox.get_container_list():
isPlain = (container.getType() == cbxContainer.ContainerTypes["plain"]) and 1 or 0 if container.get_device() == device:
isMounted = container.isMounted() and 1 or 0 is_encrypted = (container.get_type() == \
self["Data.CurrentDisk.device"] = container.getDevice() cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0
self["Data.CurrentDisk.name"] = container.getName() is_plain = (container.get_type() == \
self["Data.CurrentDisk.encryption"] = isEncrypted cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0
self["Data.CurrentDisk.plaintext"] = isPlain is_mounted = container.is_mounted() and 1 or 0
self["Data.CurrentDisk.active"] = isMounted self["Data.CurrentDisk.device"] = container.get_device()
self["Data.CurrentDisk.size"] = cbxTools.getBlockDeviceSizeHumanly(container.getDevice()) self["Data.CurrentDisk.name"] = container.get_name()
if isMounted: self["Data.CurrentDisk.encryption"] = is_encrypted
(size, avail, used) = container.getCapacity() 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 percent = used / size
self["Data.CurrentDisk.capacity.used"] = used self["Data.CurrentDisk.capacity.used"] = used
self["Data.CurrentDisk.capacity.free"] = avail self["Data.CurrentDisk.capacity.free"] = avail
@ -105,51 +120,63 @@ class WebInterfaceDataset(dict):
self["Settings.LinkAttrs.device"] = device 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 avail_counter = 0
active_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 ## useful if the container was changed during an action
container.resetObject() container.reset_object()
isEncrypted = (container.getType() == cbxContainer.ContainerTypes["luks"]) and 1 or 0 is_encrypted = (container.get_type() == \
isPlain = (container.getType() == cbxContainer.ContainerTypes["plain"]) and 1 or 0 cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0
isMounted = container.isMounted() and 1 or 0 is_plain = (container.get_type() == \
self["Data.Disks.%d.device" % avail_counter] = container.getDevice() cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0
self["Data.Disks.%d.name" % avail_counter] = container.getName() is_mounted = container.is_mounted() and 1 or 0
self["Data.Disks.%d.encryption" % avail_counter] = isEncrypted self["Data.Disks.%d.device" % avail_counter] = container.get_device()
self["Data.Disks.%d.plaintext" % avail_counter] = isPlain self["Data.Disks.%d.name" % avail_counter] = container.get_name()
self["Data.Disks.%d.active" % avail_counter] = isMounted self["Data.Disks.%d.encryption" % avail_counter] = is_encrypted
self["Data.Disks.%d.size" % avail_counter] = cbxTools.getBlockDeviceSizeHumanly(container.getDevice()) self["Data.Disks.%d.plaintext" % avail_counter] = is_plain
if isMounted: active_counter += 1 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 avail_counter += 1
self["Data.activeDisksCount"] = active_counter self["Data.activeDisksCount"] = active_counter
def setPluginData(self): def set_plugin_data(self):
for p in self.plugins: """Set some hdf values according to the available features.
entryName = "Settings.PluginList." + p.getName() """
for plugin in self.plugins:
entry_name = "Settings.PluginList." + plugin.get_name()
## first: remove all existing settings of this plugin ## first: remove all existing settings of this plugin
for key in self.keys(): for key in self.keys():
if key.startswith(entryName): del self[key] if key.startswith(entry_name):
lang_data = p.getLanguageData() del self[key]
self[entryName] = p.getName() lang_data = plugin.get_language_data()
self[entryName + ".Name"] = lang_data.getValue("Name", p.getName()) self[entry_name] = plugin.get_name()
self[entryName + ".Link"] = lang_data.getValue("Link", p.getName()) self[entry_name + ".Name"] = lang_data.getValue("Name", plugin.get_name())
self[entryName + ".Rank"] = p.getRank() self[entry_name + ".Link"] = lang_data.getValue("Link", plugin.get_name())
self[entryName + ".RequestAuth"] = p.isAuthRequired() and "1" or "0" self[entry_name + ".Rank"] = plugin.get_rank()
for a in p.pluginCapabilities: self[entry_name + ".RequestAuth"] = plugin.is_auth_required() and "1" or "0"
self[entryName + ".Types." + a] = "1" for capy in plugin.plugin_capabilities:
for a in p.getVisibility(): self[entry_name + ".Types." + capy] = "1"
self[entryName + ".Visible." + a] = "1" for visi in plugin.get_visibility():
self[entry_name + ".Visible." + visi] = "1"
def __setConfigValues(self): def __set_config_values(self):
self["Settings.TemplateDir"] = os.path.abspath(self.prefs["Locations"]["TemplateDir"]) """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.DocDir"] = os.path.abspath(self.prefs["Locations"]["DocDir"])
self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"] self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"]
self["Settings.Language"] = self.prefs["WebSettings"]["Languages"][0] self["Settings.Language"] = self.prefs["WebSettings"]["Languages"][0]
for num,d in enumerate(self.prefs["Locations"]["PluginDir"]): for (num, dpath) in enumerate(self.prefs["Locations"]["PluginDir"]):
self["Settings.PluginDir.%d" % num] = d self["Settings.PluginDir.%d" % num] = dpath
## store the first directory in this settings variable - backward compatibility ## store the first directory in this settings variable - backward compatibility
self["Settings.PluginDir"] = self.prefs["Locations"]["PluginDir"][0] self["Settings.PluginDir"] = self.prefs["Locations"]["PluginDir"][0]
self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"] self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"]

View file

@ -22,23 +22,28 @@
"""supply information about existing languages """supply information about existing languages
""" """
__revision__ = "$Id"
## every language information should contain (name, pluralformat) ## every language information should contain (name, pluralformat)
LANGUAGE_INFO = { LANGUAGE_INFO = {
"cs": ('Český', ('3', '(n==1) ? 0 : (n>=2 && n< =4) ? 1 : 2')), "cs": ('Český', ('3', '(n==1) ? 0 : (n>=2 && n< =4) ? 1 : 2')),
"da": ('Dansk', ('2', '(n != 1)')), "da": ('Dansk', ('2', '(n != 1)')),
"de": ('Deutsch', ('2', '(n != 1)')), "de": ('Deutsch', ('2', '(n != 1)')),
"en": ('English', ('2', '(n != 1)')), "en": ('English', ('2', '(n != 1)')),
"es": ('Español', ('2', '(n != 1)')), "es": ('Español', ('2', '(n != 1)')),
"fi": ('Suomi', ('2', '(n != 1)')), "fi": ('Suomi', ('2', '(n != 1)')),
"fr": ('Français', ('2', '(n != 1)')), "fr": ('Français', ('2', '(n != 1)')),
"hu": ('Magyar', ('1', '0')), "hu": ('Magyar', ('1', '0')),
"it": ('Italiano', ('2', '(n != 1)')), "it": ('Italiano', ('2', '(n != 1)')),
"ja": ('日本語', ('1', '0')), "ja": ('日本語', ('1', '0')),
"nl": ('Nederlands', ('2', '(n != 1)')), "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)')), "pl": ('Polski', ('3', '(n==1 ? 0 : n%10>=2 && n%10< =4 '
"pt": ('Português', ('2', '(n != 1)')), + '&& (n%100<10 || n%100>=20) ? 1 : 2)')),
"ru": ('Русский', ('3', '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10< =4 && (n%100<10 || n%100>=20) ? 1 : 2)')), "pt": ('Português', ('2', '(n != 1)')),
"sl": ('Slovensko', ('4', '(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3)')), "ru": ('Русский', ('3', '(n%10==1 && n%100!=11 ? 0 : '
"sv": ('Svenska', ('2', '(n != 1)')), + '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)')),
}

View file

@ -17,103 +17,150 @@
# along with the CryptoBox; if not, write to the Free Software # along with the CryptoBox; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 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.core.main
import cryptobox.web.dataset import cryptobox.web.dataset
import cryptobox.plugins.manage import cryptobox.plugins.manage
from cryptobox.core.exceptions import * import cryptobox.core.exceptions
import re import re
import cherrypy import cherrypy
import types
import os import os
import sys
try: try:
import neo_cgi, neo_util, neo_cs import neo_cgi, neo_util, neo_cs
except ImportError: except ImportError:
errorMsg = "Could not import clearsilver module. Try 'apt-get install python-clearsilver'." _ERRMSG = "Could not import clearsilver module. \
self.log.error(errorMsg) Try 'apt-get install python-clearsilver'."
sys.stderr.write(errorMsg) sys.stderr.write(_ERRMSG)
raise ImportError, errorMsg raise ImportError, _ERRMSG
GETTEXT_DOMAIN = 'cryptobox-server' GETTEXT_DOMAIN = 'cryptobox-server'
class PluginIconHandler: class PluginIconHandler:
"""deliver the icons of available plugins via cherrypy"""
def __init__(self, plugins): def __init__(self, plugins):
for plugin in plugins.getPlugins(): for plugin in plugins.get_plugins():
if not plugin: continue if not plugin:
plname = plugin.getName() continue
## expose the getIcon function of this plugin plname = plugin.get_name()
setattr(self, plname, plugin.getIcon) ## expose the get_icon function of this plugin
setattr(self, plname, plugin.get_icon)
class WebInterfaceSites: 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 ## this template is used under strange circumstances
defaultTemplate = "empty" defaultTemplate = "empty"
def __init__(self, conf_file=None): def __init__(self, conf_file=None):
import logging,sys ## we should only use variables preceded by "__" to avoid name conflicts
self.cbox = cryptobox.core.main.CryptoBoxProps(conf_file) ## when loading features
self.log = logging.getLogger("CryptoBox") self.cbox = cryptobox.core.main.CryptoBox(conf_file)
self.prefs = self.cbox.prefs self.__cached_language_data = None
self.__resetDataset() self.__dataset = None
self.icons = None
self.__plugin_manager = None
self.__reset_dataset()
## store the original http error handler ## 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 ## 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 """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: only at the beginning of an action (to not loose information)
important: for _every_ "site" action (cherrypy is stateful) important: for _every_ "site" action (cherrypy is stateful)
also take care for the plugins, as they also contain datasets also take care for the plugins, as they also contain datasets
""" """
self.__loadPlugins() self.__load_plugins()
self.dataset = cryptobox.web.dataset.WebInterfaceDataset(self.cbox, self.prefs, self.pluginList.getPlugins()) self.__dataset = cryptobox.web.dataset.WebInterfaceDataset(
self.cbox, self.cbox.prefs, self.__plugin_manager.get_plugins())
## publish plugin icons ## publish plugin icons
self.icons = PluginIconHandler(self.pluginList) self.icons = PluginIconHandler(self.__plugin_manager)
self.icons.exposed = True self.icons.exposed = True
## check, if a configuration partition has become available ## check, if a configuration partition has become available
self.cbox.prefs.preparePartition() self.cbox.prefs.prepare_partition()
def __loadPlugins(self): def __load_plugins(self):
self.pluginList = cryptobox.plugins.manage.PluginManager(self.cbox, self.prefs["Locations"]["PluginDir"], self) """reinitialize the list of available plugins
for plugin in self.pluginList.getPlugins():
if not plugin: continue this includes the following:
plname = plugin.getName() - reload all plugins and check their state (disabled or not)
if plugin.isEnabled(): - reinitilize the datasets of all plugins
self.cbox.log.info("Plugin '%s' loaded" % plname) """
## this should be the "easiest" way to expose all plugins as URLs self.__plugin_manager = cryptobox.plugins.manage.PluginManager(
setattr(self, plname, self.return_plugin_action(plugin)) self.cbox, self.cbox.prefs["Locations"]["PluginDir"], self)
setattr(getattr(self, plname), "exposed", True) for plugin in self.__plugin_manager.get_plugins():
# TODO: check, if this really works - for now the "stream_response" feature seems to be broken if not plugin:
#setattr(getattr(self, plname), "stream_respones", True) continue
else: plname = plugin.get_name()
self.cbox.log.info("Plugin '%s' is disabled" % plname) ## check if there are name conflicts: e.g. a local variable has the
## remove the plugin, if it was active before ## same name as a plugin to be loaded -> skip these plugins
setattr(self, plname, None) ## 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 ## sub pages requiring authentication may not be defined above
## it has to be defined before any page definition requiring authentification def __request_auth(self=None):
def __requestAuth(self=None): """ this is a function decorator to check authentication
"""
def check_credentials(site): def check_credentials(site):
""" see description of _inner_wrapper - please simplify this!
"""
def _inner_wrapper(self, *args, **kargs): 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 import base64
## define a "non-allowed" function ## define a "non-allowed" function
user, password = None, None user, password = None, None
try: try:
resp = cherrypy.request.headers["Authorization"][6:] # ignore "Basic " ## ignore the "Basic " (first six letters) part
(user, password) = base64.b64decode(resp).split(":",1) resp = cherrypy.request.headers["Authorization"][6:]
(user, password) = base64.b64decode(resp).split(":", 1)
except KeyError: except KeyError:
## no "authorization" header was sent ## no "authorization" header was sent
pass pass
@ -123,18 +170,20 @@ class WebInterfaceSites:
except AttributeError: except AttributeError:
## no cherrypy response header defined ## no cherrypy response header defined
pass pass
authDict = self.cbox.prefs.userDB["admins"] auth_dict = self.cbox.prefs.user_db["admins"]
if user in authDict.keys(): if user in auth_dict.keys():
if self.cbox.prefs.userDB.getDigest(password) == authDict[user]: if self.cbox.prefs.user_db.get_digest(password) == auth_dict[user]:
## ok: return the choosen page ## ok: return the choosen page
self.cbox.log.info("access granted for: %s" % user) self.cbox.log.info("access granted for: %s" % user)
return site(self, *args, **kargs) return site(self, *args, **kargs)
else: else:
self.cbox.log.info("wrong password supplied for: %s" % user) self.cbox.log.info(
"wrong password supplied for: %s" % user)
else: else:
self.cbox.log.info("unknown user: %s" % str(user)) self.cbox.log.info("unknown user: %s" % str(user))
## wrong credentials: return "access denied" ## 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 cherrypy.response.status = 401
return self.__render("access_denied") return self.__render("access_denied")
return _inner_wrapper return _inner_wrapper
@ -147,15 +196,18 @@ class WebInterfaceSites:
@cherrypy.expose @cherrypy.expose
def index(self, weblang=""): def index(self, weblang=""):
self.__resetDataset() """the default page on startup - we show the list of available disks
self.__setWebLang(weblang) """
self.__checkEnvironment() self.__reset_dataset()
self.__set_web_lang(weblang)
self.__check_environment()
## do not forget the language! ## do not forget the language!
param_dict = {"weblang":weblang} param_dict = {"weblang":weblang}
## render "disks" plugin by default ## 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 """handle http errors gracefully
404 - not found errors: ignored if url is below /cryptobox-misc/ 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 500 - runtime errors: return "ok" exit code and show a polite excuse
others: are there any other possible http errors? others: are there any other possible http errors?
""" """
import traceback, sys import traceback
## we ignore uninteresting not-found errors ## 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.startswith("/cryptobox-misc/") or \
cherrypy.request.path in ['/robots.txt','/favicon.ico']): cherrypy.request.path in ['/robots.txt','/favicon.ico']):
cherrypy.response.status = errorCode cherrypy.response.status = error_code
return return
## an invalid action was requested ## an invalid action was requested
if errorCode == 404: if error_code == 404:
## we send a not-found error (with the usual interface) ## we send a not-found error (with the usual interface)
cherrypy.response.status = errorCode cherrypy.response.status = error_code
self.dataset["Data.Warning"] = "InvalidAction" self.__dataset["Data.Warning"] = "InvalidAction"
cherrypy.response.body = self.__render("empty") cherrypy.response.body = self.__render("empty")
return return
## are there still bugs in the code? ## are there still bugs in the code?
if errorCode == 500: if error_code == 500:
## we fix the error code (200 is "OK") ## we fix the error code (200 is "OK")
cherrypy.response.status = 200 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 ## add a traceback and exception information to the lo
for a in traceback.format_exception(*sys.exc_info()): for log_line in traceback.format_exception(*sys.exc_info()):
self.cbox.log.error("\t%s" % a) self.cbox.log.error("\t%s" % log_line)
self.dataset["Data.Warning"] = "RuntimeError" self.__dataset["Data.Warning"] = "RuntimeError"
cherrypy.response.body = self.__render("empty") cherrypy.response.body = self.__render("empty")
return return
## unknown error type ## unknown error type
cherrypy.response.status = errorCode cherrypy.response.status = error_code
self.cbox.log.warn("HTTP-ERROR[%d] - an unknown error occoured: %s" % (errorCode, message)) self.cbox.log.warn("HTTP-ERROR[%d] - an unknown error occoured: %s" \
% (error_code, message))
cherrypy.response.body = self.__render("empty") cherrypy.response.body = self.__render("empty")
def return_plugin_action(self, plugin): def return_plugin_action(self, plugin):
""" returns a function that is suitable for handling a cherrypy
page request
"""
def handler(self, **args): def handler(self, **args):
self.__resetDataset() """this function handles a cherrypy page request
self.__checkEnvironment() """
args_orig = dict(args) self.__reset_dataset()
self.__check_environment()
## set web interface language ## set web interface language
try: try:
self.__setWebLang(args["weblang"]) self.__set_web_lang(args["weblang"])
del args["weblang"] del args["weblang"]
except KeyError: except KeyError:
self.__setWebLang("") self.__set_web_lang("")
## we always read the "device" setting - otherwise volume-plugin links ## we always read the "device" setting - otherwise volume-plugin
## would not work easily (see "volume_props" linking to "volume_format_fs") ## links would not work easily
## (see "volume_props" linking to "volume_format_fs")
## it will get ignored for non-volume plugins ## it will get ignored for non-volume plugins
try: try:
plugin.device = None plugin.device = None
if self.__setDevice(args["device"]): if self.__set_device(args["device"]):
plugin.device = args["device"] plugin.device = args["device"]
del args["device"] del args["device"]
except KeyError: except KeyError:
pass plugin.device = None
## check the device argument of volume plugins ## 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 ## initialize the dataset of the selected device if necessary
if plugin.device: if plugin.device:
self.dataset.setCurrentDiskState(plugin.device) self.__dataset.set_current_disk_state(plugin.device)
else: else:
## invalid (or missing) device setting ## invalid (or missing) device setting
return self.__render(self.defaultTemplate) return self.__render(self.defaultTemplate)
## check if there is a "redirect" setting - this will override the return ## check if there is a "redirect" setting - this will override
## value of the doAction function (e.g. useful for umount-before-format) ## the return value of the do_action function
## (e.g. useful for umount-before-format)
try: try:
if args["redirect"]: if args["redirect"]:
override_nextTemplate = { "plugin":args["redirect"] } override_next_template = { "plugin":args["redirect"] }
if "volume" in plugin.pluginCapabilities: if "volume" in plugin.plugin_capabilities:
override_nextTemplate["values"] = {"device":plugin.device} override_next_template["values"] = {"device":plugin.device}
del args["redirect"] del args["redirect"]
except KeyError: except KeyError:
override_nextTemplate = None override_next_template = None
## check for information to be kept after the last call ## check for information to be kept after the last call
try: try:
keep_values = args["message_keep"] keep_values = args["message_keep"]
del args["message_keep"] del args["message_keep"]
for key, value in keep_values["dataset"].items(): for key, value in keep_values["dataset"].items():
self.dataset[key] = value self.__dataset[key] = value
except KeyError: except KeyError:
keep_values = None keep_values = None
## call the plugin handler ## call the plugin handler
nextTemplate = plugin.doAction(**args) next_template = plugin.do_action(**args)
## for 'volume' plugins: reread the dataset of the current disk ## for 'volume' plugins: reread the dataset of the current disk
## additionally: set the default template for plugins ## 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? ## maybe the state of the current volume was changed?
self.dataset.setCurrentDiskState(plugin.device) self.__dataset.set_current_disk_state(plugin.device)
if not nextTemplate: nextTemplate = { "plugin":"volume_mount", "values":{"device":plugin.device}} if not next_template:
next_template = { "plugin":"volume_mount",
"values":{"device":plugin.device}}
else: else:
## maybe a non-volume plugin changed some plugin settings (e.g. plugin_manager) ## some non-volume plugins change the internal state of other
self.dataset.setPluginData() ## plugins - e.g.: plugin_manager
## update the container hdf-dataset (maybe a plugin changed the state of a container) self.__dataset.set_plugin_data()
self.dataset.setContainersState() ## 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 ## 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? ## was a redirect requested?
if override_nextTemplate: if override_next_template:
nextTemplate = override_nextTemplate next_template = override_next_template
## if another plugins was choosen for 'nextTemplate', then do it! ## if another plugins was choosen for 'next_template', then do it!
if isinstance(nextTemplate, types.DictType) \ if isinstance(next_template, dict) \
and "plugin" in nextTemplate.keys() \ and "plugin" in next_template.keys() \
and "values" in nextTemplate.keys() \ and "values" in next_template.keys() \
and self.pluginList.getPlugin(nextTemplate["plugin"]): and self.__plugin_manager.get_plugin(next_template["plugin"]):
valueDict = dict(nextTemplate["values"]) value_dict = dict(next_template["values"])
## force the current weblang attribute - otherwise it gets lost ## 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 ## check for warnings/success messages, that should be kept
if "Data.Warning" in plugin.hdf.keys() \ if "Data.Success" in plugin.hdf.keys() \
or "Data.Success" in plugin.hdf.keys(): or "Data.Warning" in plugin.hdf.keys():
self.cbox.log.info("keep warning message") value_dict["message_keep"] = {"plugin":plugin, "dataset":{}}
valueDict["message_keep"] = { "plugin":plugin, "dataset":{}}
for keep_key in ("Data.Warning", "Data.Success"): for keep_key in ("Data.Warning", "Data.Success"):
if keep_key in plugin.hdf.keys(): if keep_key in plugin.hdf.keys():
valueDict["message_keep"]["dataset"][keep_key] = plugin.hdf[keep_key] self.cbox.log.info("keeping message: %s" % \
new_plugin = self.pluginList.getPlugin(nextTemplate["plugin"]) plugin.hdf[keep_key])
return self.return_plugin_action(new_plugin)(**valueDict) 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 ## save the currently active plugin name
self.dataset["Data.ActivePlugin"] = plugin.getName() self.__dataset["Data.ActivePlugin"] = plugin.get_name()
return self.__render(nextTemplate, plugin) return self.__render(next_template, plugin)
## apply authentication? ## apply authentication?
if plugin.isAuthRequired(): if plugin.is_auth_required():
return lambda **args: self.__requestAuth()(handler)(self, **args) return lambda **args: self.__request_auth()(handler)(self, **args)
else: else:
return lambda **args: handler(self, **args) return lambda **args: handler(self, **args)
## test authentication
@cherrypy.expose @cherrypy.expose
@__requestAuth @__request_auth
def test(self, weblang=""): def test(self, weblang=""):
self.__resetDataset() """test authentication - this function may be safely removed
self.__setWebLang(weblang) """
self.__checkEnvironment() self.__reset_dataset()
self.__set_web_lang(weblang)
self.__check_environment()
return "test passed" return "test passed"
@cherrypy.expose @cherrypy.expose
def test_stream(self): def test_stream(self):
"""just for testing purposes - to check if the "stream_response" feature """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 import time
yield "<html><head><title>neu</title></head><body><p><ul>" yield "<html><head><title>neu</title></head><body><p><ul>"
for a in range(10): for num in range(10):
yield "<li>yes: %d - %s</li>" % (a, str(time.time())) yield "<li>yes: %d - %s</li>" % (num, str(time.time()))
time.sleep(1) time.sleep(1)
yield "</ul></p></html>" yield "</ul></p></html>"
@ -312,69 +380,77 @@ class WebInterfaceSites:
##################### input checker ########################## ##################### input checker ##########################
def __checkEnvironment(self): def __check_environment(self):
"""here we should place all interesting checks to inform the user of problems """inform the user of suspicious environmental problems
examples are: non-https, readonly-config, ... examples are: non-https, readonly-config, ...
""" """
## this check is done _after_ "resetDataSet" -> a possible config partition was ## this check is done _after_ "reset_dataset" -> if there is
## loaded before ## a config partition, then it was loaded before
if self.cbox.prefs.requiresPartition() and not self.cbox.prefs.getActivePartition(): if self.cbox.prefs.requires_partition() \
self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig" and not self.cbox.prefs.get_active_partition():
# TODO: turn this on soon (add "not") - for now it is annoying self.__dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig"
if self.__checkHTTPS(): #TODO: turn this on soon (add "not") - for now it is annoying
self.dataset["Data.EnvironmentWarning"] = "NoSSL" if self.__check_https():
self.__dataset["Data.EnvironmentWarning"] = "NoSSL"
def __checkHTTPS(self): def __check_https(self):
## check the request scheme """check the request scheme
if cherrypy.request.scheme == "https": return True """
if cherrypy.request.scheme == "https":
return True
## check an environment setting - this is quite common behind proxies ## check an environment setting - this is quite common behind proxies
try: try:
if os.environ["HTTPS"]: return True if os.environ["HTTPS"]:
return True
except KeyError: except KeyError:
pass ## check http header for ssl information
## check http header TODO (check pound for the name) #TODO: (check pound for the name)
try: try:
if cherrypy.request.headers["TODO"]: return True if cherrypy.request.headers["XXX"]:
except KeyError: return True
pass except KeyError:
## the connection seems to be unencrypted ## the connection seems to be unencrypted
return False return False
def __setWebLang(self, value): def __set_web_lang(self, value):
"""set the preferred priority of languages according to the following order: """set the preferred priority of languages according to this order:
1. language selected via web interface 1. language selected via web interface
2. preferred browser language setting 2. preferred browser language setting
3. languages defined in the config file 3. languages defined in the config file
""" """
## start with the configured language order ## start with the configured language order
langOrder = self.cbox.prefs["WebSettings"]["Languages"][:] lang_order = self.cbox.prefs["WebSettings"]["Languages"][:]
self.cbox.log.debug("updating language preferences (default: %s)" % str(langOrder)) self.cbox.log.debug(
"updating language preferences (default: %s)" % str(lang_order))
## put the preferred browser language in front ## put the preferred browser language in front
guess = self.__getPreferredBrowserLanguage(langOrder) guess = self.__get_browser_language(lang_order)
if guess: if guess:
langOrder.remove(guess) lang_order.remove(guess)
langOrder.insert(0,guess) lang_order.insert(0, guess)
self.cbox.log.debug("raised priority of preferred browser language: %s" % 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 ## 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)): if value and (value in lang_order) and (not re.search(u'\W', value)):
langOrder.remove(value) lang_order.remove(value)
langOrder.insert(0,value) lang_order.insert(0, value)
self.cbox.log.debug("raised priority of selected language: %s" % value) self.cbox.log.debug(
"raised priority of selected language: %s" % value)
elif value: elif value:
self.cbox.log.info("invalid language selected: %s" % value) self.cbox.log.info("invalid language selected: %s" % value)
## store current language setting ## store current language setting
self.cbox.log.debug("current language preference: %s" % str(langOrder)) self.cbox.log.debug(
self.langOrder = langOrder "current language preference: %s" % str(lang_order))
self.dataset["Settings.Language"] = langOrder[0] self.lang_order = lang_order
self.dataset["Settings.LinkAttrs.weblang"] = langOrder[0] 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) """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: try:
pref_lang_header = cherrypy.request.headers["Accept-Language"] pref_lang_header = cherrypy.request.headers["Accept-Language"]
@ -382,56 +458,64 @@ class WebInterfaceSites:
## no language header was specified ## no language header was specified
return None return None
## this could be a typical 'Accept-Language' header: ## 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\.]+)?$") 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(",") for e in pref_lang_header.split(",")
if regex.match(e)] if regex.match(e)]
## is one of these preferred languages available? ## is one of these preferred languages available?
for lang in pref_langs: 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 ... ## we try to be nice: also look for "de" if "de-de" was specified ...
for lang in pref_langs: for lang in pref_langs:
## use only the first part of the language ## use only the first part of the language
short_lang = lang.split("-",1)[0] short_lang = lang.split("-", 1)[0]
if short_lang in availLangs: return short_lang if short_lang in avail_langs:
return short_lang
## we give up ## we give up
return None return None
def __setDevice(self, device): def __set_device(self, device):
"""check a device name that was chosen via the web interface """check a device name that was chosen via the web interface
issue a warning if the device is invalid""" issue a warning if the device is invalid"""
if device and re.match(u'[\w /\-]+$', device) and self.cbox.getContainer(device): if device and re.match(u'[\w /\-]+$', device) \
self.log.debug("select device: %s" % device) and self.cbox.get_container(device):
self.cbox.log.debug("select device: %s" % device)
return True return True
else: else:
self.log.warn("invalid device: %s" % device) self.cbox.log.warn("invalid device: %s" % device)
self.dataset["Data.Warning"] = "InvalidDevice" self.__dataset["Data.Warning"] = "InvalidDevice"
return False 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 """substitute all texts in the hdf dataset with their translated
counterparts as returned by gettext counterparts as returned by gettext
""" """
import gettext import gettext
try: try:
translator = gettext.translation(textDomain, languages=languages) translator = gettext.translation(text_domain, languages=languages)
except IOError, errMsg: except IOError, err_msg:
## no translation found ## 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 return hdf
def walk_tree(hdf_node): def walk_tree(hdf_node):
"""iterate through all nodes"""
def translate_node(node): def translate_node(node):
for (key,value) in node.attrs(): """turn one single string into unicode"""
if key == 'LINK': return for (key, value) in node.attrs():
if key == "LINK":
return
try: try:
node.setValue("",translator.ugettext(node.value())) node.setValue("", translator.ugettext(node.value()))
except UnicodeEncodeError, errMsg: except UnicodeEncodeError, err_msg:
self.cbox.log.info("Failed unicode encoding for gettext: %s - %s" % (node.value(),errMsg)) self.cbox.log.info(
"Failed unicode encoding for gettext: %s - %s" \
% (node.value(),err_msg))
## fallback to default encoding ## fallback to default encoding
node.setValue("",translator.gettext(node.value())) node.setValue("", translator.gettext(node.value()))
while hdf_node: while hdf_node:
translate_node(hdf_node) translate_node(hdf_node)
walk_tree(hdf_node.child()) walk_tree(hdf_node.child())
@ -439,86 +523,95 @@ class WebInterfaceSites:
walk_tree(hdf) walk_tree(hdf)
def __getLanguageData(self): def __get_language_data(self):
"""return the hdf dataset of the main interface and all plugins """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 ## check if the language setting has changed - use cache if possible
try: if self.__cached_language_data and \
if self.cachedLanguageData["langOrder"] == self.langOrder: self.__cached_language_data["lang_order"] == self.lang_order:
self.cbox.log.debug("using cached language data: %s" % str(self.langOrder)) self.cbox.log.debug(
return self.cachedLanguageData["hdf"] "using cached language data: %s" % str(self.lang_order))
except AttributeError: return self.__cached_language_data["hdf"]
pass
self.cbox.log.debug("generating language data") self.cbox.log.debug("generating language data")
hdf = neo_util.HDF() hdf = neo_util.HDF()
hdf.readFile(os.path.join(self.prefs["Locations"]["TemplateDir"],"language.hdf")) hdf.readFile(os.path.join(
self.__substituteGettext(self.langOrder, GETTEXT_DOMAIN, hdf) self.cbox.prefs["Locations"]["TemplateDir"],"language.hdf"))
self.__substitute_gettext(self.lang_order, GETTEXT_DOMAIN, hdf)
## load the language data of all plugins ## load the language data of all plugins
for p in self.pluginList.getPlugins(): for plugin in self.__plugin_manager.get_plugins():
pl_lang = p.getLanguageData() pl_lang = plugin.get_language_data()
self.__substituteGettext(self.langOrder, "%s-feature-%s" % (GETTEXT_DOMAIN, p.getName()), pl_lang) self.__substitute_gettext(self.lang_order, "%s-feature-%s" % \
hdf.copy("Plugins.%s" % p.getName(), pl_lang) (GETTEXT_DOMAIN, plugin.get_name()), pl_lang)
self.cbox.log.debug("language data for plugin loaded: %s" % p.getName()) 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 ## 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 return hdf
def __render(self, renderInfo, plugin=None): def __render(self, render_info, plugin=None):
'''renders from clearsilver templates and returns the resulting html '''renders from clearsilver templates and returns the resulting html
''' '''
## is renderInfo a string (filename of the template) or a dictionary? ## is render_info a string (filename of the template) or a dictionary?
if type(renderInfo) == types.DictType: if isinstance(render_info, dict):
template = renderInfo["template"] template = render_info["template"]
if renderInfo.has_key("generator"): if render_info.has_key("generator"):
generator = renderInfo["generator"] generator = render_info["generator"]
else: else:
generator = None generator = None
else: else:
(template, generator) = (renderInfo, None) (template, generator) = (render_info, None)
## load the language data ## load the language data
hdf = neo_util.HDF() 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 ## first: assume, that the template file is in the global
self.dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], template + ".cs")) ## template directory
self.__dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join(
self.cbox.prefs["Locations"]["TemplateDir"],
template + ".cs"))
if plugin: if plugin:
## check, if the plugin provides the template file -> overriding ## 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: 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 ## 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 ## 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): 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 yield "Couldn't read clearsilver file: %s" % cs_path
return return
self.log.debug(self.dataset) self.cbox.log.debug(self.__dataset)
for key in self.dataset.keys(): for key in self.__dataset.keys():
hdf.setValue(key,str(self.dataset[key])) hdf.setValue(key, str(self.__dataset[key]))
cs = neo_cs.CS(hdf) cs_data = neo_cs.CS(hdf)
cs.parseFile(cs_path) cs_data.parseFile(cs_path)
## is there a generator containing additional information? ## is there a generator containing additional information?
if not generator: if not generator:
## all content in one flush ## all content in one flush
yield cs.render() yield cs_data.render()
else: else:
content_generate = generator() content_generate = generator()
dummy_line = """<!-- CONTENT_DUMMY -->""" dummy_line = """<!-- CONTENT_DUMMY -->"""
## now we do it linewise - checking for the content marker ## 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: if line.find(dummy_line) != -1:
yield line.replace(dummy_line, content_generate.next()) yield line.replace(dummy_line, content_generate.next())
else: else:

View file

@ -24,6 +24,8 @@ super class of all web interface unittests for the cryptobox
just inherit this class and add some test functions just inherit this class and add some test functions
""" """
__revision__ = "$Id"
import unittest import unittest
import twill import twill
import cherrypy import cherrypy
@ -34,10 +36,12 @@ import os
## we do the following, for easy surfing ## we do the following, for easy surfing
## e.g. use: cbx.go(your_url) ## e.g. use: cbx.go(your_url)
## commands api: http://twill.idyll.org/commands.html ## commands api: http://twill.idyll.org/commands.html
CBXHOST="localhost" CBXHOST = "localhost"
CBXPORT=8081 CBXPORT = 8081
CBX_URL="http://%s:%d/" % (CBXHOST, CBXPORT) CBX_URL = "http://%s:%d/" % (CBXHOST, CBXPORT)
LOG_FILE="/tmp/twill.log" LOG_FILE = "/tmp/cryptobox-twill.log"
WEBLOG_FILE = "/tmp/cryptobox-cherrypy.log"
class WebInterfaceTestClass(unittest.TestCase): class WebInterfaceTestClass(unittest.TestCase):
'''this class checks the webserver, using "twill" '''this class checks the webserver, using "twill"
@ -62,9 +66,12 @@ class WebInterfaceTestClass(unittest.TestCase):
'server.logToScreen' : False, 'server.logToScreen' : False,
'autoreload.on': False, 'autoreload.on': False,
'server.threadPool': 1, '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) cherrypy.server.start(initOnly=True, serverClass=None)
from cherrypy._cpwsgi import wsgiApp from cherrypy._cpwsgi import wsgiApp
@ -74,19 +81,13 @@ class WebInterfaceTestClass(unittest.TestCase):
self.output = open(LOG_FILE,"a") self.output = open(LOG_FILE,"a")
twill.set_output(self.output) twill.set_output(self.output)
self.cmd = twill.commands self.cmd = twill.commands
self.URL = CBX_URL self.url = CBX_URL
self.cbox = cherrypy.root.cbox self.cbox = cherrypy.root.cbox
self.globals, self.locals = twill.namespaces.get_twill_glocals() self.globals, self.locals = twill.namespaces.get_twill_glocals()
## search for a usable block device ## search for a usable block device
## use /dev/ubd? if possible - otherwise /dev/hd? ## use /dev/ubd? if possible - otherwise /dev/hd?
## so it will be possible to use these tests inside of an uml ## 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"]: self.blockdevice, self.device = self.__find_test_device()
if os.path.exists("/dev/%s1" % d):
device = d
break
else:
device = "hda"
self.device = device
@ -103,6 +104,34 @@ class WebInterfaceTestClass(unittest.TestCase):
browser = twill.commands.get_browser() browser = twill.commands.get_browser()
soup = BeautifulSoup(browser.get_html()) soup = BeautifulSoup(browser.get_html())
return soup 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"): def register_auth(self, url, user="admin", password="admin"):

44
src/pylintrc Normal file
View file

@ -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 <file or directory> 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