diff --git a/pythonrewrite/bin/CryptoBox.py b/pythonrewrite/bin/CryptoBox.py index daf75da..ac74e63 100755 --- a/pythonrewrite/bin/CryptoBox.py +++ b/pythonrewrite/bin/CryptoBox.py @@ -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() diff --git a/pythonrewrite/bin/CryptoBoxContainer.py b/pythonrewrite/bin/CryptoBoxContainer.py index 8e1e334..90ea84a 100755 --- a/pythonrewrite/bin/CryptoBoxContainer.py +++ b/pythonrewrite/bin/CryptoBoxContainer.py @@ -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): diff --git a/pythonrewrite/bin/CryptoBoxTools.py b/pythonrewrite/bin/CryptoBoxTools.py new file mode 100644 index 0000000..a73a659 --- /dev/null +++ b/pythonrewrite/bin/CryptoBoxTools.py @@ -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) + diff --git a/pythonrewrite/bin/Plugins.py b/pythonrewrite/bin/Plugins.py index 1d87df6..368ce40 100644 --- a/pythonrewrite/bin/Plugins.py +++ b/pythonrewrite/bin/Plugins.py @@ -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) diff --git a/pythonrewrite/bin/WebInterfaceDataset.py b/pythonrewrite/bin/WebInterfaceDataset.py index a91e7be..fb770a1 100644 --- a/pythonrewrite/bin/WebInterfaceDataset.py +++ b/pythonrewrite/bin/WebInterfaceDataset.py @@ -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): diff --git a/pythonrewrite/bin/WebInterfaceSites.py b/pythonrewrite/bin/WebInterfaceSites.py index 7534ac6..04a6e17 100755 --- a/pythonrewrite/bin/WebInterfaceSites.py +++ b/pythonrewrite/bin/WebInterfaceSites.py @@ -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,17 +28,10 @@ class WebInterfaceSites: self.log = logging.getLogger("CryptoBox") self.prefs = self.cbox.prefs self.__resetDataset() - self.plugins = PluginManager(self.prefs["Locations"]["PluginDir"]) - self.__exposePlugins() - + 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 __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) - def __resetDataset(self): self.dataset = WebInterfaceDataset.WebInterfaceDataset(self.cbox, self.prefs) @@ -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() diff --git a/pythonrewrite/plugins/date/date.py b/pythonrewrite/plugins/date/date.py index 5f70e0b..2502075 100644 --- a/pythonrewrite/plugins/date/date.py +++ b/pythonrewrite/plugins/date/date.py @@ -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() diff --git a/pythonrewrite/plugins/date/form_date.cs b/pythonrewrite/plugins/date/form_date.cs index 838db44..451f96d 100644 --- a/pythonrewrite/plugins/date/form_date.cs +++ b/pythonrewrite/plugins/date/form_date.cs @@ -1,42 +1,42 @@ -
+ - + -
+
+
:
+
.
+ var:Data.Plugins.network.ip.oc1 ?>" />.
.
+ var:Data.Plugins.network.ip.oc2 ?>" />.
.
+ var:Data.Plugins.network.ip.oc3 ?>" />.
+
+
- -
+ + + + +- -
+ + + + + + + 0 ?> + + + + + + + + 0 ?> + + + + + + + diff --git a/pythonrewrite/plugins/plugin-interface.txt b/pythonrewrite/plugins/plugin-interface.txt index 97ff779..a801d5f 100644 --- a/pythonrewrite/plugins/plugin-interface.txt +++ b/pythonrewrite/plugins/plugin-interface.txt @@ -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 "could not find warning message: ''
+# 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 ?>could not find message: ''
could not find error message: ''
-could not find success message: ''
-