language namespace for plugins separated

plugin interface changed ("prepareForm" removed)
plugins do not raise exceptions anymore
first part of the partitioning plugin
device-specific stuff moved to CryptoBoxTools
This commit is contained in:
lars 2006-09-14 12:33:01 +00:00
parent f2a7ceb61c
commit de3280806f
26 changed files with 622 additions and 309 deletions

View File

@ -18,6 +18,7 @@ import CryptoBoxContainer
from CryptoBoxExceptions import *
import re
import os
import CryptoBoxTools
@ -112,10 +113,16 @@ class CryptoBoxProps(CryptoBox):
def __init__(self, config_file=None):
'''read config and fill class variables'''
CryptoBox.__init__(self, config_file)
self.__reReadContainerList()
def __reReadContainerList(self):
self.containers = []
for device in self.__getAvailablePartitions():
for device in CryptoBoxTools.getAvailablePartitions():
if self.isDeviceAllowed(device):
self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self))
## sort by container name
self.containers.sort(cmp = lambda x,y: x.getName() < y.getName() and -1 or 1)
def isDeviceAllowed(self, devicename):
@ -185,9 +192,22 @@ class CryptoBoxProps(CryptoBox):
"assign a name to a uuid in the ContainerNameDatabase"
used_uuid = self.getUUIDForName(name)
"first remove potential conflicting uuid/name combination"
if used_uuid: del self.prefs.nameDB[used_uuid]
if used_uuid:
## remember the container which name was overriden
for e in self.containers:
if e.getName() == name:
forcedRename = e
break
del self.prefs.nameDB[used_uuid]
self.prefs.nameDB[uuid] = name
self.prefs.nameDB.write()
## rename the container that lost its name (necessary while we use cherrypy)
if used_uuid:
## this is surely not the best way to regenerate the name
dev = e.getDevice()
old_index = self.containers.index(e)
self.containers.remove(e)
self.containers.insert(old_index, CryptoBoxContainer.CryptoBoxContainer(dev,self))
def getNameForUUID(self, uuid):
@ -244,102 +264,6 @@ class CryptoBoxProps(CryptoBox):
return []
""" ************ internal stuff starts here *********** """
def __getAvailablePartitions(self):
"retrieve a list of all available containers"
ret_list = []
try:
"the following reads all lines of /proc/partitions and adds the mentioned devices"
fpart = open("/proc/partitions", "r")
try:
line = fpart.readline()
while line:
p_details = line.split()
if (len(p_details) == 4):
"the following code prevents double entries like /dev/hda and /dev/hda1"
(p_major, p_minor, p_size, p_device) = p_details
if re.search('^[0-9]*$', p_major) and re.search('^[0-9]*$', p_minor):
p_parent = re.sub('[1-9]?[0-9]$', '', p_device)
if p_parent == p_device:
if [e for e in ret_list if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]:
"major partition - its children are already in the list"
pass
else:
"major partition - but there are no children for now"
ret_list.append(p_device)
else:
"minor partition - remove parent if necessary"
if p_parent in ret_list: ret_list.remove(p_parent)
ret_list.append(p_device)
line = fpart.readline()
finally:
fpart.close()
return [self.__getAbsoluteDeviceName(e) for e in ret_list]
except IOError:
self.log.warning("Could not read /proc/partitions")
return []
def __getAbsoluteDeviceName(self, shortname):
""" returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1")
this does also work for device mapper devices
if the result is non-unique, one arbitrary value is returned"""
if re.search('^/', shortname): return shortname
default = os.path.join("/dev", shortname)
if os.path.exists(default): return default
result = self.__findMajorMinorOfDevice(shortname)
"if no valid major/minor was found -> exit"
if not result: return default
(major, minor) = result
"for device-mapper devices (major == 254) ..."
if major == 254:
result = self.__findMajorMinorDeviceName("/dev/mapper", major, minor)
if result: return result[0]
"now check all files in /dev"
result = self.__findMajorMinorDeviceName("/dev", major, minor)
if result: return result[0]
return default
def __findMajorMinorOfDevice(self, device):
"return the major/minor numbers of a block device by querying /sys/block/?/dev"
if not os.path.exists(os.path.join("/sys/block", device)): return None
blockdev_info_file = os.path.join(os.path.join("/sys/block", device), "dev")
try:
f_blockdev_info = open(blockdev_info_file, "r")
blockdev_info = f_blockdev_info.read()
f_blockdev_info.close()
(str_major, str_minor) = blockdev_info.split(":")
"numeric conversion"
try:
major = int(str_major)
minor = int(str_minor)
return (major, minor)
except ValueError:
"unknown device numbers -> stop guessing"
return None
except IOError:
pass
def __findMajorMinorDeviceName(self, dir, major, minor):
"returns the names of devices with the specified major and minor number"
collected = []
try:
subdirs = [os.path.join(dir, e) for e in os.listdir(dir) if (not os.path.islink(os.path.join(dir, e))) and os.path.isdir(os.path.join(dir, e))]
"do a recursive call to parse the directory tree"
for dirs in subdirs:
collected.extend(self.__findMajorMinorDeviceName(dirs, major, minor))
"filter all device inodes in this directory"
collected.extend([os.path.realpath(os.path.join(dir, e)) for e in os.listdir(dir) if (os.major(os.stat(os.path.join(dir, e)).st_rdev) == major) and (os.minor(os.stat(os.path.join(dir, e)).st_rdev) == minor)])
result = []
for e in collected:
if e not in result: result.append(e)
return collected
except OSError:
return []
if __name__ == "__main__":
cb = CryptoBox()

View File

