lars
1c8db28989
simple partitioning interface finished order in navigation bar changed ignore extended partitions (in container list)
495 lines
16 KiB
Python
Executable file
495 lines
16 KiB
Python
Executable file
import CryptoBox
|
|
import WebInterfaceDataset
|
|
import re
|
|
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'}
|
|
'''
|
|
|
|
|
|
def __init__(self):
|
|
import logging
|
|
self.cbox = CryptoBox.CryptoBoxProps()
|
|
self.log = logging.getLogger("CryptoBox")
|
|
self.prefs = self.cbox.prefs
|
|
self.__resetDataset()
|
|
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):
|
|
"""this method has to be called at the beginning of every "site" action
|
|
important: only at the beginning of an action (to not loose information)
|
|
important: for _every_ "site" action (cherrypy is stateful)
|
|
"""
|
|
self.dataset = WebInterfaceDataset.WebInterfaceDataset(self.cbox, self.prefs)
|
|
|
|
|
|
def __isHDAvailable(self):
|
|
#TODO: implement this
|
|
return True
|
|
|
|
|
|
def __check_config(self):
|
|
#TODO: from now on a cryptobox is always configured
|
|
return True
|
|
|
|
|
|
def __check_init_running(self):
|
|
#TODO: implement this check (is mkfs still running?)
|
|
return False
|
|
|
|
|
|
######################################################################
|
|
## put real sites down here and don't forget to expose them at the end
|
|
|
|
def logs(self, weblang=""):
|
|
'''displays a HTML version of the logfile
|
|
'''
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
self.dataset["Data.Log"] = "<br/>".join(self.cbox.getLogData(lines=30, maxSize=2000))
|
|
return self.__render("show_log")
|
|
|
|
|
|
def status(self, weblang=""):
|
|
'''shows the current status of the box
|
|
'''
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
if not self.__check_config():
|
|
self.dataset["Data.Warning"] = "NotInitialized"
|
|
return self.__render("form_init")
|
|
elif self.__check_init_running():
|
|
self.dataset["Data.Warning"] = "InitNotFinished"
|
|
self.dataset["Data.Redirect.Action"] = "form_config"
|
|
self.dataset["Data.Redirect.Delay"] = "30"
|
|
return self.__render("empty")
|
|
else:
|
|
self.dataset["Data.Redirect.Delay"] = "60"
|
|
return self.__render("show_status")
|
|
|
|
|
|
def doc(self,page="",weblang=""):
|
|
'''prints the offline wikipage
|
|
'''
|
|
import re
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
|
|
## check for invalid characters
|
|
if page and not re.search(u'\W', page):
|
|
self.dataset["Data.Doc.Page"] = page
|
|
else:
|
|
## display this page as default help page
|
|
self.dataset["Data.Doc.Page"] ="CryptoBoxUser"
|
|
|
|
return self.__render("show_doc")
|
|
|
|
|
|
def system(self, weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
return self.__render("form_system")
|
|
|
|
|
|
def index(self, weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
return self.__render("show_status")
|
|
|
|
|
|
def show_volume(self, device="", weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
if self.__setDevice(device):
|
|
return self.__render("show_volume")
|
|
else:
|
|
if self.cbox.getContainerList():
|
|
return self.__render("show_volumes")
|
|
else:
|
|
return self.__render("show_status")
|
|
|
|
|
|
def show_volumes(self, weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
return self.__render("show_volumes")
|
|
|
|
|
|
def volume_name_set(self, device="", volume_name="", weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
if self.__setDevice(device):
|
|
volume_name = volume_name.strip()
|
|
if self.__checkVolumeName(volume_name):
|
|
container = self.cbox.getContainer(device)
|
|
try:
|
|
container.setName(volume_name)
|
|
# TODO: specify the possible exceptions
|
|
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))
|
|
self.dataset.setCurrentDiskState(device)
|
|
else:
|
|
self.dataset["Data.Warning"] = "InvalidVolumeName"
|
|
return self.__render("show_volume")
|
|
else:
|
|
if self.cbox.getContainerList():
|
|
return self.__render("show_volumes")
|
|
else:
|
|
return self.__render("show_status")
|
|
|
|
|
|
def mount_do(self, device, crypto_password=None, weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
if self.__setDevice(device):
|
|
container = self.cbox.getContainer(device)
|
|
if container.isMounted():
|
|
self.dataset["Data.Warning"] = "IsMounted"
|
|
self.log.warn("the device (%s) is already mounted" % device)
|
|
else:
|
|
try:
|
|
if container.getType() == container.Types["luks"]:
|
|
## encrypted luks container
|
|
if not crypto_password:
|
|
self.dataset["Data.Warning"] = "EmptyCryptoPassword"
|
|
self.log.warn("no password was supplied for mounting of device '%s'" % device)
|
|
return self.__render("show_volume")
|
|
else:
|
|
container.mount(crypto_password)
|
|
elif container.getType() == container.Types["plain"]:
|
|
## plain container
|
|
container.mount()
|
|
else:
|
|
## mounting is not possible
|
|
# TODO: wrong warning message - replace it
|
|
self.dataset["Data.Warning"] = "MountFailed"
|
|
self.log.warn("this type of container (%s) cannot be mounted - sorry!" % device)
|
|
except (Exception, "MountError"):
|
|
self.dataset["Data.Warning"] = "MountFailed"
|
|
self.log.warn("failed to mount the device (%s)" % device)
|
|
else:
|
|
self.log.info("successfully mounted the container (%s)" % device)
|
|
self.dataset.setCurrentDiskState(device)
|
|
else:
|
|
if self.cbox.getContainerList():
|
|
return self.__render("show_volumes")
|
|
else:
|
|
return self.__render("show_status")
|
|
return self.__render("show_volume")
|
|
|
|
|
|
def volume_init_ask(self, device, encryption=None, weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
if self.__setDevice(device):
|
|
container = self.cbox.getContainer(device)
|
|
if container.isMounted():
|
|
self.dataset["Data.Warning"] = "VolumeMayNotBeMounted"
|
|
self.log.warn("initialization is not possible as long as the device (%s) is mounted" % device)
|
|
return self.__render("show_volume")
|
|
else:
|
|
if encryption is None:
|
|
self.dataset["Data.Init.isCrypto"] = 0
|
|
else:
|
|
self.dataset["Data.Init.isCrypto"] = 1
|
|
return self.__render("form_init")
|
|
else:
|
|
if self.cbox.getContainerList():
|
|
return self.__render("show_volumes")
|
|
else:
|
|
return self.__render("show_status")
|
|
|
|
|
|
def init_do(self, device, confirm, crypto_password=None, crypto_password2=None, encryption=None, weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
if self.__setDevice(device):
|
|
container = self.cbox.getContainer(device)
|
|
## set 'Data.Init.isCrypto' - just in case, we have to show the same form again
|
|
if encryption is None:
|
|
self.dataset["Data.Init.isCrypto"] = 0
|
|
else:
|
|
self.dataset["Data.Init.isCrypto"] = 1
|
|
if container.isMounted():
|
|
self.dataset["Data.Warning"] = "VolumeMayNotBeMounted"
|
|
self.log.warn("initialization is not possible as long as the device (%s) is mounted" % device)
|
|
return self.__render("form_init")
|
|
else:
|
|
# TODO: we have to compare 'confirm' with the value in the language file - IMPORTANT!
|
|
if not confirm:
|
|
self.dataset["Data.Warning"] = "InitNotConfirmed"
|
|
self.log.warn("the confirmation sentence for initialization of the device '%s' was wrong" % device)
|
|
return self.__render("form_init")
|
|
try:
|
|
if not encryption is None:
|
|
if not crypto_password:
|
|
self.dataset["Data.Warning"] = "EmptyCryptoPassword"
|
|
self.log.warn("no crypto password was supplied for initialization of device '%s'" % device)
|
|
return self.__render("form_init")
|
|
if crypto_password != crypto_password2:
|
|
self.dataset["Data.Warning"] = "DifferentCryptoPasswords"
|
|
self.log.warn("the crypto password was not repeated correctly for initialization of device '%s'" % device)
|
|
return self.__render("form_init")
|
|
container.create(container.Types["luks"], crypto_password)
|
|
else:
|
|
container.create(container.Types["plain"])
|
|
# TODO: specify the exception
|
|
except Exception, errMsg:
|
|
# TODO: wrong error/warning message - change it
|
|
self.dataset["Data.Error"] = "InitFailed"
|
|
self.log.warn("initialization of device '%s' failed" % device)
|
|
self.log.warn("reason: %s" % errMsg)
|
|
return self.__render("form_init")
|
|
else:
|
|
self.log.info("successfully initialized device '%s'" % device)
|
|
# reread the dataset
|
|
self.dataset.setCurrentDiskState(device)
|
|
return self.__render("show_volume")
|
|
else:
|
|
if self.cbox.getContainerList():
|
|
return self.__render("show_volumes")
|
|
else:
|
|
return self.__render("show_status")
|
|
|
|
|
|
def test(self, weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
return "test passed"
|
|
|
|
|
|
def umount_do(self, device, weblang=""):
|
|
self.__resetDataset()
|
|
self.__setWebLang(weblang)
|
|
if self.__setDevice(device):
|
|
container = self.cbox.getContainer(device)
|
|
if not container.isMounted():
|
|
self.dataset["Data.Warning"] = "NotMounted"
|
|
self.log.warn("the device (%s) is currently not mounted" % device)
|
|
else:
|
|
try:
|
|
if container.getType() == container.Types["luks"]:
|
|
## encrypted luks container
|
|
container.umount()
|
|
elif container.getType() == container.Types["plain"]:
|
|
## plain container
|
|
container.umount()
|
|
else:
|
|
## mounting is not possible
|
|
# TODO: wrong warning message - replace it
|
|
self.dataset["Data.Warning"] = "UmountFailed"
|
|
self.log.warn("this type of container (%s) cannot be umounted - sorry!" % device)
|
|
except (Exception, "UmountError"):
|
|
self.dataset["Data.Warning"] = "UmountFailed"
|
|
self.log.warn("failed to unmount the device (%s)" % device)
|
|
else:
|
|
self.log.info("successfully unmounted the container (%s)" % device)
|
|
# reread the dataset
|
|
self.dataset.setCurrentDiskState(device)
|
|
else:
|
|
if self.cbox.getContainerList():
|
|
return self.__render("show_volumes")
|
|
else:
|
|
return self.__render("show_status")
|
|
return self.__render("show_volume")
|
|
|
|
|
|
def return_plugin_action(self, plugin_name):
|
|
def handler(**args):
|
|
self.__resetDataset()
|
|
try:
|
|
self.__setWebLang(args["weblang"])
|
|
del args["weblang"]
|
|
except KeyError:
|
|
pass
|
|
plugin = self.pluginList.getPlugin(plugin_name)
|
|
nextTemplate = plugin.doAction(self.dataset, self.cbox, **args)
|
|
return self.__render(nextTemplate, plugin_name)
|
|
return handler
|
|
|
|
|
|
'''
|
|
## DONE: these functions are pythonized
|
|
#################### show_log #######################
|
|
##################### doc ############################
|
|
##################### poweroff ######################
|
|
##################### reboot ########################
|
|
|
|
## but there are even more TODO
|
|
#-------------------------------------------------------#
|
|
# here you may define all cases that require a harddisk #
|
|
#-------------------------------------------------------#
|
|
################ umount_do #######################
|
|
elif action == "unmount_do":
|
|
if not device:
|
|
self.log.debug("invalid device chosen: %s" device
|
|
settings["Data.Warning"] = "InvalidDevice"
|
|
settings["Data.Action"] = "empty"
|
|
elif not True: #TODO: replace True with check_config()
|
|
settings["Data.Warning"] = "NotInitialized"
|
|
settings["Data.Action"] = "form_init"
|
|
elif True: #TODO: replace True with check_init_running()
|
|
settings["Data.Warning"] = "InitNotFinished"
|
|
settings["Data.Action"] = "empty"
|
|
settings["Data.Redirect.Action"] = "form_config"
|
|
settings["Data.Redirect.Delay"] = "30"
|
|
elif not True: #TODO: replace True with check_mounted(device)
|
|
settings["Data.Warning"] = "NotMounted"
|
|
settings["Data.Action"] = "show_volume"
|
|
else: #unmount
|
|
#TODO: replace this line with umount_vol(device)
|
|
if True: #TODO: replace True with check_mounted(device)
|
|
settings["Data.Warning"] = "UmountFailed"
|
|
settings["Data.Action"] = "show_volume"
|
|
else:
|
|
settings["Data.Action"] = "show_volume"
|
|
################ mount_do ########################
|
|
elif action == "mount_do":
|
|
if device:
|
|
pass #TODO: is_encrypted = check_device_encryption(device)
|
|
else:
|
|
self.log.debug("invalid device chosen: %s" device
|
|
settings["Data.Warning"] = "InvalidDevice"
|
|
settings["Data.Action"] = "empty"
|
|
elif not True: #TODO: replace True with check_config()
|
|
settings["Data.Warning"] = "NotInitialized"
|
|
settings["Data.Action"] = "form_init"
|
|
#at cryptobox.pl line 568
|
|
'''
|
|
|
|
|
|
##################### input checker ##########################
|
|
def __setWebLang(self, value):
|
|
## TODO: add some code to evaluate the language setting of the browser
|
|
guess = value
|
|
availLangs = self.cbox.getAvailableLanguages()
|
|
## TODO: add some warnings for an invalid choosen language
|
|
if not guess or \
|
|
not guess in availLangs or \
|
|
re.search(u'\W', guess):
|
|
guess = self.prefs["WebSettings"]["Language"]
|
|
## maybe the language is still not valid
|
|
if not guess in availLangs:
|
|
self.log.warn("the configured language is invalid: %s" % guess)
|
|
guess = availLangs[0]
|
|
self.dataset["Settings.Language"] = guess
|
|
self.dataset["Settings.DocLang"] = guess
|
|
self.dataset["Settings.LinkAttrs.weblang"] = guess
|
|
|
|
|
|
def __setDevice(self, device):
|
|
if device and re.match(u'[\w /\-]+$', device) and self.cbox.getContainer(device):
|
|
self.log.debug("select device: %s" % device)
|
|
self.dataset.setCurrentDiskState(device)
|
|
return True
|
|
else:
|
|
self.log.warn("invalid device: %s" % device)
|
|
self.dataset["Data.Warning"] = "InvalidDevice"
|
|
return False
|
|
|
|
|
|
def __checkVolumeName(self, name):
|
|
if name and re.match(u'[\w \-]+$', name):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def __render(self, template, plugin=None):
|
|
'''renders from clearsilver templates and returns the resulting html
|
|
|
|
Gets a dictionary with all settings, nessessary for rendering.
|
|
In fact the dictionary is a copy of the CryptoBoxWerbserverSite
|
|
object, that calls this render method. This might be bloat but
|
|
this way the render method has always a complete, actual set of values.
|
|
'''
|
|
import os
|
|
try:
|
|
import neo_cgi, neo_util, neo_cs
|
|
except ImportError:
|
|
errorMsg = "Could not import clearsilver module. Try 'apt-get install python-clearsilver'."
|
|
self.log.error(errorMsg)
|
|
sys.stderr.write(errorMsg)
|
|
raise ImportError, errorMsg
|
|
|
|
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"] = plugin_cs_file or default_cs_file
|
|
self.log.info("rendering site: " + template)
|
|
|
|
cs_path = os.path.join(self.prefs["Locations"]["TemplateDir"], "main.cs")
|
|
if not os.access(cs_path, os.R_OK):
|
|
log.error("Couldn't read clearsilver file: %s" % cs_path)
|
|
return "Couldn't read clearsilver file: %s" % cs_path
|
|
|
|
# use the user selected language instead of the configured
|
|
hdf_path = os.path.join(self.prefs["Locations"]["LangDir"], self.dataset["Settings.Language"] + ".hdf")
|
|
if not os.access(hdf_path, os.R_OK):
|
|
log.error("Couldn't read language file: %s" % hdf_path)
|
|
return "Couldn't read language file: %s" % hdf_path
|
|
|
|
## add the current state of the plugins to the hdf dataset
|
|
self.dataset.setPluginState(self.pluginList)
|
|
|
|
## update the container information
|
|
self.dataset.setContainersState()
|
|
|
|
hdf = neo_util.HDF()
|
|
hdf.readFile(hdf_path)
|
|
self.log.debug(self.dataset)
|
|
for key in self.dataset.keys():
|
|
hdf.setValue(key,str(self.dataset[key]))
|
|
## load languaga data of plugins
|
|
self.pluginList.loadLanguageData(hdf, lang=self.dataset["Settings.Language"])
|
|
cs = neo_cs.CS(hdf)
|
|
cs.parseFile(cs_path)
|
|
return cs.render()
|
|
|
|
|
|
############################################################################
|
|
## to make the sites visible through the webserver they must be exposed here
|
|
index.exposed = True
|
|
doc.exposed = True
|
|
logs.exposed = True
|
|
system.exposed = True
|
|
status.exposed = True
|
|
show_volume.exposed = True
|
|
volume_name_set.exposed = True
|
|
mount_do.exposed = True
|
|
volume_init_ask.exposed = True
|
|
init_do.exposed = True
|
|
umount_do.exposed = True
|
|
show_volumes.exposed = True
|
|
test.exposed = True
|
|
|
|
|
|
|
|
"""
|
|
## TODO: check this before anything else
|
|
if self.cbox.getAvailableDocLanguages():
|
|
self.dataset["Data.Error"] = "NoDocumentation"
|
|
return self.__render("show_status")
|
|
"""
|