@ -205,6 +205,7 @@ class CryptoBoxContainer:
def __getUUID(self):
"""return UUID for luks partitions, ext2/3 and vfat filesystems"""
emergency_default = self.device.replace(os.path.sep, "_")
devnull = None
try:
devnull = open(os.devnull, "w")
@ -214,7 +215,7 @@ class CryptoBoxContainer:
shell=False,
stdin=None,
stdout=subprocess.PIPE,
stderr=devnull,
stderr=subprocess.PIPE,
args=[self.Progs["blkid"],
"-s", "UUID",
"-o", "value",
@ -225,10 +226,10 @@ class CryptoBoxContainer:
result = proc.stdout.read().strip()
if proc.returncode != 0:
self.log.warn("retrieving of partition type via 'blkid' failed: %s" % (proc.stderr.read().strip(), ))
return None
return emergency_default
devnull.close()
if result: return result
return self.device.replace(os.path.sep, "_")
return emergency_default
def __getTypeOfPartition(self):

View File

@ -0,0 +1,117 @@
import logging
import os
import re
logger = logging.getLogger("CryptoBox")
def getAvailablePartitions():
"retrieve a list of all available containers"
ret_list = []
try:
"the following reads all lines of /proc/partitions and adds the mentioned devices"
fpart = open("/proc/partitions", "r")
try:
line = fpart.readline()
while line:
p_details = line.split()
if (len(p_details) == 4):
"the following code prevents double entries like /dev/hda and /dev/hda1"
(p_major, p_minor, p_size, p_device) = p_details
if re.search('^[0-9]*$', p_major) and re.search('^[0-9]*$', p_minor):
p_parent = re.sub('[1-9]?[0-9]$', '', p_device)
if p_parent == p_device:
if [e for e in ret_list if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]:
"major partition - its children are already in the list"
pass
else:
"major partition - but there are no children for now"
ret_list.append(p_device)
else:
"minor partition - remove parent if necessary"
if p_parent in ret_list: ret_list.remove(p_parent)
ret_list.append(p_device)
line = fpart.readline()
finally:
fpart.close()
return map(getAbsoluteDeviceName, ret_list)
except IOError:
logger.warning("Could not read /proc/partitions")
return []
def getAbsoluteDeviceName(shortname):
""" returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1")
this does also work for device mapper devices
if the result is non-unique, one arbitrary value is returned"""
if re.search('^/', shortname): return shortname
default = os.path.join("/dev", shortname)
if os.path.exists(default): return default
result = findMajorMinorOfDevice(shortname)
"if no valid major/minor was found -> exit"
if not result: return default
(major, minor) = result
"for device-mapper devices (major == 254) ..."
if major == 254:
result = findMajorMinorDeviceName("/dev/mapper", major, minor)
if result: return result[0]
"now check all files in /dev"
result = findMajorMinorDeviceName("/dev", major, minor)
if result: return result[0]
return default
def findMajorMinorOfDevice(device):
"return the major/minor numbers of a block device by querying /sys/block/?/dev"
if not os.path.exists(os.path.join(os.path.sep,"sys","block",device)): return None
blockdev_info_file = os.path.join(os.path.join(os.path.sep,"sys","block", device), "dev")
try:
f_blockdev_info = open(blockdev_info_file, "r")
blockdev_info = f_blockdev_info.read()
f_blockdev_info.close()
(str_major, str_minor) = blockdev_info.split(":")
"numeric conversion"
try:
major = int(str_major)
minor = int(str_minor)
return (major, minor)
except ValueError:
"unknown device numbers -> stop guessing"
return None
except IOError:
pass
def findMajorMinorDeviceName(dir, major, minor):
"returns the names of devices with the specified major and minor number"
collected = []
try:
subdirs = [os.path.join(dir, e) for e in os.listdir(dir) if (not os.path.islink(os.path.join(dir, e))) and os.path.isdir(os.path.join(dir, e))]
"do a recursive call to parse the directory tree"
for dirs in subdirs:
collected.extend(findMajorMinorDeviceName(dirs, major, minor))
"filter all device inodes in this directory"
collected.extend([os.path.realpath(os.path.join(dir, e)) for e in os.listdir(dir) if (os.major(os.stat(os.path.join(dir, e)).st_rdev) == major) and (os.minor(os.stat(os.path.join(dir, e)).st_rdev) == minor)])
## remove double entries
result = []
for e in collected:
if e not in result: result.append(e)
return result
except OSError:
return []
def getParentBlockDevices():
devs = []
for line in file("/proc/partitions"):
p_details = line.split()
## we expect four values - otherwise continue with next iteration
if len(p_details) != 4: continue
(p_major, p_minor, p_size, p_device) = p_details
## we expect numeric values in the first two columns
if re.search(u'\D',p_major) or re.search(u'\D',p_minor): continue
## 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
devs.append(p_device)
return map(getAbsoluteDeviceName, devs)

View File

@ -44,14 +44,19 @@ class PluginManager:
def loadLanguageData(self, hdf, lang="en"):
import neo_cgi, neo_util
for plfile in self.__getPluginFiles():
plname = os.path.basename(plfile)[:-3]
langdir = os.path.join(os.path.dirname(plfile), "lang")
selected_langfile = os.path.join(langdir, lang + ".hdf")
default_langfile = os.path.join(langdir, "en.hdf")
for langfile in (selected_langfile, default_langfile):
if os.access(langfile, os.R_OK):
self.log.debug("Loading plugin language file: %s" % langfile)
hdf.readFile(langfile)
lang_hdf = neo_util.HDF()
lang_hdf.readFile(langfile)
## add the language data below "Lang.Plugins.PLUGINNAME"
hdf.copy("Lang.Plugins." + plname, lang_hdf)
break
else:
self.log.debug("Couldn't find a plugin language file (%s)" % default_langfile)

View File

@ -18,7 +18,7 @@ class WebInterfaceDataset(dict):
def setPluginState(self, plugins):
for pl in plugins.allPlugins():
self["Data.Status.Modules." + pl] = plugins.getPlugin(pl).getStatus(self.cbox)
self["Data.Status.Plugins." + pl] = plugins.getPlugin(pl).getStatus(self.cbox)
def setCurrentDiskState(self, device):
@ -48,6 +48,7 @@ class WebInterfaceDataset(dict):
self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"]
self["Settings.Language"] = self.prefs["WebSettings"]["Language"]
self["Settings.DocLang"] = self.prefs["WebSettings"]["DocLanguage"]
self["Settings.PluginDir"] = self.prefs["Locations"]["PluginDir"]
def __setCryptoBoxState(self):

View File

@ -1,9 +1,21 @@
import CryptoBox
import WebInterfaceDataset
import re
from Plugins import PluginManager
import Plugins
from CryptoBoxExceptions import *
class WebInterfacePlugins:
def __init__(self, log, plugins, handler_func):
for plname in plugins.allPlugins():
log.info("Plugin '%s' loaded" % plname)
## this should be the "easiest" way to expose all plugins as URLs
setattr(self, plname, handler_func(plname))
setattr(getattr(self, plname), "exposed", True)
class WebInterfaceSites:
'''
url2func = {'index':'show_status','doc':'show_doc','logs':'show_log'}
@ -16,16 +28,9 @@ class WebInterfaceSites:
self.log = logging.getLogger("CryptoBox")
self.prefs = self.cbox.prefs
self.__resetDataset()
self.plugins = PluginManager(self.prefs["Locations"]["PluginDir"])
self.__exposePlugins()
def __exposePlugins(self):
for plname in self.plugins.allPlugins():
self.log.info("Plugin '%s' loaded" % plname)
## this should be the "easiest" way to expose all modules as URLs
setattr(self, "module_" + plname, self.return_module_action(plname))
setattr(getattr(self, "module_" + plname), "exposed", True)
self.pluginList = Plugins.PluginManager(self.prefs["Locations"]["PluginDir"])
self.plugins = WebInterfacePlugins(self.log, self.pluginList, self.return_plugin_action)
self.plugins.index = self.system
def __resetDataset(self):
@ -134,8 +139,8 @@ class WebInterfaceSites:
try:
container.setName(volume_name)
# TODO: specify the possible exceptions
except Exception:
self.log.warn("failed to rename the volume '%s' to '%s'" % (device, volume_name))
except Exception, errMsg:
self.log.warn("failed to rename the volume '%s' to '%s: %s'" % (device, volume_name, errMsg))
self.dataset["Data.Warning"] = "SetVolumeNameFailed"
else:
self.log.info("successfully renamed volume '%s' to '%s'" % (device, volume_name))
@ -311,7 +316,7 @@ class WebInterfaceSites:
return self.__render("show_volume")
def return_module_action(self, module):
def return_plugin_action(self, plugin_name):
def handler(**args):
self.__resetDataset()
try:
@ -319,15 +324,9 @@ class WebInterfaceSites:
del args["weblang"]
except KeyError:
pass
plugin = self.plugins.getPlugin(module)
try:
nextTemplate = plugin.doAction(self.cbox, **args)
except CBPluginActionError, errMsg:
self.log.debug(errMsg)
self.dataset["Data.Warning"] = errMsg
nextTemplate = "empty"
plugin.prepareForm(self.dataset, self.cbox)
return self.__render(nextTemplate, module)
plugin = self.pluginList.getPlugin(plugin_name)
nextTemplate = plugin.doAction(self.dataset, self.cbox, **args)
return self.__render(nextTemplate, plugin_name)
return handler
@ -418,7 +417,7 @@ class WebInterfaceSites:
return False
def __render(self, template, module=None):
def __render(self, template, plugin=None):
'''renders from clearsilver templates and returns the resulting html
Gets a dictionary with all settings, nessessary for rendering.
@ -430,16 +429,16 @@ class WebInterfaceSites:
try:
import neo_cgi, neo_util, neo_cs
except ImportError:
errorMsg = "Could not import clearsilver modules. Try 'apt-get install python-clearsilver'."
errorMsg = "Could not import clearsilver module. Try 'apt-get install python-clearsilver'."
self.log.error(errorMsg)
sys.stderr.write(errorMsg)
raise ImportError, errorMsg
module_cs_file = False
if module:
module_cs_file = self.plugins.getTemplateFileName(module, template)
plugin_cs_file = False
if plugin:
plugin_cs_file = self.pluginList.getTemplateFileName(plugin, template)
default_cs_file = os.path.join(self.prefs["Locations"]["TemplateDir"], template + ".cs")
self.dataset["Data.TemplateFile"] = module_cs_file or default_cs_file
self.dataset["Data.TemplateFile"] = plugin_cs_file or default_cs_file
self.log.info("rendering site: " + template)
cs_path = os.path.join(self.prefs["Locations"]["TemplateDir"], "main.cs")
@ -454,7 +453,7 @@ class WebInterfaceSites:
return "Couldn't read language file: %s" % hdf_path
## add the current state of the plugins to the hdf dataset
self.dataset.setPluginState(self.plugins)
self.dataset.setPluginState(self.pluginList)
hdf = neo_util.HDF()
hdf.readFile(hdf_path)
@ -462,7 +461,7 @@ class WebInterfaceSites:
for key in self.dataset.keys():
hdf.setValue(key,str(self.dataset[key]))
## load languaga data of plugins
self.plugins.loadLanguageData(hdf, lang=self.dataset["Settings.Language"])
self.pluginList.loadLanguageData(hdf, lang=self.dataset["Settings.Language"])
cs = neo_cs.CS(hdf)
cs.parseFile(cs_path)
return cs.render()

View File

@ -1,26 +1,17 @@
from CryptoBoxExceptions import CBPluginActionError
import subprocess
import os
def prepareForm(hdf, cbox):
date = __getCurrentDate()
hdf["Data.Modules.date.year"] = date.year
hdf["Data.Modules.date.month"] = date.month
hdf["Data.Modules.date.day"] = date.day
hdf["Data.Modules.date.hour"] = date.hour
hdf["Data.Modules.date.minute"] = date.minute
def doAction(cbox, store=None, year=0, month=0, day=0, hour=0, minute=0):
def doAction(hdf, cbox, store=None, year=0, month=0, day=0, hour=0, minute=0):
import datetime
__prepareFormData(hdf, cbox)
if store:
try:
year, month, day = int(year), int(month), int(day)
hour, minute = int(hour), int(minute)
new_date = datetime.datetime(year, month, day, hour, minute)
except ValueError:
raise CBPluginActionError, "InvalidDate"
hdf["Data.Warning"] = "Plugins.date.InvalidDate"
proc = subprocess.Popen(
shell = False,
args = [
@ -33,7 +24,8 @@ def doAction(cbox, store=None, year=0, month=0, day=0, hour=0, minute=0):
if proc.returncode == 0:
return "form_system"
else:
raise CBPluginActionError, "InvalidDate"
hdf["Data.Warning"] = "Plugins.date.InvalidDate"
return "form_date"
else:
return "form_date"
@ -42,6 +34,15 @@ def getStatus(cbox):
return str(__getCurrentDate())
def __prepareFormData(hdf, cbox):
date = __getCurrentDate()
hdf["Data.Plugins.date.year"] = date.year
hdf["Data.Plugins.date.month"] = date.month
hdf["Data.Plugins.date.day"] = date.day
hdf["Data.Plugins.date.hour"] = date.hour
hdf["Data.Plugins.date.minute"] = date.minute
def __getCurrentDate():
import datetime
return datetime.datetime(2000,1,1).now()

View File

@ -1,42 +1,42 @@
<?cs # $Id$ ?>
<h1><?cs var:html_escape(Lang.Title.ConfigDate) ?></h1>
<h1><?cs var:html_escape(Lang.Plugins.date.Title.ConfigDate) ?></h1>
<?cs call:print_form_header("module_date") ?>
<?cs call:print_form_header("plugins/date") ?>
<p><label for="date"><?cs var:html_escape(Lang.Text.Date) ?>: </label><br/>
<p><label for="date"><?cs var:html_escape(Lang.Plugins.date.Text.Date) ?>: </label><br/>
<select name="day" size="0"><?cs
loop: x = #1, #31, #1 ?>
<?cs if:x == Data.Modules.date.day ?><option selected="selected"><?cs
<?cs if:x == Data.Plugins.date.day ?><option selected="selected"><?cs
else ?><option><?cs /if ?><?cs var:x ?></option><?cs /loop ?>
</select>
<select name="month" size="0"><?cs
loop: x = #1, #12, #1 ?>
<?cs if:x == Data.Modules.date.month ?><option selected="selected" <?cs
<?cs if:x == Data.Plugins.date.month ?><option selected="selected" <?cs
else ?><option <?cs /if ?>value="<?cs var:x ?>"><?cs
var:html_escape(Lang.Text.Months[x]) ?></option><?cs /loop ?>
var:html_escape(Lang.Plugins.date.Text.Months[x]) ?></option><?cs /loop ?>
</select>
<select name="year" size="0"><?cs
loop: x = #2006, #2025, #1 ?>
<?cs if:x == Data.Modules.date.year ?><option selected="selected"><?cs
<?cs if:x == Data.Plugins.date.year ?><option selected="selected"><?cs
else ?><option><?cs /if ?><?cs var:x ?></option><?cs /loop ?>
</select></p>
<p><label for="time"><?cs var:html_escape(Lang.Text.Time) ?>: </label><br/>
<p><label for="time"><?cs var:html_escape(Lang.Plugins.date.Text.Time) ?>: </label><br/>
<select name="hour" size="0"><?cs
loop: x = #0, #23, #1 ?>
<?cs if:x == Data.Modules.date.hour ?><option selected="selected"><?cs
<?cs if:x == Data.Plugins.date.hour ?><option selected="selected"><?cs
else ?><option><?cs /if ?><?cs if:x<10 ?>0<?cs /if ?><?cs var:x ?></option><?cs /loop ?>
</select>&nbsp;:&nbsp;
<select name="minute" size="0"><?cs
loop: x = #0, #59, #1 ?>
<?cs if:x == Data.Modules.date.minute ?><option selected="selected"><?cs
<?cs if:x == Data.Plugins.date.minute ?><option selected="selected"><?cs
else ?><option><?cs /if ?><?cs if:x<10 ?>0<?cs /if ?><?cs var:x ?></option><?cs /loop ?>
</select></p>
<input type="hidden" name="store" value="yes" />
<button type="submit"><?cs var:html_escape(Lang.Button.ConfigDate) ?></button>
<button type="submit"><?cs var:html_escape(Lang.Plugins.date.Button.ConfigDate) ?></button>
</form>

View File

@ -1,12 +1,14 @@
Lang {
Name = Change date and time
Link = Set date/time
Rank = 10
Title.ConfigDate = Date and time setting
Title.ConfigDate = Date and time setting
Button.ConfigDate = Set date and time
Button.ConfigDate = Set date and time
Text.Date = Date
Text.Time = Time
Text.Months {
Text.Date = Date
Text.Time = Time
Text.Months {
1 = January
2 = February
3 = March
@ -19,17 +21,9 @@ Lang {
10 = October
11 = November
12 = December
}
}
Modules.date {
Name = Change date and time
Link = Set date/time
Rank = 10
}
WarningMessage.InvalidDate {
WarningMessage.InvalidDate {
Title = Invalid value
Text = An invalid value for date or time was supplied. Please try again.
}
}

View File

@ -1,13 +1,7 @@
Lang {
Name = Show the content of the log file
Link = Show log file
Rank = 90
Title.Log = CryptoBox logfiles
Title.Log = CryptoBox logfiles
Text.EmptyLog = The logfile of the CryptoBox is empty.
Modules.logs {
Name = Show the content of the log file
Link = Show log file
Rank = 90
}
}
Text.EmptyLog = The logfile of the CryptoBox is empty.

View File

@ -1,11 +1,6 @@
from CryptoBoxExceptions import CBPluginActionError
def prepareForm(hdf, cbox):
hdf["Data.Modules.logs.Content"] = __getLogContent(cbox)
def doAction(cbox):
def doAction(hdf, cbox):
__prepareFormData(hdf,cbox)
return "show_log"
@ -16,6 +11,10 @@ def getStatus(cbox):
cbox.prefs["Log"]["Details"])
def __prepareFormData(hdf, cbox):
hdf["Data.Plugins.logs.Content"] = __getLogContent(cbox)
def __getLogContent(cbox, lines=30, maxSize=2000):
return "<br/>".join(cbox.getLogData(lines, maxSize))

View File

@ -1,13 +1,13 @@
<?cs # $Id: show_log.cs 374 2006-05-30 18:47:34Z lars $ ?>
<?cs # $Id$ ?>
<div id="log">
<h1><?cs var:html_escape(Lang.Title.Log) ?></h1>
<h1><?cs var:html_escape(Lang.Plugins.logs.Title.Log) ?></h1>
<?cs if:Data.Modules.logs.Content ?>
<p class="console"><?cs var:Data.Modules.logs.Content ?></p>
<?cs if:Data.Plugins.logs.Content ?>
<p class="console"><?cs var:Data.Plugins.logs.Content ?></p>
<?cs else ?>
<p><?cs var:html_escape(Lang.Text.EmptyLog) ?></p>
<p><?cs var:html_escape(Lang.Plugins.logs.Text.EmptyLog) ?></p>
<?cs /if ?>
</div>

View File

@ -1,22 +1,22 @@
<?cs # $Id$ ?>
<h1><?cs var:html_escape(Lang.Title.Network) ?></h1>
<h1><?cs var:html_escape(Lang.Plugins.network.Title.Network) ?></h1>
<?cs call:print_form_header("module_network") ?>
<?cs call:print_form_header("plugins/network") ?>
<p><label for="ip"><?cs var:html_escape(Lang.Text.IP) ?>: </label><br/>
<p><label for="ip"><?cs var:html_escape(Lang.Plugins.network.Text.IP) ?>: </label><br/>
<input type="text" name="ip1" size="3" id="ip" value="<?cs
var:Data.Modules.network.ip.oc1 ?>" />.
var:Data.Plugins.network.ip.oc1 ?>" />.
<input type="text" name="ip2" size="3" value="<?cs
var:Data.Modules.network.ip.oc2 ?>" />.
var:Data.Plugins.network.ip.oc2 ?>" />.
<input type="text" name="ip3" size="3" value="<?cs
var:Data.Modules.network.ip.oc3 ?>" />.
var:Data.Plugins.network.ip.oc3 ?>" />.
<input type="text" name="ip4" size="3" value="<?cs
var:Data.Modules.network.ip.oc4 ?>" /></p>
var:Data.Plugins.network.ip.oc4 ?>" /></p>
<input type="hidden" name="store" value="yes" />
<button type="submit"><?cs var:html_escape(Lang.Button.Network) ?></button>
<button type="submit"><?cs var:html_escape(Lang.Plugins.network.Button.Network) ?></button>
</form>

View File

@ -1,20 +1,24 @@
Lang {
Name = Configure network
Link = Configure network
Rank = 30
Title.Network = Network settings
Title.Network = Network settings
Button.Network = Update settings
Button.Network = Update settings
Text.IP = Network address
Text.IP = Network address
Modules.network {
Name = Configure network
Link = Configure network
Rank = 30
}
WarningMessage.InvalidIP {
WarningMessage {
InvalidIP {
Title = Invalid value
Text = An invalid network address (IP) was supplied. Please try again.
}
}
SuccessMessage {
IPChanged {
Title = Network address changed
Text = The network address has been changed. In a few seconds you will get redirected to the new address.
}
}

View File

@ -1,19 +1,11 @@
from CryptoBoxExceptions import CBPluginActionError
import re
import subprocess
import imp
import os
def prepareForm(hdf, cbox):
(oc1, oc2, oc3, oc4) = __getCurrentIP(cbox)
hdf["Data.Modules.network.ip.oc1"] = oc1
hdf["Data.Modules.network.ip.oc2"] = oc2
hdf["Data.Modules.network.ip.oc3"] = oc3
hdf["Data.Modules.network.ip.oc4"] = oc4
def doAction(cbox, store=None, ip1="", ip2="", ip3="", ip4=""):
def doAction(hdf, cbox, store=None, ip1="", ip2="", ip3="", ip4=""):
__prepareFormData(hdf, cbox)
if store:
try:
for ip_in in (ip1, ip2, ip3, ip4):
@ -22,11 +14,16 @@ def doAction(cbox, store=None, ip1="", ip2="", ip3="", ip4=""):
raise ValueError
ip = "%d.%d.%d.%d" % (int(ip1), int(ip2), int(ip3), int(ip4))
except ValueError:
raise CBPluginActionError, "InvalidIP"
hdf["Data.Warning"] = "Plugins.network.InvalidIP"
return "form_system"
if __setIP(cbox, ip):
hdf["Data.Success"] = "Plugins.network.IPChanged"
hdf["Data.Redirect.URL"] = ""
hdf["Data.Redirect.Delay"] = 30
return "form_system"
else:
raise CBPluginActionError, "InvalidIP"
hdf["Data.Warning"] = "Plugins.network.InvalidIP"
return "form_network"
else:
return "form_network"
@ -35,14 +32,22 @@ def getStatus(cbox):
return "%d.%d.%d.%d" % __getCurrentIP(cbox)
def __prepareFormData(hdf, cbox):
(oc1, oc2, oc3, oc4) = __getCurrentIP(cbox)
hdf["Data.Plugins.network.ip.oc1"] = oc1
hdf["Data.Plugins.network.ip.oc2"] = oc2
hdf["Data.Plugins.network.ip.oc3"] = oc3
hdf["Data.Plugins.network.ip.oc4"] = oc4
def __getCurrentIP(cbox):
root_action_mod = imp.load_source("root_action", os.path.join(os.path.dirname(__file__), "root_action.py"))
root_action_plug = imp.load_source("root_action", os.path.join(os.path.dirname(__file__), "root_action.py"))
proc = subprocess.Popen(
shell = False,
stdout = subprocess.PIPE,
args = [
root_action_mod.IFCONFIG_BIN,
root_action_mod.IFACE])
root_action_plug.IFCONFIG_BIN,
root_action_plug.IFACE])
(output, error) = proc.communicate()
if proc.returncode != 0: return (0,0,0,0)
match = re.search(u'inet [\w]+:(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s',output)

View File

@ -0,0 +1,8 @@
<?cs loop: x = #0, subcount(Data.Plugins.partition.Parts)-1, #1 ?>
<input type="hidden" name="part<?cs var:x ?>_size" value="<?cs var:Data.Plugins.partition.Parts[x].Size ?>" />
<input type="hidden" name="part<?cs var:x ?>_type" value="<?cs var:Data.Plugins.partition.Parts[x].Type ?>" /><?cs
/loop ?>
<input type="hidden" name="block_device" value="<?cs var:html_escape(Data.Plugins.partition.Device) ?>" />

View File

@ -0,0 +1,33 @@
Name = Disk partitioning
Link = Disk partitioning
Rank = 80
Title.Partition = Disk partitions
Button {
SelectDevice = Repartition disk
AddPartition = Add another partition
Back = Back
SavePartitions = Save changes
AbortPartitions = Cancel
}
Text {
FS {
Type = Filesystem type
Fat = FAT (Windows)
Ext2 = Ext2
Ext3 = Ext3
Reiser = Reiser
}
Size = Size (MB)
SelectDevice = Choose a disk for partitioning
NoDevicesAvailable = No suitable disks found - please check your configuration and hardware setup.
WarningMessage {
InvalidInput {
Title = Invalid input
Text = You entered an invalid value.
}
}

View File

@ -0,0 +1,132 @@
import re
import subprocess
import imp
import os
import logging
import CryptoBoxTools
PartTypes = {
"linux" : "L",
"windows" : "0xC"}
logger = logging.getLogger("CryptoBox")
def doAction(hdf, cbox, **args):
try:
step = args["step"]
del args["step"]
except KeyError:
step = "select_device"
if step == "add_partition":
return __actionAddPartition(hdf, cbox, args)
if step == "del_partition":
return __actionDelPartition(hdf, cbox, args)
elif step == "finish":
return __actionFinish(hdf, cbox, args)
else: # for "select_device" and for invalid targets
return __actionSelectDevice(hdf, cbox, args)
def getStatus(cbox):
return "%d.%d.%d.%d" % __getCurrentIP(cbox)
def __isDeviceValid(device, cbox):
if not cbox.isDeviceAllowed(device):
return False
if not device in CryptoBoxTools.getParentBlockDevices():
return False
def __actionSelectDevice(hdf, cbox, args):
block_devices = [e
for e in CryptoBoxTools.getParentBlockDevices()
if cbox.isDeviceAllowed(e)]
counter = 0
for a in block_devices:
hdf["Data.Plugins.partition.BlockDevices.%d" % counter] = a
cbox.log.debug("found a suitable block device: %s" % a)
counter += 1
return "select_device"
def __actionAddPartition(hdf, cbox, args):
try:
device = args["block_device"]
except KeyError:
return __actionSelectDevice(hdf, cbox, args)
#FIXME: the following check should obviuosly get reversed
if __isDeviceValid(device, cbox): return __actionSelectDevice(hdf, cbox, args)
size = __getDeviceSize(device)
hdf["Data.Plugins.partition.Device"] = device
hdf["Data.Plugins.partition.Device.Size"] = size
parts = __getPartitionsFromArgs(args, size)
__setPartitionData(hdf, parts, size)
return "set_partitions"
def __actionDelPartition(hdf, cbox, args):
try:
device = args["block_device"]
except KeyError:
return __actionSelectDevice(hdf, cbox, args)
#FIXME: the following check should obviuosly get reversed
if __isDeviceValid(device, cbox): return __actionSelectDevice(hdf, cbox, args)
size = __getDeviceSize(device)
hdf["Data.Plugins.partition.Device"] = device
hdf["Data.Plugins.partition.Device.Size"] = size
parts = __getPartitionsFromArgs(args, size)
__setPartitionData(hdf, parts[:-1], size)
return "set_partitions"
def __setPartitionData(hdf, parts, size):
availSize = size
i = 0
for part in parts:
logger.debug(part)
hdf["Data.Plugins.partition.Parts.%d.Size" % i] = part["size"]
hdf["Data.Plugins.partition.Parts.%d.Type" % i] = part["type"]
availSize -= part["size"]
i += 1
hdf["Data.Plugins.partition.availSize"] = availSize
for t in PartTypes.keys():
hdf["Data.Plugins.partition.Types.%s" % t] = t
def __getPartitionsFromArgs(args, maxSize):
parts = []
done = False
availSize = maxSize
i = -1
while not done:
i += 1
try:
size = int(args["part%d_size" % i])
partType = args["part%d_type" % i]
if int(size) > availSize: continue
if int(size) <= 0: continue
if not partType in PartTypes.keys(): continue
parts.append({"size":size, "type":partType})
availSize -= size
except TypeError:
pass
except KeyError:
done = True
return parts
def __getDeviceSize(device):
rdev = os.stat(device).st_rdev
minor = os.minor(rdev)
major = os.major(rdev)
for f in file("/proc/partitions"):
try:
elements = f.split()
if len(elements) != 4: continue
if (int(elements[0]) == major) and (int(elements[1]) == minor):
return int(elements[2])
except ValueError:
pass
return 0

View File

@ -0,0 +1,33 @@
#!/usr/bin/env python2.4
#TODO: add netmask and gateway
## necessary: otherwise CryptoBoxRootActions.py will refuse to execute this script
PLUGIN_TYPE = "cryptobox"
import subprocess
import re
import sys
import os
if __name__ == "__main__":
args = sys.argv[1:]
self_bin =sys.argv[0]
if len(args) > 1:
sys.stderr.write("%s: too many arguments (%s)\n" % (self_bin, args))
sys.exit(1)
if len(args) == 0:
sys.stderr.write("%s: no argument supplied\n" % self_bin)
sys.exit(1)
proc = subprocess.Popen(
shell = False,
args = [IFCONFIG_BIN, IFACE, args[0]])
proc.communicate()
sys.exit(proc.returncode)

View File

@ -0,0 +1,27 @@
<?cs # $Id$ ?>
<h1><?cs var:html_escape(Lang.Plugins.partition.Title.Partition) ?></h1>
<?cs if:subcount(Data.Plugins.partition.BlockDevices) > 0 ?>
<?cs call:print_form_header("plugins/partition") ?>
<p><label for="block_device"><?cs var:html_escape(Lang.Plugins.partition.Text.SelectDevice) ?>: </label><br/>
<select name="block_device" id="block_device" size="0">
<?cs each:x = Data.Plugins.partition.BlockDevices
?><option><?cs var:html_escape(x) ?></option>
<?cs /each ?>
</select></p>
<input type="hidden" name="step" value="add_partition" />
<button type="submit"><?cs var:html_escape(Lang.Plugins.partition.Button.SelectDevice) ?></button>
</form>
<?cs else ?>
<p><?cs var:html_escape(Lang.Plugins.partition.Text.NoDevicesAvailable) ?></p>
<?cs /if ?>

View File

@ -0,0 +1,37 @@
<?cs # $Id$ ?>
<h1><?cs var:html_escape(Lang.Plugins.partition.Title.Partition) ?></h1>
<p> <?cs loop: x = #0, subcount(Data.Plugins.partition.Parts)-1, #1 ?>
<p><?cs var:x ?> - <?cs var:Data.Plugins.partition.Parts[x].Size ?> - <?cs var:Data.Plugins.partition.Parts[x].Type ?></p><?cs
/loop ?></p>
<?cs if:Data.Plugins.partition.availSize ?>
<?cs call:print_form_header("plugins/partition") ?>
<?cs include:Settings.PluginDir + "/partition/current_partition_info.cs" ?><?cs
# new partition input if space is available ?><?cs
set: x = subcount(Data.Plugins.partition.Parts) ?>
<p><?cs var:x ?> - <input type="text" name="part<?cs var:x ?>_size" value="<?cs var:Data.Plugins.partition.availSize ?>" /> - <select name="part<?cs var:x ?>_type" size="0"><?cs each: t = Data.Plugins.partition.Types ?><option><?cs var:t ?></option><?cs /each ?></select></p>
<input type="hidden" name="step" value="add_partition" />
<button type="submit"><?cs var:html_escape(Lang.Plugins.partition.Button.AddPartition) ?></button>
</form>
<?cs /if ?>
<?cs if:subcount(Data.Plugins.partition.Parts) > 0 ?>
<?cs call:print_form_header("plugins/partition") ?>
<?cs include:Settings.PluginDir + "/partition/current_partition_info.cs" ?>
<input type="hidden" name="step" value="del_partition" />
<button type="submit"><?cs var:html_escape(Lang.Plugins.partition.Button.Back) ?></button>
</form>
<?cs /if ?>
<?cs if:subcount(Data.Plugins.partition.Parts) > 0 ?>
<?cs call:print_form_header("plugins/partition") ?>
<?cs include:Settings.PluginDir + "/partition/current_partition_info.cs" ?>
<input type="hidden" name="step" value="finish" />
<button type="submit"><?cs var:html_escape(Lang.Plugins.partition.Button.SavePartitions) ?></button>
</form>
<?cs /if ?>

View File

@ -1,36 +1,33 @@
The following directory structure is required:
- python code: plugins/MODULENAME/MODULENAME.py (all lower case is recommended)
- language files: plugins/MODULENAME/lang/(en|de|??).hdf
- clearsilver templates: plugins/MODULENAME/*.cs
- python code: plugins/PLUGINNAME/PLUGINNAME.py (all lower case is recommended)
- language files: plugins/PLUGINNAME/lang/(en|de|??).hdf
- clearsilver templates: plugins/PLUGINNAME/*.cs
Python code interface:
def prepareForm(hdf, cbox):
- here you may add some items to the hdf dataset used by the templates
- the recommended namespace is Data.Modules.MODULENAME.???
def doAction(cbox, store=None, ???):
- this function will get called whenever this module is involved in a request
def doAction(hdf, cbox, store=None, ???):
- this function will get called whenever this plugins is involved in a request
- all arguments should be optional (e.g. for displaying a form without previous input values)
- the argument "store" should be used to process a form submission (just a recommendation)
- if the processing failed for some reason (invalid input, ...), it should raise a CBPluginException (e.g. CBPluginActionError) - the error message should be the name of a warning message (maybe defined in the plugin specific language file) - e.g. "InvalidDate" for Lang.WarningMessage.InvalidDate
- the return value should be the name of the template that should be displayed after processing (a template file in the module directory takes precedence over global template files)
- if the processing failed for some reason (invalid input, ...), it should manually set the "Data.Warning" (resp. "Data.Error" or "Data.Success") to a value of your choice (preferably you may want to use messages of your namespace (e.g. "Plugins.PLUGINNAME.InvalidInput"))
- the return value should be the name of the template that should be displayed after processing (a template file in the plugin directory takes precedence over global template files)
def def getStatus(cbox):
- returns a string, that described a state connected to this module (e.g. the current date and time (for the "date" plugin)
- returns a string, that described a state connected to this plugin (e.g. the current date and time (for the "date" plugin)
Language file structure:
- the content of the language file will be added to the hdf dataset below "Lang.Plugins.PLUGINNAME" (this avoids namespace conflicts)
- the following values _must_ be defined:
Lang.Modules.MODULENAME.Name (a short description)
Lang.Modules.MODULENAME.Link (the visible text for links to this module)
Lang.Modules.MODULENAME.Rank (defines the order of the plugins displayed)
- all other elements should follow the usual structure of language files
Name (a short description)
Link (the visible text for links to this plugin)
Rank (defines the order of the plugins displayed (0..100))
- all warnings, error and success messages should be stored below WarningMessage.??? (resp. ErrorMessage or SuccessMessage)
Clearsilver template:
- should start with a "<h1>" tag
- links to the module (e.g. in form headers) could look like the following:
<?cs call:link("module_MODULENAME",'','','','') ?>
- links to the plugin (e.g. in form headers) could look like the following:
<?cs call:link("plugins/PLUGINNAME",'','','','') ?>
- a hidden input field called "store" should be used to indicate a form submission

View File

@ -26,7 +26,7 @@ Data.Status.Config=<?cs var:html_escape(Data.Status.Config) ?>
Data.Status.InitRunning=<?cs var:html_escape(Data.Status.InitRunning) ?>
Data.Status.IP=<?cs var:html_escape(Data.Status.IP) ?>
Data.Status.Mounted=<?cs var:html_escape(Data.Status.Mounted) ?>
<?cs each:x = Data.Status.Modules ?>Data.Status.Modules.<?cs
<?cs each:x = Data.Status.Plugins ?>Data.Status.Plugins.<?cs
var:name(x) ?>=<?cs var: html_escape(x) ?>
<?cs /each
?>CBOX-STATUS-end -->

View File

@ -6,18 +6,18 @@
<?cs # sort the Plugins - using the most stupid way :) ?>
<?cs loop: order = #0, #100, #1
?><?cs # plugins ?><?cs each:x = Lang.Modules
?><?cs # plugins ?><?cs each:x = Lang.Plugins
?><?cs if:x.Rank == order ?>
<li><a href="<?cs call:link('module_' + name(x),'','','','') ?>" title="<?cs
<li><a href="<?cs call:link('plugins/' + name(x),'','','','') ?>" title="<?cs
var:html_escape(x.Link) ?>"><?cs var:html_escape(x.Link) ?></a></li><?cs
/if ?><?cs
/each ?><?cs
/loop ?>
<?cs # maybe someone forgot to set the rank of a module: we try to catch them ?>
<?cs # maybe someone forgot to set the rank of a plugin: we try to catch them ?>
<?cs # plugins ?><?cs each:x = Lang.Modules ?><?cs
<?cs # plugins ?><?cs each:x = Lang.Plugins ?><?cs
if:!x.Rank || !(x.Rank >= 0 && x.Rank <= 100) ?>
<li><a href="<?cs call:link('module_' + name(x),'','','','') ?>" title="<?cs
<li><a href="<?cs call:link('plugins/' + name(x),'','','','') ?>" title="<?cs
var:html_escape(x.Link) ?>"><?cs var:html_escape(x.Link) ?></a></li><?cs
/if ?><?cs
/each ?>

View File

@ -1,41 +1,45 @@
<?cs # $Id$ ?><?cs
def:warning(warnname)
?><div class="warning"><?cs
if:?Lang.WarningMessage[warnname].Title
?>
<h1><?cs var:html_escape(Lang.WarningMessage[warnname].Title) ?></h1>
<p><?cs var:html_escape(Lang.WarningMessage[warnname].Text) ?></p>
<?cs else ?>
<h1>unknown warning message</h1>
<p>could not find warning message: '<?cs var:warnname ?>'</p>
# the following macro is as ugly as possible - but somehow we have to manage
to use 'normal' and 'plugin' messages in a clean way:
Lang.WarningMessage.??? - used by core functions
Lang.Plugins.PLUGINNAME.WarningMessage.??? - used by plugins ?><?cs
def:message_dispatch(mname, type, category)
?><?cs # split the message name into a (potentially existing) plugin-name prefix and the suffix (the python equivalent of the following three lines would be:
plugPrefix, PlugSuffix = mname[0:mname.find(".",8), mname[mname.find(".",8)+1:]
?><?cs loop:x = #8, #40, #1 ?><?cs if:(string.slice(mname,x,x+1) == ".") && !(?savedX) ?><?cs set:savedX = x ?><?cs /if ?><?cs /loop
?><?cs set:plugPrefix = string.slice(mname,0,savedX)
?><?cs set:plugSuffix = string.slice(mname,savedX+1,string.length(mname))
?><?cs # preparations are done - now start writing
?><div class="<?cs var:type ?>"><?cs
# check if it is a 'normal' message ?><?cs
if:?Lang[category][mname].Title ?>
<h1><?cs var:html_escape(Lang[category][mname].Title) ?></h1>
<p><?cs var:html_escape(Lang[category][mname].Text) ?></p>
<?cs # check if the mname starts with "Plugins." ... ?><?cs
elif:(string.slice(mname,0,8) == "Plugins.") && ?Lang[plugPrefix][category][plugSuffix].Title ?>
<h1><?cs var:html_escape(Lang[plugPrefix][category][plugSuffix].Title) ?></h1>
<p><?cs var:html_escape(Lang[plugPrefix][category][plugSuffix].Text) ?></p>
<?cs # the message does not seem to exist ... ?><?cs
else ?>
<h1>unknown <?cs var:type ?> message</h1>
<p>could not find <?cs var:type ?> message: '<?cs var:mname ?>'</p>
<?cs /if ?></div><?cs
/def ?><?cs
def:error(errname)
?><div class="error"><?cs
if:?Lang.ErrorMessage[errname].Title
?>
<h1><?cs var:html_escape(Lang.ErrorMessage[errname].Title) ?></h1>
<p><?cs var:html_escape(Lang.ErrorMessage[errname].Text) ?></p>
<?cs else ?>
<h1>unknown error message</h1>
<p>could not find error message: '<?cs var:errname ?>'</p>
<?cs /if ?></div><?cs
def:warning(mname)
?><?cs call:message_dispatch(mname, "warning", "WarningMessage") ?><?cs
/def ?><?cs
def:success(succname)
?><div class="success"><?cs
if:?Lang.SuccessMessage[succname].Title
?>
<h1><?cs var:html_escape(Lang.SuccessMessage[succname].Title) ?></h1>
<p><?cs var:html_escape(Lang.SuccessMessage[succname].Text) ?></p>
<?cs else ?>
<h1>unknown success message</h1>
<p>could not find success message: '<?cs var:succname ?>'</p>
<?cs /if ?></div><?cs
def:error(mname)
?><?cs call:message_dispatch(mname, "error", "ErrorMessage") ?><?cs
/def ?><?cs
def:success(mname)
?><?cs call:message_dispatch(mname, "success", "SuccessMessage") ?><?cs
/def ?><?cs

View File

@ -1,17 +1,15 @@
<?cs # $Id$ ?>
<!-- TODO: completely useless for now - do something about it :) -->
<?cs # we use "loop" instead of "each" to keep the order of the disks ?>
<?cs loop: index = #0, subcount(Data.Disks)-1, #1 ?>
<?cs each:volume = Data.Disks ?>
<a href="<?cs call:link('show_volume','device',volume.device,'','') ?>" title="<?cs
var:volume.name ?>">
<a href="<?cs call:link('show_volume','device',Data.Disks[index].device,'','') ?>" title="<?cs
var:Data.Disks[index].name ?>">
<?cs # the "div" is the container for the background image ?>
<div class="<?cs if:volume.isActive ?>active<?cs else ?>passive<?cs /if ?><?cs
if:volume.device == Data.CurrentDisk.device ?> current<?cs /if ?>">
<p><?cs var:volume.name ?></p>
<div class="<?cs if:Data.Disks[index].active ?>active<?cs else ?>passive<?cs /if ?><?cs
if:Data.Disks[index].device == Data.CurrentDisk.device ?> current<?cs /if ?>">
<p><?cs var:Data.Disks[index].name ?></p>
</div>
</a>
<?cs /each ?>
<?cs /loop ?>