From 3224d59dfe1f6e56b367786b74c9fa0336250045 Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 18 Aug 2007 00:45:12 +0000 Subject: [PATCH] change identation from tabs to spaces --- plugins/date/date.py | 142 +- plugins/date/root_action.py | 42 +- plugins/date/unittests.py | 128 +- plugins/disks/disks.py | 30 +- plugins/disks/unittests.py | 28 +- .../encrypted_webinterface.py | 416 ++--- plugins/encrypted_webinterface/root_action.py | 102 +- plugins/encrypted_webinterface/unittests.py | 16 +- plugins/help/help.py | 92 +- plugins/help/unittests.py | 110 +- .../language_selection/language_selection.py | 28 +- plugins/language_selection/unittests.py | 34 +- plugins/logs/logs.py | 384 ++--- plugins/logs/unittests.py | 74 +- plugins/network/network.py | 682 ++++---- plugins/network/root_action.py | 142 +- plugins/network/unittests.py | 94 +- plugins/partition/partition.py | 908 +++++----- plugins/partition/root_action.py | 138 +- plugins/partition/unittests.py | 10 +- plugins/plugin_manager/plugin_manager.py | 220 +-- plugins/plugin_manager/unittests.py | 118 +- plugins/plugin_template/plugin_template.py | 56 +- plugins/shutdown/root_action.py | 56 +- plugins/shutdown/shutdown.py | 80 +- plugins/shutdown/unittests.py | 16 +- .../system_preferences/system_preferences.py | 20 +- plugins/system_preferences/unittests.py | 18 +- plugins/user_manager/unittests.py | 270 +-- plugins/user_manager/user_manager.py | 140 +- plugins/volume_automount/unittests.py | 54 +- plugins/volume_automount/volume_automount.py | 158 +- plugins/volume_chpasswd/unittests.py | 12 +- plugins/volume_chpasswd/volume_chpasswd.py | 114 +- plugins/volume_details/unittests.py | 10 +- plugins/volume_details/volume_details.py | 20 +- plugins/volume_format_fs/unittests.py | 136 +- plugins/volume_format_fs/volume_format_fs.py | 192 +-- plugins/volume_mount/unittests.py | 306 ++-- plugins/volume_mount/volume_mount.py | 192 +-- plugins/volume_props/unittests.py | 10 +- plugins/volume_props/volume_props.py | 106 +- plugins/volume_rename/unittests.py | 248 +-- plugins/volume_rename/volume_rename.py | 82 +- scripts/update_po_files.py | 274 +-- setup.py | 188 +-- src/cryptobox/core/container.py | 1496 ++++++++--------- src/cryptobox/core/exceptions.py | 166 +- src/cryptobox/core/main.py | 442 ++--- src/cryptobox/core/settings.py | 1212 ++++++------- src/cryptobox/core/tools.py | 384 ++--- src/cryptobox/plugins/base.py | 464 ++--- src/cryptobox/plugins/manage.py | 130 +- src/cryptobox/tests/__init__.py | 2 +- src/cryptobox/tests/base.py | 180 +- src/cryptobox/tests/test.cryptobox.py | 216 +-- src/cryptobox/tests/test.cryptoboxtools.py | 58 +- src/cryptobox/tests/test.plugins.py | 38 +- src/cryptobox/tests/test.websites.py | 42 +- src/cryptobox/tests/tools.py | 178 +- src/cryptobox/web/dataset.py | 364 ++-- src/cryptobox/web/sites.py | 1160 ++++++------- 62 files changed, 6614 insertions(+), 6614 deletions(-) diff --git a/plugins/date/date.py b/plugins/date/date.py index 88b0229..f415787 100644 --- a/plugins/date/date.py +++ b/plugins/date/date.py @@ -31,86 +31,86 @@ import cryptobox.plugins.base class date(cryptobox.plugins.base.CryptoBoxPlugin): - """The date feature of the CryptoBox. - """ + """The date feature of the CryptoBox. + """ - plugin_capabilities = [ "system" ] - plugin_visibility = [ "preferences" ] - request_auth = False - rank = 10 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = False + rank = 10 - def do_action(self, store=None, year=0, month=0, day=0, hour=0, minute=0): - """The action handler. - """ - import datetime - if store: - try: - year, month, day = int(year), int(month), int(day) - hour, minute = int(hour), int(minute) - ## check if the values are valid - datetime.datetime(year, month, day, hour, minute) - except ValueError: - self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" - else: - new_date = "%02d%02d%02d%02d%d" % (month, day, hour, minute, year) - if self.__set_date(new_date): - self.cbox.log.info("changed date to: %s" % new_date) - self.hdf["Data.Success"] = "Plugins.date.DateChanged" - else: - ## a failure should usually be an invalid date (we do not check it really) - self.cbox.log.info("failed to set date: %s" % new_date) - self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" - self.__prepare_form_data() - return "form_date" + def do_action(self, store=None, year=0, month=0, day=0, hour=0, minute=0): + """The action handler. + """ + import datetime + if store: + try: + year, month, day = int(year), int(month), int(day) + hour, minute = int(hour), int(minute) + ## check if the values are valid + datetime.datetime(year, month, day, hour, minute) + except ValueError: + self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" + else: + new_date = "%02d%02d%02d%02d%d" % (month, day, hour, minute, year) + if self.__set_date(new_date): + self.cbox.log.info("changed date to: %s" % new_date) + self.hdf["Data.Success"] = "Plugins.date.DateChanged" + else: + ## a failure should usually be an invalid date (we do not check it really) + self.cbox.log.info("failed to set date: %s" % new_date) + self.hdf["Data.Warning"] = "Plugins.date.InvalidDate" + self.__prepare_form_data() + return "form_date" - def get_status(self): - """Retrieve the status of the feature. - """ - now = self.__get_current_date() - return "%d/%d/%d/%d/%d/%d" % \ - (now.year, now.month, now.day, now.hour, now.minute, now.second) + def get_status(self): + """Retrieve the status of the feature. + """ + now = self.__get_current_date() + return "%d/%d/%d/%d/%d/%d" % \ + (now.year, now.month, now.day, now.hour, now.minute, now.second) - def get_warnings(self): - import os - warnings = [] - if not os.path.isfile(self.root_action.DATE_BIN): - warnings.append((48, "Plugins.%s.MissingProgramDate" % self.get_name())) - return warnings + def get_warnings(self): + import os + warnings = [] + if not os.path.isfile(self.root_action.DATE_BIN): + warnings.append((48, "Plugins.%s.MissingProgramDate" % self.get_name())) + return warnings - def __prepare_form_data(self): - """Set some hdf values. - """ - cur_date = self.__get_current_date() - self.hdf[self.hdf_prefix + "year"] = cur_date.year - self.hdf[self.hdf_prefix + "month"] = cur_date.month - self.hdf[self.hdf_prefix + "day"] = cur_date.day - self.hdf[self.hdf_prefix + "hour"] = cur_date.hour - self.hdf[self.hdf_prefix + "minute"] = cur_date.minute + def __prepare_form_data(self): + """Set some hdf values. + """ + cur_date = self.__get_current_date() + self.hdf[self.hdf_prefix + "year"] = cur_date.year + self.hdf[self.hdf_prefix + "month"] = cur_date.month + self.hdf[self.hdf_prefix + "day"] = cur_date.day + self.hdf[self.hdf_prefix + "hour"] = cur_date.hour + self.hdf[self.hdf_prefix + "minute"] = cur_date.minute - def __get_current_date(self): - """Retrieve the current date and time. - """ - import datetime - return datetime.datetime(2000, 1, 1).now() - + def __get_current_date(self): + """Retrieve the current date and time. + """ + import datetime + return datetime.datetime(2000, 1, 1).now() + - def __set_date(self, new_date): - """Set a new date and time. - """ - import subprocess - import os - proc = subprocess.Popen( - shell = False, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "plugin", - os.path.join(self.plugin_dir, "root_action.py"), - new_date]) - proc.wait() - return proc.returncode == 0 + def __set_date(self, new_date): + """Set a new date and time. + """ + import subprocess + import os + proc = subprocess.Popen( + shell = False, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.plugin_dir, "root_action.py"), + new_date]) + proc.wait() + return proc.returncode == 0 diff --git a/plugins/date/root_action.py b/plugins/date/root_action.py index 7d3b91d..1b5da94 100755 --- a/plugins/date/root_action.py +++ b/plugins/date/root_action.py @@ -33,26 +33,26 @@ import sys import os if __name__ == "__main__": - args = sys.argv[1:] + 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) - - if re.search(r'\D', args[0]): - sys.stderr.write("%s: illegal argument (%s)\n" % (self_bin, args[0])) - sys.exit(1) - - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - args = [DATE_BIN, args[0]]) - proc.wait() - sys.exit(proc.returncode) + 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) + + if re.search(r'\D', args[0]): + sys.stderr.write("%s: illegal argument (%s)\n" % (self_bin, args[0])) + sys.exit(1) + + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [DATE_BIN, args[0]]) + proc.wait() + sys.exit(proc.returncode) diff --git a/plugins/date/unittests.py b/plugins/date/unittests.py index 4d90010..15f52ea 100644 --- a/plugins/date/unittests.py +++ b/plugins/date/unittests.py @@ -23,75 +23,75 @@ __revision__ = "$Id$" from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - - def test_get_date(self): - """retrieve the current date""" - date = self._get_current_date() - + + def test_get_date(self): + """retrieve the current date""" + date = self._get_current_date() + - def test_change_date(self): - """set the date back and forth""" - now = self._get_current_date() - ## copy current time - new_date = dict(now) - ## move three minutes forward (more is not nice because of screensavers) - new_date["minute"] = (now["minute"] + 3) % 60 - ## in case of minute-overflow we also have to move the hour a little bit forward - new_date["hour"] = now["hour"] + ((now["minute"] + 3) / 60) - ## move forward ... - self._setDate(new_date) - self.assertEquals(new_date, self._get_current_date()) - ## ... and backward - self._setDate(now) - self.assertEquals(now, self._get_current_date()) - + def test_change_date(self): + """set the date back and forth""" + now = self._get_current_date() + ## copy current time + new_date = dict(now) + ## move three minutes forward (more is not nice because of screensavers) + new_date["minute"] = (now["minute"] + 3) % 60 + ## in case of minute-overflow we also have to move the hour a little bit forward + new_date["hour"] = now["hour"] + ((now["minute"] + 3) / 60) + ## move forward ... + self._setDate(new_date) + self.assertEquals(new_date, self._get_current_date()) + ## ... and backward + self._setDate(now) + self.assertEquals(now, self._get_current_date()) + - def test_try_broken_date(self): - """expect error messages for invalid dates""" - self._setDate({"hour":12, "minute":40, "year":2004, "month":7, "day":0}) - self.cmd.find("invalid value for date") - self._setDate({"hour":12, "minute":40, "year":"x", "month":7, "day":2}) - self.cmd.find("invalid value for date") - self._setDate({"hour":12, "minute":40, "year":2004, "month":2, "day":31}) - self.cmd.find("invalid value for date") + def test_try_broken_date(self): + """expect error messages for invalid dates""" + self._setDate({"hour":12, "minute":40, "year":2004, "month":7, "day":0}) + self.cmd.find("invalid value for date") + self._setDate({"hour":12, "minute":40, "year":"x", "month":7, "day":2}) + self.cmd.find("invalid value for date") + self._setDate({"hour":12, "minute":40, "year":2004, "month":2, "day":31}) + self.cmd.find("invalid value for date") - def _get_current_date(self): - date_url = self.url + "date" - self.register_auth(date_url) - self.cmd.go(date_url) - self.cmd.find("Data.Status.Plugins.date=([0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+)$", "m") - dateNumbers = self.locals["__match__"].split("/") - self.assertEquals(len(dateNumbers), 6) - ## we ignore seconds - dateField = { - "year" : int(dateNumbers[0]), - "month" : int(dateNumbers[1]), - "day" : int(dateNumbers[2]), - "hour" : int(dateNumbers[3]), - "minute" : int(dateNumbers[4])} - return dateField - + def _get_current_date(self): + date_url = self.url + "date" + self.register_auth(date_url) + self.cmd.go(date_url) + self.cmd.find("Data.Status.Plugins.date=([0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+/[0-9]+)$", "m") + dateNumbers = self.locals["__match__"].split("/") + self.assertEquals(len(dateNumbers), 6) + ## we ignore seconds + dateField = { + "year" : int(dateNumbers[0]), + "month" : int(dateNumbers[1]), + "day" : int(dateNumbers[2]), + "hour" : int(dateNumbers[3]), + "minute" : int(dateNumbers[4])} + return dateField + - def _setDate(self, date): - """for now we have to use this function instead of the one below""" - date_url = self.url + "date?weblang=en&store=1&year=%s&month=%s&day=%s&hour=%s&minute=%s"\ - % (str(date["year"]), str(date["month"]), str(date["day"]), - str(date["hour"]), str(date["minute"])) - self.register_auth(date_url) - self.cmd.go(date_url) + def _setDate(self, date): + """for now we have to use this function instead of the one below""" + date_url = self.url + "date?weblang=en&store=1&year=%s&month=%s&day=%s&hour=%s&minute=%s"\ + % (str(date["year"]), str(date["month"]), str(date["day"]), + str(date["hour"]), str(date["minute"])) + self.register_auth(date_url) + self.cmd.go(date_url) - def _setDateBroken(self, date): - """this should work, but the parsing of twill seems to be broken - as soon as the twill bug is fixed, we should use this function""" - date_url = self.url + "date" - self.register_auth(date_url) - self.cmd.go(date_url) - self.cmd.formvalue("set_date", "year", str(date["year"])) - self.cmd.formvalue("set_date", "month", str(date["month"])) - self.cmd.formvalue("set_date", "day", str(date["day"])) - self.cmd.formvalue("set_date", "hour", str(date["hour"])) - self.cmd.formvalue("set_date", "minute", str(date["minute"])) - self.cmd.submit() + def _setDateBroken(self, date): + """this should work, but the parsing of twill seems to be broken + as soon as the twill bug is fixed, we should use this function""" + date_url = self.url + "date" + self.register_auth(date_url) + self.cmd.go(date_url) + self.cmd.formvalue("set_date", "year", str(date["year"])) + self.cmd.formvalue("set_date", "month", str(date["month"])) + self.cmd.formvalue("set_date", "day", str(date["day"])) + self.cmd.formvalue("set_date", "hour", str(date["hour"])) + self.cmd.formvalue("set_date", "minute", str(date["minute"])) + self.cmd.submit() diff --git a/plugins/disks/disks.py b/plugins/disks/disks.py index f82472e..37e7813 100644 --- a/plugins/disks/disks.py +++ b/plugins/disks/disks.py @@ -26,23 +26,23 @@ __revision__ = "$Id$" import cryptobox.plugins.base class disks(cryptobox.plugins.base.CryptoBoxPlugin): - """The disk feature of the CryptoBox. - """ + """The disk feature of the CryptoBox. + """ - plugin_capabilities = [ "system" ] - plugin_visibility = [ "menu" ] - request_auth = False - rank = 10 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "menu" ] + request_auth = False + rank = 10 - def do_action(self): - """The action handler. - """ - self.cbox.reread_container_list() - return "disks" + def do_action(self): + """The action handler. + """ + self.cbox.reread_container_list() + return "disks" - def get_status(self): - """Retrieve the current status of the feature. - """ - return ":".join([e.get_device() for e in self.cbox.get_container_list()]) + def get_status(self): + """Retrieve the current status of the feature. + """ + return ":".join([e.get_device() for e in self.cbox.get_container_list()]) diff --git a/plugins/disks/unittests.py b/plugins/disks/unittests.py index 80dd257..4ed5ed3 100644 --- a/plugins/disks/unittests.py +++ b/plugins/disks/unittests.py @@ -24,20 +24,20 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_form(self): - '''display all devices''' - self.register_auth(self.url) - self.cmd.go(self.url + "disks?weblang=en") - self.cmd.find("Available disks") + def test_read_form(self): + '''display all devices''' + self.register_auth(self.url) + self.cmd.go(self.url + "disks?weblang=en") + self.cmd.find("Available disks") - def test_is_device_in_list(self): - """check if the device-under-test is in the device list""" - self.register_auth(self.url) - self.cmd.go(self.url + "disks?weblang=en") - self.cmd.find("Available disks") - self.cmd.find(r'Data.Status.Plugins.disks=(.*)$', "m") - devices = self.locals["__match__"].split(":") - self.assertTrue(len(devices)>0) - self.assertTrue(self.device in devices) + def test_is_device_in_list(self): + """check if the device-under-test is in the device list""" + self.register_auth(self.url) + self.cmd.go(self.url + "disks?weblang=en") + self.cmd.find("Available disks") + self.cmd.find(r'Data.Status.Plugins.disks=(.*)$', "m") + devices = self.locals["__match__"].split(":") + self.assertTrue(len(devices)>0) + self.assertTrue(self.device in devices) diff --git a/plugins/encrypted_webinterface/encrypted_webinterface.py b/plugins/encrypted_webinterface/encrypted_webinterface.py index 915a81d..56228d2 100644 --- a/plugins/encrypted_webinterface/encrypted_webinterface.py +++ b/plugins/encrypted_webinterface/encrypted_webinterface.py @@ -35,232 +35,232 @@ import cherrypy CERT_FILENAME = 'cryptobox-ssl-certificate.pem' KEY_BITS = 1024 ISSUER_INFOS = { - "ST": "SomeIssuerState", - "L": "SomeIssuerLocality", - "O": "SomeIssuerOrganization", - "OU": "CryptoBox-ServerIssuer", - "CN": "cryptoboxIssuer", - "emailAddress": "infoIssuer@cryptobox.org"} + "ST": "SomeIssuerState", + "L": "SomeIssuerLocality", + "O": "SomeIssuerOrganization", + "OU": "CryptoBox-ServerIssuer", + "CN": "cryptoboxIssuer", + "emailAddress": "infoIssuer@cryptobox.org"} CERT_INFOS = { - "ST": "SomeState", - "L": "SomeLocality", - "O": "SomeOrganization", - "OU": "CryptoBox-Server", - "CN": "*", - "emailAddress": "info@cryptobox.org"} + "ST": "SomeState", + "L": "SomeLocality", + "O": "SomeOrganization", + "OU": "CryptoBox-Server", + "CN": "*", + "emailAddress": "info@cryptobox.org"} EXPIRE_TIME = 60*60*24*365*20 # 20 years forward and backward SIGN_DIGEST = "md5" PID_FILE = os.path.join("/tmp/cryptobox-stunnel.pid") class encrypted_webinterface(cryptobox.plugins.base.CryptoBoxPlugin): - """Provide an encrypted webinterface connection via stunnel - """ + """Provide an encrypted webinterface connection via stunnel + """ - plugin_capabilities = [ "system" ] - plugin_visibility = [] - request_auth = True - rank = 80 + plugin_capabilities = [ "system" ] + plugin_visibility = [] + request_auth = True + rank = 80 - def do_action(self): - """The action handler. - """ - return None + def do_action(self): + """The action handler. + """ + return None - def get_status(self): - """Retrieve the current state of the webinterface connection - """ - if self.__is_encrypted(): - return "1" - else: - return "0" + def get_status(self): + """Retrieve the current state of the webinterface connection + """ + if self.__is_encrypted(): + return "1" + else: + return "0" - def get_warnings(self): - """check if the connection is encrypted - """ - warnings = [] - ## check if m2crypto is available - try: - import M2Crypto - except ImportError: - warnings.append((45, "Plugins.%s.MissingModuleM2Crypto" % self.get_name())) - if not os.path.isfile(self.root_action.STUNNEL_BIN): - warnings.append((44, "Plugins.%s.MissingProgramStunnel" % self.get_name())) - if not self.__is_encrypted(): - ## plaintext connection -> "heavy security risk" (priority=20..39) - warnings.append((25, "Plugins.%s.NoSSL" % self.get_name())) - return warnings - + def get_warnings(self): + """check if the connection is encrypted + """ + warnings = [] + ## check if m2crypto is available + try: + import M2Crypto + except ImportError: + warnings.append((45, "Plugins.%s.MissingModuleM2Crypto" % self.get_name())) + if not os.path.isfile(self.root_action.STUNNEL_BIN): + warnings.append((44, "Plugins.%s.MissingProgramStunnel" % self.get_name())) + if not self.__is_encrypted(): + ## plaintext connection -> "heavy security risk" (priority=20..39) + warnings.append((25, "Plugins.%s.NoSSL" % self.get_name())) + return warnings + - def __is_encrypted(self): - """perform some checks for encrypted connections - """ - if cherrypy.request.scheme == "https": - return True - ## check an environment setting - this is quite common behind proxies - if os.environ.has_key("HTTPS"): - return True - ## check if it is a local connection (or via stunnel) - if cherrypy.request.headers.has_key("Remote-Host") \ - and (cherrypy.request.headers["Remote-Host"] == "127.0.0.1"): - return True - ## the arbitrarily chosen header is documented in README.proxy - if cherrypy.request.headers.has_key("X-SSL-Request") \ - and (cherrypy.request.headers["X-SSL-Request"] == "1"): - return True - ## it looks like a plain connection - return False - + def __is_encrypted(self): + """perform some checks for encrypted connections + """ + if cherrypy.request.scheme == "https": + return True + ## check an environment setting - this is quite common behind proxies + if os.environ.has_key("HTTPS"): + return True + ## check if it is a local connection (or via stunnel) + if cherrypy.request.headers.has_key("Remote-Host") \ + and (cherrypy.request.headers["Remote-Host"] == "127.0.0.1"): + return True + ## the arbitrarily chosen header is documented in README.proxy + if cherrypy.request.headers.has_key("X-SSL-Request") \ + and (cherrypy.request.headers["X-SSL-Request"] == "1"): + return True + ## it looks like a plain connection + return False + - def handle_event(self, event, event_info=None): - """Create a certificate during startup (if it does not exist) and run stunnel - """ - if event == "bootup": - cert_abs_name = self.cbox.prefs.get_misc_config_filename(CERT_FILENAME) - if not os.path.isfile(cert_abs_name): - cert = self.__get_certificate() - if cert is None: - ## failed to create a certificate? - self.cbox.log.warn("Failed to import M2Crypto python module" \ - + " required for SSL certificate generation") - return - try: - self.cbox.prefs.create_misc_config_file(CERT_FILENAME, cert) - self.cbox.log.info("Created new SSL certificate: %s" % \ - cert_abs_name) - ## make it non-readable for other users - try: - os.chmod(cert_abs_name, 0600) - except OSError, err_msg: - self.cbox.log.warn("Failed to change permissions of secret " \ - + "certificate file (%s): %s" % \ - (cert_abs_name, err_msg)) - except IOError, err_msg: - ## do not run stunnel without a certificate - self.cbox.log.warn("Failed to create new SSL certificate (%s): %s" \ - % (cert_abs_name, err_msg)) - return - self.__run_stunnel(cert_abs_name) - elif event == "shutdown": - self.__kill_stunnel() - + def handle_event(self, event, event_info=None): + """Create a certificate during startup (if it does not exist) and run stunnel + """ + if event == "bootup": + cert_abs_name = self.cbox.prefs.get_misc_config_filename(CERT_FILENAME) + if not os.path.isfile(cert_abs_name): + cert = self.__get_certificate() + if cert is None: + ## failed to create a certificate? + self.cbox.log.warn("Failed to import M2Crypto python module" \ + + " required for SSL certificate generation") + return + try: + self.cbox.prefs.create_misc_config_file(CERT_FILENAME, cert) + self.cbox.log.info("Created new SSL certificate: %s" % \ + cert_abs_name) + ## make it non-readable for other users + try: + os.chmod(cert_abs_name, 0600) + except OSError, err_msg: + self.cbox.log.warn("Failed to change permissions of secret " \ + + "certificate file (%s): %s" % \ + (cert_abs_name, err_msg)) + except IOError, err_msg: + ## do not run stunnel without a certificate + self.cbox.log.warn("Failed to create new SSL certificate (%s): %s" \ + % (cert_abs_name, err_msg)) + return + self.__run_stunnel(cert_abs_name) + elif event == "shutdown": + self.__kill_stunnel() + - def __kill_stunnel(self): - """try to kill a running stunnel daemon - """ - if not os.path.isfile(PID_FILE): - self.cbox.log.warn("Could not find the pid file of a running stunnel " \ - + "daemon: %s" % PID_FILE) - return - try: - pfile = open(PID_FILE, "r") - try: - pid = pfile.read().strip() - except IOError, err_msg: - self.cbox.log.warn("Failed to read the pid file (%s): %s" % (PID_FILE, err_msg)) - pfile.close() - return - pfile.close() - except IOError, err_msg: - self.cbox.log.warn("Failed to open the pid file (%s): %s" % (PID_FILE, err_msg)) - return - if pid.isdigit(): - pid = int(pid) - else: - return - try: - ## SIGTERM = 15 - os.kill(pid, 15) - self.cbox.log.info("Successfully stopped stunnel") - try: - os.remove(PID_FILE) - except OSError, err_msg: - self.cbox.log.warn("Failed to remove the pid file (%s) of stunnel: %s" \ - % (PID_FILE, err_msg)) - except OSError, err_msg: - self.cbox.log.warn("Failed to kill stunnel process (PID: %d): %s" % \ - (pid, err_msg)) + def __kill_stunnel(self): + """try to kill a running stunnel daemon + """ + if not os.path.isfile(PID_FILE): + self.cbox.log.warn("Could not find the pid file of a running stunnel " \ + + "daemon: %s" % PID_FILE) + return + try: + pfile = open(PID_FILE, "r") + try: + pid = pfile.read().strip() + except IOError, err_msg: + self.cbox.log.warn("Failed to read the pid file (%s): %s" % (PID_FILE, err_msg)) + pfile.close() + return + pfile.close() + except IOError, err_msg: + self.cbox.log.warn("Failed to open the pid file (%s): %s" % (PID_FILE, err_msg)) + return + if pid.isdigit(): + pid = int(pid) + else: + return + try: + ## SIGTERM = 15 + os.kill(pid, 15) + self.cbox.log.info("Successfully stopped stunnel") + try: + os.remove(PID_FILE) + except OSError, err_msg: + self.cbox.log.warn("Failed to remove the pid file (%s) of stunnel: %s" \ + % (PID_FILE, err_msg)) + except OSError, err_msg: + self.cbox.log.warn("Failed to kill stunnel process (PID: %d): %s" % \ + (pid, err_msg)) - def __run_stunnel(self, cert_name, dest_port=443): - ## retrieve currently requested port (not necessarily the port served - ## by cherrypy - e.g. in a proxy setup) - request_port = cherrypy.config.get("server.socket_port", 80) - self.cbox.log.debug("[encrypted_webinterface] starting " \ - + "%s on port %s for %s" % \ - (self.root_action.STUNNEL_BIN, dest_port, request_port)) - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "plugin", os.path.join(self.plugin_dir, "root_action.py"), - cert_name, - str(request_port), - str(dest_port), - PID_FILE ]) - (output, error) = proc.communicate() - if proc.returncode == 0: - self.cbox.log.info("Successfully started 'stunnel'") - return True - else: - self.cbox.log.warn("Failed to run 'stunnel': %s" % error) - return False + def __run_stunnel(self, cert_name, dest_port=443): + ## retrieve currently requested port (not necessarily the port served + ## by cherrypy - e.g. in a proxy setup) + request_port = cherrypy.config.get("server.socket_port", 80) + self.cbox.log.debug("[encrypted_webinterface] starting " \ + + "%s on port %s for %s" % \ + (self.root_action.STUNNEL_BIN, dest_port, request_port)) + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", os.path.join(self.plugin_dir, "root_action.py"), + cert_name, + str(request_port), + str(dest_port), + PID_FILE ]) + (output, error) = proc.communicate() + if proc.returncode == 0: + self.cbox.log.info("Successfully started 'stunnel'") + return True + else: + self.cbox.log.warn("Failed to run 'stunnel': %s" % error) + return False - def __get_certificate(self): - """Create a self-signed certificate and return its pem content - - The code is mainly inspired by: - https://dev.tribler.org/browser/m2crypto/trunk/contrib/SimpleX509create.py - """ - ## check if m2crypto is available - try: - import M2Crypto - except ImportError: - ## failed to import the module - return None - import time - string_type = 0x1000 | 1 # see http://www.koders.com/python/.. - # ../fid07A99E089F55187896A06CD4E0B6F21B9B8F5B0B.aspx?s=bavaria - key_gen_number = 0x10001 # commonly used for key generation: 65537 - rsa_key = M2Crypto.RSA.gen_key(KEY_BITS, key_gen_number, callback=lambda: None) - rsa_key2 = M2Crypto.RSA.gen_key(KEY_BITS, key_gen_number, callback=lambda: None) - pkey = M2Crypto.EVP.PKey(md=SIGN_DIGEST) - pkey.assign_rsa(rsa_key) - sign_key = M2Crypto.EVP.PKey(md=SIGN_DIGEST) - sign_key.assign_rsa(rsa_key2) - subject = M2Crypto.X509.X509_Name() - for (key, value) in CERT_INFOS.items(): - subject.add_entry_by_txt(key, string_type, value, -1, -1, 0) - issuer = M2Crypto.X509.X509_Name(M2Crypto.m2.x509_name_new()) - for (key, value) in ISSUER_INFOS.items(): - issuer.add_entry_by_txt(key, string_type, value, -1, -1, 0) - ## time object - asn_time1 = M2Crypto.ASN1.ASN1_UTCTIME() - asn_time1.set_time(long(time.time()) - EXPIRE_TIME) - asn_time2 = M2Crypto.ASN1.ASN1_UTCTIME() - asn_time2.set_time(long(time.time()) + EXPIRE_TIME) - request = M2Crypto.X509.Request() - request.set_subject_name(subject) - request.set_pubkey(pkey) - request.sign(pkey=pkey, md=SIGN_DIGEST) - cert = M2Crypto.X509.X509() - ## always create a unique version number - cert.set_version(0) - cert.set_serial_number(long(time.time())) - cert.set_pubkey(pkey) - cert.set_not_before(asn_time1) - cert.set_not_after(asn_time2) - cert.set_subject_name(request.get_subject()) - cert.set_issuer_name(issuer) - cert.sign(pkey, SIGN_DIGEST) - result = "" - result += cert.as_pem() - result += pkey.as_pem(cipher=None) - return result + def __get_certificate(self): + """Create a self-signed certificate and return its pem content + + The code is mainly inspired by: + https://dev.tribler.org/browser/m2crypto/trunk/contrib/SimpleX509create.py + """ + ## check if m2crypto is available + try: + import M2Crypto + except ImportError: + ## failed to import the module + return None + import time + string_type = 0x1000 | 1 # see http://www.koders.com/python/.. + # ../fid07A99E089F55187896A06CD4E0B6F21B9B8F5B0B.aspx?s=bavaria + key_gen_number = 0x10001 # commonly used for key generation: 65537 + rsa_key = M2Crypto.RSA.gen_key(KEY_BITS, key_gen_number, callback=lambda: None) + rsa_key2 = M2Crypto.RSA.gen_key(KEY_BITS, key_gen_number, callback=lambda: None) + pkey = M2Crypto.EVP.PKey(md=SIGN_DIGEST) + pkey.assign_rsa(rsa_key) + sign_key = M2Crypto.EVP.PKey(md=SIGN_DIGEST) + sign_key.assign_rsa(rsa_key2) + subject = M2Crypto.X509.X509_Name() + for (key, value) in CERT_INFOS.items(): + subject.add_entry_by_txt(key, string_type, value, -1, -1, 0) + issuer = M2Crypto.X509.X509_Name(M2Crypto.m2.x509_name_new()) + for (key, value) in ISSUER_INFOS.items(): + issuer.add_entry_by_txt(key, string_type, value, -1, -1, 0) + ## time object + asn_time1 = M2Crypto.ASN1.ASN1_UTCTIME() + asn_time1.set_time(long(time.time()) - EXPIRE_TIME) + asn_time2 = M2Crypto.ASN1.ASN1_UTCTIME() + asn_time2.set_time(long(time.time()) + EXPIRE_TIME) + request = M2Crypto.X509.Request() + request.set_subject_name(subject) + request.set_pubkey(pkey) + request.sign(pkey=pkey, md=SIGN_DIGEST) + cert = M2Crypto.X509.X509() + ## always create a unique version number + cert.set_version(0) + cert.set_serial_number(long(time.time())) + cert.set_pubkey(pkey) + cert.set_not_before(asn_time1) + cert.set_not_after(asn_time2) + cert.set_subject_name(request.get_subject()) + cert.set_issuer_name(issuer) + cert.sign(pkey, SIGN_DIGEST) + result = "" + result += cert.as_pem() + result += pkey.as_pem(cipher=None) + return result diff --git a/plugins/encrypted_webinterface/root_action.py b/plugins/encrypted_webinterface/root_action.py index c500853..0a76370 100755 --- a/plugins/encrypted_webinterface/root_action.py +++ b/plugins/encrypted_webinterface/root_action.py @@ -32,61 +32,61 @@ import os def _get_username(): - if ("SUPERCMD" in os.environ) and ("ORIG_USER" in os.environ): - return os.environ["ORIG_USER"] - elif "USER" in os.environ: - return os.environ["USER"] - else: - return "cryptobox" + if ("SUPERCMD" in os.environ) and ("ORIG_USER" in os.environ): + return os.environ["ORIG_USER"] + elif "USER" in os.environ: + return os.environ["USER"] + else: + return "cryptobox" def run_stunnel(cert_file, src_port, dst_port, pid_file): - import subprocess - if not src_port.isdigit(): - sys.stderr.write("Source port is not a number: %s" % src_port) - return False - if not dst_port.isdigit(): - sys.stderr.write("Destination port is not a number: %s" % dst_port) - return False - if not os.path.isfile(cert_file): - sys.stderr.write("The certificate file (%s) does not exist!" % cert_file) - return False - username = _get_username() - if not username: - sys.stderr.write("Could not retrieve the username with uid=%d." % os.getuid()) - return False - ## the environment (especially PATH) should be clean, as 'stunnel' cares about - ## this in a setuid situation - proc = subprocess.Popen( - shell = False, - env = {}, - stdin = subprocess.PIPE, - args = [ STUNNEL_BIN, - "-fd", - "0"]) - proc.stdin.write("setuid = %s\n" % username) - proc.stdin.write("pid = %s\n" % pid_file) - proc.stdin.write("[cryptobox-server]\n") - proc.stdin.write("connect = %s\n" % src_port) - proc.stdin.write("accept = %s\n" % dst_port) - proc.stdin.write("cert = %s\n" % cert_file) - (output, error) = proc.communicate() - return proc.returncode == 0 - + import subprocess + if not src_port.isdigit(): + sys.stderr.write("Source port is not a number: %s" % src_port) + return False + if not dst_port.isdigit(): + sys.stderr.write("Destination port is not a number: %s" % dst_port) + return False + if not os.path.isfile(cert_file): + sys.stderr.write("The certificate file (%s) does not exist!" % cert_file) + return False + username = _get_username() + if not username: + sys.stderr.write("Could not retrieve the username with uid=%d." % os.getuid()) + return False + ## the environment (especially PATH) should be clean, as 'stunnel' cares about + ## this in a setuid situation + proc = subprocess.Popen( + shell = False, + env = {}, + stdin = subprocess.PIPE, + args = [ STUNNEL_BIN, + "-fd", + "0"]) + proc.stdin.write("setuid = %s\n" % username) + proc.stdin.write("pid = %s\n" % pid_file) + proc.stdin.write("[cryptobox-server]\n") + proc.stdin.write("connect = %s\n" % src_port) + proc.stdin.write("accept = %s\n" % dst_port) + proc.stdin.write("cert = %s\n" % cert_file) + (output, error) = proc.communicate() + return proc.returncode == 0 + if __name__ == "__main__": - args = sys.argv[1:] + args = sys.argv[1:] - self_bin = sys.argv[0] - - if len(args) != 4: - sys.stderr.write("%s: invalid number of arguments (%d instead of %d))\n" % \ - (self_bin, len(args), 4)) - sys.exit(1) - - if not run_stunnel(args[0], args[1], args[2], args[3]): - sys.stderr.write("%s: failed to run 'stunnel'!" % self_bin) - sys.exit(100) - - sys.exit(0) + self_bin = sys.argv[0] + + if len(args) != 4: + sys.stderr.write("%s: invalid number of arguments (%d instead of %d))\n" % \ + (self_bin, len(args), 4)) + sys.exit(1) + + if not run_stunnel(args[0], args[1], args[2], args[3]): + sys.stderr.write("%s: failed to run 'stunnel'!" % self_bin) + sys.exit(100) + + sys.exit(0) diff --git a/plugins/encrypted_webinterface/unittests.py b/plugins/encrypted_webinterface/unittests.py index 8f02065..265a1b7 100644 --- a/plugins/encrypted_webinterface/unittests.py +++ b/plugins/encrypted_webinterface/unittests.py @@ -23,12 +23,12 @@ __revision__ = "$Id$" from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - - def test_get_cert_form(self): - """retrieve the default form of the certificate manager""" - url = self.url + "encrypted_webinterface" - self.register_auth(url) - self.cmd.go(url) - ## TODO: enable it, as soon as the plugin is enabled by default - #self.cmd.find("Data.Status.Plugins.encrypted_webinterface") + + def test_get_cert_form(self): + """retrieve the default form of the certificate manager""" + url = self.url + "encrypted_webinterface" + self.register_auth(url) + self.cmd.go(url) + ## TODO: enable it, as soon as the plugin is enabled by default + #self.cmd.find("Data.Status.Plugins.encrypted_webinterface") diff --git a/plugins/help/help.py b/plugins/help/help.py index 895d071..ba5143b 100644 --- a/plugins/help/help.py +++ b/plugins/help/help.py @@ -26,56 +26,56 @@ __revision__ = "$Id$" import cryptobox.plugins.base class help(cryptobox.plugins.base.CryptoBoxPlugin): - """The help feature of the CryptoBox. - """ + """The help feature of the CryptoBox. + """ - #plugin_capabilities = [ "system" ] - #TODO: enable this plugin as soon as the user documentation is ready again - plugin_capabilities = [ ] - plugin_visibility = [ "menu" ] - request_auth = False - rank = 80 + #plugin_capabilities = [ "system" ] + #TODO: enable this plugin as soon as the user documentation is ready again + plugin_capabilities = [ ] + plugin_visibility = [ "menu" ] + request_auth = False + rank = 80 - default_lang = 'en' - default_page = "CryptoBoxUser" + default_lang = 'en' + default_page = "CryptoBoxUser" - def do_action(self, page=""): - '''prints the offline wikipage - ''' - import re, os - ## check for invalid characters and if the page exists in the default language - if page and \ - not re.search(r'\W', page) and \ - os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], - self.default_lang, page + '.html')): - ## everything is ok - pass - else: - ## display this page as default help page - page = self.default_page - if page: - ## issue warning - self.cbox.log.info("could not find the selected documentation page: %s" % str(page)) - ## store the name of the page - self.hdf[self.hdf_prefix + "Page"] = page - ## choose the right language - for lang in self.site.lang_order: - if os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], - lang, page + '.html')): - doc_lang = lang - break - else: - doc_lang = self.default_lang - self.hdf[self.hdf_prefix + "Language"] = doc_lang - ## store the current setting for a later "getStatus" call - self.current_lang = doc_lang - self.current_page = page - return "doc" + def do_action(self, page=""): + '''prints the offline wikipage + ''' + import re, os + ## check for invalid characters and if the page exists in the default language + if page and \ + not re.search(r'\W', page) and \ + os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], + self.default_lang, page + '.html')): + ## everything is ok + pass + else: + ## display this page as default help page + page = self.default_page + if page: + ## issue warning + self.cbox.log.info("could not find the selected documentation page: %s" % str(page)) + ## store the name of the page + self.hdf[self.hdf_prefix + "Page"] = page + ## choose the right language + for lang in self.site.lang_order: + if os.path.isfile(os.path.join(self.cbox.prefs["Locations"]["DocDir"], + lang, page + '.html')): + doc_lang = lang + break + else: + doc_lang = self.default_lang + self.hdf[self.hdf_prefix + "Language"] = doc_lang + ## store the current setting for a later "getStatus" call + self.current_lang = doc_lang + self.current_page = page + return "doc" - def get_status(self): - """Retrieve the current status of the feature. - """ - return "%s:%s" % (self.current_lang, self.current_page) + def get_status(self): + """Retrieve the current status of the feature. + """ + return "%s:%s" % (self.current_lang, self.current_page) diff --git a/plugins/help/unittests.py b/plugins/help/unittests.py index c1fb878..921f6cf 100644 --- a/plugins/help/unittests.py +++ b/plugins/help/unittests.py @@ -25,70 +25,70 @@ from twill.errors import * class unittests(WebInterfaceTestClass): - def test_help_language_texts(self): - '''help pages should be available in different languages''' + def test_help_language_texts(self): + '''help pages should be available in different languages''' - ## check english help pages - self.cmd.go(self.url + "help?weblang=en") - self.cmd.find("Table of Contents") - self.cmd.find("Getting started") - (lang,page) = self._getHelpStatus() - self.assertTrue(lang == "en") - self.assertTrue(page == "CryptoBoxUser") + ## check english help pages + self.cmd.go(self.url + "help?weblang=en") + self.cmd.find("Table of Contents") + self.cmd.find("Getting started") + (lang,page) = self._getHelpStatus() + self.assertTrue(lang == "en") + self.assertTrue(page == "CryptoBoxUser") - ## check german help pages - self.cmd.go(self.url + "help?weblang=de") - self.cmd.find("Table of Contents") - self.cmd.find("Wie geht es los") - (lang,page) = self._getHelpStatus() - self.assertTrue(lang == "de") - self.assertTrue(page == "CryptoBoxUser") + ## check german help pages + self.cmd.go(self.url + "help?weblang=de") + self.cmd.find("Table of Contents") + self.cmd.find("Wie geht es los") + (lang,page) = self._getHelpStatus() + self.assertTrue(lang == "de") + self.assertTrue(page == "CryptoBoxUser") - ## check slovene help pages - self.cmd.go(self.url + "help?weblang=sl") - self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") - ## add a slovene text here, as soon as the help is translated - (lang,page) = self._getHelpStatus() - ## change this to "sl" as soon as the help is translated - self.assertTrue(lang == "en") - self.assertTrue(page == "CryptoBoxUser") + ## check slovene help pages + self.cmd.go(self.url + "help?weblang=sl") + self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") + ## add a slovene text here, as soon as the help is translated + (lang,page) = self._getHelpStatus() + ## change this to "sl" as soon as the help is translated + self.assertTrue(lang == "en") + self.assertTrue(page == "CryptoBoxUser") - ## check french help pages - self.cmd.go(self.url + "help?weblang=fr") - self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") - ## add a french text here, as soon as the help is translated - (lang,page) = self._getHelpStatus() - ## change this to "fr" as soon as the help is translated - self.assertTrue(lang == "en") - self.assertTrue(page == "CryptoBoxUser") + ## check french help pages + self.cmd.go(self.url + "help?weblang=fr") + self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") + ## add a french text here, as soon as the help is translated + (lang,page) = self._getHelpStatus() + ## change this to "fr" as soon as the help is translated + self.assertTrue(lang == "en") + self.assertTrue(page == "CryptoBoxUser") - ## test a random language - it should fall back to english - self.cmd.go(self.url + "help?weblang=foobar") - self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") - (lang,page) = self._getHelpStatus() - self.assertTrue(lang == "en") - self.assertTrue(page == "CryptoBoxUser") - + ## test a random language - it should fall back to english + self.cmd.go(self.url + "help?weblang=foobar") + self.assertRaises(TwillAssertionError, self.cmd.notfind, "Table of Contents") + (lang,page) = self._getHelpStatus() + self.assertTrue(lang == "en") + self.assertTrue(page == "CryptoBoxUser") + - def test_help_pages(self): - """check invalid page requests""" - self.cmd.go(self.url + "help?page=foobar") - (lang,page) = self._getHelpStatus() - self.assertTrue(page == "CryptoBoxUser") + def test_help_pages(self): + """check invalid page requests""" + self.cmd.go(self.url + "help?page=foobar") + (lang,page) = self._getHelpStatus() + self.assertTrue(page == "CryptoBoxUser") - self.cmd.go(self.url + "help?page=CryptoBoxUser") - (lang,page) = self._getHelpStatus() - self.assertTrue(page == "CryptoBoxUser") + self.cmd.go(self.url + "help?page=CryptoBoxUser") + (lang,page) = self._getHelpStatus() + self.assertTrue(page == "CryptoBoxUser") - def test_help_default_languages(self): - """check invalid page requests""" - self.cmd.go(self.url + "help?weblang=foobar") - (lang,page) = self._getHelpStatus() - self.assertTrue(lang == "en") + def test_help_default_languages(self): + """check invalid page requests""" + self.cmd.go(self.url + "help?weblang=foobar") + (lang,page) = self._getHelpStatus() + self.assertTrue(lang == "en") - def _getHelpStatus(self): - self.cmd.find(r'Data.Status.Plugins.help=(.*)$', "m") - return tuple(self.locals["__match__"].split(":")) + def _getHelpStatus(self): + self.cmd.find(r'Data.Status.Plugins.help=(.*)$', "m") + return tuple(self.locals["__match__"].split(":")) diff --git a/plugins/language_selection/language_selection.py b/plugins/language_selection/language_selection.py index 68d3018..ee246b7 100644 --- a/plugins/language_selection/language_selection.py +++ b/plugins/language_selection/language_selection.py @@ -27,22 +27,22 @@ import cryptobox.plugins.base class language_selection(cryptobox.plugins.base.CryptoBoxPlugin): - """The language_selection feature of the CryptoBox. - """ + """The language_selection feature of the CryptoBox. + """ - plugin_capabilities = [ "system" ] - plugin_visibility = [ "menu", "preferences" ] - request_auth = False - rank = 60 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "menu", "preferences" ] + request_auth = False + rank = 60 - def do_action(self): - """Show all available languages. - """ - return "language_selection" + def do_action(self): + """Show all available languages. + """ + return "language_selection" - def get_status(self): - """The current status of the feature is defined as the current language. - """ - return ":".join(self.site.lang_order) + def get_status(self): + """The current status of the feature is defined as the current language. + """ + return ":".join(self.site.lang_order) diff --git a/plugins/language_selection/unittests.py b/plugins/language_selection/unittests.py index 771a04b..c613600 100644 --- a/plugins/language_selection/unittests.py +++ b/plugins/language_selection/unittests.py @@ -24,23 +24,23 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_form(self): - """Check if the 'language_selection' plugin works. - """ - url = self.url + "language_selection?weblang=en" - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('Choose your tongue') + def test_read_form(self): + """Check if the 'language_selection' plugin works. + """ + url = self.url + "language_selection?weblang=en" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('Choose your tongue') - def test_check_language_list(self): - """Check the list of available languages. - """ - url = self.url + "language_selection?weblang=en" - self.register_auth(url) - self.cmd.go(url) - self.cmd.find(r'Data.Status.Plugins.language_selection=(.*)$', "m") - langs = self.locals["__match__"].split(":") - self.assertTrue(len(langs)>1) - self.assertTrue(langs[0] == "en") + def test_check_language_list(self): + """Check the list of available languages. + """ + url = self.url + "language_selection?weblang=en" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find(r'Data.Status.Plugins.language_selection=(.*)$', "m") + langs = self.locals["__match__"].split(":") + self.assertTrue(len(langs)>1) + self.assertTrue(langs[0] == "en") diff --git a/plugins/logs/logs.py b/plugins/logs/logs.py index 88afc91..b4ae765 100644 --- a/plugins/logs/logs.py +++ b/plugins/logs/logs.py @@ -32,215 +32,215 @@ import cherrypy LOG_LEVELS = [ 'DEBUG', 'INFO', 'NOTICE', 'WARNING', 'ERROR' ] LINE_REGEX = re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2}) " \ - + r"(?P\d{2}):(?P\d{2}):\d{2},\d{3} (?P" \ - + "|".join([ "(?:%s)" % e for e in LOG_LEVELS]) + r"): (?P.*)$") + + r"(?P\d{2}):(?P\d{2}):\d{2},\d{3} (?P" \ + + "|".join([ "(?:%s)" % e for e in LOG_LEVELS]) + r"): (?P.*)$") class logs(cryptobox.plugins.base.CryptoBoxPlugin): - """The logs feature of the CryptoBox. - """ + """The logs feature of the CryptoBox. + """ - plugin_capabilities = [ "system" ] - plugin_visibility = [ "preferences" ] - request_auth = False - rank = 90 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = False + rank = 90 - def do_action(self, lines=50, size=3000, level=None): - """Show the latest part of the log file. - """ - ## filter input - try: - lines = int(lines) - if lines <= 0: - raise(ValueError) - except ValueError: - self.cbox.log.info("[logs] invalid line number: %s" % str(lines)) - lines = 50 - try: - size = int(size) - if size <= 0: - raise(ValueError) - except ValueError: - self.cbox.log.info("[logs] invalid log size: %s" % str(size)) - size = 3000 - if not level is None: - level = str(level) - if not level in LOG_LEVELS: - self.cbox.log.info("[logs] invalid log level: %s" % str(level)) - level = None - for (index, line) in enumerate(self.__filter_log_content(lines, size, level)): - self.__set_line_hdf_data(self.hdf_prefix + "Content.%d" % index, line) - self.hdf[self.hdf_prefix + "Destination"] = \ - self.cbox.prefs["Log"]["Destination"].lower() + def do_action(self, lines=50, size=3000, level=None): + """Show the latest part of the log file. + """ + ## filter input + try: + lines = int(lines) + if lines <= 0: + raise(ValueError) + except ValueError: + self.cbox.log.info("[logs] invalid line number: %s" % str(lines)) + lines = 50 + try: + size = int(size) + if size <= 0: + raise(ValueError) + except ValueError: + self.cbox.log.info("[logs] invalid log size: %s" % str(size)) + size = 3000 + if not level is None: + level = str(level) + if not level in LOG_LEVELS: + self.cbox.log.info("[logs] invalid log level: %s" % str(level)) + level = None + for (index, line) in enumerate(self.__filter_log_content(lines, size, level)): + self.__set_line_hdf_data(self.hdf_prefix + "Content.%d" % index, line) + self.hdf[self.hdf_prefix + "Destination"] = \ + self.cbox.prefs["Log"]["Destination"].lower() - ## this generates more download buttons if the files are available - if "syslogfile" in self.defaults: - try: - syslogfile = self.defaults["syslogfile"] - except KeyError: - self.cbox.log.error("could not evaluate the config setting: " - + "[logs]->syslogfile") - if syslogfile and os.access(syslogfile, os.R_OK): - self.cbox.log.info("[logs]->syslogfile: '%s' will be exported" % syslogfile) - self.hdf[self.hdf_prefix + "syslogfile"] = "readable" - self.cbox.prefs["Log"]["syslogfile"] = syslogfile - else: - self.cbox.log.warn("[logs]->syslogfile: '%s' is not readable for cryptobox user" % syslogfile) - self.hdf[self.hdf_prefix + "syslogfile"] = "not readable" - return "show_log" + ## this generates more download buttons if the files are available + if "syslogfile" in self.defaults: + try: + syslogfile = self.defaults["syslogfile"] + except KeyError: + self.cbox.log.error("could not evaluate the config setting: " + + "[logs]->syslogfile") + if syslogfile and os.access(syslogfile, os.R_OK): + self.cbox.log.info("[logs]->syslogfile: '%s' will be exported" % syslogfile) + self.hdf[self.hdf_prefix + "syslogfile"] = "readable" + self.cbox.prefs["Log"]["syslogfile"] = syslogfile + else: + self.cbox.log.warn("[logs]->syslogfile: '%s' is not readable for cryptobox user" % syslogfile) + self.hdf[self.hdf_prefix + "syslogfile"] = "not readable" + return "show_log" - @cherrypy.expose - def download(self, **kargs): - """Download the complete log file + @cherrypy.expose + def download(self, **kargs): + """Download the complete log file - **kargs are necessary - we have to ignore 'weblang' and so on ... - """ - ##TODO: check the download url to know which file is wanted - if self.cbox.prefs["Log"]["syslogfile"]: - try: - syslogfile = self.cbox.prefs["Log"]["syslogfile"] - except KeyError: - self.cbox.log.error("could not evaluate the config setting: " - + "[logs]->syslogfile %s") - if syslogfile and os.access(syslogfile, os.R_OK): - return cherrypy.lib.cptools.serveFile(syslogfile, - disposition="attachment", name="syslog.txt") - else: - self.cbox.log.error("'%s' could not be exposed" % syslogfile) + **kargs are necessary - we have to ignore 'weblang' and so on ... + """ + ##TODO: check the download url to know which file is wanted + if self.cbox.prefs["Log"]["syslogfile"]: + try: + syslogfile = self.cbox.prefs["Log"]["syslogfile"] + except KeyError: + self.cbox.log.error("could not evaluate the config setting: " + + "[logs]->syslogfile %s") + if syslogfile and os.access(syslogfile, os.R_OK): + return cherrypy.lib.cptools.serveFile(syslogfile, + disposition="attachment", name="syslog.txt") + else: + self.cbox.log.error("'%s' could not be exposed" % syslogfile) - log_file = self.__get_log_destination_file() - if log_file is None: - return "" - else: - return cherrypy.lib.cptools.serveFile(log_file, - disposition="attachment", name="cryptobox_logfile.txt") - + log_file = self.__get_log_destination_file() + if log_file is None: + return "" + else: + return cherrypy.lib.cptools.serveFile(log_file, + disposition="attachment", name="cryptobox_logfile.txt") + - def get_status(self): - """The current status includes the log configuration details. - """ - return "%s:%s:%s" % ( - self.cbox.prefs["Log"]["Level"], - self.cbox.prefs["Log"]["Destination"], - self.cbox.prefs["Log"]["Details"]) - + def get_status(self): + """The current status includes the log configuration details. + """ + return "%s:%s:%s" % ( + self.cbox.prefs["Log"]["Level"], + self.cbox.prefs["Log"]["Destination"], + self.cbox.prefs["Log"]["Details"]) + - def __filter_log_content(self, lines, max_size, level): - """Filter, sort and shorten the log content. - """ - if level and level in LOG_LEVELS: - filtered_levels = LOG_LEVELS[:] - ## only the given and higher levels are accepted - while filtered_levels[0] != level: - del filtered_levels[0] - content = [] - current_length = 0 - for line in self.__get_log_data(): - ## search for matching lines for the given log level - for one_level in filtered_levels: - if line.find(" %s: " % one_level) != -1: - break - else: - ## the line does not contain an appropriate level name - continue - ## we found a line that fits - content.append(line) - current_length += len(line) - if lines and len(content) >= lines: - break - if max_size and current_length >= max_size: - break - else: - content = self.__get_log_data(lines, max_size) - return content + def __filter_log_content(self, lines, max_size, level): + """Filter, sort and shorten the log content. + """ + if level and level in LOG_LEVELS: + filtered_levels = LOG_LEVELS[:] + ## only the given and higher levels are accepted + while filtered_levels[0] != level: + del filtered_levels[0] + content = [] + current_length = 0 + for line in self.__get_log_data(): + ## search for matching lines for the given log level + for one_level in filtered_levels: + if line.find(" %s: " % one_level) != -1: + break + else: + ## the line does not contain an appropriate level name + continue + ## we found a line that fits + content.append(line) + current_length += len(line) + if lines and len(content) >= lines: + break + if max_size and current_length >= max_size: + break + else: + content = self.__get_log_data(lines, max_size) + return content - def __set_line_hdf_data(self, hdf_prefix, line): - """Parse the log line for time and log level. + def __set_line_hdf_data(self, hdf_prefix, line): + """Parse the log line for time and log level. - If parsing fails, then the output line is simply displayed without - meta information. - """ - self.hdf[hdf_prefix + ".Text"] = line.strip() - match = LINE_REGEX.match(line) - if not match: - ## we could not parse the line - just return the text without meta info - return - ## matching was successful - we can parse the line for details - ## calculate time difference of log line (aka: age of event) - try: - (year, month, day, hour, minute) = match.group( - 'year', 'month', 'day', 'hour', 'minute') - (year, month, day, hour, minute) = \ - (int(year), int(month), int(day), int(hour), int(minute)) - ## timediff is a timedelta object - timediff = datetime.datetime.today() - \ - datetime.datetime(year, month, day, hour, minute) - ## the time units (see below) correspond to the names within the language - ## file: Text.TimeUnits.Days ... - if timediff.days >= 1: - self.hdf[hdf_prefix + ".TimeDiff.Unit"] = 'Days' - self.hdf[hdf_prefix + ".TimeDiff.Value"] = timediff.days - elif timediff.seconds >= 3600: - self.hdf[hdf_prefix + ".TimeDiff.Unit"] = 'Hours' - self.hdf[hdf_prefix + ".TimeDiff.Value"] = timediff.seconds / 3600 - elif timediff.seconds >= 60: - self.hdf[hdf_prefix + ".TimeDiff.Unit"] = 'Minutes' - self.hdf[hdf_prefix + ".TimeDiff.Value"] = timediff.seconds / 60 - else: - self.hdf[hdf_prefix + ".TimeDiff.Unit"] = 'Seconds' - self.hdf[hdf_prefix + ".TimeDiff.Value"] = timediff.seconds - except (OverflowError, TypeError, ValueError, IndexError), err_msg: - pass - ## retrieve the level - try: - self.hdf[hdf_prefix + ".Level"] = match.group('level') - except IndexError: - pass - try: - self.hdf[hdf_prefix + ".Text"] = match.group('text').strip() - except IndexError: - pass - + If parsing fails, then the output line is simply displayed without + meta information. + """ + self.hdf[hdf_prefix + ".Text"] = line.strip() + match = LINE_REGEX.match(line) + if not match: + ## we could not parse the line - just return the text without meta info + return + ## matching was successful - we can parse the line for details + ## calculate time difference of log line (aka: age of event) + try: + (year, month, day, hour, minute) = match.group( + 'year', 'month', 'day', 'hour', 'minute') + (year, month, day, hour, minute) = \ + (int(year), int(month), int(day), int(hour), int(minute)) + ## timediff is a timedelta object + timediff = datetime.datetime.today() - \ + datetime.datetime(year, month, day, hour, minute) + ## the time units (see below) correspond to the names within the language + ## file: Text.TimeUnits.Days ... + if timediff.days >= 1: + self.hdf[hdf_prefix + ".TimeDiff.Unit"] = 'Days' + self.hdf[hdf_prefix + ".TimeDiff.Value"] = timediff.days + elif timediff.seconds >= 3600: + self.hdf[hdf_prefix + ".TimeDiff.Unit"] = 'Hours' + self.hdf[hdf_prefix + ".TimeDiff.Value"] = timediff.seconds / 3600 + elif timediff.seconds >= 60: + self.hdf[hdf_prefix + ".TimeDiff.Unit"] = 'Minutes' + self.hdf[hdf_prefix + ".TimeDiff.Value"] = timediff.seconds / 60 + else: + self.hdf[hdf_prefix + ".TimeDiff.Unit"] = 'Seconds' + self.hdf[hdf_prefix + ".TimeDiff.Value"] = timediff.seconds + except (OverflowError, TypeError, ValueError, IndexError), err_msg: + pass + ## retrieve the level + try: + self.hdf[hdf_prefix + ".Level"] = match.group('level') + except IndexError: + pass + try: + self.hdf[hdf_prefix + ".Text"] = match.group('text').strip() + except IndexError: + pass + - def __get_log_destination_file(self): - """For non-file log destinations return 'None' and output a warning - """ - try: - if self.cbox.prefs["Log"]["Destination"].upper() == "FILE": - import os - return os.path.abspath(self.cbox.prefs["Log"]["Details"]) - else: - return None - except KeyError: - self.cbox.log.error( - "could not evaluate one of the following config settings: " - + "[Log]->Destination or [Log]->Details") - return None + def __get_log_destination_file(self): + """For non-file log destinations return 'None' and output a warning + """ + try: + if self.cbox.prefs["Log"]["Destination"].upper() == "FILE": + import os + return os.path.abspath(self.cbox.prefs["Log"]["Details"]) + else: + return None + except KeyError: + self.cbox.log.error( + "could not evaluate one of the following config settings: " + + "[Log]->Destination or [Log]->Details") + return None - def __get_log_data(self, lines=None, max_size=None): - """get the most recent log entries of the log file + def __get_log_data(self, lines=None, max_size=None): + """get the most recent log entries of the log file - the maximum number and size of these entries can be limited by - 'lines' and 'max_size' - """ - log_file = self.__get_log_destination_file() - ## return nothing if the currently selected log output is not a file - if log_file is None: - return [] - try: - fdesc = open(log_file, "r") - if max_size: - fdesc.seek(-max_size, 2) # seek relative to the end of the file - content = fdesc.readlines() - fdesc.close() - except IOError: - self.cbox.log.warn("failed to read the log file (%s)" % log_file) - return [] - if lines: - content = content[-lines:] - content.reverse() - return content + the maximum number and size of these entries can be limited by + 'lines' and 'max_size' + """ + log_file = self.__get_log_destination_file() + ## return nothing if the currently selected log output is not a file + if log_file is None: + return [] + try: + fdesc = open(log_file, "r") + if max_size: + fdesc.seek(-max_size, 2) # seek relative to the end of the file + content = fdesc.readlines() + fdesc.close() + except IOError: + self.cbox.log.warn("failed to read the log file (%s)" % log_file) + return [] + if lines: + content = content[-lines:] + content.reverse() + return content diff --git a/plugins/logs/unittests.py b/plugins/logs/unittests.py index 45af15d..dc972fc 100644 --- a/plugins/logs/unittests.py +++ b/plugins/logs/unittests.py @@ -24,42 +24,42 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_logs(self): - """Read the log files. - """ - log_url = self.url + "logs" - self.register_auth(log_url) - self.cmd.go(log_url) - self.cmd.find('') + def test_read_logs(self): + """Read the log files. + """ + log_url = self.url + "logs" + self.register_auth(log_url) + self.cmd.go(log_url) + self.cmd.find('
') - def test_write_logs(self): - """Send a log message and read it again. - """ - log_text = "unittest - just a marker - please ignore" - self.cbox.log.error(log_text) - log_url = self.url + "logs" - self.register_auth(log_url) - self.cmd.go(log_url + "?level=ERROR") - self.cmd.find(log_text) + def test_write_logs(self): + """Send a log message and read it again. + """ + log_text = "unittest - just a marker - please ignore" + self.cbox.log.error(log_text) + log_url = self.url + "logs" + self.register_auth(log_url) + self.cmd.go(log_url + "?level=ERROR") + self.cmd.find(log_text) - def test_invalid_args(self): - """Send various invalid input combinations to the 'log' plugin. - """ - log_url = self.url + "logs" - self.cmd.go(log_url + "?lines=10") - self.cmd.find('
') - self.cmd.go(log_url + "?lines=0") - self.cmd.find('
') - self.cmd.go(log_url + "?lines=x") - self.cmd.find('
') - self.cmd.go(log_url + "?size=1000") - self.cmd.find('
') - self.cmd.go(log_url + "?size=0") - self.cmd.find('
') - self.cmd.go(log_url + "?size=x") - self.cmd.find('
') - self.cmd.go(log_url + "?level=foobar") - self.cmd.find('
') - self.cmd.go(log_url + r"?level=kfj!^(]") - self.cmd.find('
') - + def test_invalid_args(self): + """Send various invalid input combinations to the 'log' plugin. + """ + log_url = self.url + "logs" + self.cmd.go(log_url + "?lines=10") + self.cmd.find('
') + self.cmd.go(log_url + "?lines=0") + self.cmd.find('
') + self.cmd.go(log_url + "?lines=x") + self.cmd.find('
') + self.cmd.go(log_url + "?size=1000") + self.cmd.find('
') + self.cmd.go(log_url + "?size=0") + self.cmd.find('
') + self.cmd.go(log_url + "?size=x") + self.cmd.find('
') + self.cmd.go(log_url + "?level=foobar") + self.cmd.find('
') + self.cmd.go(log_url + r"?level=kfj!^(]") + self.cmd.find('
') + diff --git a/plugins/network/network.py b/plugins/network/network.py index 28bf9a5..d728a42 100644 --- a/plugins/network/network.py +++ b/plugins/network/network.py @@ -39,371 +39,371 @@ CHANGE_IP_DELAY = 5 DEFAULT_INTERFACE = "eth0" class network(cryptobox.plugins.base.CryptoBoxPlugin): - """The network feature of the CryptoBox. - """ + """The network feature of the CryptoBox. + """ - plugin_capabilities = [ "system" ] - plugin_visibility = [ "preferences" ] - request_auth = True - rank = 30 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = True + rank = 30 - def do_action(self, store=None, redirected="", ip1="", ip2="", ip3="", ip4="", - nm1="", nm2="", nm3="", nm4="", confirm_dhcp=""): - """Show a form containing the current IP - change it if requested. - """ - ## if we were redirected, then we should display the default page - self.cbox.log.debug("executing network plugin") - if redirected == "1": - self.cbox.log.debug("network plugin: redirected") - return "form_network" - ## check possible actions - if store is None: - ## no action was requested -> just show the form - self.cbox.log.debug("network plugin: show form (interface %s)" \ - % self.__get_interface()) - self.__prepare_form_data() - return "form_network" - ## change of ip address and/or netmask requested - elif store == "set_ip": - self.cbox.log.debug("network plugin: changing server IP") - if self.__IP_is_valid(ip1, ip2, ip3, ip4): - new_ip = "%d.%d.%d.%d" % (int(ip1), int(ip2), int(ip3), int(ip4)) - else: - self.hdf["Data.Warning"] = "Plugins.network.InvalidServerIP" - self.__prepare_form_data() - return "form_network" - if self.__IP_is_valid(nm1, nm2, nm3, nm4): - new_nm = "%d.%d.%d.%d" % (int(nm1), int(nm2), int(nm3), int(nm4)) - else: - self.hdf["Data.Warning"] = "Plugins.network.InvalidNetmask" - self.__prepare_form_data() - return "form_network" - if self.__set_ip(new_ip, new_nm): - self.cbox.log.info("[network] the IP was successfully changed: %s" % new_ip) - self.hdf["Data.Success"] = "Plugins.network.IPChanged" - self.hdf["Data.Redirect.URL"] = self.__get_redirect_destination(new_ip) - self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY - self.prefs["_address"] = new_ip - self.prefs["_netmask"] = new_nm - ## if an ip is set manually, don't use dhcp any longer - if self.prefs.has_key("_dhcp"): - del self.prefs["_dhcp"] - try: - self.cbox.prefs.plugin_conf.write() - except IOError: - self.cbox.log.warn("Could not write plugin configuration") - self.__prepare_form_data() - return "empty" - else: - self.cbox.log.warn("[network] failed to change IP address to: %s" % \ - new_ip) - self.hdf["Data.Warning"] = "Plugins.network.AddressChangeFailed" - self.__prepare_form_data() - return "form_network" - ## request for default gateway change - elif store == "set_gateway": - old_gw = self.__get_current_gw() - old_gw_str = ".".join([str(e) for e in old_gw]) - if self.__IP_is_valid(ip1, ip2, ip3, ip4): - new_gw = (int(ip1), int(ip2), int(ip3), int(ip4)) - new_gw_str = ".".join([str(e) for e in new_gw]) - else: - self.hdf["Data.Warning"] = "Plugins.network.InvalidGatewayIP" - self.__prepare_form_data() - return "form_network" - if self.__set_gw(old_gw_str, new_gw_str): - self.cbox.log.info( "[network] successfully changed gateway address:" \ - + new_gw_str) - self.hdf["Data.Success"] = "Plugins.network.GWChanged" - self.prefs["_gateway"] = new_gw_str - ## if an gw is set manually, don't use dhcp any longer - if self.prefs.has_key("_dhcp"): - del self.prefs["_dhcp"] - try: - self.cbox.prefs.plugin_conf.write() - except IOError: - self.cbox.log.warn("Could not write plugin configuration") - else: - self.cbox.log.warn("[network] failed to change gateway address to: %s" \ - % new_gw_str) - self.hdf["Data.Warning"] = "Plugins.network.GatewayChangeFailed" - self.__prepare_form_data() - return "form_network" - ## request for dhcp usage - elif store == "use_dhcp": - if confirm_dhcp != "1": - ## do nothing as the action was not confirmed with the checkbox - self.hdf["Data.Warning"] = "Plugins.network.DHCPNotConfirmed" - self.__prepare_form_data() - return "form_network" - else: - self.cbox.log.info( "[network] recieve network settings via DHCP") - if not os.path.isfile(self.root_action.DHCLIENT_BIN): - self.hdf["Data.Warning"] = "Plugins.network.DHCPNotFound" - elif self.__use_dhcp(): - self.hdf["Data.Success"] = "Plugins.network.DHCPRunning" - self.prefs["_dhcp"] = "use" - try: - self.cbox.prefs.plugin_conf.write() - except IOError: - self.cbox.log.warn("Could not write plugin configuration") - else: - self.hdf["Data.Warning"] = "Plugins.network.DHCPNotRunning" - self.__prepare_form_data() - return "form_network" - else: - ## invalid action was requested -> show default form - self.cbox.log.debug("network plugin: invalid request (%s)" % str(store)) - self.__prepare_form_data() - return "form_network" + def do_action(self, store=None, redirected="", ip1="", ip2="", ip3="", ip4="", + nm1="", nm2="", nm3="", nm4="", confirm_dhcp=""): + """Show a form containing the current IP - change it if requested. + """ + ## if we were redirected, then we should display the default page + self.cbox.log.debug("executing network plugin") + if redirected == "1": + self.cbox.log.debug("network plugin: redirected") + return "form_network" + ## check possible actions + if store is None: + ## no action was requested -> just show the form + self.cbox.log.debug("network plugin: show form (interface %s)" \ + % self.__get_interface()) + self.__prepare_form_data() + return "form_network" + ## change of ip address and/or netmask requested + elif store == "set_ip": + self.cbox.log.debug("network plugin: changing server IP") + if self.__IP_is_valid(ip1, ip2, ip3, ip4): + new_ip = "%d.%d.%d.%d" % (int(ip1), int(ip2), int(ip3), int(ip4)) + else: + self.hdf["Data.Warning"] = "Plugins.network.InvalidServerIP" + self.__prepare_form_data() + return "form_network" + if self.__IP_is_valid(nm1, nm2, nm3, nm4): + new_nm = "%d.%d.%d.%d" % (int(nm1), int(nm2), int(nm3), int(nm4)) + else: + self.hdf["Data.Warning"] = "Plugins.network.InvalidNetmask" + self.__prepare_form_data() + return "form_network" + if self.__set_ip(new_ip, new_nm): + self.cbox.log.info("[network] the IP was successfully changed: %s" % new_ip) + self.hdf["Data.Success"] = "Plugins.network.IPChanged" + self.hdf["Data.Redirect.URL"] = self.__get_redirect_destination(new_ip) + self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY + self.prefs["_address"] = new_ip + self.prefs["_netmask"] = new_nm + ## if an ip is set manually, don't use dhcp any longer + if self.prefs.has_key("_dhcp"): + del self.prefs["_dhcp"] + try: + self.cbox.prefs.plugin_conf.write() + except IOError: + self.cbox.log.warn("Could not write plugin configuration") + self.__prepare_form_data() + return "empty" + else: + self.cbox.log.warn("[network] failed to change IP address to: %s" % \ + new_ip) + self.hdf["Data.Warning"] = "Plugins.network.AddressChangeFailed" + self.__prepare_form_data() + return "form_network" + ## request for default gateway change + elif store == "set_gateway": + old_gw = self.__get_current_gw() + old_gw_str = ".".join([str(e) for e in old_gw]) + if self.__IP_is_valid(ip1, ip2, ip3, ip4): + new_gw = (int(ip1), int(ip2), int(ip3), int(ip4)) + new_gw_str = ".".join([str(e) for e in new_gw]) + else: + self.hdf["Data.Warning"] = "Plugins.network.InvalidGatewayIP" + self.__prepare_form_data() + return "form_network" + if self.__set_gw(old_gw_str, new_gw_str): + self.cbox.log.info( "[network] successfully changed gateway address:" \ + + new_gw_str) + self.hdf["Data.Success"] = "Plugins.network.GWChanged" + self.prefs["_gateway"] = new_gw_str + ## if an gw is set manually, don't use dhcp any longer + if self.prefs.has_key("_dhcp"): + del self.prefs["_dhcp"] + try: + self.cbox.prefs.plugin_conf.write() + except IOError: + self.cbox.log.warn("Could not write plugin configuration") + else: + self.cbox.log.warn("[network] failed to change gateway address to: %s" \ + % new_gw_str) + self.hdf["Data.Warning"] = "Plugins.network.GatewayChangeFailed" + self.__prepare_form_data() + return "form_network" + ## request for dhcp usage + elif store == "use_dhcp": + if confirm_dhcp != "1": + ## do nothing as the action was not confirmed with the checkbox + self.hdf["Data.Warning"] = "Plugins.network.DHCPNotConfirmed" + self.__prepare_form_data() + return "form_network" + else: + self.cbox.log.info( "[network] recieve network settings via DHCP") + if not os.path.isfile(self.root_action.DHCLIENT_BIN): + self.hdf["Data.Warning"] = "Plugins.network.DHCPNotFound" + elif self.__use_dhcp(): + self.hdf["Data.Success"] = "Plugins.network.DHCPRunning" + self.prefs["_dhcp"] = "use" + try: + self.cbox.prefs.plugin_conf.write() + except IOError: + self.cbox.log.warn("Could not write plugin configuration") + else: + self.hdf["Data.Warning"] = "Plugins.network.DHCPNotRunning" + self.__prepare_form_data() + return "form_network" + else: + ## invalid action was requested -> show default form + self.cbox.log.debug("network plugin: invalid request (%s)" % str(store)) + self.__prepare_form_data() + return "form_network" - def get_status(self): - """The current IP is the status of this feature. - """ - return "%d.%d.%d.%d" % self.__get_current_ip() + def get_status(self): + """The current IP is the status of this feature. + """ + return "%d.%d.%d.%d" % self.__get_current_ip() - def handle_event(self, event, event_info=None): - """Override bootup behaviour + def handle_event(self, event, event_info=None): + """Override bootup behaviour - Apply the configured network settings - """ - if event == "bootup": - if "_address" in self.prefs: - if "_netmask" in self.prefs: - ## change the ip without any delay - otherwise the following - ## gateway setting will fail, if the network range changes - self.__set_ip(self.prefs["_address"], self.prefs["_netmask"], - change_delay=0) - else: - ## no netmask setting stored - self.__set_ip(self.prefs["_address"]) - if "_gateway" in self.prefs: - self.__set_gw(".".join([str(e) for e in self.__get_current_gw()]), - self.prefs["_gateway"]) + Apply the configured network settings + """ + if event == "bootup": + if "_address" in self.prefs: + if "_netmask" in self.prefs: + ## change the ip without any delay - otherwise the following + ## gateway setting will fail, if the network range changes + self.__set_ip(self.prefs["_address"], self.prefs["_netmask"], + change_delay=0) + else: + ## no netmask setting stored + self.__set_ip(self.prefs["_address"]) + if "_gateway" in self.prefs: + self.__set_gw(".".join([str(e) for e in self.__get_current_gw()]), + self.prefs["_gateway"]) - def get_warnings(self): - """Check for missing programs - """ - warnings = [] - if not os.path.isfile(self.root_action.IFCONFIG_BIN): - warnings.append((55, "Plugins.%s.MissingProgramIfconfig" % self.get_name())) - if not os.path.isfile(self.root_action.ROUTE_BIN): - warnings.append((52, "Plugins.%s.MissingProgramRoute" % self.get_name())) - return warnings + def get_warnings(self): + """Check for missing programs + """ + warnings = [] + if not os.path.isfile(self.root_action.IFCONFIG_BIN): + warnings.append((55, "Plugins.%s.MissingProgramIfconfig" % self.get_name())) + if not os.path.isfile(self.root_action.ROUTE_BIN): + warnings.append((52, "Plugins.%s.MissingProgramRoute" % self.get_name())) + return warnings - def __get_redirect_destination(self, ip): - """Put the new URL together. - """ - import cherrypy - req = cherrypy.request - base_parts = req.base.split(":") - dest = "%s://%s" % (base_parts[0], ip) - if len(base_parts) == 3: - dest += ":%s" % base_parts[2] - dest += "/network" - return dest + def __get_redirect_destination(self, ip): + """Put the new URL together. + """ + import cherrypy + req = cherrypy.request + base_parts = req.base.split(":") + dest = "%s://%s" % (base_parts[0], ip) + if len(base_parts) == 3: + dest += ":%s" % base_parts[2] + dest += "/network" + return dest - def __prepare_form_data(self): - """Set some hdf values. - """ - #TODO: the following looks nicer in a loop - (oc1, oc2, oc3, oc4) = self.__get_current_ip("ip") - self.hdf[self.hdf_prefix + "ip.oc1"] = oc1 - self.hdf[self.hdf_prefix + "ip.oc2"] = oc2 - self.hdf[self.hdf_prefix + "ip.oc3"] = oc3 - self.hdf[self.hdf_prefix + "ip.oc4"] = oc4 - (oc1, oc2, oc3, oc4) = self.__get_current_ip("nm") - self.hdf[self.hdf_prefix + "nm.oc1"] = oc1 - self.hdf[self.hdf_prefix + "nm.oc2"] = oc2 - self.hdf[self.hdf_prefix + "nm.oc3"] = oc3 - self.hdf[self.hdf_prefix + "nm.oc4"] = oc4 - (oc1, oc2, oc3, oc4) = self.__get_current_gw() - self.hdf[self.hdf_prefix + "gw.oc1"] = oc1 - self.hdf[self.hdf_prefix + "gw.oc2"] = oc2 - self.hdf[self.hdf_prefix + "gw.oc3"] = oc3 - self.hdf[self.hdf_prefix + "gw.oc4"] = oc4 - if self.prefs.has_key("_dhcp"): - self.hdf[self.hdf_prefix + "dhcp"] = str(self.prefs["_dhcp"]) + def __prepare_form_data(self): + """Set some hdf values. + """ + #TODO: the following looks nicer in a loop + (oc1, oc2, oc3, oc4) = self.__get_current_ip("ip") + self.hdf[self.hdf_prefix + "ip.oc1"] = oc1 + self.hdf[self.hdf_prefix + "ip.oc2"] = oc2 + self.hdf[self.hdf_prefix + "ip.oc3"] = oc3 + self.hdf[self.hdf_prefix + "ip.oc4"] = oc4 + (oc1, oc2, oc3, oc4) = self.__get_current_ip("nm") + self.hdf[self.hdf_prefix + "nm.oc1"] = oc1 + self.hdf[self.hdf_prefix + "nm.oc2"] = oc2 + self.hdf[self.hdf_prefix + "nm.oc3"] = oc3 + self.hdf[self.hdf_prefix + "nm.oc4"] = oc4 + (oc1, oc2, oc3, oc4) = self.__get_current_gw() + self.hdf[self.hdf_prefix + "gw.oc1"] = oc1 + self.hdf[self.hdf_prefix + "gw.oc2"] = oc2 + self.hdf[self.hdf_prefix + "gw.oc3"] = oc3 + self.hdf[self.hdf_prefix + "gw.oc4"] = oc4 + if self.prefs.has_key("_dhcp"): + self.hdf[self.hdf_prefix + "dhcp"] = str(self.prefs["_dhcp"]) - def __get_current_ip(self, address_type="ip"): - """Retrieve the current IP. + def __get_current_ip(self, address_type="ip"): + """Retrieve the current IP. - TODO: do not use "address_type" for ip and netmask, but return both in - two tuples - """ - import re - ## get the current IP of the network interface - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - args = [ - self.root_action.IFCONFIG_BIN, - self.__get_interface()]) - (stdout, stderr) = proc.communicate() - if proc.returncode != 0: - return (0, 0, 0, 0) - if address_type == "ip": - ## this regex matches the four numbers of the IP - match = re.search(r'inet [\w]+:(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s', stdout) - if match: - ## use the previously matched numbers - return tuple([int(e) for e in match.groups()]) - else: - return (0, 0, 0, 0) - elif address_type == "nm": - ## this greps the netmask - match = re.search( - r'inet [\w]+:.*Mask:(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s', - stdout) - if match: - ## use the previously matched numbers - return tuple([int(e) for e in match.groups()]) - else: - return (0, 0, 0, 0) + TODO: do not use "address_type" for ip and netmask, but return both in + two tuples + """ + import re + ## get the current IP of the network interface + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [ + self.root_action.IFCONFIG_BIN, + self.__get_interface()]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + return (0, 0, 0, 0) + if address_type == "ip": + ## this regex matches the four numbers of the IP + match = re.search(r'inet [\w]+:(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s', stdout) + if match: + ## use the previously matched numbers + return tuple([int(e) for e in match.groups()]) + else: + return (0, 0, 0, 0) + elif address_type == "nm": + ## this greps the netmask + match = re.search( + r'inet [\w]+:.*Mask:(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s', + stdout) + if match: + ## use the previously matched numbers + return tuple([int(e) for e in match.groups()]) + else: + return (0, 0, 0, 0) - def __get_current_gw(self): - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.root_action.ROUTE_BIN, - "-n"]) - (stdout, stderr) = proc.communicate() - if proc.returncode != 0: - self.cbox.log.warn( - "[network] failed to retrieve gateway address: %s" % stdout) - return (0, 0, 0, 0) - current_interface = self.__get_interface() - ## skip the first two heading lines - for line in stdout.splitlines()[2:]: - attrs = line.split() - if len(attrs) != 8: - self.cbox.log.info("[network] misformed route entry: %s" % line) - continue - interface = attrs[7] - netmask = attrs[2] - gateway = attrs[1] - destination = attrs[0] - if (destination == "0.0.0.0") and (netmask == "0.0.0.0") and \ - (interface == current_interface): - gw_octet = tuple(gateway.split(".")) - if len(gw_octet) != 4: - self.cbox.log.info( - "[network] ignored invalid gateway setting: %s" % gateway) - else: - return gw_octet - return (0, 0, 0, 0) + def __get_current_gw(self): + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.root_action.ROUTE_BIN, + "-n"]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.cbox.log.warn( + "[network] failed to retrieve gateway address: %s" % stdout) + return (0, 0, 0, 0) + current_interface = self.__get_interface() + ## skip the first two heading lines + for line in stdout.splitlines()[2:]: + attrs = line.split() + if len(attrs) != 8: + self.cbox.log.info("[network] misformed route entry: %s" % line) + continue + interface = attrs[7] + netmask = attrs[2] + gateway = attrs[1] + destination = attrs[0] + if (destination == "0.0.0.0") and (netmask == "0.0.0.0") and \ + (interface == current_interface): + gw_octet = tuple(gateway.split(".")) + if len(gw_octet) != 4: + self.cbox.log.info( + "[network] ignored invalid gateway setting: %s" % gateway) + else: + return gw_octet + return (0, 0, 0, 0) - def __set_ip(self, new_ip, new_nm="255.255.255.0", change_delay=None): - """Change the IP, additionally a netmask can be applied - """ - import threading - if change_delay is None: - change_delay = CHANGE_IP_DELAY - ## call the root_action script after some seconds - so we can deliver the page before - def delayed_ip_change(): - """A threaded function to change the IP. - """ - import time - if change_delay > 0: - time.sleep(change_delay) - proc = subprocess.Popen( - shell = False, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "plugin", - os.path.join(self.plugin_dir, "root_action.py"), - "change_ip", - self.__get_interface(), - new_ip, - new_nm]) - proc.wait() - if proc.returncode != 0: - self.cbox.log.warn("failed to change IP address: %s" % new_ip) - self.cbox.log.warn("error output: %s" % str(proc.stderr.read())) - return - thread = threading.Thread() - thread.run = delayed_ip_change - thread.setDaemon(True) - thread.start() - # TODO: how could we guess, if it failed? - return True - + def __set_ip(self, new_ip, new_nm="255.255.255.0", change_delay=None): + """Change the IP, additionally a netmask can be applied + """ + import threading + if change_delay is None: + change_delay = CHANGE_IP_DELAY + ## call the root_action script after some seconds - so we can deliver the page before + def delayed_ip_change(): + """A threaded function to change the IP. + """ + import time + if change_delay > 0: + time.sleep(change_delay) + proc = subprocess.Popen( + shell = False, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.plugin_dir, "root_action.py"), + "change_ip", + self.__get_interface(), + new_ip, + new_nm]) + proc.wait() + if proc.returncode != 0: + self.cbox.log.warn("failed to change IP address: %s" % new_ip) + self.cbox.log.warn("error output: %s" % str(proc.stderr.read())) + return + thread = threading.Thread() + thread.run = delayed_ip_change + thread.setDaemon(True) + thread.start() + # TODO: how could we guess, if it failed? + return True + - def __set_gw(self, old_ip, new_ip): - """Change the gateway IP adress - """ - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "plugin", - os.path.join(self.plugin_dir, "root_action.py"), - "change_gw", - old_ip, - new_ip]) - (output, error) = proc.communicate() - if proc.returncode != 0: - self.cbox.log.warn("[network] gateway setting failed: %s" % str(error)) - return False - else: - return True + def __set_gw(self, old_ip, new_ip): + """Change the gateway IP adress + """ + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.plugin_dir, "root_action.py"), + "change_gw", + old_ip, + new_ip]) + (output, error) = proc.communicate() + if proc.returncode != 0: + self.cbox.log.warn("[network] gateway setting failed: %s" % str(error)) + return False + else: + return True - def __get_interface(self): - """Return the name of the configured network interface - """ - if "interface" in self.defaults: - return self.defaults["interface"] - else: - return DEFAULT_INTERFACE + def __get_interface(self): + """Return the name of the configured network interface + """ + if "interface" in self.defaults: + return self.defaults["interface"] + else: + return DEFAULT_INTERFACE - def __IP_is_valid(self, ip1, ip2, ip3, ip4): - try: - for ip_in in (ip1, ip2, ip3, ip4): - if (int(ip_in) < 0) or (int(ip_in) > 255): - ## we give an info only and a webwarning - ## further reaction depends on the case - self.cbox.log.info("IP number is invalid: %s" % \ - str((ip1, ip2, ip3, ip4))) - raise ValueError - except ValueError: - ## handled by individual caller - #self.hdf["Data.Warning"] = "Plugins.network.InvalidIP" - return False - return True + def __IP_is_valid(self, ip1, ip2, ip3, ip4): + try: + for ip_in in (ip1, ip2, ip3, ip4): + if (int(ip_in) < 0) or (int(ip_in) > 255): + ## we give an info only and a webwarning + ## further reaction depends on the case + self.cbox.log.info("IP number is invalid: %s" % \ + str((ip1, ip2, ip3, ip4))) + raise ValueError + except ValueError: + ## handled by individual caller + #self.hdf["Data.Warning"] = "Plugins.network.InvalidIP" + return False + return True - def __use_dhcp(self): - """Try to recieve network settings via dhcp - """ - proc = subprocess.Popen( - shell = False, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "plugin", - os.path.join(self.plugin_dir, "root_action.py"), - "use_dhcp", - self.__get_interface() ]) - proc.wait() - if proc.returncode != 0: - self.cbox.log.warn("failed to recieve IP address via DHCP") - self.cbox.log.warn("error output: %s" % str(proc.stderr.read())) - return True + def __use_dhcp(self): + """Try to recieve network settings via dhcp + """ + proc = subprocess.Popen( + shell = False, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.plugin_dir, "root_action.py"), + "use_dhcp", + self.__get_interface() ]) + proc.wait() + if proc.returncode != 0: + self.cbox.log.warn("failed to recieve IP address via DHCP") + self.cbox.log.warn("error output: %s" % str(proc.stderr.read())) + return True diff --git a/plugins/network/root_action.py b/plugins/network/root_action.py index 5d2e2c2..a6930f0 100755 --- a/plugins/network/root_action.py +++ b/plugins/network/root_action.py @@ -34,88 +34,88 @@ import re import sys def __changeIP(interface, ipaddress, netmask="0"): - __check_address(ipaddress) - if netmask == "0": - ## change the IP only - proc = subprocess.Popen( - shell = False, - args = [IFCONFIG_BIN, interface, ipaddress]) - proc.wait() - else: - ## someone wants to change the netmask too - __check_address(netmask) - proc = subprocess.Popen( - shell = False, - args = [IFCONFIG_BIN, interface, ipaddress, "netmask", netmask]) - proc.wait() - return proc.returncode == 0 + __check_address(ipaddress) + if netmask == "0": + ## change the IP only + proc = subprocess.Popen( + shell = False, + args = [IFCONFIG_BIN, interface, ipaddress]) + proc.wait() + else: + ## someone wants to change the netmask too + __check_address(netmask) + proc = subprocess.Popen( + shell = False, + args = [IFCONFIG_BIN, interface, ipaddress, "netmask", netmask]) + proc.wait() + return proc.returncode == 0 def __changeGW(old_gw, new_gw): - __check_address(old_gw) - __check_address(new_gw) - if old_gw != "0.0.0.0": - ## assume that a default route exists and delete it - proc = subprocess.Popen( - shell = False, - args = [ROUTE_BIN, "del", "default", "gw", old_gw]) - proc.wait() - ## ignore errors - proc = subprocess.Popen( - shell = False, - args = [ROUTE_BIN, "add", "default", "gw", new_gw]) - proc.wait() - return proc.returncode == 0 + __check_address(old_gw) + __check_address(new_gw) + if old_gw != "0.0.0.0": + ## assume that a default route exists and delete it + proc = subprocess.Popen( + shell = False, + args = [ROUTE_BIN, "del", "default", "gw", old_gw]) + proc.wait() + ## ignore errors + proc = subprocess.Popen( + shell = False, + args = [ROUTE_BIN, "add", "default", "gw", new_gw]) + proc.wait() + return proc.returncode == 0 def __check_address(address): - """Check for correct numbers in given address - """ - match = re.search(r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$', address) - ## did we match? If yes, then: are there wrong values inside? - if not match or [e for e in match.groups() if int(e) > 255]: - sys.stderr.write("%s: illegal argument (%s)\n" % (self_bin, address)) - sys.exit(1) - return + """Check for correct numbers in given address + """ + match = re.search(r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$', address) + ## did we match? If yes, then: are there wrong values inside? + if not match or [e for e in match.groups() if int(e) > 255]: + sys.stderr.write("%s: illegal argument (%s)\n" % (self_bin, address)) + sys.exit(1) + return def __use_DHCP(interface): - """Recieve network settings via dhcp - """ - proc = subprocess.Popen( - shell = False, - args = [DHCLIENT_BIN, interface]) - proc.wait() + """Recieve network settings via dhcp + """ + proc = subprocess.Popen( + shell = False, + args = [DHCLIENT_BIN, interface]) + proc.wait() if __name__ == "__main__": - args = sys.argv[1:] + args = sys.argv[1:] - self_bin = sys.argv[0] - - if len(args) == 0: - sys.stderr.write("%s: no argument supplied\n" % self_bin) - sys.exit(1) - - try: - if args[0] == "change_ip": - if len(args) != 4: raise "InvalidArgNum" - result = __changeIP(args[1], args[2], args[3]) - elif args[0] == "change_gw": - if len(args) != 3: raise "InvalidArgNum" - result = __changeGW(args[1], args[2]) - elif args[0] == "use_dhcp": - if len(args) != 2: raise "InvalidArgNum" - result = __use_DHCP(args[1]) - else: - sys.stderr.write("%s: invalid action (%s)\n" % (self_bin, args[0])) - sys.exit(1) - if result: - sys.exit(0) - else: - sys.exit(1) - except "InvalidArgNum": - sys.stderr.write("%s: invalid number of arguments (%s)\n" % (self_bin, args)) - sys.exit(1) - + self_bin = sys.argv[0] + + if len(args) == 0: + sys.stderr.write("%s: no argument supplied\n" % self_bin) + sys.exit(1) + + try: + if args[0] == "change_ip": + if len(args) != 4: raise "InvalidArgNum" + result = __changeIP(args[1], args[2], args[3]) + elif args[0] == "change_gw": + if len(args) != 3: raise "InvalidArgNum" + result = __changeGW(args[1], args[2]) + elif args[0] == "use_dhcp": + if len(args) != 2: raise "InvalidArgNum" + result = __use_DHCP(args[1]) + else: + sys.stderr.write("%s: invalid action (%s)\n" % (self_bin, args[0])) + sys.exit(1) + if result: + sys.exit(0) + else: + sys.exit(1) + except "InvalidArgNum": + sys.stderr.write("%s: invalid number of arguments (%s)\n" % (self_bin, args)) + sys.exit(1) + diff --git a/plugins/network/unittests.py b/plugins/network/unittests.py index 0695d4f..ec88ccf 100644 --- a/plugins/network/unittests.py +++ b/plugins/network/unittests.py @@ -26,53 +26,53 @@ from network import CHANGE_IP_DELAY class unittests(WebInterfaceTestClass): - def test_ip_change(self): - '''Change network address.''' - ## the time module is necessary for the CHANGE_IP_DELAY - import time - self.register_auth(self.url + "network") - ## do not follow redirects - they would break the test otherwise - self.cmd.config("acknowledge_equiv_refresh", 0) - self.cmd.go(self.url + "network") - ## extract the current IP from the network plugin output - def get_current_ip(): - self.register_auth(self.url + "network") - self.cmd.go(self.url + "network") - self.cmd.find(r'Data.Status.Plugins.network=([0-9\.]*)$', "m") - return self.locals["__match__"] - orig_ip_text = get_current_ip() - orig_ip_octs = orig_ip_text.split(".") - ## check, if the original IP is valid (contains four octets) - self.assertEquals(4, len(orig_ip_octs)) - def set_ip((ip1, ip2, ip3, ip4)): - self.cmd.go(self.url + "network") - self.cmd.formvalue("network_address", "ip1", str(ip1)) - self.cmd.formvalue("network_address", "ip2", str(ip2)) - self.cmd.formvalue("network_address", "ip3", str(ip3)) - self.cmd.formvalue("network_address", "ip4", str(ip4)) - self.cmd.submit() - ## sleep a little bit longer than the delay necessary for ip-change - time.sleep(CHANGE_IP_DELAY + 3) - set_ip([1,-2,0,1]) - self.assertEquals(orig_ip_text, get_current_ip()) - set_ip([1,0,0,256]) - self.assertEquals(orig_ip_text, get_current_ip()) - set_ip([1,"foo",0,1]) - self.assertEquals(orig_ip_text, get_current_ip()) - new_ip = orig_ip_octs[:] - new_ip[3] = str((int(orig_ip_octs[3]) + 128) % 256) - set_ip(new_ip) - self.assertEquals(".".join(new_ip), get_current_ip()) - set_ip(orig_ip_octs) - self.assertEquals(orig_ip_text, get_current_ip()) + def test_ip_change(self): + '''Change network address.''' + ## the time module is necessary for the CHANGE_IP_DELAY + import time + self.register_auth(self.url + "network") + ## do not follow redirects - they would break the test otherwise + self.cmd.config("acknowledge_equiv_refresh", 0) + self.cmd.go(self.url + "network") + ## extract the current IP from the network plugin output + def get_current_ip(): + self.register_auth(self.url + "network") + self.cmd.go(self.url + "network") + self.cmd.find(r'Data.Status.Plugins.network=([0-9\.]*)$', "m") + return self.locals["__match__"] + orig_ip_text = get_current_ip() + orig_ip_octs = orig_ip_text.split(".") + ## check, if the original IP is valid (contains four octets) + self.assertEquals(4, len(orig_ip_octs)) + def set_ip((ip1, ip2, ip3, ip4)): + self.cmd.go(self.url + "network") + self.cmd.formvalue("network_address", "ip1", str(ip1)) + self.cmd.formvalue("network_address", "ip2", str(ip2)) + self.cmd.formvalue("network_address", "ip3", str(ip3)) + self.cmd.formvalue("network_address", "ip4", str(ip4)) + self.cmd.submit() + ## sleep a little bit longer than the delay necessary for ip-change + time.sleep(CHANGE_IP_DELAY + 3) + set_ip([1,-2,0,1]) + self.assertEquals(orig_ip_text, get_current_ip()) + set_ip([1,0,0,256]) + self.assertEquals(orig_ip_text, get_current_ip()) + set_ip([1,"foo",0,1]) + self.assertEquals(orig_ip_text, get_current_ip()) + new_ip = orig_ip_octs[:] + new_ip[3] = str((int(orig_ip_octs[3]) + 128) % 256) + set_ip(new_ip) + self.assertEquals(".".join(new_ip), get_current_ip()) + set_ip(orig_ip_octs) + self.assertEquals(orig_ip_text, get_current_ip()) - def test_inputs(self): - """Check various input patterns for 'network' plugin. - """ - self.register_auth(self.url + "network") - self.cmd.go(self.url + "network" + "?redirected=1") - self.cmd.notfind("problem") - self.cmd.go(self.url + "network" + "?store=set_ip") - self.cmd.find("server address is not valid") + def test_inputs(self): + """Check various input patterns for 'network' plugin. + """ + self.register_auth(self.url + "network") + self.cmd.go(self.url + "network" + "?redirected=1") + self.cmd.notfind("problem") + self.cmd.go(self.url + "network" + "?store=set_ip") + self.cmd.find("server address is not valid") diff --git a/plugins/partition/partition.py b/plugins/partition/partition.py index 80e81c4..791c693 100644 --- a/plugins/partition/partition.py +++ b/plugins/partition/partition.py @@ -33,505 +33,505 @@ from cryptobox.core.exceptions import * PARTTYPES = { - "windows" : ["0xC", "vfat"], - "linux" : ["L", "ext3"]} + "windows" : ["0xC", "vfat"], + "linux" : ["L", "ext3"]} CONFIGPARTITION = { - "size" : 5, # size of configuration partition (if necessary) in MB - "type" : "L", - "fs" : "ext2"} + "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. - """ + """The partition feature of the CryptoBox. + """ - plugin_capabilities = [ "system" ] - plugin_visibility = [ "preferences" ] - request_auth = True - rank = 80 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = True + rank = 80 - def do_action(self, **args): - """Show the partitioning form and execute the requested action. - """ - ## load default hdf values - self.__prepare_dataset() - ## retrieve some values from 'args' - defaults are empty - self.blockdevice = self.__get_selected_device(args) - self.with_config_partition = self.__is_with_config_partition() - self.cbox.log.debug( - "partition plugin: selected device=%s" % str(self.blockdevice)) - self.blockdevice_size = self.__get_available_device_size(self.blockdevice) - ## no (or invalid) device was supplied - if not self.blockdevice: - return self.__action_select_device() - ## exit if the blockdevice is not writeable - if not os.access(self.blockdevice, os.W_OK): - self.hdf["Data.Warning"] = "DeviceNotWriteable" - return self.__action_select_device() - ## no confirm setting? - if not args.has_key("confirm") or (args["confirm"] != "1"): - self.hdf["Data.Warning"] = "Plugins.partition.FormatNotConfirmed" - return self.__action_select_device() - elif args.has_key("easy"): - return self.__action_easy_setup() - elif args.has_key("add_part"): - return self.__action_add_partition(args) - elif args.has_key("finish"): - return self.__action_finish(args) - elif args.has_key("cancel"): - return self.__action_select_device() - ## check if we should remove a partition - del_args = [ e for e in args.keys() if re.match(r"del_part_[\d]+$", e) ] - if len(del_args) == 1: - try: - num_part = int(del_args[0][9:]) - except ValueError: - self.cbox.log.warn( - "partition: invalid partition number to delete (%s)" % del_args[0]) - return self.__action_select_device() - return self.__action_del_partition(args, num_part) - else: # for "select_device" and for invalid targets - return self.__action_select_device() + def do_action(self, **args): + """Show the partitioning form and execute the requested action. + """ + ## load default hdf values + self.__prepare_dataset() + ## retrieve some values from 'args' - defaults are empty + self.blockdevice = self.__get_selected_device(args) + self.with_config_partition = self.__is_with_config_partition() + self.cbox.log.debug( + "partition plugin: selected device=%s" % str(self.blockdevice)) + self.blockdevice_size = self.__get_available_device_size(self.blockdevice) + ## no (or invalid) device was supplied + if not self.blockdevice: + return self.__action_select_device() + ## exit if the blockdevice is not writeable + if not os.access(self.blockdevice, os.W_OK): + self.hdf["Data.Warning"] = "DeviceNotWriteable" + return self.__action_select_device() + ## no confirm setting? + if not args.has_key("confirm") or (args["confirm"] != "1"): + self.hdf["Data.Warning"] = "Plugins.partition.FormatNotConfirmed" + return self.__action_select_device() + elif args.has_key("easy"): + return self.__action_easy_setup() + elif args.has_key("add_part"): + return self.__action_add_partition(args) + elif args.has_key("finish"): + return self.__action_finish(args) + elif args.has_key("cancel"): + return self.__action_select_device() + ## check if we should remove a partition + del_args = [ e for e in args.keys() if re.match(r"del_part_[\d]+$", e) ] + if len(del_args) == 1: + try: + num_part = int(del_args[0][9:]) + except ValueError: + self.cbox.log.warn( + "partition: invalid partition number to delete (%s)" % del_args[0]) + return self.__action_select_device() + return self.__action_del_partition(args, num_part) + else: # for "select_device" and for invalid targets + return self.__action_select_device() - def get_status(self): - """The status of this plugin is the selected device and some information. - """ - return "%s / %s / %s" % (self.blockdevice, self.blockdevice_size, - self.with_config_partition) + def get_status(self): + """The status of this plugin is the selected device and some information. + """ + return "%s / %s / %s" % (self.blockdevice, self.blockdevice_size, + self.with_config_partition) - def get_warnings(self): - warnings = [] - ## this check is done _after_ "reset_dataset" -> if there is - ## a config partition, then it was loaded before - if self.cbox.prefs.requires_partition() \ - and not self.cbox.prefs.get_active_partition(): - warnings.append((50, "Plugins.%s.ReadOnlyConfig" % self.get_name())) - ## check required programs - if not os.path.isfile(self.root_action.SFDISK_BIN): - warnings.append((53, "Plugins.%s.MissingProgramSfdisk" % self.get_name())) - if not os.path.isfile(self.root_action.MKFS_BIN): - warnings.append((56, "Plugins.%s.MissingProgramMkfs" % self.get_name())) - if not os.path.isfile(self.root_action.LABEL_BIN): - warnings.append((40, "Plugins.%s.MissingProgramE2label" % self.get_name())) - return warnings + def get_warnings(self): + warnings = [] + ## this check is done _after_ "reset_dataset" -> if there is + ## a config partition, then it was loaded before + if self.cbox.prefs.requires_partition() \ + and not self.cbox.prefs.get_active_partition(): + warnings.append((50, "Plugins.%s.ReadOnlyConfig" % self.get_name())) + ## check required programs + if not os.path.isfile(self.root_action.SFDISK_BIN): + warnings.append((53, "Plugins.%s.MissingProgramSfdisk" % self.get_name())) + if not os.path.isfile(self.root_action.MKFS_BIN): + warnings.append((56, "Plugins.%s.MissingProgramMkfs" % self.get_name())) + if not os.path.isfile(self.root_action.LABEL_BIN): + warnings.append((40, "Plugins.%s.MissingProgramE2label" % self.get_name())) + return warnings - def __prepare_dataset(self): - """Set some hdf values. - """ - self.hdf["Data.AdditionalStylesheets.%s" % self.get_name()] = \ - os.path.join(self.plugin_dir, "partition.css") - self.hdf[self.hdf_prefix + "PluginDir"] = self.plugin_dir + def __prepare_dataset(self): + """Set some hdf values. + """ + self.hdf["Data.AdditionalStylesheets.%s" % self.get_name()] = \ + os.path.join(self.plugin_dir, "partition.css") + self.hdf[self.hdf_prefix + "PluginDir"] = self.plugin_dir - def __get_selected_device(self, args): - """Check the selected device (valid, not busy, ...). - """ - try: - blockdevice = args["block_device"] - except KeyError: - return None - if not self.__is_device_valid(blockdevice): - return None - if self.__is_device_busy(blockdevice): - self.hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy" - return None - return blockdevice + def __get_selected_device(self, args): + """Check the selected device (valid, not busy, ...). + """ + try: + blockdevice = args["block_device"] + except KeyError: + return None + if not self.__is_device_valid(blockdevice): + return None + if self.__is_device_busy(blockdevice): + self.hdf["Data.Warning"] = "Plugins.partition.DiskIsBusy" + return None + return blockdevice - def __is_device_valid(self, blockdevice): - """Check if the device is valid and allowed. - """ - if not blockdevice: - return False - if not self.cbox.is_device_allowed(blockdevice): - return False - if not blockdevice in cbox_tools.get_parent_blockdevices(): - return False - return True + def __is_device_valid(self, blockdevice): + """Check if the device is valid and allowed. + """ + if not blockdevice: + return False + if not self.cbox.is_device_allowed(blockdevice): + return False + if not blockdevice in cbox_tools.get_parent_blockdevices(): + return False + return True - def __is_device_busy(self, blockdevice): - """check if the device (or one of its partitions) is mounted - """ - ## the config partition is ignored, as it will get unmounted if necessary - for cont in self.cbox.get_container_list(): - if cbox_tools.is_part_of_blockdevice(blockdevice, cont.get_device()) \ - and cont.is_mounted(): - return True - return False + def __is_device_busy(self, blockdevice): + """check if the device (or one of its partitions) is mounted + """ + ## the config partition is ignored, as it will get unmounted if necessary + for cont in self.cbox.get_container_list(): + if cbox_tools.is_part_of_blockdevice(blockdevice, cont.get_device()) \ + and cont.is_mounted(): + return True + return False - def __action_select_device(self): - """Show a form to select the device for partitioning. - """ - block_devices = [e - for e in cbox_tools.get_parent_blockdevices() - if self.cbox.is_device_allowed(e)] - counter = 0 - for dev in block_devices: - self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = dev - self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = \ - cbox_tools.get_blockdevice_size_humanly(dev) - self.cbox.log.debug("found a suitable block device: %s" % dev) - counter += 1 - if self.with_config_partition: - self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" - ## there is no disk available - if not block_devices: - self.hdf["Data.Warning"] = "Plugins.partition.NoDisksAvailable" - return "select_device" + def __action_select_device(self): + """Show a form to select the device for partitioning. + """ + block_devices = [e + for e in cbox_tools.get_parent_blockdevices() + if self.cbox.is_device_allowed(e)] + counter = 0 + for dev in block_devices: + self.hdf[self.hdf_prefix + "BlockDevices.%d.name" % counter] = dev + self.hdf[self.hdf_prefix + "BlockDevices.%d.size" % counter] = \ + cbox_tools.get_blockdevice_size_humanly(dev) + self.cbox.log.debug("found a suitable block device: %s" % dev) + counter += 1 + if self.with_config_partition: + self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" + ## there is no disk available + if not block_devices: + self.hdf["Data.Warning"] = "Plugins.partition.NoDisksAvailable" + return "select_device" - def __action_add_partition(self, args): - """Add a selected partition to the currently proposed partition table. - """ - self.hdf[self.hdf_prefix + "Device"] = self.blockdevice - self.hdf[self.hdf_prefix + "Device.Size"] = self.blockdevice_size - parts = self.__get_partitions_from_args(args) - self.__set_partition_data(parts) - return "set_partitions" - + def __action_add_partition(self, args): + """Add a selected partition to the currently proposed partition table. + """ + self.hdf[self.hdf_prefix + "Device"] = self.blockdevice + self.hdf[self.hdf_prefix + "Device.Size"] = self.blockdevice_size + parts = self.__get_partitions_from_args(args) + self.__set_partition_data(parts) + return "set_partitions" + - def __action_del_partition(self, args, part_num): - """Remove a partition from the proposed partition table. - """ - self.hdf[self.hdf_prefix + "Device"] = self.blockdevice - self.hdf[self.hdf_prefix + "Device.Size"] = self.blockdevice_size - parts = self.__get_partitions_from_args(args) - ## valid partition number to be deleted? - if part_num < len(parts): - del parts[part_num] - self.__set_partition_data(parts) - return "set_partitions" + def __action_del_partition(self, args, part_num): + """Remove a partition from the proposed partition table. + """ + self.hdf[self.hdf_prefix + "Device"] = self.blockdevice + self.hdf[self.hdf_prefix + "Device.Size"] = self.blockdevice_size + parts = self.__get_partitions_from_args(args) + ## valid partition number to be deleted? + if part_num < len(parts): + del parts[part_num] + self.__set_partition_data(parts) + return "set_partitions" - def __action_finish(self, args): - """Write the partition table. - """ - parts = self.__get_partitions_from_args(args) - if parts: - self.__set_partition_data(parts) - if cbox_tools.is_part_of_blockdevice(self.blockdevice, - self.cbox.prefs.get_active_partition()): - self.cbox.prefs.umount_partition() - if not self.__run_fdisk(parts): - self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" - self.cbox.log.warn( - "partition: failed to partition device: %s" % self.blockdevice) - return self.__action_add_partition(args) - else: - ## tricky problem: if the device was partitioned, then a created config - ## partition is still part of the containerlist, as the label is not - ## checked again - very ugly!!! So we will call reReadContainerList - ## after formatting the last partition - see below - #self.cbox.reread_container_list() - format_ok = True - counter = 0 - ## initialize the generator - format_part_gen = self.__format_partitions(parts) - while counter < len(parts): - ## first part: get the device name - counter += 1 - ## second part: do the real formatting of a partition - result = format_part_gen.next() - ## after the first partiton, we can reRead the containerList - ## (as the possible config partition was already created) - if self.with_config_partition and (counter == 1): - ## important: reRead the containerList - but somehow it - ## breaks the flow (hanging process) - #self.cbox.reReadContainerList() - ## write config data - self.cbox.prefs.mount_partition() - try: - self.cbox.prefs.write() - self.cbox.log.info("settings stored on config partition") - except IOError: - self.cbox.log.warn( - "Failed to store settings on new config partition") - ## return the result - if not result: - format_ok = False - if format_ok: - self.hdf["Data.Success"] = "Plugins.partition.Partitioned" - else: - self.hdf["Data.Warning"] = "Plugins.partition.FormattingFailed" - return "empty" - else: - return self.__action_add_partition(args) + def __action_finish(self, args): + """Write the partition table. + """ + parts = self.__get_partitions_from_args(args) + if parts: + self.__set_partition_data(parts) + if cbox_tools.is_part_of_blockdevice(self.blockdevice, + self.cbox.prefs.get_active_partition()): + self.cbox.prefs.umount_partition() + if not self.__run_fdisk(parts): + self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" + self.cbox.log.warn( + "partition: failed to partition device: %s" % self.blockdevice) + return self.__action_add_partition(args) + else: + ## tricky problem: if the device was partitioned, then a created config + ## partition is still part of the containerlist, as the label is not + ## checked again - very ugly!!! So we will call reReadContainerList + ## after formatting the last partition - see below + #self.cbox.reread_container_list() + format_ok = True + counter = 0 + ## initialize the generator + format_part_gen = self.__format_partitions(parts) + while counter < len(parts): + ## first part: get the device name + counter += 1 + ## second part: do the real formatting of a partition + result = format_part_gen.next() + ## after the first partiton, we can reRead the containerList + ## (as the possible config partition was already created) + if self.with_config_partition and (counter == 1): + ## important: reRead the containerList - but somehow it + ## breaks the flow (hanging process) + #self.cbox.reReadContainerList() + ## write config data + self.cbox.prefs.mount_partition() + try: + self.cbox.prefs.write() + self.cbox.log.info("settings stored on config partition") + except IOError: + self.cbox.log.warn( + "Failed to store settings on new config partition") + ## return the result + if not result: + format_ok = False + if format_ok: + self.hdf["Data.Success"] = "Plugins.partition.Partitioned" + else: + self.hdf["Data.Warning"] = "Plugins.partition.FormattingFailed" + return "empty" + else: + return self.__action_add_partition(args) - def __action_easy_setup(self): - """Do automatic partitioning (create only one big partition). - """ - import types - ## we do not have to take special care for a possible config partition - parts = [ { "size": self.blockdevice_size, "type": "windows" } ] - ## umount partition if necessary - if cbox_tools.is_part_of_blockdevice(self.blockdevice, - self.cbox.prefs.get_active_partition()): - self.cbox.prefs.umount_partition() - ## partition it - if not self.__run_fdisk(parts): - self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" - return None - ## "formatPartitions" is a generator, returning device names and bolean values - result = [e for e in self.__format_partitions(parts) - if type(e) == types.BooleanType] - if self.with_config_partition: - self.cbox.prefs.mount_partition() - if not self.cbox.prefs.write(): - self.cbox.log.warn("Failed to store settings on new config partition") - ## check if there is a "False" return value - if False in result: - ## operation failed - self.hdf["Data.Warning"] = "Plugins.partition.FormattingFailed" - self.cbox.log.info("easy partitioning failed") - return "select_partitions" - else: - ## operation was successful - self.hdf["Data.Success"] = "Plugins.partition.EasySetup" - self.cbox.log.info("easy partitioning succeeded") - ## do not show the disk overview immediately - ## it does not get updated that fast - return { "plugin":"system_preferences", "values":[] } + def __action_easy_setup(self): + """Do automatic partitioning (create only one big partition). + """ + import types + ## we do not have to take special care for a possible config partition + parts = [ { "size": self.blockdevice_size, "type": "windows" } ] + ## umount partition if necessary + if cbox_tools.is_part_of_blockdevice(self.blockdevice, + self.cbox.prefs.get_active_partition()): + self.cbox.prefs.umount_partition() + ## partition it + if not self.__run_fdisk(parts): + self.hdf["Data.Warning"] = "Plugins.partition.PartitioningFailed" + return None + ## "formatPartitions" is a generator, returning device names and bolean values + result = [e for e in self.__format_partitions(parts) + if type(e) == types.BooleanType] + if self.with_config_partition: + self.cbox.prefs.mount_partition() + if not self.cbox.prefs.write(): + self.cbox.log.warn("Failed to store settings on new config partition") + ## check if there is a "False" return value + if False in result: + ## operation failed + self.hdf["Data.Warning"] = "Plugins.partition.FormattingFailed" + self.cbox.log.info("easy partitioning failed") + return "select_partitions" + else: + ## operation was successful + self.hdf["Data.Success"] = "Plugins.partition.EasySetup" + self.cbox.log.info("easy partitioning succeeded") + ## do not show the disk overview immediately + ## it does not get updated that fast + return { "plugin":"system_preferences", "values":[] } - def __set_partition_data(self, parts): - """Set some hdf values for the currently proposed partition table. - """ - avail_size = self.blockdevice_size - i = 0 - for part in parts: - self.cbox.log.debug(part) - self.hdf[self.hdf_prefix + "Parts.%d.Size" % i] = part["size"] - self.hdf[self.hdf_prefix + "Parts.%d.Type" % i] = part["type"] - avail_size -= part["size"] - i += 1 - self.hdf[self.hdf_prefix + "availSize"] = avail_size - if self.with_config_partition: - self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" - for ptype in PARTTYPES.keys(): - self.hdf[self.hdf_prefix + "Types.%s" % ptype] = ptype - ## store the currently existing partitions of the choosen block device - current_containers = [ e for e in self.cbox.get_container_list() - if cbox_tools.is_part_of_blockdevice(self.blockdevice, e.get_device()) ] - for (index, cont) in enumerate(current_containers): - self.hdf[self.hdf_prefix + "ExistingContainers.%d" % index] = \ - cont.get_device() + def __set_partition_data(self, parts): + """Set some hdf values for the currently proposed partition table. + """ + avail_size = self.blockdevice_size + i = 0 + for part in parts: + self.cbox.log.debug(part) + self.hdf[self.hdf_prefix + "Parts.%d.Size" % i] = part["size"] + self.hdf[self.hdf_prefix + "Parts.%d.Type" % i] = part["type"] + avail_size -= part["size"] + i += 1 + self.hdf[self.hdf_prefix + "availSize"] = avail_size + if self.with_config_partition: + self.hdf[self.hdf_prefix + "CreateConfigPartition"] = "1" + for ptype in PARTTYPES.keys(): + self.hdf[self.hdf_prefix + "Types.%s" % ptype] = ptype + ## store the currently existing partitions of the choosen block device + current_containers = [ e for e in self.cbox.get_container_list() + if cbox_tools.is_part_of_blockdevice(self.blockdevice, e.get_device()) ] + for (index, cont) in enumerate(current_containers): + self.hdf[self.hdf_prefix + "ExistingContainers.%d" % index] = \ + cont.get_device() - def __get_partitions_from_args(self, args): - """Filter the given arguments and construct a partition table. - """ - parts = [] - done = False - avail_size = self.blockdevice_size - i = -1 - while not done: - i += 1 - try: - ## skip every unconfirmed (probably the last) partition if we should not add it - if args.has_key("part%d_unconfirmed" % i) and \ - not args.has_key("add_part"): - continue - size = int(args["part%d_size" % i]) - part_type = args["part%d_type" % i] - if int(size) > avail_size: - self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooBig" - continue - if int(size) < 10: - self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooSmall" - continue - if not part_type in PARTTYPES.keys(): - continue - parts.append({"size":size, "type":part_type}) - avail_size -= size - except TypeError: - pass - except KeyError: - done = True - return parts + def __get_partitions_from_args(self, args): + """Filter the given arguments and construct a partition table. + """ + parts = [] + done = False + avail_size = self.blockdevice_size + i = -1 + while not done: + i += 1 + try: + ## skip every unconfirmed (probably the last) partition if we should not add it + if args.has_key("part%d_unconfirmed" % i) and \ + not args.has_key("add_part"): + continue + size = int(args["part%d_size" % i]) + part_type = args["part%d_type" % i] + if int(size) > avail_size: + self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooBig" + continue + if int(size) < 10: + self.hdf["Data.Warning"] = "Plugins.partition.PartitionTooSmall" + continue + if not part_type in PARTTYPES.keys(): + continue + parts.append({"size":size, "type":part_type}) + avail_size -= size + except TypeError: + pass + except KeyError: + done = True + return parts - def __get_available_device_size(self, device): - """calculate the available size (MB) of the device - also consider a (possible) configuration partition - """ - device_size = cbox_tools.get_blockdevice_size(device) - if device_size < 0: - return 0 - if self.with_config_partition: - device_size -= CONFIGPARTITION["size"] - return device_size + def __get_available_device_size(self, device): + """calculate the available size (MB) of the device + also consider a (possible) configuration partition + """ + device_size = cbox_tools.get_blockdevice_size(device) + if device_size < 0: + return 0 + if self.with_config_partition: + device_size -= CONFIGPARTITION["size"] + return device_size - def __is_with_config_partition(self): - """check if we have to create a configuration partition - """ - if self.cbox.prefs.requires_partition(): - active = self.cbox.prefs.get_active_partition() - ## we need a partition, if there is no active one - if not active: - return True - ## check if the active one is part of the current device - return cbox_tools.is_part_of_blockdevice(self.blockdevice, active) - return False + def __is_with_config_partition(self): + """check if we have to create a configuration partition + """ + if self.cbox.prefs.requires_partition(): + active = self.cbox.prefs.get_active_partition() + ## we need a partition, if there is no active one + if not active: + return True + ## check if the active one is part of the current device + return cbox_tools.is_part_of_blockdevice(self.blockdevice, active) + return False - def __run_fdisk(self, parts): - """Call fdisk to partition the device. - """ - ## check if the device is completely filled (to avoid some empty last blocks) - avail_size = self.blockdevice_size - for one_part in parts: - avail_size -= one_part["size"] - self.cbox.log.debug("remaining size: %d" % avail_size) - is_filled = avail_size == 0 - proc = subprocess.Popen( - shell = False, - stdin = subprocess.PIPE, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "plugin", - os.path.join(self.plugin_dir, "root_action.py"), - "partition", - self.blockdevice]) - for line in self.__get_sfdisk_layout(parts, is_filled): - proc.stdin.write(line + "\n") - #TODO: if running inside of an uml, then sfdisk hangs at "nanosleep({3,0})" - # very ugly - maybe a uml bug? - # it seems, like this can be avoided by running uml with the param "aio=2.4" - (output, error) = proc.communicate() - if proc.returncode != 0: - self.cbox.log.debug("partitioning failed: %s" % error) - return proc.returncode == 0 + def __run_fdisk(self, parts): + """Call fdisk to partition the device. + """ + ## check if the device is completely filled (to avoid some empty last blocks) + avail_size = self.blockdevice_size + for one_part in parts: + avail_size -= one_part["size"] + self.cbox.log.debug("remaining size: %d" % avail_size) + is_filled = avail_size == 0 + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.plugin_dir, "root_action.py"), + "partition", + self.blockdevice]) + for line in self.__get_sfdisk_layout(parts, is_filled): + proc.stdin.write(line + "\n") + #TODO: if running inside of an uml, then sfdisk hangs at "nanosleep({3,0})" + # very ugly - maybe a uml bug? + # it seems, like this can be avoided by running uml with the param "aio=2.4" + (output, error) = proc.communicate() + if proc.returncode != 0: + self.cbox.log.debug("partitioning failed: %s" % error) + return proc.returncode == 0 - def __get_sfdisk_layout(self, param_parts, is_filled): - """this generator returns the input lines for sfdisk - """ - parts = param_parts[:] - ## first a (possible) configuration partition - so it will be reusable - if self.with_config_partition: - ## fill the main table (including a config partition) - yield ",%d,%s" % (CONFIGPARTITION["size"], CONFIGPARTITION["type"]) - ## one primary partition - if is_filled and (len(parts) == 1): - ## fill the rest of the device - yield ",,%s,*" % PARTTYPES[parts[0]["type"]][0] - else: - ## only use the specified size - yield ",%d,%s,*" % (parts[0]["size"], PARTTYPES[parts[0]["type"]][0]) - del parts[0] - ## no extended partition, if there is only one disk - if not parts: - return - ## an extended container for the rest - yield ",,E" - ## an empty partition in main table - yield ";" - ## maybe another empty partition if there is no config partition - if not self.with_config_partition: - yield ";" - while parts: - if is_filled and (len(parts) == 1): - yield ",,%s" % (PARTTYPES[parts[0]["type"]][0],) - else: - yield ",%d,%s" % (parts[0]["size"], PARTTYPES[parts[0]["type"]][0]) - del parts[0] + def __get_sfdisk_layout(self, param_parts, is_filled): + """this generator returns the input lines for sfdisk + """ + parts = param_parts[:] + ## first a (possible) configuration partition - so it will be reusable + if self.with_config_partition: + ## fill the main table (including a config partition) + yield ",%d,%s" % (CONFIGPARTITION["size"], CONFIGPARTITION["type"]) + ## one primary partition + if is_filled and (len(parts) == 1): + ## fill the rest of the device + yield ",,%s,*" % PARTTYPES[parts[0]["type"]][0] + else: + ## only use the specified size + yield ",%d,%s,*" % (parts[0]["size"], PARTTYPES[parts[0]["type"]][0]) + del parts[0] + ## no extended partition, if there is only one disk + if not parts: + return + ## an extended container for the rest + yield ",,E" + ## an empty partition in main table + yield ";" + ## maybe another empty partition if there is no config partition + if not self.with_config_partition: + yield ";" + while parts: + if is_filled and (len(parts) == 1): + yield ",,%s" % (PARTTYPES[parts[0]["type"]][0],) + else: + yield ",%d,%s" % (parts[0]["size"], PARTTYPES[parts[0]["type"]][0]) + del parts[0] - def __format_partitions(self, param_parts): - """Format all partitions of the device. - """ - parts = param_parts[:] - part_num = 1 - ## maybe a config partition? - if self.with_config_partition: - dev_name = self.__get_partition_name(self.blockdevice, part_num) - self.cbox.log.info("formatting config partition (%s)" % dev_name) - if self.__format_one_partition(dev_name, CONFIGPARTITION["fs"]): - self.__set_label_of_partition(dev_name, - self.cbox.prefs["Main"]["ConfigVolumeLabel"]) - part_num += 1 - ## the first data partition - dev_name = self.__get_partition_name(self.blockdevice, part_num) - part_type = PARTTYPES[parts[0]["type"]][1] - self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, part_type)) - yield self.__format_one_partition(dev_name, part_type) - del parts[0] - ## other data partitions - part_num = 5 - while parts: - dev_name = self.__get_partition_name(self.blockdevice, part_num) - part_type = PARTTYPES[parts[0]["type"]][1] - self.cbox.log.info("formatting partition (%s) as '%s'" % \ - (dev_name, part_type)) - yield self.__format_one_partition(dev_name, part_type) - part_num += 1 - del parts[0] - return - + def __format_partitions(self, param_parts): + """Format all partitions of the device. + """ + parts = param_parts[:] + part_num = 1 + ## maybe a config partition? + if self.with_config_partition: + dev_name = self.__get_partition_name(self.blockdevice, part_num) + self.cbox.log.info("formatting config partition (%s)" % dev_name) + if self.__format_one_partition(dev_name, CONFIGPARTITION["fs"]): + self.__set_label_of_partition(dev_name, + self.cbox.prefs["Main"]["ConfigVolumeLabel"]) + part_num += 1 + ## the first data partition + dev_name = self.__get_partition_name(self.blockdevice, part_num) + part_type = PARTTYPES[parts[0]["type"]][1] + self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, part_type)) + yield self.__format_one_partition(dev_name, part_type) + del parts[0] + ## other data partitions + part_num = 5 + while parts: + dev_name = self.__get_partition_name(self.blockdevice, part_num) + part_type = PARTTYPES[parts[0]["type"]][1] + self.cbox.log.info("formatting partition (%s) as '%s'" % \ + (dev_name, part_type)) + yield self.__format_one_partition(dev_name, part_type) + part_num += 1 + del parts[0] + return + - def __get_partition_name(self, blockdev, number): - """Return the devicename of a specific partition of a device - No tests are performed, whether the partition exists or - not. - """ - if re.search("[0-9]$", blockdev): - ## blockdev ends with a digit, so it is a partition, we insert a 'p' - return "%sp%d" % (blockdev, number) - else: - ## whole disk, no 'p' necessary - return "%s%d" % (blockdev, number) + def __get_partition_name(self, blockdev, number): + """Return the devicename of a specific partition of a device + No tests are performed, whether the partition exists or + not. + """ + if re.search("[0-9]$", blockdev): + ## blockdev ends with a digit, so it is a partition, we insert a 'p' + return "%sp%d" % (blockdev, number) + else: + ## whole disk, no 'p' necessary + return "%s%d" % (blockdev, number) - def __format_one_partition(self, dev_name, fs_type): - """Format a single partition - """ - import cryptobox.core.container - ## first: retrieve UUID - it can be removed from the database afterwards - prev_name = [e.get_name() for e in self.cbox.get_container_list() - if e.get_device() == dev_name] - ## call "mkfs" - try: - cont = cryptobox.core.container.CryptoBoxContainer(dev_name, self.cbox) - cont.create(cryptobox.core.container.CONTAINERTYPES["plain"], fs_type=fs_type) - except (CBInvalidType, CBCreateError, CBVolumeIsActive), err_msg: - self.cbox.log.warn(err_msg) - return False - ## remove unused volume entry - if prev_name: - del self.cbox.prefs.volumes_db[prev_name[0]] - return True + def __format_one_partition(self, dev_name, fs_type): + """Format a single partition + """ + import cryptobox.core.container + ## first: retrieve UUID - it can be removed from the database afterwards + prev_name = [e.get_name() for e in self.cbox.get_container_list() + if e.get_device() == dev_name] + ## call "mkfs" + try: + cont = cryptobox.core.container.CryptoBoxContainer(dev_name, self.cbox) + cont.create(cryptobox.core.container.CONTAINERTYPES["plain"], fs_type=fs_type) + except (CBInvalidType, CBCreateError, CBVolumeIsActive), err_msg: + self.cbox.log.warn(err_msg) + return False + ## remove unused volume entry + if prev_name: + del self.cbox.prefs.volumes_db[prev_name[0]] + return True - def __set_label_of_partition(self, dev_name, label): - """Set the label of a partition - useful for the config partition. - """ - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "plugin", - os.path.join(self.plugin_dir, "root_action.py"), - "label", - dev_name, - label]) - (output, error) = proc.communicate() - if proc.returncode == 0: - return True - else: - self.cbox.log.warn("failed to create filesystem on %s: %s" % (dev_name, error)) - return False + def __set_label_of_partition(self, dev_name, label): + """Set the label of a partition - useful for the config partition. + """ + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.plugin_dir, "root_action.py"), + "label", + dev_name, + label]) + (output, error) = proc.communicate() + if proc.returncode == 0: + return True + else: + self.cbox.log.warn("failed to create filesystem on %s: %s" % (dev_name, error)) + return False diff --git a/plugins/partition/root_action.py b/plugins/partition/root_action.py index 3af9bb2..b8713a8 100755 --- a/plugins/partition/root_action.py +++ b/plugins/partition/root_action.py @@ -36,83 +36,83 @@ import os def __partitionDevice(device): - ## do not use the "-q" flag, as this spoils the exit code of sfdisk (seems to be a bug) - proc = subprocess.Popen( - shell = False, - args = [ - SFDISK_BIN, - "-uM", - device]) - proc.wait() - return proc.returncode == 0 + ## do not use the "-q" flag, as this spoils the exit code of sfdisk (seems to be a bug) + proc = subprocess.Popen( + shell = False, + args = [ + SFDISK_BIN, + "-uM", + device]) + proc.wait() + return proc.returncode == 0 def __formatPartition(device, type): - import time, threading - result = True - def formatting(): - proc = subprocess.Popen( - shell = False, - stdin = subprocess.PIPE, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - MKFS_BIN, - "-t", type, - device]) - proc.wait() - ## TODO: very ugly way of communication: it assumes, that failures are fast - success is slow - if proc.returncode == 0: - time.sleep(1) - return True - else: - return False - thread = threading.Thread() - thread.setDaemon(True) - thread.run = formatting - thread.start() - time.sleep(0.5) - return thread.isAlive() + import time, threading + result = True + def formatting(): + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + MKFS_BIN, + "-t", type, + device]) + proc.wait() + ## TODO: very ugly way of communication: it assumes, that failures are fast - success is slow + if proc.returncode == 0: + time.sleep(1) + return True + else: + return False + thread = threading.Thread() + thread.setDaemon(True) + thread.run = formatting + thread.start() + time.sleep(0.5) + return thread.isAlive() def __labelPartition(device, label): - proc = subprocess.Popen( - shell = False, - args = [ - LABEL_BIN, - device, - label]) - proc.wait() - return proc.returncode == 0 + proc = subprocess.Popen( + shell = False, + args = [ + LABEL_BIN, + device, + label]) + proc.wait() + return proc.returncode == 0 if __name__ == "__main__": - args = sys.argv[1:] + args = sys.argv[1:] - self_bin =sys.argv[0] - - if len(args) == 0: - sys.stderr.write("%s: no argument supplied\n" % self_bin) - sys.exit(1) + self_bin =sys.argv[0] + + if len(args) == 0: + sys.stderr.write("%s: no argument supplied\n" % self_bin) + sys.exit(1) - try: - if args[0] == "partition": - if len(args) != 2: raise "InvalidArgNum" - result = __partitionDevice(args[1]) - elif args[0] == "format": - if len(args) != 3: raise "InvalidArgNum" - result = __formatPartition(args[1], args[2]) - elif args[0] == "label": - if len(args) != 3: raise "InvalidArgNum" - result = __labelPartition(args[1], args[2]) - else: - sys.stderr.write("%s: invalid action (%s)\n" % (self_bin, args[0])) - sys.exit(1) - if result: - sys.exit(0) - else: - sys.exit(1) - except "InvalidArgNum": - sys.stderr.write("%s: invalid number of arguments (%s)\n" % (self_bin, args)) - sys.exit(1) - + try: + if args[0] == "partition": + if len(args) != 2: raise "InvalidArgNum" + result = __partitionDevice(args[1]) + elif args[0] == "format": + if len(args) != 3: raise "InvalidArgNum" + result = __formatPartition(args[1], args[2]) + elif args[0] == "label": + if len(args) != 3: raise "InvalidArgNum" + result = __labelPartition(args[1], args[2]) + else: + sys.stderr.write("%s: invalid action (%s)\n" % (self_bin, args[0])) + sys.exit(1) + if result: + sys.exit(0) + else: + sys.exit(1) + except "InvalidArgNum": + sys.stderr.write("%s: invalid number of arguments (%s)\n" % (self_bin, args)) + sys.exit(1) + diff --git a/plugins/partition/unittests.py b/plugins/partition/unittests.py index da5b2f4..68d5721 100644 --- a/plugins/partition/unittests.py +++ b/plugins/partition/unittests.py @@ -24,9 +24,9 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_form(self): - url = self.url + "partition?weblang=en" - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('VERY careful') + def test_read_form(self): + url = self.url + "partition?weblang=en" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('VERY careful') diff --git a/plugins/plugin_manager/plugin_manager.py b/plugins/plugin_manager/plugin_manager.py index 5e44bbb..3d807d4 100644 --- a/plugins/plugin_manager/plugin_manager.py +++ b/plugins/plugin_manager/plugin_manager.py @@ -26,124 +26,124 @@ import cryptobox.plugins.manage class plugin_manager(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "system" ] - plugin_visibility = [ "preferences" ] - request_auth = True - rank = 90 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = True + rank = 90 - def do_action(self, store=None, action=None, plugin_name=None, **args): - import re - if plugin_name: - ## check for invalid characters - if re.search(r'\W', plugin_name): return "plugin_list" - plugin_manager = cryptobox.plugins.manage.PluginManager( - self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) - plugin = plugin_manager.get_plugin(plugin_name) - if not plugin: return "plugin_list" - ## take only plugins, that are of the same type as the choosen one - self.plugins = [e for e in plugin_manager.get_plugins() - if e.plugin_capabilities == plugin.plugin_capabilities and \ - e != "volume_props" ] - if action == "up": - self.__move_up(plugin) - elif action == "down": - self.__move_down(plugin) - return "plugin_list" - elif store: - for key in args.keys(): - if key.endswith("_listed"): - if not re.search(r'\W',key): - self.__set_config(key[:-7], args) - else: - self.cbox.log.info("plugin_manager: invalid plugin name (%s)" % \ - str(key[:-7])) - try: - self.cbox.prefs.plugin_conf.write() - self.cbox.log.info("Successfully stored plugin configuration") - except IOError, err_msg: - self.cbox.log.warn("Failed to write plugin configuration: %s" % err_msg) - return "plugin_list" + def do_action(self, store=None, action=None, plugin_name=None, **args): + import re + if plugin_name: + ## check for invalid characters + if re.search(r'\W', plugin_name): return "plugin_list" + plugin_manager = cryptobox.plugins.manage.PluginManager( + self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) + plugin = plugin_manager.get_plugin(plugin_name) + if not plugin: return "plugin_list" + ## take only plugins, that are of the same type as the choosen one + self.plugins = [e for e in plugin_manager.get_plugins() + if e.plugin_capabilities == plugin.plugin_capabilities and \ + e != "volume_props" ] + if action == "up": + self.__move_up(plugin) + elif action == "down": + self.__move_down(plugin) + return "plugin_list" + elif store: + for key in args.keys(): + if key.endswith("_listed"): + if not re.search(r'\W',key): + self.__set_config(key[:-7], args) + else: + self.cbox.log.info("plugin_manager: invalid plugin name (%s)" % \ + str(key[:-7])) + try: + self.cbox.prefs.plugin_conf.write() + self.cbox.log.info("Successfully stored plugin configuration") + except IOError, err_msg: + self.cbox.log.warn("Failed to write plugin configuration: %s" % err_msg) + return "plugin_list" - def get_status(self): - plugin_manager = cryptobox.plugins.manage.PluginManager( - self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) - return ":".join([e.get_name() for e in plugin_manager.get_plugins()]) + def get_status(self): + plugin_manager = cryptobox.plugins.manage.PluginManager( + self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) + return ":".join([e.get_name() for e in plugin_manager.get_plugins()]) - def __sort_plugins(self): - """sort all plugins in the list according to their rank""" - def cmp_func(x,y): - x_rank = x.get_rank() - y_rank = y.get_rank() - if x_rank < y_rank: - return -1 - elif x_rank == y_rank: - return 0 - else: - return 1 - self.plugins.sort(cmp = cmp_func) - - - def __distribute_ranks(self): - """evenly distribute the 'rank' values according to the current order of - the list""" - dist = 100 / (len(self.plugins) - 1) - for (index, pl) in enumerate(self.plugins): - pl.set_rank(dist*index) - try: - self.cbox.prefs.plugin_conf.write() - except IOError: - self.cbox.log.warn("failed to write plugin configuration") + def __sort_plugins(self): + """sort all plugins in the list according to their rank""" + def cmp_func(x,y): + x_rank = x.get_rank() + y_rank = y.get_rank() + if x_rank < y_rank: + return -1 + elif x_rank == y_rank: + return 0 + else: + return 1 + self.plugins.sort(cmp = cmp_func) + + + def __distribute_ranks(self): + """evenly distribute the 'rank' values according to the current order of + the list""" + dist = 100 / (len(self.plugins) - 1) + for (index, pl) in enumerate(self.plugins): + pl.set_rank(dist*index) + try: + self.cbox.prefs.plugin_conf.write() + except IOError: + self.cbox.log.warn("failed to write plugin configuration") - def __move_up(self, plugin): - self.__sort_plugins() - try: - index = self.plugins.index(plugin) - ## first elements may not move up - if index == 0: - return - except ValueError: - return - self.plugins.remove(plugin) - self.plugins.insert(index-1, plugin) - self.__distribute_ranks() - + def __move_up(self, plugin): + self.__sort_plugins() + try: + index = self.plugins.index(plugin) + ## first elements may not move up + if index == 0: + return + except ValueError: + return + self.plugins.remove(plugin) + self.plugins.insert(index-1, plugin) + self.__distribute_ranks() + - def __move_down(self, plugin): - self.__sort_plugins() - try: - index = self.plugins.index(plugin) - ## last elements may not move down - if index == len(self.plugins) - 1: - return - except ValueError: - return - self.plugins.remove(plugin) - self.plugins.insert(index+1, plugin) - self.__distribute_ranks() + def __move_down(self, plugin): + self.__sort_plugins() + try: + index = self.plugins.index(plugin) + ## last elements may not move down + if index == len(self.plugins) - 1: + return + except ValueError: + return + self.plugins.remove(plugin) + self.plugins.insert(index+1, plugin) + self.__distribute_ranks() - def __set_config(self, name, args): - import re - if not self.cbox.prefs.plugin_conf.has_key(name): - self.cbox.prefs.plugin_conf[name] = {} - setting = self.cbox.prefs.plugin_conf[name] - setting["visibility"] = [] - ## look for "_visible_" values and apply them - pattern = re.compile(r'%s_visible_([\w]+)$' % name) - for key in args.keys(): - if key.startswith(name + "_visible_"): - (vis_type, ) = pattern.match(key).groups() - if vis_type: - setting["visibility"].append(vis_type) - ## the plugin_manager _must_ always be visible - if (self.get_name() == name) and (not setting["visibility"]): - ## reset to default - setting["visibility"] = self.plugin_visibility[:] - if args.has_key("%s_auth" % name): - setting["requestAuth"] = True - else: - setting["requestAuth"] = False + def __set_config(self, name, args): + import re + if not self.cbox.prefs.plugin_conf.has_key(name): + self.cbox.prefs.plugin_conf[name] = {} + setting = self.cbox.prefs.plugin_conf[name] + setting["visibility"] = [] + ## look for "_visible_" values and apply them + pattern = re.compile(r'%s_visible_([\w]+)$' % name) + for key in args.keys(): + if key.startswith(name + "_visible_"): + (vis_type, ) = pattern.match(key).groups() + if vis_type: + setting["visibility"].append(vis_type) + ## the plugin_manager _must_ always be visible + if (self.get_name() == name) and (not setting["visibility"]): + ## reset to default + setting["visibility"] = self.plugin_visibility[:] + if args.has_key("%s_auth" % name): + setting["requestAuth"] = True + else: + setting["requestAuth"] = False diff --git a/plugins/plugin_manager/unittests.py b/plugins/plugin_manager/unittests.py index 4497994..6aed75d 100644 --- a/plugins/plugin_manager/unittests.py +++ b/plugins/plugin_manager/unittests.py @@ -26,67 +26,67 @@ COMMON_STRING = 'Volume plugins' class unittests(WebInterfaceTestClass): - def test_read_form(self): - """Check if the 'plugin_manager' works. - """ - url = self.url + "plugin_manager?weblang=en" - self.register_auth(url) - self.cmd.go(url) - self.cmd.find(COMMON_STRING) + def test_read_form(self): + """Check if the 'plugin_manager' works. + """ + url = self.url + "plugin_manager?weblang=en" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find(COMMON_STRING) - def test_set_options(self): - """Do some various stuff. - """ - #TODO: these 'tests' are really a bit stupid - someone should fix this - url = self.url + "plugin_manager" - self.register_auth(url) - self.cmd.go(url + r"?plugin_name=t/-!") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?plugin_name=foobar") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?plugin_name=disks&action=up") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?plugin_name=disks&action=down") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?store=1&dis/ks_listed") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=50") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=x") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_auth=1") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=50&disks_auth=1") - self.cmd.find(COMMON_STRING) - - - def test_move_up(self): - """Move some plugins up. - """ - #TODO: if we want to be perfect, then we should check the change of the rank - url = self.url + "plugin_manager" - self.register_auth(url) - self.cmd.go(url + r"?plugin_name=disks&action=up") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=0") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?plugin_name=disks&action=up") - self.cmd.find(COMMON_STRING) + def test_set_options(self): + """Do some various stuff. + """ + #TODO: these 'tests' are really a bit stupid - someone should fix this + url = self.url + "plugin_manager" + self.register_auth(url) + self.cmd.go(url + r"?plugin_name=t/-!") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?plugin_name=foobar") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?plugin_name=disks&action=up") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?plugin_name=disks&action=down") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?store=1&dis/ks_listed") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=50") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=x") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_auth=1") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=50&disks_auth=1") + self.cmd.find(COMMON_STRING) + + + def test_move_up(self): + """Move some plugins up. + """ + #TODO: if we want to be perfect, then we should check the change of the rank + url = self.url + "plugin_manager" + self.register_auth(url) + self.cmd.go(url + r"?plugin_name=disks&action=up") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=0") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?plugin_name=disks&action=up") + self.cmd.find(COMMON_STRING) - def test_move_down(self): - """Move some plugins down. - """ - ## TODO: if we want to be perfect, then we should check the change of the rank - url = self.url + "plugin_manager" - self.register_auth(url) - self.cmd.go(url + r"?plugin_name=disks&action=down") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=100") - self.cmd.find(COMMON_STRING) - self.cmd.go(url + r"?plugin_name=disks&action=down") - self.cmd.find(COMMON_STRING) + def test_move_down(self): + """Move some plugins down. + """ + ## TODO: if we want to be perfect, then we should check the change of the rank + url = self.url + "plugin_manager" + self.register_auth(url) + self.cmd.go(url + r"?plugin_name=disks&action=down") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?store=1&disks_listed&disks_visible_menu=1&disks_rank=100") + self.cmd.find(COMMON_STRING) + self.cmd.go(url + r"?plugin_name=disks&action=down") + self.cmd.find(COMMON_STRING) diff --git a/plugins/plugin_template/plugin_template.py b/plugins/plugin_template/plugin_template.py index d64aec2..e2ca062 100644 --- a/plugins/plugin_template/plugin_template.py +++ b/plugins/plugin_template/plugin_template.py @@ -30,41 +30,41 @@ import cryptobox.plugins.base class plugin_template(cryptobox.plugins.base.CryptoBoxPlugin): - """a template for CryptoBox plugins - """ + """a template for CryptoBox plugins + """ - plugin_capabilities = [ "system" ] - plugin_visibility = [ "preferences" ] - request_auth = False - rank = 99 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = False + rank = 99 - def do_action(self): - """The action handler. - """ - self.cbox.log.info("This is a log entry generated by the plugin_template.") - if True: - self.hdf["Data.Success"] = "Plugins.plugin_template.TemplateLaunched" - else: - self.hdf["Data.Warning"] = "Plugins.plugin_template.SomeError" - self.__prepare_form_data() - return "form_plugin_template" + def do_action(self): + """The action handler. + """ + self.cbox.log.info("This is a log entry generated by the plugin_template.") + if True: + self.hdf["Data.Success"] = "Plugins.plugin_template.TemplateLaunched" + else: + self.hdf["Data.Warning"] = "Plugins.plugin_template.SomeError" + self.__prepare_form_data() + return "form_plugin_template" - def get_status(self): - """Retrieve the status of the feature. - """ - return + def get_status(self): + """Retrieve the status of the feature. + """ + return - def get_warnings(self): - warnings = [] - ## uncomment this to see the environment warning message - #warnings.append((48, "Plugins.%s.MissingFoo" % self.get_name())) - return warnings + def get_warnings(self): + warnings = [] + ## uncomment this to see the environment warning message + #warnings.append((48, "Plugins.%s.MissingFoo" % self.get_name())) + return warnings - def __prepare_form_data(self): - """Set some hdf values. - """ + def __prepare_form_data(self): + """Set some hdf values. + """ diff --git a/plugins/shutdown/root_action.py b/plugins/shutdown/root_action.py index edb395d..b1e84ab 100755 --- a/plugins/shutdown/root_action.py +++ b/plugins/shutdown/root_action.py @@ -35,36 +35,36 @@ import sys def call_prog(progy): - proc = subprocess.Popen( - shell = False, - args = progy) - proc.communicate() - return proc.returncode == 0 + proc = subprocess.Popen( + shell = False, + args = progy) + proc.communicate() + return proc.returncode == 0 if __name__ == "__main__": - args = sys.argv[1:] + 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) - - if args[0] == "reboot": - result = call_prog([SHUTDOWN_BIN, "-t", str(SHUTDOWN_DELAY), "-r", "now"]) - elif args[0] == "shutdown": - result = call_prog([SHUTDOWN_BIN, "-t", str(SHUTDOWN_DELAY), "-h", "now"]) - else: - sys.stderr.write("%s: illegal argument (%s)\n" % (self_bin, args[0])) - sys.exit(1) - - if result: - sys.exit(0) - else: - sys.exit(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) + + if args[0] == "reboot": + result = call_prog([SHUTDOWN_BIN, "-t", str(SHUTDOWN_DELAY), "-r", "now"]) + elif args[0] == "shutdown": + result = call_prog([SHUTDOWN_BIN, "-t", str(SHUTDOWN_DELAY), "-h", "now"]) + else: + sys.stderr.write("%s: illegal argument (%s)\n" % (self_bin, args[0])) + sys.exit(1) + + if result: + sys.exit(0) + else: + sys.exit(1) diff --git a/plugins/shutdown/shutdown.py b/plugins/shutdown/shutdown.py index dacb9d8..f38e4ad 100644 --- a/plugins/shutdown/shutdown.py +++ b/plugins/shutdown/shutdown.py @@ -26,49 +26,49 @@ REDIRECT_DELAY = 120 class shutdown(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "system" ] - plugin_visibility = [ "menu" ] - request_auth = False - rank = 90 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "menu" ] + request_auth = False + rank = 90 - def do_action(self, type=None): - if not type: - return "form_shutdown" - elif type == "shutdown": - if self.__do_shutdown("shutdown"): - self.hdf["Data.Success"] = "Plugins.shutdown.Shutdown" - return "progress_shutdown" - else: - self.hdf["Data.Warning"] = "Plugins.shutdown.ShutdownFailed" - return "form_shutdown" - elif type == "reboot": - if self.__do_shutdown("reboot"): - self.hdf["Data.Success"] = "Plugins.shutdown.Reboot" - self.hdf["Data.Redirect.URL"] = "/" - self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY - return "progress_reboot" - else: - self.hdf["Data.Warning"] = "Plugins.shutdown.RebootFailed" - return "form_shutdown" - else: - return "form_shutdown" + def do_action(self, type=None): + if not type: + return "form_shutdown" + elif type == "shutdown": + if self.__do_shutdown("shutdown"): + self.hdf["Data.Success"] = "Plugins.shutdown.Shutdown" + return "progress_shutdown" + else: + self.hdf["Data.Warning"] = "Plugins.shutdown.ShutdownFailed" + return "form_shutdown" + elif type == "reboot": + if self.__do_shutdown("reboot"): + self.hdf["Data.Success"] = "Plugins.shutdown.Reboot" + self.hdf["Data.Redirect.URL"] = "/" + self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY + return "progress_reboot" + else: + self.hdf["Data.Warning"] = "Plugins.shutdown.RebootFailed" + return "form_shutdown" + else: + return "form_shutdown" - def get_status(self): - return "the box is up'n'running" + def get_status(self): + return "the box is up'n'running" - def __do_shutdown(self, action): - import subprocess - import os - proc = subprocess.Popen( - shell = False, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "plugin", - os.path.join(self.plugin_dir, "root_action.py"), - action]) - proc.wait() - return proc.returncode == 0 + def __do_shutdown(self, action): + import subprocess + import os + proc = subprocess.Popen( + shell = False, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "plugin", + os.path.join(self.plugin_dir, "root_action.py"), + action]) + proc.wait() + return proc.returncode == 0 diff --git a/plugins/shutdown/unittests.py b/plugins/shutdown/unittests.py index 2a7c482..46ff42c 100644 --- a/plugins/shutdown/unittests.py +++ b/plugins/shutdown/unittests.py @@ -24,12 +24,12 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_form(self): - """just read the form - I do not know of a way how to check success""" - url = self.url + "shutdown" - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('shutdown') - self.cmd.find('type=reboot') - self.cmd.find('type=shutdown') + def test_read_form(self): + """just read the form - I do not know of a way how to check success""" + url = self.url + "shutdown" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('shutdown') + self.cmd.find('type=reboot') + self.cmd.find('type=shutdown') diff --git a/plugins/system_preferences/system_preferences.py b/plugins/system_preferences/system_preferences.py index 3199876..d55206b 100644 --- a/plugins/system_preferences/system_preferences.py +++ b/plugins/system_preferences/system_preferences.py @@ -24,17 +24,17 @@ import cryptobox.plugins.base class system_preferences(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "system" ] - plugin_visibility = [ "menu" ] - request_auth = False - rank = 25 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "menu" ] + request_auth = False + rank = 25 - def do_action(self): - return "show_plugins" + def do_action(self): + return "show_plugins" - def get_status(self): - plugin_manager = cryptobox.plugins.manage.PluginManager( - self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) - return ":".join([p.get_name() for p in plugin_manager.get_plugins()]) + def get_status(self): + plugin_manager = cryptobox.plugins.manage.PluginManager( + self.cbox, self.cbox.prefs["Locations"]["PluginDir"]) + return ":".join([p.get_name() for p in plugin_manager.get_plugins()]) diff --git a/plugins/system_preferences/unittests.py b/plugins/system_preferences/unittests.py index 85e3812..024da9c 100644 --- a/plugins/system_preferences/unittests.py +++ b/plugins/system_preferences/unittests.py @@ -24,15 +24,15 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_form(self): - self.cmd.go(self.url + "system_preferences") - self.cmd.find("Preferences") + def test_read_form(self): + self.cmd.go(self.url + "system_preferences") + self.cmd.find("Preferences") - def test_check_plugins(self): - self.cmd.go(self.url + "system_preferences") - self.cmd.find(r'Data.Status.Plugins.system_preferences=(.*)$', "m") - plugins = self.locals["__match__"].split(":") - self.assertTrue(len(plugins) > 1) - self.assertTrue("disks" in plugins) + def test_check_plugins(self): + self.cmd.go(self.url + "system_preferences") + self.cmd.find(r'Data.Status.Plugins.system_preferences=(.*)$', "m") + plugins = self.locals["__match__"].split(":") + self.assertTrue(len(plugins) > 1) + self.assertTrue("disks" in plugins) diff --git a/plugins/user_manager/unittests.py b/plugins/user_manager/unittests.py index f5a15eb..a2b8512 100644 --- a/plugins/user_manager/unittests.py +++ b/plugins/user_manager/unittests.py @@ -26,171 +26,171 @@ from cryptobox.tests.base import WebInterfaceTestClass from user_manager import RESERVED_USERS class unittests(WebInterfaceTestClass): - + - def test_read_users(self): - """does the 'admin' user exist?""" - cur_users = self._getUsers() - self.cmd.find("Add new user") - self.assertTrue("admin" in cur_users) + def test_read_users(self): + """does the 'admin' user exist?""" + cur_users = self._getUsers() + self.cmd.find("Add new user") + self.assertTrue("admin" in cur_users) - def test_test_wrong_credentials(self): - """check if the user_manager is protected""" - url = self.url + "user_manager" - self.register_auth(url,"foo","bar") - self.cmd.go(url) - self.cmd.notfind("Manage users") - + def test_test_wrong_credentials(self): + """check if the user_manager is protected""" + url = self.url + "user_manager" + self.register_auth(url,"foo","bar") + self.cmd.go(url) + self.cmd.notfind("Manage users") + - def test_add_existing_user(self): - """adding an existing user should fail""" - url = self.url + "user_manager" - self.register_auth(url) - self._add_user("admin","foo","foo") - self.cmd.find("The choosen username does already exist") + def test_add_existing_user(self): + """adding an existing user should fail""" + url = self.url + "user_manager" + self.register_auth(url) + self._add_user("admin","foo","foo") + self.cmd.find("The choosen username does already exist") - def test_add_invalid_username(self): - """adding an invalid username should fail""" - url = self.url + "user_manager" - self.register_auth(url) - self._add_user("foo/bar","foo","foo") - self.cmd.find("Invalid username") - self.assertFalse("foo/bar" in self._getUsers()) + def test_add_invalid_username(self): + """adding an invalid username should fail""" + url = self.url + "user_manager" + self.register_auth(url) + self._add_user("foo/bar","foo","foo") + self.cmd.find("Invalid username") + self.assertFalse("foo/bar" in self._getUsers()) - def test_add_without_password(self): - """adding a user without password should fail""" - url = self.url + "user_manager" - self.register_auth(url) - self.assertFalse("foo" in self._getUsers()) - self._add_user("foo","","foo") - self.cmd.find("Missing new password") - self.assertFalse("foo" in self._getUsers()) + def test_add_without_password(self): + """adding a user without password should fail""" + url = self.url + "user_manager" + self.register_auth(url) + self.assertFalse("foo" in self._getUsers()) + self._add_user("foo","","foo") + self.cmd.find("Missing new password") + self.assertFalse("foo" in self._getUsers()) - def test_add_with_different_passwords(self): - """adding a user with different passwords should fail""" - url = self.url + "user_manager" - self.register_auth(url) - self.assertFalse("foo" in self._getUsers()) - self._add_user("foo","bar","foo") - self.cmd.find("Different passwords") - self.assertFalse("foo" in self._getUsers()) + def test_add_with_different_passwords(self): + """adding a user with different passwords should fail""" + url = self.url + "user_manager" + self.register_auth(url) + self.assertFalse("foo" in self._getUsers()) + self._add_user("foo","bar","foo") + self.cmd.find("Different passwords") + self.assertFalse("foo" in self._getUsers()) - def test_change_pw_for_invalid_user(self): - """changing a password of a non existing user should fail""" - url = self.url + "user_manager" - self.register_auth(url) - self.assertFalse("barfoo" in self._getUsers()) - self.cmd.go(url + "?store=change_password&user=foobar&new_pw=foo&new_pw2=foo") - self.cmd.notfind("Password changed") + def test_change_pw_for_invalid_user(self): + """changing a password of a non existing user should fail""" + url = self.url + "user_manager" + self.register_auth(url) + self.assertFalse("barfoo" in self._getUsers()) + self.cmd.go(url + "?store=change_password&user=foobar&new_pw=foo&new_pw2=foo") + self.cmd.notfind("Password changed") - def test_change_pw_without_password(self): - """changing a password without a new password should fail""" - url = self.url + "user_manager" - self.register_auth(url) - self.assertFalse("foo" in self._getUsers()) - self._add_user("foo","bar","bar") - self.assertTrue("foo" in self._getUsers()) - self._change_password("foo","","foo") - self.cmd.find("Missing new password") - self._del_user("foo") - self.assertFalse("foo" in self._getUsers()) + def test_change_pw_without_password(self): + """changing a password without a new password should fail""" + url = self.url + "user_manager" + self.register_auth(url) + self.assertFalse("foo" in self._getUsers()) + self._add_user("foo","bar","bar") + self.assertTrue("foo" in self._getUsers()) + self._change_password("foo","","foo") + self.cmd.find("Missing new password") + self._del_user("foo") + self.assertFalse("foo" in self._getUsers()) - def test_change_pw_wit_different_passwords(self): - """changing a password while supplying different passwords should fail""" - url = self.url + "user_manager" - self.register_auth(url) - self.assertFalse("foo" in self._getUsers()) - self._add_user("foo","bar","bar") - self.assertTrue("foo" in self._getUsers()) - self._change_password("foo","bar","foo") - self.cmd.find("Different passwords") - self._del_user("foo") - self.assertFalse("foo" in self._getUsers()) + def test_change_pw_wit_different_passwords(self): + """changing a password while supplying different passwords should fail""" + url = self.url + "user_manager" + self.register_auth(url) + self.assertFalse("foo" in self._getUsers()) + self._add_user("foo","bar","bar") + self.assertTrue("foo" in self._getUsers()) + self._change_password("foo","bar","foo") + self.cmd.find("Different passwords") + self._del_user("foo") + self.assertFalse("foo" in self._getUsers()) - def _remove_reserved_user(self): - """removing a reserved user should fail""" - url = self.url + "user_manager" - self.register_auth(url) - self.assertTrue("admin" in self._getUsers()) - self._del_user("admin") - self.cmd.find("may not remove a reserved user") - self.assertTrue("admin" in self._getUsers()) + def _remove_reserved_user(self): + """removing a reserved user should fail""" + url = self.url + "user_manager" + self.register_auth(url) + self.assertTrue("admin" in self._getUsers()) + self._del_user("admin") + self.cmd.find("may not remove a reserved user") + self.assertTrue("admin" in self._getUsers()) - def _remove_non_existing_user(self): - """removing a non-existing user should fail""" - url = self.url + "user_manager" - self.register_auth(url) - self.assertFalse("barfoo" in self._getUsers()) - self._del_user("barfoo") - self.cmd.notfind("User removed") + def _remove_non_existing_user(self): + """removing a non-existing user should fail""" + url = self.url + "user_manager" + self.register_auth(url) + self.assertFalse("barfoo" in self._getUsers()) + self._del_user("barfoo") + self.cmd.notfind("User removed") - def test_manage_users(self): - """add a new user, change its password and remove the user afterwards""" - url = self.url + "user_manager" - self.register_auth(url) - ## remove the user that should be added - just in case a previous run was unclean - ## check its existence before - if "foobar" in self._getUsers(): self._del_user("foobar") - ## add a new user - self._add_user("foobar","foo","foo") - self.cmd.find("User added") - users = self._getUsers() - self.assertTrue("foobar" in users) - ## change the password of the new user - self.register_auth(url,"foobar","foo") - self._change_password("foobar","bar","bar") - self.cmd.find("Password changed") - ## remove the new user - self.register_auth(url,"foobar","bar") - self._del_user("foobar") - self.cmd.find("User removed") - users = self._getUsers() - self.assertFalse("foobar" in users) + def test_manage_users(self): + """add a new user, change its password and remove the user afterwards""" + url = self.url + "user_manager" + self.register_auth(url) + ## remove the user that should be added - just in case a previous run was unclean + ## check its existence before + if "foobar" in self._getUsers(): self._del_user("foobar") + ## add a new user + self._add_user("foobar","foo","foo") + self.cmd.find("User added") + users = self._getUsers() + self.assertTrue("foobar" in users) + ## change the password of the new user + self.register_auth(url,"foobar","foo") + self._change_password("foobar","bar","bar") + self.cmd.find("Password changed") + ## remove the new user + self.register_auth(url,"foobar","bar") + self._del_user("foobar") + self.cmd.find("User removed") + users = self._getUsers() + self.assertFalse("foobar" in users) - def test_invalid_input(self): - """check all combinations of invalid input""" - url = self.url + "user_manager" - self.register_auth(url) - self.cmd.go(url + "?store=foobar") + def test_invalid_input(self): + """check all combinations of invalid input""" + url = self.url + "user_manager" + self.register_auth(url) + self.cmd.go(url + "?store=foobar") - def _add_user(self, username, pw, pw2): - self.cmd.go(self.url + "user_manager") - self.cmd.formvalue("add_user","user",username) - self.cmd.formvalue("add_user","new_pw",pw) - self.cmd.formvalue("add_user","new_pw2",pw2) - self.cmd.submit() + def _add_user(self, username, pw, pw2): + self.cmd.go(self.url + "user_manager") + self.cmd.formvalue("add_user","user",username) + self.cmd.formvalue("add_user","new_pw",pw) + self.cmd.formvalue("add_user","new_pw2",pw2) + self.cmd.submit() - def _del_user(self, username): - self.cmd.go(self.url + "user_manager") - self.cmd.formvalue("del_user","user",username) - self.cmd.submit() + def _del_user(self, username): + self.cmd.go(self.url + "user_manager") + self.cmd.formvalue("del_user","user",username) + self.cmd.submit() - def _change_password(self, username, pw, pw2): - self.cmd.go(self.url + "user_manager") - self.cmd.formvalue("change_password","user",username) - self.cmd.formvalue("change_password","new_pw",pw) - self.cmd.formvalue("change_password","new_pw2",pw2) - self.cmd.submit() + def _change_password(self, username, pw, pw2): + self.cmd.go(self.url + "user_manager") + self.cmd.formvalue("change_password","user",username) + self.cmd.formvalue("change_password","new_pw",pw) + self.cmd.formvalue("change_password","new_pw2",pw2) + self.cmd.submit() - def _getUsers(self): - url = self.url + "user_manager" - self.register_auth(url) - self.cmd.go(url) - self.cmd.find("Data.Status.Plugins.user_manager=([\w:]+)") - return self.locals["__match__"].split(":") + def _getUsers(self): + url = self.url + "user_manager" + self.register_auth(url) + self.cmd.go(url) + self.cmd.find("Data.Status.Plugins.user_manager=([\w:]+)") + return self.locals["__match__"].split(":") diff --git a/plugins/user_manager/user_manager.py b/plugins/user_manager/user_manager.py index 1f4d0a2..1460c7e 100644 --- a/plugins/user_manager/user_manager.py +++ b/plugins/user_manager/user_manager.py @@ -26,81 +26,81 @@ RESERVED_USERS = [ "admin" ] class user_manager(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "system" ] - plugin_visibility = [ "preferences" ] - request_auth = True - rank = 45 + plugin_capabilities = [ "system" ] + plugin_visibility = [ "preferences" ] + request_auth = True + rank = 45 - def do_action(self, store=None, user=None, new_pw=None, new_pw2=None): - import re - admin_dict = self.cbox.prefs.user_db["admins"] - self.__clean_hdf() - if store is None: - pass - elif store == "add_user": - if (user is None) or (re.search(r'\W', user)): - self.hdf["Data.Warning"] = "Plugins.user_manager.InvalidUserName" - elif not new_pw: - self.hdf["Data.Warning"] = "EmptyNewPassword" - elif new_pw != new_pw2: - self.hdf["Data.Warning"] = "DifferentPasswords" - elif user in admin_dict.keys(): - self.hdf["Data.Warning"] = "Plugins.user_manager.UserAlreadyExists" - else: - admin_dict[user] = self.cbox.prefs.user_db.get_digest(new_pw) - self.hdf["Data.Success"] = "Plugins.user_manager.UserAdded" - try: - self.cbox.prefs.user_db.write() - except IOError: - self.cbox.log.warn("failed to write user database") - elif store == "change_password": - if not new_pw: - self.hdf["Data.Warning"] = "EmptyNewPassword" - elif new_pw != new_pw2: - self.hdf["Data.Warning"] = "DifferentPasswords" - elif user in admin_dict.keys(): - admin_dict[user] = self.cbox.prefs.user_db.get_digest(new_pw) - self.hdf["Data.Success"] = "Plugins.user_manager.PasswordChanged" - try: - self.cbox.prefs.user_db.write() - except IOError: - self.cbox.log.warn("failed to write user database") - else: - self.cbox.log.info("user_manager: invalid user choosen (%s)" % str(user)) - elif store == "del_user": - if user in RESERVED_USERS: - self.cbox.log.info("user_manager: tried to remove reserved user (%s)" % user) - self.hdf["Data.Warning"] = "NeverRemoveReservedUser" - elif user in admin_dict.keys(): - del admin_dict[user] - self.hdf["Data.Success"] = "Plugins.user_manager.UserRemoved" - try: - self.cbox.prefs.user_db.write() - except IOError: - self.cbox.log.warn("failed to write user database") - else: - self.cbox.log.info("user_manager: tried to remove non-existing user (%s)" \ - % str(user)) - else: - self.cbox.log.info("user_manager: invalid value of 'store' (%s)" % store) - self.__prepare_hdf(admin_dict) - return "user_list" + def do_action(self, store=None, user=None, new_pw=None, new_pw2=None): + import re + admin_dict = self.cbox.prefs.user_db["admins"] + self.__clean_hdf() + if store is None: + pass + elif store == "add_user": + if (user is None) or (re.search(r'\W', user)): + self.hdf["Data.Warning"] = "Plugins.user_manager.InvalidUserName" + elif not new_pw: + self.hdf["Data.Warning"] = "EmptyNewPassword" + elif new_pw != new_pw2: + self.hdf["Data.Warning"] = "DifferentPasswords" + elif user in admin_dict.keys(): + self.hdf["Data.Warning"] = "Plugins.user_manager.UserAlreadyExists" + else: + admin_dict[user] = self.cbox.prefs.user_db.get_digest(new_pw) + self.hdf["Data.Success"] = "Plugins.user_manager.UserAdded" + try: + self.cbox.prefs.user_db.write() + except IOError: + self.cbox.log.warn("failed to write user database") + elif store == "change_password": + if not new_pw: + self.hdf["Data.Warning"] = "EmptyNewPassword" + elif new_pw != new_pw2: + self.hdf["Data.Warning"] = "DifferentPasswords" + elif user in admin_dict.keys(): + admin_dict[user] = self.cbox.prefs.user_db.get_digest(new_pw) + self.hdf["Data.Success"] = "Plugins.user_manager.PasswordChanged" + try: + self.cbox.prefs.user_db.write() + except IOError: + self.cbox.log.warn("failed to write user database") + else: + self.cbox.log.info("user_manager: invalid user choosen (%s)" % str(user)) + elif store == "del_user": + if user in RESERVED_USERS: + self.cbox.log.info("user_manager: tried to remove reserved user (%s)" % user) + self.hdf["Data.Warning"] = "NeverRemoveReservedUser" + elif user in admin_dict.keys(): + del admin_dict[user] + self.hdf["Data.Success"] = "Plugins.user_manager.UserRemoved" + try: + self.cbox.prefs.user_db.write() + except IOError: + self.cbox.log.warn("failed to write user database") + else: + self.cbox.log.info("user_manager: tried to remove non-existing user (%s)" \ + % str(user)) + else: + self.cbox.log.info("user_manager: invalid value of 'store' (%s)" % store) + self.__prepare_hdf(admin_dict) + return "user_list" - def get_status(self): - return ":".join(self.cbox.prefs.user_db["admins"].keys()) + def get_status(self): + return ":".join(self.cbox.prefs.user_db["admins"].keys()) - def __clean_hdf(self): - for key in self.hdf.keys(): - del self.hdf[key] + def __clean_hdf(self): + for key in self.hdf.keys(): + del self.hdf[key] - def __prepare_hdf(self, dataset): - ## sort by name - users = dataset.keys() - users.sort() - ## export all users - for name in users: - self.hdf[self.hdf_prefix + "Users." + name] = name + def __prepare_hdf(self, dataset): + ## sort by name + users = dataset.keys() + users.sort() + ## export all users + for name in users: + self.hdf[self.hdf_prefix + "Users." + name] = name diff --git a/plugins/volume_automount/unittests.py b/plugins/volume_automount/unittests.py index 3792f33..eef2986 100644 --- a/plugins/volume_automount/unittests.py +++ b/plugins/volume_automount/unittests.py @@ -24,33 +24,33 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_form(self): - """try to read automount form""" - url = self.url + "volume_automount?weblang=en&device=" + self.device_html - self.register_auth(url) - ## first: turn it off - self.cmd.go(url + "&action=disable") - self.cmd.go(url) - self.cmd.find('is disabled') + def test_read_form(self): + """try to read automount form""" + url = self.url + "volume_automount?weblang=en&device=" + self.device_html + self.register_auth(url) + ## first: turn it off + self.cmd.go(url + "&action=disable") + self.cmd.go(url) + self.cmd.find('is disabled') - def test_toggle(self): - """try to toggle automount property""" - url = self.url + "volume_automount" - self.register_auth(url) - self.cmd.go(url + "?device=%s&action=disable" % self.device_html) - self.cmd.find("Automatic activation disabled") - self.cmd.find("is disabled") - self.cmd.go(url + "?device=%s&action=enable" % self.device_html) - self.cmd.find("Automatic activation enabled") - self.cmd.find("is enabled") - + def test_toggle(self): + """try to toggle automount property""" + url = self.url + "volume_automount" + self.register_auth(url) + self.cmd.go(url + "?device=%s&action=disable" % self.device_html) + self.cmd.find("Automatic activation disabled") + self.cmd.find("is disabled") + self.cmd.go(url + "?device=%s&action=enable" % self.device_html) + self.cmd.find("Automatic activation enabled") + self.cmd.find("is enabled") + - def test_invalid_input(self): - """check invalid inputs""" - url = self.url + "volume_automount" - self.register_auth(url) - self.cmd.go(url + "?device=%s&action=foobar" % self.device_html) - self.cmd.notfind("Automatic activation disabled") - self.cmd.notfind("Automatic activation enabled") - + def test_invalid_input(self): + """check invalid inputs""" + url = self.url + "volume_automount" + self.register_auth(url) + self.cmd.go(url + "?device=%s&action=foobar" % self.device_html) + self.cmd.notfind("Automatic activation disabled") + self.cmd.notfind("Automatic activation enabled") + diff --git a/plugins/volume_automount/volume_automount.py b/plugins/volume_automount/volume_automount.py index 83f2888..b76f421 100644 --- a/plugins/volume_automount/volume_automount.py +++ b/plugins/volume_automount/volume_automount.py @@ -27,94 +27,94 @@ from cryptobox.core.exceptions import * class volume_automount(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "volume" ] - plugin_visibility = [ "properties" ] - request_auth = False - rank = 80 + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "properties" ] + request_auth = False + rank = 80 - true_string = "yes" - false_string = "no" + true_string = "yes" + false_string = "no" - def do_action(self, action=None): - container = self.cbox.get_container(self.device) - if action is None: - pass - elif action == "enable": - self.__set_auto_mount(container, True) - self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountEnabled" - self.cbox.log.info("volume_automount: enabled for device '%s'" % self.device) - elif action == "disable": - self.__set_auto_mount(container, False) - self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountDisabled" - self.cbox.log.info("volume_automount: disabled for device '%s'" % self.device) - else: - self.cbox.log.info("volume_automount: invalid action (%s)" % str(action)) - self.__prepare_hdf() - return "volume_automount" - + def do_action(self, action=None): + container = self.cbox.get_container(self.device) + if action is None: + pass + elif action == "enable": + self.__set_auto_mount(container, True) + self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountEnabled" + self.cbox.log.info("volume_automount: enabled for device '%s'" % self.device) + elif action == "disable": + self.__set_auto_mount(container, False) + self.hdf["Data.Success"] = "Plugins.volume_automount.AutoMountDisabled" + self.cbox.log.info("volume_automount: disabled for device '%s'" % self.device) + else: + self.cbox.log.info("volume_automount: invalid action (%s)" % str(action)) + self.__prepare_hdf() + return "volume_automount" + - def handle_event(self, event, event_info=None): - """Override bootup behaviour. - - Mount all volumes marked as 'automount'. - """ - cryptobox.plugins.base.CryptoBoxPlugin.handle_event(self, event, event_info) - if event == "bootup": - for cont in self.cbox.get_container_list(): - if self.__is_auto_mount(cont) and not cont.is_mounted(): - cont.mount() - + def handle_event(self, event, event_info=None): + """Override bootup behaviour. + + Mount all volumes marked as 'automount'. + """ + cryptobox.plugins.base.CryptoBoxPlugin.handle_event(self, event, event_info) + if event == "bootup": + for cont in self.cbox.get_container_list(): + if self.__is_auto_mount(cont) and not cont.is_mounted(): + cont.mount() + - def is_useful(self, device): - """Automount does not work for encrypted volumes. - """ - cont = self.cbox.get_container(device) - if not cont: - return False - if cont.get_type() != cryptobox.core.container.CONTAINERTYPES["luks"]: - return True - return False + def is_useful(self, device): + """Automount does not work for encrypted volumes. + """ + cont = self.cbox.get_container(device) + if not cont: + return False + if cont.get_type() != cryptobox.core.container.CONTAINERTYPES["luks"]: + return True + return False - def get_status(self): - return str(self.__is_auto_mount(self.cbox.get_container(self.device))) - + def get_status(self): + return str(self.__is_auto_mount(self.cbox.get_container(self.device))) + - def __prepare_hdf(self): - if self.__is_auto_mount(self.cbox.get_container(self.device)): - self.hdf[self.hdf_prefix + "automount_setting"] = "1" - else: - self.hdf[self.hdf_prefix + "automount_setting"] = "0" + def __prepare_hdf(self): + if self.__is_auto_mount(self.cbox.get_container(self.device)): + self.hdf[self.hdf_prefix + "automount_setting"] = "1" + else: + self.hdf[self.hdf_prefix + "automount_setting"] = "0" - def __is_auto_mount(self, container): - if not container: - return False - ## only valid for plain volumes - if container.get_type() != cryptobox.core.container.CONTAINERTYPES["plain"]: - return False - if "_automount_names" in self.prefs: - if container.get_name() in self.prefs["_automount_names"]: - return True - else: - return False - else: - return False - + def __is_auto_mount(self, container): + if not container: + return False + ## only valid for plain volumes + if container.get_type() != cryptobox.core.container.CONTAINERTYPES["plain"]: + return False + if "_automount_names" in self.prefs: + if container.get_name() in self.prefs["_automount_names"]: + return True + else: + return False + else: + return False + - def __set_auto_mount(self, container, state): - if state == self.__is_auto_mount(container): - return - name = container.get_name() - if not "_automount_names" in self.prefs: - self.prefs["_automount_names"] = [] - if state: - self.prefs["_automount_names"].append(name) - else: - self.prefs["_automount_names"].remove(name) - try: - self.cbox.prefs.plugin_conf.write() - except IOError: - self.cbox.log.warn("Failed to store plugin configuration") + def __set_auto_mount(self, container, state): + if state == self.__is_auto_mount(container): + return + name = container.get_name() + if not "_automount_names" in self.prefs: + self.prefs["_automount_names"] = [] + if state: + self.prefs["_automount_names"].append(name) + else: + self.prefs["_automount_names"].remove(name) + try: + self.cbox.prefs.plugin_conf.write() + except IOError: + self.cbox.log.warn("Failed to store plugin configuration") diff --git a/plugins/volume_chpasswd/unittests.py b/plugins/volume_chpasswd/unittests.py index 57c2c28..c34c4a5 100644 --- a/plugins/volume_chpasswd/unittests.py +++ b/plugins/volume_chpasswd/unittests.py @@ -24,10 +24,10 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_form(self): - url = self.url + "volume_chpasswd?weblang=en&device=" + self.device_html - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('hange') - self.cmd.find('password') + def test_read_form(self): + url = self.url + "volume_chpasswd?weblang=en&device=" + self.device_html + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('hange') + self.cmd.find('password') diff --git a/plugins/volume_chpasswd/volume_chpasswd.py b/plugins/volume_chpasswd/volume_chpasswd.py index 87edaa7..633e52a 100644 --- a/plugins/volume_chpasswd/volume_chpasswd.py +++ b/plugins/volume_chpasswd/volume_chpasswd.py @@ -26,66 +26,66 @@ from cryptobox.core.exceptions import * class volume_chpasswd(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "volume" ] - plugin_visibility = [ "properties" ] - request_auth = False - rank = 70 + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "properties" ] + request_auth = False + rank = 70 - def do_action(self, store=None, old_pw=None, new_pw=None, new_pw2=None): - self.container = self.cbox.get_container(self.device) - if not self.container: - return None - elif store == "change_pw": - return self.__change_password(old_pw, new_pw, new_pw2) - elif not store: - return "volume_chpasswd" - else: - self.cbox.log.info("plugin 'volume_chpasswd' - unknown action: %s" % store) - return "volume_chpasswd" - + def do_action(self, store=None, old_pw=None, new_pw=None, new_pw2=None): + self.container = self.cbox.get_container(self.device) + if not self.container: + return None + elif store == "change_pw": + return self.__change_password(old_pw, new_pw, new_pw2) + elif not store: + return "volume_chpasswd" + else: + self.cbox.log.info("plugin 'volume_chpasswd' - unknown action: %s" % store) + return "volume_chpasswd" + - def get_status(self): - """chpasswd doesn't give any useful status information, other - than True - """ - return True + def get_status(self): + """chpasswd doesn't give any useful status information, other + than True + """ + return True - - def is_useful(self, device): - from cryptobox.core.container import CONTAINERTYPES as cont_types - cont = self.cbox.get_container(device) - if not cont: - return False - if cont.get_type() == cont_types["luks"]: - return True - return False - + + def is_useful(self, device): + from cryptobox.core.container import CONTAINERTYPES as cont_types + cont = self.cbox.get_container(device) + if not cont: + return False + if cont.get_type() == cont_types["luks"]: + return True + return False + - def __change_password(self, old_pw, new_pw, new_pw2): - if not old_pw: - self.hdf["Data.Warning"] = "EmptyPassword" - elif not new_pw: - self.hdf["Data.Warning"] = "EmptyNewPassword" - elif new_pw != new_pw2: - self.hdf["Data.Warning"] = "DifferentPasswords" - elif old_pw == new_pw: - ## do nothing - pass - else: - try: - self.container.change_password(old_pw, new_pw) - except CBInvalidType, err_msg: - self.cbox.log.info("plugin 'volume_chpasswd' - cannot change " \ - + "passphrase for non-encrypted container (%s): %s" \ - % (self.device, err_msg)) - except CBVolumeIsActive: - self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" - except CBChangePasswordError, err_msg: - self.cbox.log.warn("plugin 'volume_chpasswd' - cannot change " \ - + "password for device (%s): %s" % (self.device, err_msg)) - self.hdf["Data.Warning"] = "Plugins.volume_chpasswd.PasswordChange" - else: - self.hdf["Data.Success"] = "Plugins.volume_chpasswd.PasswordChange" - return "volume_chpasswd" + def __change_password(self, old_pw, new_pw, new_pw2): + if not old_pw: + self.hdf["Data.Warning"] = "EmptyPassword" + elif not new_pw: + self.hdf["Data.Warning"] = "EmptyNewPassword" + elif new_pw != new_pw2: + self.hdf["Data.Warning"] = "DifferentPasswords" + elif old_pw == new_pw: + ## do nothing + pass + else: + try: + self.container.change_password(old_pw, new_pw) + except CBInvalidType, err_msg: + self.cbox.log.info("plugin 'volume_chpasswd' - cannot change " \ + + "passphrase for non-encrypted container (%s): %s" \ + % (self.device, err_msg)) + except CBVolumeIsActive: + self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" + except CBChangePasswordError, err_msg: + self.cbox.log.warn("plugin 'volume_chpasswd' - cannot change " \ + + "password for device (%s): %s" % (self.device, err_msg)) + self.hdf["Data.Warning"] = "Plugins.volume_chpasswd.PasswordChange" + else: + self.hdf["Data.Success"] = "Plugins.volume_chpasswd.PasswordChange" + return "volume_chpasswd" diff --git a/plugins/volume_details/unittests.py b/plugins/volume_details/unittests.py index ea0d65c..f9a3949 100644 --- a/plugins/volume_details/unittests.py +++ b/plugins/volume_details/unittests.py @@ -24,9 +24,9 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_form(self): - url = self.url + "volume_details?weblang=en&device=" + self.device_html - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('Technical details') + def test_read_form(self): + url = self.url + "volume_details?weblang=en&device=" + self.device_html + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('Technical details') diff --git a/plugins/volume_details/volume_details.py b/plugins/volume_details/volume_details.py index 3ae72c3..7bf9b99 100644 --- a/plugins/volume_details/volume_details.py +++ b/plugins/volume_details/volume_details.py @@ -25,17 +25,17 @@ import cryptobox.plugins.base class volume_details(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "volume" ] - plugin_visibility = [ "volume" ] - request_auth = False - rank = 100 + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "volume" ] + request_auth = False + rank = 100 - def do_action(self): - ## all variables are already set somewhere else - return "volume_details" - + def do_action(self): + ## all variables are already set somewhere else + return "volume_details" + - def get_status(self): - return "no status" + def get_status(self): + return "no status" diff --git a/plugins/volume_format_fs/unittests.py b/plugins/volume_format_fs/unittests.py index 704c8bc..7a9616d 100644 --- a/plugins/volume_format_fs/unittests.py +++ b/plugins/volume_format_fs/unittests.py @@ -26,76 +26,76 @@ import cryptobox.tests.tools as cbox_tools class unittests(WebInterfaceTestClass): - def test_read_form(self): - """Simply check if the plugin works. - """ - url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('Initializing filesystem') + def test_read_form(self): + """Simply check if the plugin works. + """ + url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('Initializing filesystem') - def test_invalid_actions(self): - """Try to issue some invalid orders. - """ - url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html - self.register_auth(url) - ## make sure, it is not mounted - cbox_tools.umount(self.device) - ## try invalid filesystem type - self.cmd.go(url + "&fs_type=foo") - self.cmd.find('Initializing filesystem') - ## try invalid container type - self.cmd.go(url + "&container_type=foo") - self.cmd.find('Initializing filesystem') - ## missing confirmation - self.cmd.go(url + "&store=step1") - self.cmd.find('Confirmation missing') - ## call luks form - self.cmd.go(url + "&store=step1&confirm=1&container_type=luks") - self.cmd.find('name="crypto_password2"') - ## try 'step2' with plain container type - self.cmd.go(url + "&store=step2&confirm=1&container_type=plain") - self.cmd.find('Initializing filesystem') - ## try invalid 'store' - self.cmd.go(url + "&store=foo") - self.cmd.find('Initializing filesystem') - ## try without password - self.cmd.go(url + "&store=step2&container_type=luks&fs_type=windows" \ - + "&crypto_password=&crypto_password2=foo") - self.cmd.find('Missing password') - ## try with different passwords - self.cmd.go(url + "&store=step2&container_type=luks&fs_type=windows" \ - + "&crypto_password=bar&crypto_password2=foo") - self.cmd.find('Different passwords') - + def test_invalid_actions(self): + """Try to issue some invalid orders. + """ + url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html + self.register_auth(url) + ## make sure, it is not mounted + cbox_tools.umount(self.device) + ## try invalid filesystem type + self.cmd.go(url + "&fs_type=foo") + self.cmd.find('Initializing filesystem') + ## try invalid container type + self.cmd.go(url + "&container_type=foo") + self.cmd.find('Initializing filesystem') + ## missing confirmation + self.cmd.go(url + "&store=step1") + self.cmd.find('Confirmation missing') + ## call luks form + self.cmd.go(url + "&store=step1&confirm=1&container_type=luks") + self.cmd.find('name="crypto_password2"') + ## try 'step2' with plain container type + self.cmd.go(url + "&store=step2&confirm=1&container_type=plain") + self.cmd.find('Initializing filesystem') + ## try invalid 'store' + self.cmd.go(url + "&store=foo") + self.cmd.find('Initializing filesystem') + ## try without password + self.cmd.go(url + "&store=step2&container_type=luks&fs_type=windows" \ + + "&crypto_password=&crypto_password2=foo") + self.cmd.find('Missing password') + ## try with different passwords + self.cmd.go(url + "&store=step2&container_type=luks&fs_type=windows" \ + + "&crypto_password=bar&crypto_password2=foo") + self.cmd.find('Different passwords') + - def test_format_open_device(self): - """Try to format an open device. - """ - url_format = self.url + "volume_format_fs?weblang=en&device=" \ - + self.device_html - self.register_auth(url_format) - url_mount = self.url + "volume_mount?weblang=en&device=" \ - + self.device_html - self.register_auth(url_mount) - ## mount device - do not care, if it was mounted before - self.cmd.go(url_mount + "&action=mount_plain") - self.cmd.find('Data.Status.Plugins.volume_mount=active') - ## try plain device - self.cmd.go(url_format + "&store=step1&confirm=1&container_type=plain&" \ - + "fs_type=linux") - self.cmd.find('This action is not available while the volume is active.') - ## try luks device - self.cmd.go(url_format + "&store=step2&container_type=luks&fs_type=" \ - + "windows&crypto_password=foo&crypto_password2=foo") - self.cmd.find('This action is not available while the volume is active.') - ## umount - cbox_tools.umount(self.device) - + def test_format_open_device(self): + """Try to format an open device. + """ + url_format = self.url + "volume_format_fs?weblang=en&device=" \ + + self.device_html + self.register_auth(url_format) + url_mount = self.url + "volume_mount?weblang=en&device=" \ + + self.device_html + self.register_auth(url_mount) + ## mount device - do not care, if it was mounted before + self.cmd.go(url_mount + "&action=mount_plain") + self.cmd.find('Data.Status.Plugins.volume_mount=active') + ## try plain device + self.cmd.go(url_format + "&store=step1&confirm=1&container_type=plain&" \ + + "fs_type=linux") + self.cmd.find('This action is not available while the volume is active.') + ## try luks device + self.cmd.go(url_format + "&store=step2&container_type=luks&fs_type=" \ + + "windows&crypto_password=foo&crypto_password2=foo") + self.cmd.find('This action is not available while the volume is active.') + ## umount + cbox_tools.umount(self.device) + - def test_format_device(self): - """Formatting of a device was done in 'volume_mount' plugin tests. - """ - pass + def test_format_device(self): + """Formatting of a device was done in 'volume_mount' plugin tests. + """ + pass diff --git a/plugins/volume_format_fs/volume_format_fs.py b/plugins/volume_format_fs/volume_format_fs.py index d64553d..7e10e40 100644 --- a/plugins/volume_format_fs/volume_format_fs.py +++ b/plugins/volume_format_fs/volume_format_fs.py @@ -26,112 +26,112 @@ import cryptobox.core.container as cbx_container ## map filesystem types to the appropriate arguments for 'mkfs' FSTYPES = { - "windows": "vfat", - "linux": "ext3" } + "windows": "vfat", + "linux": "ext3" } class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "volume" ] - plugin_visibility = [ "volume" ] - request_auth = True - rank = 60 + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "volume" ] + request_auth = True + rank = 60 - def do_action(self, store=None, fs_type="windows", container_type="luks", - crypto_password=None, crypto_password2=None, confirm=None): - container = self.cbox.get_container(self.device) - ## exit immediately if the device is not writeable - if not container.is_writeable(): - self.hdf["Data.Warning"] = "DeviceNotWriteable" - return "empty" - if not fs_type in FSTYPES.keys(): - self.cbox.log.info("Invalid filesystem type choosen: %s" % str(fs_type)) - return "volume_format" - self.hdf[self.hdf_prefix + "fs_type"] = fs_type - if not container_type in ['plain', 'luks']: - self.cbox.log.info("Invalid container type type choosen: %s" % \ - str(container_type)) - return "volume_format" - self.hdf[self.hdf_prefix + "container_type"] = container_type - for t in FSTYPES.keys(): - self.hdf[self.hdf_prefix + "fs_types." + t] = t - if store == "step1": - if not confirm: - self.cbox.log.info("Missing confirmation for formatting of " + \ - "filesystem: %s" % self.device) - self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatNotConfirmed" - return "volume_format" - if container_type == "luks": - return "volume_format_luks" - elif container_type == "plain": - return self.__format_plain(FSTYPES[fs_type]) - elif store == "step2": - if container_type == "luks": - return self.__format_luks(FSTYPES[fs_type], - crypto_password, crypto_password2) - else: - self.cbox.log.info("Invalid input value for 'container_type': %s" \ - % container_type) - return "volume_format" - elif store: - self.cbox.log.info("Invalid input value for 'store': %s" % store) - return "volume_format" - else: - return "volume_format" + def do_action(self, store=None, fs_type="windows", container_type="luks", + crypto_password=None, crypto_password2=None, confirm=None): + container = self.cbox.get_container(self.device) + ## exit immediately if the device is not writeable + if not container.is_writeable(): + self.hdf["Data.Warning"] = "DeviceNotWriteable" + return "empty" + if not fs_type in FSTYPES.keys(): + self.cbox.log.info("Invalid filesystem type choosen: %s" % str(fs_type)) + return "volume_format" + self.hdf[self.hdf_prefix + "fs_type"] = fs_type + if not container_type in ['plain', 'luks']: + self.cbox.log.info("Invalid container type type choosen: %s" % \ + str(container_type)) + return "volume_format" + self.hdf[self.hdf_prefix + "container_type"] = container_type + for t in FSTYPES.keys(): + self.hdf[self.hdf_prefix + "fs_types." + t] = t + if store == "step1": + if not confirm: + self.cbox.log.info("Missing confirmation for formatting of " + \ + "filesystem: %s" % self.device) + self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatNotConfirmed" + return "volume_format" + if container_type == "luks": + return "volume_format_luks" + elif container_type == "plain": + return self.__format_plain(FSTYPES[fs_type]) + elif store == "step2": + if container_type == "luks": + return self.__format_luks(FSTYPES[fs_type], + crypto_password, crypto_password2) + else: + self.cbox.log.info("Invalid input value for 'container_type': %s" \ + % container_type) + return "volume_format" + elif store: + self.cbox.log.info("Invalid input value for 'store': %s" % store) + return "volume_format" + else: + return "volume_format" - def get_status(self): - return "no status" + def get_status(self): + return "no status" - def __format_plain(self, fs_type): - try: - container = self.cbox.get_container(self.device) - container.create(cbx_container.CONTAINERTYPES["plain"], fs_type=fs_type) - except CBVolumeIsActive: - self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" - self.cbox.log.info("Initialization is not possible as long as the device " \ - + "(%s) is mounted" % self.device) - return "volume_format" - except CBContainerError, err_msg: - self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatFailed" - self.cbox.log.error("Initialization of the device (%s) failed: %s" % \ - (self.device, err_msg)) - return "volume_format" - else: - self.hdf["Data.Success"] = "Plugins.volume_format_fs.FormatSuccess" - self.cbox.log.info("Successfully initialized device '%s'" % self.device) - return { "plugin":"disks", "values":{} } + def __format_plain(self, fs_type): + try: + container = self.cbox.get_container(self.device) + container.create(cbx_container.CONTAINERTYPES["plain"], fs_type=fs_type) + except CBVolumeIsActive: + self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" + self.cbox.log.info("Initialization is not possible as long as the device " \ + + "(%s) is mounted" % self.device) + return "volume_format" + except CBContainerError, err_msg: + self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatFailed" + self.cbox.log.error("Initialization of the device (%s) failed: %s" % \ + (self.device, err_msg)) + return "volume_format" + else: + self.hdf["Data.Success"] = "Plugins.volume_format_fs.FormatSuccess" + self.cbox.log.info("Successfully initialized device '%s'" % self.device) + return { "plugin":"disks", "values":{} } - def __format_luks(self, fs_type, pw, pw2): - if not pw: - self.hdf["Data.Warning"] = "EmptyPassword" - self.cbox.log.info("No crypto password was supplied for initialization " \ - + "of device '%s'" % self.device) - return "volume_format" - if pw != pw2: - self.hdf["Data.Warning"] = "DifferentPasswords" - self.cbox.log.info("The crypto password was not repeated correctly for " \ - + "initialization of device '%s'" % self.device) - return "volume_format" - container = self.cbox.get_container(self.device) - try: - container.create(cbx_container.CONTAINERTYPES["luks"], - fs_type=fs_type, password=pw) - except CBVolumeIsActive: - self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" - self.cbox.log.info("Initialization is not possible as long as the device " \ - + "(%s) is mounted" % self.device) - return "volume_format" - except CBContainerError, err_msg: - self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatFailed" - self.cbox.log.error("Initialization of the device (%s) failed: %s" % \ - (self.device, err_msg)) - return "volume_format" - else: - self.hdf["Data.Success"] = "Plugins.volume_format_fs.FormatSuccess" - self.cbox.log.info("Successfully initialized device '%s'" % self.device) - return { "plugin":"disks", "values":{} } + def __format_luks(self, fs_type, pw, pw2): + if not pw: + self.hdf["Data.Warning"] = "EmptyPassword" + self.cbox.log.info("No crypto password was supplied for initialization " \ + + "of device '%s'" % self.device) + return "volume_format" + if pw != pw2: + self.hdf["Data.Warning"] = "DifferentPasswords" + self.cbox.log.info("The crypto password was not repeated correctly for " \ + + "initialization of device '%s'" % self.device) + return "volume_format" + container = self.cbox.get_container(self.device) + try: + container.create(cbx_container.CONTAINERTYPES["luks"], + fs_type=fs_type, password=pw) + except CBVolumeIsActive: + self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" + self.cbox.log.info("Initialization is not possible as long as the device " \ + + "(%s) is mounted" % self.device) + return "volume_format" + except CBContainerError, err_msg: + self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatFailed" + self.cbox.log.error("Initialization of the device (%s) failed: %s" % \ + (self.device, err_msg)) + return "volume_format" + else: + self.hdf["Data.Success"] = "Plugins.volume_format_fs.FormatSuccess" + self.cbox.log.info("Successfully initialized device '%s'" % self.device) + return { "plugin":"disks", "values":{} } diff --git a/plugins/volume_mount/unittests.py b/plugins/volume_mount/unittests.py index 0e170dd..54c9e42 100644 --- a/plugins/volume_mount/unittests.py +++ b/plugins/volume_mount/unittests.py @@ -26,164 +26,164 @@ import twill class unittests(WebInterfaceTestClass): - - def test_read_form(self): - """Simply check if the plugin works - """ - ## umount, if necessary - cbox_tools.umount(self.device) - url = self.url + "volume_mount?weblang=en&device=" + self.device_html - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('Open this volume') - self.cmd.find('Data.Status.Plugins.volume_mount=passive') - + + def test_read_form(self): + """Simply check if the plugin works + """ + ## umount, if necessary + cbox_tools.umount(self.device) + url = self.url + "volume_mount?weblang=en&device=" + self.device_html + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('Open this volume') + self.cmd.find('Data.Status.Plugins.volume_mount=passive') + - def test_mount(self): - """Do all variations of mount/umount actions. - """ - url = self.url + "volume_mount?weblang=en&device=" + self.device_html - self.register_auth(url) - ## umount, if necessary - cbox_tools.umount(self.device) - ## we may not split these tests into two functions, as the order - ## is important (we must leave a clean plain volume behind) - self.__do_tests_with_luks() - self.__do_tests_with_plain() - + def test_mount(self): + """Do all variations of mount/umount actions. + """ + url = self.url + "volume_mount?weblang=en&device=" + self.device_html + self.register_auth(url) + ## umount, if necessary + cbox_tools.umount(self.device) + ## we may not split these tests into two functions, as the order + ## is important (we must leave a clean plain volume behind) + self.__do_tests_with_luks() + self.__do_tests_with_plain() + - def __do_tests_with_luks(self): - """Some tests with a luks partition. - """ - url = self.url + "volume_mount?weblang=en&device=" + self.device_html - self.__format_luks() - ## mount the volume - self.cmd.go(url) - self.cmd.find('Open this volume') - # the following _should_ work, but it does not - probably a parsing problem - # of twill - for now we use the direct link instead - #self.cmd.formvalue("mount", "pw", "foo") - #self.cmd.submit() - self.cmd.go(url + "&action=mount_luks&pw=foo") - self.cmd.find('Volume opened') - self.cmd.find('Data.Status.Plugins.volume_mount=active') - ## try to mount active volume - self.cmd.go(url + "&action=mount_luks&pw=foo") - self.cmd.find('The volume is already open.') - ## close the volume - # the following _should_ work, but it does not - probably a parsing problem - # of twill - for now we use the direct link instead - #self.cmd.submit() - self.cmd.go(url + "&action=umount") - self.cmd.find('Volume closed') - self.cmd.find('Data.Status.Plugins.volume_mount=passive') - ## try plain instead of luks - self.cmd.go(url + "&action=mount_plain") - self.cmd.find('Data.Status.Plugins.volume_mount=passive') - self.cmd.find('Unknown format') - ## no password supplied - self.cmd.go(url + "&action=mount_luks") - self.cmd.find('Data.Status.Plugins.volume_mount=passive') - self.cmd.find('Missing password') - ## wrong password supplied - self.cmd.go(url + "&action=mount_luks&pw=bar") - self.cmd.find('Data.Status.Plugins.volume_mount=passive') - self.cmd.find('Maybe you entered a wrong password?') - + def __do_tests_with_luks(self): + """Some tests with a luks partition. + """ + url = self.url + "volume_mount?weblang=en&device=" + self.device_html + self.__format_luks() + ## mount the volume + self.cmd.go(url) + self.cmd.find('Open this volume') + # the following _should_ work, but it does not - probably a parsing problem + # of twill - for now we use the direct link instead + #self.cmd.formvalue("mount", "pw", "foo") + #self.cmd.submit() + self.cmd.go(url + "&action=mount_luks&pw=foo") + self.cmd.find('Volume opened') + self.cmd.find('Data.Status.Plugins.volume_mount=active') + ## try to mount active volume + self.cmd.go(url + "&action=mount_luks&pw=foo") + self.cmd.find('The volume is already open.') + ## close the volume + # the following _should_ work, but it does not - probably a parsing problem + # of twill - for now we use the direct link instead + #self.cmd.submit() + self.cmd.go(url + "&action=umount") + self.cmd.find('Volume closed') + self.cmd.find('Data.Status.Plugins.volume_mount=passive') + ## try plain instead of luks + self.cmd.go(url + "&action=mount_plain") + self.cmd.find('Data.Status.Plugins.volume_mount=passive') + self.cmd.find('Unknown format') + ## no password supplied + self.cmd.go(url + "&action=mount_luks") + self.cmd.find('Data.Status.Plugins.volume_mount=passive') + self.cmd.find('Missing password') + ## wrong password supplied + self.cmd.go(url + "&action=mount_luks&pw=bar") + self.cmd.find('Data.Status.Plugins.volume_mount=passive') + self.cmd.find('Maybe you entered a wrong password?') + - def __do_tests_with_plain(self): - """Some tests with a plain partition. - """ - url = self.url + "volume_mount?weblang=en&device=" + self.device_html - self.__format_plain() - ## open plain volume - self.cmd.go(url) - self.cmd.find('Open this volume') - # the following _should_ work, but it does not - probably a parsing problem - # of twill - for now we use the direct link instead - #self.cmd.submit() - self.cmd.go(url + "&action=mount_plain") - self.cmd.find('Volume opened') - self.cmd.find('Data.Status.Plugins.volume_mount=active') - ## try to mount active volume - self.cmd.go(url + "&action=mount_plain") - self.cmd.find('The volume is already open.') - ## umount - # the following _should_ work, but it does not - probably a parsing problem - # of twill - for now we use the direct link instead - #self.cmd.submit() - self.cmd.go(url + "&action=umount") - self.cmd.find('Volume closed') - self.cmd.find('Data.Status.Plugins.volume_mount=passive') - ## try to umount closed volume - self.cmd.go(url + "&action=umount") - self.cmd.find('The volume is already closed.') + def __do_tests_with_plain(self): + """Some tests with a plain partition. + """ + url = self.url + "volume_mount?weblang=en&device=" + self.device_html + self.__format_plain() + ## open plain volume + self.cmd.go(url) + self.cmd.find('Open this volume') + # the following _should_ work, but it does not - probably a parsing problem + # of twill - for now we use the direct link instead + #self.cmd.submit() + self.cmd.go(url + "&action=mount_plain") + self.cmd.find('Volume opened') + self.cmd.find('Data.Status.Plugins.volume_mount=active') + ## try to mount active volume + self.cmd.go(url + "&action=mount_plain") + self.cmd.find('The volume is already open.') + ## umount + # the following _should_ work, but it does not - probably a parsing problem + # of twill - for now we use the direct link instead + #self.cmd.submit() + self.cmd.go(url + "&action=umount") + self.cmd.find('Volume closed') + self.cmd.find('Data.Status.Plugins.volume_mount=passive') + ## try to umount closed volume + self.cmd.go(url + "&action=umount") + self.cmd.find('The volume is already closed.') - def __format_luks(self): - """Format a luks partition. - """ - url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('select name="fs_type"') - # the following _should_ work, but it does not - probably a parsing problem - # of twill - for now we use the direct link instead - #self.cmd.formvalue("set_type", "fs_type", "linux") - #self.cmd.formvalue("set_type", "container_type", "luks") - #self.cmd.formvalue("set_type", "confirm", "1") - #self.cmd.submit() - self.cmd.go(url + "&fs_type=linux&container_type=luks&confirm=1&store=step1") - self.cmd.find('name="crypto_password"') - # the following _should_ work, but it does not - probably a parsing problem - # of twill - for now we use the direct link instead - #self.cmd.formvalue("set_luks", "crypto_password", "foo") - #self.cmd.formvalue("set_luks", "crypto_password2", "foo") - #self.cmd.submit() - self.cmd.go(url + "&store=step2&container_type=luks&fs_type=windows&" \ - + "crypto_password=foo&crypto_password2=foo") - self.cmd.find('Formatting is running') - self.__wait_until_ready() - + def __format_luks(self): + """Format a luks partition. + """ + url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('select name="fs_type"') + # the following _should_ work, but it does not - probably a parsing problem + # of twill - for now we use the direct link instead + #self.cmd.formvalue("set_type", "fs_type", "linux") + #self.cmd.formvalue("set_type", "container_type", "luks") + #self.cmd.formvalue("set_type", "confirm", "1") + #self.cmd.submit() + self.cmd.go(url + "&fs_type=linux&container_type=luks&confirm=1&store=step1") + self.cmd.find('name="crypto_password"') + # the following _should_ work, but it does not - probably a parsing problem + # of twill - for now we use the direct link instead + #self.cmd.formvalue("set_luks", "crypto_password", "foo") + #self.cmd.formvalue("set_luks", "crypto_password2", "foo") + #self.cmd.submit() + self.cmd.go(url + "&store=step2&container_type=luks&fs_type=windows&" \ + + "crypto_password=foo&crypto_password2=foo") + self.cmd.find('Formatting is running') + self.__wait_until_ready() + - def __format_plain(self): - """Format a plaintext partition. - """ - url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('select name="fs_type"') - # the following _should_ work, but it does not - probably a parsing problem - # of twill - for now we use the direct link instead - #self.cmd.formvalue("set_type", "fs_type", "windows") - #self.cmd.formvalue("set_type", "container_type", "plain") - #self.cmd.formvalue("set_type", "confirm", "1") - #self.cmd.submit() - self.cmd.go(url + "&store=step1&confirm=1&container_type=plain&fs_type=windows") - self.cmd.find('Formatting is running') - self.__wait_until_ready() - + def __format_plain(self): + """Format a plaintext partition. + """ + url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('select name="fs_type"') + # the following _should_ work, but it does not - probably a parsing problem + # of twill - for now we use the direct link instead + #self.cmd.formvalue("set_type", "fs_type", "windows") + #self.cmd.formvalue("set_type", "container_type", "plain") + #self.cmd.formvalue("set_type", "confirm", "1") + #self.cmd.submit() + self.cmd.go(url + "&store=step1&confirm=1&container_type=plain&fs_type=windows") + self.cmd.find('Formatting is running') + self.__wait_until_ready() + - def __wait_until_ready(self): - """Wait until the device is ready again. - """ - import time - finish_time = time.time() + 120 - while self.__is_device_busy(): - if (time.time() > finish_time): - self.fail("timeout for plain formatting expired") - time.sleep(5) - - - def __is_device_busy(self): - """Check if the device is busy. - """ - url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html - self.register_auth(url) - self.cmd.go(url) - try: - self.cmd.find("Disk is busy") - return True - except twill.errors.TwillAssertionError: - return False + def __wait_until_ready(self): + """Wait until the device is ready again. + """ + import time + finish_time = time.time() + 120 + while self.__is_device_busy(): + if (time.time() > finish_time): + self.fail("timeout for plain formatting expired") + time.sleep(5) + + + def __is_device_busy(self): + """Check if the device is busy. + """ + url = self.url + "volume_format_fs?weblang=en&device=" + self.device_html + self.register_auth(url) + self.cmd.go(url) + try: + self.cmd.find("Disk is busy") + return True + except twill.errors.TwillAssertionError: + return False diff --git a/plugins/volume_mount/volume_mount.py b/plugins/volume_mount/volume_mount.py index bf6d131..940e607 100644 --- a/plugins/volume_mount/volume_mount.py +++ b/plugins/volume_mount/volume_mount.py @@ -27,109 +27,109 @@ import cryptobox.core.container as cbxContainer class volume_mount(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "volume" ] - plugin_visibility = [ "volume" ] - request_auth = False - rank = 0 + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "volume" ] + request_auth = False + rank = 0 - def do_action(self, action=None, pw=None): - self.hdf[self.hdf_prefix + "PluginDir"] = self.plugin_dir - self.container = self.cbox.get_container(self.device) - if action == "mount_plain": - return self.__do_mount_plain() - elif action == "mount_luks": - return self.__do_mount_luks(pw) - elif action == "umount": - return self.__do_umount() - elif not action: - return "volume_status" - else: - self.cbox.log.info("plugin 'volume_mount' - unknown action: %s" % action) - return "volume_status" - + def do_action(self, action=None, pw=None): + self.hdf[self.hdf_prefix + "PluginDir"] = self.plugin_dir + self.container = self.cbox.get_container(self.device) + if action == "mount_plain": + return self.__do_mount_plain() + elif action == "mount_luks": + return self.__do_mount_luks(pw) + elif action == "umount": + return self.__do_umount() + elif not action: + return "volume_status" + else: + self.cbox.log.info("plugin 'volume_mount' - unknown action: %s" % action) + return "volume_status" + - def get_status(self): - container = self.cbox.get_container(self.device) - if not self.container: - return "invalid device" - if container.is_mounted(): - return "active" - else: - return "passive" + def get_status(self): + container = self.cbox.get_container(self.device) + if not self.container: + return "invalid device" + if container.is_mounted(): + return "active" + else: + return "passive" - def __do_mount_plain(self): - if self.container.is_mounted(): - self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" - self.cbox.log.info("the device (%s) is already mounted" % self.device) - return "volume_status" - if self.container.get_type() != cbxContainer.CONTAINERTYPES["plain"]: - ## not a plain container - self.cbox.log.info("plugin 'volume_mount' - invalid container type") - self.hdf["Data.Warning"] = "Plugins.volume_mount.InvalidContainerType" - return "volume_status" - try: - self.container.mount() - except CBMountError, err_msg: - self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" - self.cbox.log.warn("failed to mount the device (%s): %s" \ - % (self.device, err_msg)) - return "volume_status" - except CBContainerError, err_msg: - self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" - self.cbox.log.warn("failed to mount the device (%s): %s" \ - % (self.device, err_msg)) - return "volume_status" - self.cbox.log.info("successfully mounted the volume: %s" % self.device) - self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" - return "volume_status" + def __do_mount_plain(self): + if self.container.is_mounted(): + self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" + self.cbox.log.info("the device (%s) is already mounted" % self.device) + return "volume_status" + if self.container.get_type() != cbxContainer.CONTAINERTYPES["plain"]: + ## not a plain container + self.cbox.log.info("plugin 'volume_mount' - invalid container type") + self.hdf["Data.Warning"] = "Plugins.volume_mount.InvalidContainerType" + return "volume_status" + try: + self.container.mount() + except CBMountError, err_msg: + self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" + self.cbox.log.warn("failed to mount the device (%s): %s" \ + % (self.device, err_msg)) + return "volume_status" + except CBContainerError, err_msg: + self.hdf["Data.Warning"] = "Plugins.volume_mount.MountFailed" + self.cbox.log.warn("failed to mount the device (%s): %s" \ + % (self.device, err_msg)) + return "volume_status" + self.cbox.log.info("successfully mounted the volume: %s" % self.device) + self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" + return "volume_status" - def __do_mount_luks(self, pw): - if self.container.get_type() != cbxContainer.CONTAINERTYPES["luks"]: - ## not a luks container - fail silently - self.cbox.log.info("plugin 'volume_mount' - invalid container type") - return "volume_status" - if self.container.is_mounted(): - self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" - self.cbox.log.info("the device (%s) is already mounted" % self.device) - return "volume_status" - if not pw: - self.hdf["Data.Warning"] = "EmptyPassword" - self.cbox.log.info("no password was supplied for mounting of device: '%s'" \ - % self.device) - return "volume_status" - try: - self.container.mount(pw) - except CBMountError, err_msg: - self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" - self.cbox.log.warn("failed to mount the device (%s): %s" \ - % (self.device, err_msg)) - return "volume_status" - except CBContainerError, err_msg: - self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" - self.cbox.log.warn("failed to mount the device (%s): %s" \ - % (self.device, err_msg)) - return "volume_status" - self.cbox.log.info("successfully mounted the volume: %s" % self.device) - self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" - return "volume_status" + def __do_mount_luks(self, pw): + if self.container.get_type() != cbxContainer.CONTAINERTYPES["luks"]: + ## not a luks container - fail silently + self.cbox.log.info("plugin 'volume_mount' - invalid container type") + return "volume_status" + if self.container.is_mounted(): + self.hdf["Data.Warning"] = "Plugins.volume_mount.IsAlreadyMounted" + self.cbox.log.info("the device (%s) is already mounted" % self.device) + return "volume_status" + if not pw: + self.hdf["Data.Warning"] = "EmptyPassword" + self.cbox.log.info("no password was supplied for mounting of device: '%s'" \ + % self.device) + return "volume_status" + try: + self.container.mount(pw) + except CBMountError, err_msg: + self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" + self.cbox.log.warn("failed to mount the device (%s): %s" \ + % (self.device, err_msg)) + return "volume_status" + except CBContainerError, err_msg: + self.hdf["Data.Warning"] = "Plugins.volume_mount.MountCryptoFailed" + self.cbox.log.warn("failed to mount the device (%s): %s" \ + % (self.device, err_msg)) + return "volume_status" + self.cbox.log.info("successfully mounted the volume: %s" % self.device) + self.hdf["Data.Success"] = "Plugins.volume_mount.MountDone" + return "volume_status" - def __do_umount(self): - if not self.container.is_mounted(): - self.hdf["Data.Warning"] = "Plugins.volume_mount.IsNotMounted" - self.cbox.log.info("the device (%s) is currently not mounted" % self.device) - return "volume_status" - try: - self.container.umount() - except CBUmountError, err_msg: - self.hdf["Data.Warning"] = "UmountFailed" - self.cbox.log.warn("could not umount the volume (%s): %s" \ - % (self.device, err_msg)) - return "volume_status" - self.cbox.log.info("successfully unmounted the container: %s" % self.device) - self.hdf["Data.Success"] = "Plugins.volume_mount.UmountDone" - return "volume_status" + def __do_umount(self): + if not self.container.is_mounted(): + self.hdf["Data.Warning"] = "Plugins.volume_mount.IsNotMounted" + self.cbox.log.info("the device (%s) is currently not mounted" % self.device) + return "volume_status" + try: + self.container.umount() + except CBUmountError, err_msg: + self.hdf["Data.Warning"] = "UmountFailed" + self.cbox.log.warn("could not umount the volume (%s): %s" \ + % (self.device, err_msg)) + return "volume_status" + self.cbox.log.info("successfully unmounted the container: %s" % self.device) + self.hdf["Data.Success"] = "Plugins.volume_mount.UmountDone" + return "volume_status" diff --git a/plugins/volume_props/unittests.py b/plugins/volume_props/unittests.py index ef08a58..06c1b32 100644 --- a/plugins/volume_props/unittests.py +++ b/plugins/volume_props/unittests.py @@ -24,9 +24,9 @@ from cryptobox.tests.base import WebInterfaceTestClass class unittests(WebInterfaceTestClass): - def test_read_form(self): - url = self.url + "volume_props?weblang=en&device=" + self.device_html - self.register_auth(url) - self.cmd.go(url) - self.cmd.find('Properties') + def test_read_form(self): + url = self.url + "volume_props?weblang=en&device=" + self.device_html + self.register_auth(url) + self.cmd.go(url) + self.cmd.find('Properties') diff --git a/plugins/volume_props/volume_props.py b/plugins/volume_props/volume_props.py index be2e1a5..17eea07 100644 --- a/plugins/volume_props/volume_props.py +++ b/plugins/volume_props/volume_props.py @@ -27,64 +27,64 @@ from cryptobox.core.exceptions import * class volume_props(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "volume" ] - plugin_visibility = [ "volume" ] - request_auth = False - rank = 30 + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "volume" ] + request_auth = False + rank = 30 - def do_action(self, **args): - import os - ## include all plugins marked as "properties" - ## skip ourselves to prevent recursion - 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()) \ - and (e.get_name() != self.get_name())] - ## sort plugins by rank - self.props_plugins.sort(cmp = self.__cmp_plugins_rank) - ## set the name of the templates for every plugin - load_string = "" - for p in self.props_plugins: - ## skip all volume plugins that are not suitable - ## (e.g. chpasswd for plain containers) - if not p.is_useful(self.device): - continue - p.device = self.device - plfname = os.path.join(p.plugin_dir, str(p.do_action(**args)) + ".cs") - load_string += "" % plfname - ## this is a little bit ugly: as it is not possible, to load cs files via - ## 'linclude' (see clearsilver doc) if they use previously defined macros - ## (see clearsilver mailing list thread - ## 'linclude file which calls a macro' - 27th December 02005) - ## our workaround: define the appropriate "include" (not 'linclude') - ## command as a hdf variable - then we can include it via 'evar' - self.hdf[self.hdf_prefix + 'includePlugins'] = load_string - return "volume_properties" - + def do_action(self, **args): + import os + ## include all plugins marked as "properties" + ## skip ourselves to prevent recursion + 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()) \ + and (e.get_name() != self.get_name())] + ## sort plugins by rank + self.props_plugins.sort(cmp = self.__cmp_plugins_rank) + ## set the name of the templates for every plugin + load_string = "" + for p in self.props_plugins: + ## skip all volume plugins that are not suitable + ## (e.g. chpasswd for plain containers) + if not p.is_useful(self.device): + continue + p.device = self.device + plfname = os.path.join(p.plugin_dir, str(p.do_action(**args)) + ".cs") + load_string += "" % plfname + ## this is a little bit ugly: as it is not possible, to load cs files via + ## 'linclude' (see clearsilver doc) if they use previously defined macros + ## (see clearsilver mailing list thread + ## 'linclude file which calls a macro' - 27th December 02005) + ## our workaround: define the appropriate "include" (not 'linclude') + ## command as a hdf variable - then we can include it via 'evar' + self.hdf[self.hdf_prefix + 'includePlugins'] = load_string + return "volume_properties" + - def get_status(self): - return ":".join([ e.get_name() for e in self.props_plugins ]) - + def get_status(self): + return ":".join([ e.get_name() for e in self.props_plugins ]) + - def load_dataset(self, hdf): - """Override the parent's function + def load_dataset(self, hdf): + """Override the parent's function - we have to get the data from all included plugins - """ - for plugin in self.props_plugins: - ## retrieve the results of an included plugin - plugin.load_dataset(hdf) - ## call our parent's method - cryptobox.plugins.base.CryptoBoxPlugin.load_dataset(self, hdf) + we have to get the data from all included plugins + """ + for plugin in self.props_plugins: + ## retrieve the results of an included plugin + plugin.load_dataset(hdf) + ## call our parent's method + cryptobox.plugins.base.CryptoBoxPlugin.load_dataset(self, hdf) - def __cmp_plugins_rank(self, p1, p2): - order = p1.get_rank() - p2.get_rank() - if order < 0: - return -1 - elif order == 0: - return 0 - else: - return 1 + def __cmp_plugins_rank(self, p1, p2): + order = p1.get_rank() - p2.get_rank() + if order < 0: + return -1 + elif order == 0: + return 0 + else: + return 1 diff --git a/plugins/volume_rename/unittests.py b/plugins/volume_rename/unittests.py index 48b4bf8..5fa3847 100644 --- a/plugins/volume_rename/unittests.py +++ b/plugins/volume_rename/unittests.py @@ -25,137 +25,137 @@ import cryptobox.tests.tools as cbox_tools class unittests(WebInterfaceTestClass): - def test_read_form(self): - """Check if the 'volume_rename' plugin works. - """ - url = self.url + "volume_rename?weblang=en&device=" + self.device_html - self.register_auth(url) - ## umount, if necessary - cbox_tools.umount(self.device) - ## check a language string - self.cmd.go(url) - self.cmd.find('Change name') + def test_read_form(self): + """Check if the 'volume_rename' plugin works. + """ + url = self.url + "volume_rename?weblang=en&device=" + self.device_html + self.register_auth(url) + ## umount, if necessary + cbox_tools.umount(self.device) + ## check a language string + self.cmd.go(url) + self.cmd.find('Change name') - def test_rename(self): - """Try to rename the volume. - """ - ## umount, if necessary - cbox_tools.umount(self.device) - save_name = self.__get_name() - ## rename if the name is already "foo" - if save_name == "foo": - self.__set_name("bar") - ## set 'foo' - self.__set_name("foo") - self.cmd.find('The name of this volume was changed successfully.') - self.assertEquals("foo", self.__get_name()) - ## set 'bar' - self.__set_name("bar") - self.cmd.find('The name of this volume was changed successfully.') - self.assertEquals("bar", self.__get_name()) - ## set the same name twice to catch all lines of code - self.__set_name("bar") - self.cmd.notfind('The name of this volume was changed successfully.') - self.assertEquals("bar", self.__get_name()) - self.__set_name(save_name) - self.assertEquals(save_name, self.__get_name()) - + def test_rename(self): + """Try to rename the volume. + """ + ## umount, if necessary + cbox_tools.umount(self.device) + save_name = self.__get_name() + ## rename if the name is already "foo" + if save_name == "foo": + self.__set_name("bar") + ## set 'foo' + self.__set_name("foo") + self.cmd.find('The name of this volume was changed successfully.') + self.assertEquals("foo", self.__get_name()) + ## set 'bar' + self.__set_name("bar") + self.cmd.find('The name of this volume was changed successfully.') + self.assertEquals("bar", self.__get_name()) + ## set the same name twice to catch all lines of code + self.__set_name("bar") + self.cmd.notfind('The name of this volume was changed successfully.') + self.assertEquals("bar", self.__get_name()) + self.__set_name(save_name) + self.assertEquals(save_name, self.__get_name()) + - def test_invalid_names(self): - """Setting of invalid names should fail. - """ - ## umount, if necessary - cbox_tools.umount(self.device) - save_name = self.__get_name() - ## we want to avoid, that if the previous name is (by accident) 'foo' - ## then the later search for "changed successfully" would fail - if save_name == "foo": - self.__set_name("bar") - self.__set_name("foo") - self.cmd.find('The name of this volume was changed successfully.') - self.assertEquals("foo", self.__get_name()) - self.__set_name("foo:") - self.cmd.find("Changing of volume's name failed") - self.assertEquals("foo", self.__get_name()) - self.__set_name("foo/") - self.cmd.find("Changing of volume's name failed") - self.assertEquals("foo", self.__get_name()) - self.__set_name("foo/") - self.cmd.find("Changing of volume's name failed") - self.assertEquals("foo", self.__get_name()) - self.__set_name("foo*") - self.cmd.find("Changing of volume's name failed") - self.assertEquals("foo", self.__get_name()) - self.__set_name("foo(") - self.cmd.find("Changing of volume's name failed") - self.assertEquals("foo", self.__get_name()) - self.__set_name("") - self.cmd.notfind("Changing of volume's name failed") - self.assertEquals("foo", self.__get_name()) - self.__set_name(save_name) - self.assertEquals(save_name, self.__get_name()) + def test_invalid_names(self): + """Setting of invalid names should fail. + """ + ## umount, if necessary + cbox_tools.umount(self.device) + save_name = self.__get_name() + ## we want to avoid, that if the previous name is (by accident) 'foo' + ## then the later search for "changed successfully" would fail + if save_name == "foo": + self.__set_name("bar") + self.__set_name("foo") + self.cmd.find('The name of this volume was changed successfully.') + self.assertEquals("foo", self.__get_name()) + self.__set_name("foo:") + self.cmd.find("Changing of volume's name failed") + self.assertEquals("foo", self.__get_name()) + self.__set_name("foo/") + self.cmd.find("Changing of volume's name failed") + self.assertEquals("foo", self.__get_name()) + self.__set_name("foo/") + self.cmd.find("Changing of volume's name failed") + self.assertEquals("foo", self.__get_name()) + self.__set_name("foo*") + self.cmd.find("Changing of volume's name failed") + self.assertEquals("foo", self.__get_name()) + self.__set_name("foo(") + self.cmd.find("Changing of volume's name failed") + self.assertEquals("foo", self.__get_name()) + self.__set_name("") + self.cmd.notfind("Changing of volume's name failed") + self.assertEquals("foo", self.__get_name()) + self.__set_name(save_name) + self.assertEquals(save_name, self.__get_name()) - def test_rename_while_open(self): - """Try to change the name of the volume while it is open. - """ - ## umount, if necessary - cbox_tools.umount(self.device) - save_name = self.__get_name() - ## first set the name to 'bar' - self.__set_name("bar") - mount_url = self.url + "volume_mount?weblang=en&device=" + self.device_html - self.register_auth(mount_url) - name_url = self.url + "volume_rename?weblang=en&device=" + self.device_html - self.register_auth(name_url) - self.cmd.go(mount_url + "&action=mount_plain") - self.cmd.find('Volume opened') - ## we have to do it manually, as there is no form when it is open - self.cmd.go(name_url + "&store=1&vol_name=foo") - self.cmd.find('You may not rename a volume while it is open.') - self.assertEquals("bar", self.__get_name()) - self.cmd.go(mount_url + "&action=umount") - self.__set_name(save_name) - self.assertEquals(save_name, self.__get_name()) + def test_rename_while_open(self): + """Try to change the name of the volume while it is open. + """ + ## umount, if necessary + cbox_tools.umount(self.device) + save_name = self.__get_name() + ## first set the name to 'bar' + self.__set_name("bar") + mount_url = self.url + "volume_mount?weblang=en&device=" + self.device_html + self.register_auth(mount_url) + name_url = self.url + "volume_rename?weblang=en&device=" + self.device_html + self.register_auth(name_url) + self.cmd.go(mount_url + "&action=mount_plain") + self.cmd.find('Volume opened') + ## we have to do it manually, as there is no form when it is open + self.cmd.go(name_url + "&store=1&vol_name=foo") + self.cmd.find('You may not rename a volume while it is open.') + self.assertEquals("bar", self.__get_name()) + self.cmd.go(mount_url + "&action=umount") + self.__set_name(save_name) + self.assertEquals(save_name, self.__get_name()) - def test_name_in_use(self): - """Try to set a name that is already in use. - """ - ## umount, if necessary - cbox_tools.umount(self.device) - used_name = [ e.get_name() for e in self.cbox.get_container_list() - if e.get_device() != self.device ] - if not used_name: - self.fail("could not find another device for this test") - old_name = self.__get_name() - self.assertNotEquals(old_name, used_name[0]) - self.__set_name(used_name[0]) - self.cmd.find('The new name is already in use by another volume.') - self.assertEquals(old_name, self.__get_name()) + def test_name_in_use(self): + """Try to set a name that is already in use. + """ + ## umount, if necessary + cbox_tools.umount(self.device) + used_name = [ e.get_name() for e in self.cbox.get_container_list() + if e.get_device() != self.device ] + if not used_name: + self.fail("could not find another device for this test") + old_name = self.__get_name() + self.assertNotEquals(old_name, used_name[0]) + self.__set_name(used_name[0]) + self.cmd.find('The new name is already in use by another volume.') + self.assertEquals(old_name, self.__get_name()) - def __set_name(self, name): - """Set the name of a volume. - """ - name = name.replace(" ", "%20") - url = self.url + "volume_rename?weblang=en&device=" + self.device_html - self.register_auth(url) - # the following should work, but twill seems to have problems to recognize - # the form - fix this later - #self.cmd.go(url) - #self.cmd.formvalue("set_name", "vol_name", name) - #self.cmd.submit() - self.cmd.go(url + "&vol_name=%s&store=1" % name) - + def __set_name(self, name): + """Set the name of a volume. + """ + name = name.replace(" ", "%20") + url = self.url + "volume_rename?weblang=en&device=" + self.device_html + self.register_auth(url) + # the following should work, but twill seems to have problems to recognize + # the form - fix this later + #self.cmd.go(url) + #self.cmd.formvalue("set_name", "vol_name", name) + #self.cmd.submit() + self.cmd.go(url + "&vol_name=%s&store=1" % name) + - def __get_name(self): - """Retrieve the current name of the volume. - """ - url = self.url + "volume_rename?weblang=en&device=" + self.device_html - self.register_auth(url) - self.cmd.go(url) - self.cmd.find("Data.Status.Plugins.volume_rename=(.*)$", "m") - return self.locals["__match__"] - + def __get_name(self): + """Retrieve the current name of the volume. + """ + url = self.url + "volume_rename?weblang=en&device=" + self.device_html + self.register_auth(url) + self.cmd.go(url) + self.cmd.find("Data.Status.Plugins.volume_rename=(.*)$", "m") + return self.locals["__match__"] + diff --git a/plugins/volume_rename/volume_rename.py b/plugins/volume_rename/volume_rename.py index 467a6c4..bf7ac34 100644 --- a/plugins/volume_rename/volume_rename.py +++ b/plugins/volume_rename/volume_rename.py @@ -26,50 +26,50 @@ from cryptobox.core.exceptions import * class volume_rename(cryptobox.plugins.base.CryptoBoxPlugin): - plugin_capabilities = [ "volume" ] - plugin_visibility = [ "properties" ] - request_auth = False - rank = 60 + plugin_capabilities = [ "volume" ] + plugin_visibility = [ "properties" ] + request_auth = False + rank = 60 - def do_action(self, store=None, vol_name=None): - self.container = self.cbox.get_container(self.device) - if not self.container: - return None - self.__prepare_hdf() - if store and vol_name: - return self.__set_volume_name(vol_name) - else: - return "volume_rename" - + def do_action(self, store=None, vol_name=None): + self.container = self.cbox.get_container(self.device) + if not self.container: + return None + self.__prepare_hdf() + if store and vol_name: + return self.__set_volume_name(vol_name) + else: + return "volume_rename" + - def get_status(self): - self.container = self.cbox.get_container(self.device) - if not self.container: - return "invalid device" - return "%s" % self.container.get_name() - + def get_status(self): + self.container = self.cbox.get_container(self.device) + if not self.container: + return "invalid device" + return "%s" % self.container.get_name() + - def __prepare_hdf(self): - self.hdf[self.hdf_prefix + "vol_name"] = self.container.get_name() - + def __prepare_hdf(self): + self.hdf[self.hdf_prefix + "vol_name"] = self.container.get_name() + - def __set_volume_name(self, vol_name): - if vol_name == self.container.get_name(): - ## nothing has to be done - return "volume_rename" - try: - self.container.set_name(vol_name) - self.hdf["Data.Success"] = "Plugins.volume_rename.VolumeNameChanged" - except CBVolumeIsActive: - self.hdf["Data.Warning"] = "Plugins.volume_rename.NoRenameIfActive" - except CBInvalidName: - self.hdf["Data.Warning"] = "Plugins.volume_rename.InvalidVolumeName" - except CBNameIsInUse: - self.hdf["Data.Warning"] = "Plugins.volume_rename.VolumeNameIsInUse" - except CBContainerError: - self.hdf["Data.Warning"] = "Plugins.volume_rename.SetVolumeNameFailed" - ## reread the volume name - self.__prepare_hdf() - return "volume_rename" + def __set_volume_name(self, vol_name): + if vol_name == self.container.get_name(): + ## nothing has to be done + return "volume_rename" + try: + self.container.set_name(vol_name) + self.hdf["Data.Success"] = "Plugins.volume_rename.VolumeNameChanged" + except CBVolumeIsActive: + self.hdf["Data.Warning"] = "Plugins.volume_rename.NoRenameIfActive" + except CBInvalidName: + self.hdf["Data.Warning"] = "Plugins.volume_rename.InvalidVolumeName" + except CBNameIsInUse: + self.hdf["Data.Warning"] = "Plugins.volume_rename.VolumeNameIsInUse" + except CBContainerError: + self.hdf["Data.Warning"] = "Plugins.volume_rename.SetVolumeNameFailed" + ## reread the volume name + self.__prepare_hdf() + return "volume_rename" diff --git a/scripts/update_po_files.py b/scripts/update_po_files.py index 3feb7ae..4f5e775 100755 --- a/scripts/update_po_files.py +++ b/scripts/update_po_files.py @@ -33,26 +33,26 @@ import os import sys try: - import translate.storage.po, translate.convert.pot2po, translate.tools.pocompile + import translate.storage.po, translate.convert.pot2po, translate.tools.pocompile except ImportError, errMsg: - sys.stderr.write("Failed to import a python module of the 'translate' package!\n") - sys.stderr.write("Please install the appropriate files - for debian just do 'apt-get install translate-toolkit'.\n") - sys.stderr.write("\tOriginal error message: %s\n\n" % errMsg) - sys.exit(1) + sys.stderr.write("Failed to import a python module of the 'translate' package!\n") + sys.stderr.write("Please install the appropriate files - for debian just do 'apt-get install translate-toolkit'.\n") + sys.stderr.write("\tOriginal error message: %s\n\n" % errMsg) + sys.exit(1) try: - import neo_cgi, neo_util + import neo_cgi, neo_util except ImportError, errMsg: - sys.stderr.write("Failed to import a python module of the 'clearsilver' package!\n") - sys.stderr.write("Please install the appropriate files - for debian just do 'apt-get install python-clearsilver'.\n") - sys.stderr.write("\tOriginal error message: %s\n\n" % errMsg) - sys.exit(1) + sys.stderr.write("Failed to import a python module of the 'clearsilver' package!\n") + sys.stderr.write("Please install the appropriate files - for debian just do 'apt-get install python-clearsilver'.\n") + sys.stderr.write("\tOriginal error message: %s\n\n" % errMsg) + sys.exit(1) try: - import subprocess + import subprocess except ImportError, errMsg: - sys.stderr.write("Failed to import the python module 'subprocess'!\n") - sys.stderr.write("Please install python v2.4 or higher.\n") - sys.stderr.write("\tOriginal error message: %s\n\n" % errMsg) - sys.exit(1) + sys.stderr.write("Failed to import the python module 'subprocess'!\n") + sys.stderr.write("Please install python v2.4 or higher.\n") + sys.stderr.write("\tOriginal error message: %s\n\n" % errMsg) + sys.exit(1) LANGUAGE_FILE = 'language.hdf' @@ -70,119 +70,119 @@ ALL_LANGUAGES = "cs da de et en es fi fr hr hu it ja nl pl pt ru sl sv ur".split # --------------=-=-=- functions -=-=-=-------------------- def revert_if_unchanged(po_file): - try: - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - args = [ "svn", "diff", po_file ] ) - except OSError, err_msg: - sys.stderr.write("Failed to execute subversion's diff: %s\n" % err_msg) - return - (stdout, stderr) = proc.communicate() - if proc.returncode != 0: - sys.stderr.write("Subversion returned an error: %d\n" % proc.returncode) - return - ## no changes at all? - if not stdout: - return - lines = [ l for l in stdout.splitlines() - if ((l.find("POT-Creation-Date:") < 0 ) and \ - ((l.startswith("+") and (not l.startswith("+++"))) or \ - (l.startswith("-") and (not l.startswith("---"))))) ] - ## are there relevant changes? then we should not revert ... - if lines: - return - ## revert to previous state - proc = subprocess.Popen( - shell = False, - args = [ "svn", "revert", po_file ] ) - proc.wait() + try: + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + args = [ "svn", "diff", po_file ] ) + except OSError, err_msg: + sys.stderr.write("Failed to execute subversion's diff: %s\n" % err_msg) + return + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + sys.stderr.write("Subversion returned an error: %d\n" % proc.returncode) + return + ## no changes at all? + if not stdout: + return + lines = [ l for l in stdout.splitlines() + if ((l.find("POT-Creation-Date:") < 0 ) and \ + ((l.startswith("+") and (not l.startswith("+++"))) or \ + (l.startswith("-") and (not l.startswith("---"))))) ] + ## are there relevant changes? then we should not revert ... + if lines: + return + ## revert to previous state + proc = subprocess.Popen( + shell = False, + args = [ "svn", "revert", po_file ] ) + proc.wait() def process_language_file(hdf_file, po_dir, textDomain): - ## prepare hdf - if not os.path.isfile(hdf_file) or not os.access(hdf_file, os.R_OK): - sys.stderr.write("Unable to read the hdf file: %s" % hdf_file) - return - if not os.path.isdir(po_dir): - os.mkdir(po_dir) - pot_file = os.path.join(po_dir, "%s.pot" % textDomain) - hdf = neo_util.HDF() - hdf.readFile(hdf_file) - ## update pot - if not os.path.isfile(pot_file): - sys.stdout.write("Creating: %s\n" % pot_file) - pot = translate.storage.po.pofile(encoding="utf-8") - pot.makeheader(pot_creation_date=True) - pot.updateheader(add=True, Project_Id_Version='CryptoBox-Server 0.3', pot_creation_date=True, language_team='sense.lab <%s>' % MAIL_ADDRESS, Report_Msgid_Bugs_To=MAIL_ADDRESS, encoding='utf-8', Plural_Forms=['nplurals=2','plural=(n != 1)']) - #TODO: somehow we need 'updateheaderplural' - else: - sys.stdout.write("Loading: %s\n" % pot_file) - pot = translate.storage.po.pofile.parsefile(pot_file) - ## remove all msgids - we will add them later - pot.units = [] - ## add new entries - def walk_hdf(prefix, node): - def addPoItem(hdf_node): - ## ignore hdf values with a "LINK" attribute - for (key,value) in hdf_node.attrs(): - if key == "LINK": - return - if not hdf_node.value(): - return - item = pot.findunit(hdf_node.value()) - if not item: - item = pot.addsourceunit(hdf_node.value()) - item.addlocation("%s%s" % (prefix, hdf_node.name())) - while node: - if node.name(): - new_prefix = prefix + node.name() + '.' - else: - new_prefix = prefix - ## as the attribute feature of clearsilver does not work yet, we - ## have to rely on magic names to prevent the translation of links - if not (new_prefix.endswith(".Link.Rel.") \ - or new_prefix.endswith(".Link.Prot.") \ - or new_prefix.endswith(".Link.Abs.") \ - or new_prefix.endswith(".Link.Attr1.name.") \ - or new_prefix.endswith(".Link.Attr1.value.") \ - or new_prefix.endswith(".Link.Attr2.name.") \ - or new_prefix.endswith(".Link.Attr2.value.")): - addPoItem(node) - walk_hdf(new_prefix, node.child()) - node = node.next() - walk_hdf("",hdf) - pot.savefile(pot_file) - p = translate.storage.po.pofile(pot_file) - for ld in ALL_LANGUAGES: - if not os.path.isdir(os.path.join(po_dir,ld)): - os.mkdir(os.path.join(po_dir, ld)) - po_file = os.path.join(po_dir, ld, "%s.po" % textDomain) - if not os.path.isfile(po_file): - translate.convert.pot2po.convertpot(file(pot_file), file(po_file,'w'), None) - else: - po2_file = po_file + '.new' - translate.convert.pot2po.convertpot(file(pot_file), file(po2_file,'w'), file(po_file)) - os.rename(po2_file, po_file) - if ld == DEFAULT_LANG: - ## set every msgstr to the respective msgid - po_data = translate.storage.po.pofile.parsefile(po_file) - for po_unit in po_data.units: - po_unit.settarget(po_unit.getsource()) - po_data.removeduplicates() - po_data.removeblanks() - po_data.savefile(po_file) - else: - po_content = translate.storage.po.pofile.parsefile(po_file) - po_content.removeduplicates() - po_content.removeblanks() - po_content.savefile(po_file) - revert_if_unchanged(po_file) - ## make it writeable for pootle - os.chmod(po_file, 0666) - ## compile po file - mo_file = po_file[:-3] + '.mo' - translate.tools.pocompile.convertmo(file(po_file), file(mo_file,'w'), file(pot_file)) + ## prepare hdf + if not os.path.isfile(hdf_file) or not os.access(hdf_file, os.R_OK): + sys.stderr.write("Unable to read the hdf file: %s" % hdf_file) + return + if not os.path.isdir(po_dir): + os.mkdir(po_dir) + pot_file = os.path.join(po_dir, "%s.pot" % textDomain) + hdf = neo_util.HDF() + hdf.readFile(hdf_file) + ## update pot + if not os.path.isfile(pot_file): + sys.stdout.write("Creating: %s\n" % pot_file) + pot = translate.storage.po.pofile(encoding="utf-8") + pot.makeheader(pot_creation_date=True) + pot.updateheader(add=True, Project_Id_Version='CryptoBox-Server 0.3', pot_creation_date=True, language_team='sense.lab <%s>' % MAIL_ADDRESS, Report_Msgid_Bugs_To=MAIL_ADDRESS, encoding='utf-8', Plural_Forms=['nplurals=2','plural=(n != 1)']) + #TODO: somehow we need 'updateheaderplural' + else: + sys.stdout.write("Loading: %s\n" % pot_file) + pot = translate.storage.po.pofile.parsefile(pot_file) + ## remove all msgids - we will add them later + pot.units = [] + ## add new entries + def walk_hdf(prefix, node): + def addPoItem(hdf_node): + ## ignore hdf values with a "LINK" attribute + for (key,value) in hdf_node.attrs(): + if key == "LINK": + return + if not hdf_node.value(): + return + item = pot.findunit(hdf_node.value()) + if not item: + item = pot.addsourceunit(hdf_node.value()) + item.addlocation("%s%s" % (prefix, hdf_node.name())) + while node: + if node.name(): + new_prefix = prefix + node.name() + '.' + else: + new_prefix = prefix + ## as the attribute feature of clearsilver does not work yet, we + ## have to rely on magic names to prevent the translation of links + if not (new_prefix.endswith(".Link.Rel.") \ + or new_prefix.endswith(".Link.Prot.") \ + or new_prefix.endswith(".Link.Abs.") \ + or new_prefix.endswith(".Link.Attr1.name.") \ + or new_prefix.endswith(".Link.Attr1.value.") \ + or new_prefix.endswith(".Link.Attr2.name.") \ + or new_prefix.endswith(".Link.Attr2.value.")): + addPoItem(node) + walk_hdf(new_prefix, node.child()) + node = node.next() + walk_hdf("",hdf) + pot.savefile(pot_file) + p = translate.storage.po.pofile(pot_file) + for ld in ALL_LANGUAGES: + if not os.path.isdir(os.path.join(po_dir,ld)): + os.mkdir(os.path.join(po_dir, ld)) + po_file = os.path.join(po_dir, ld, "%s.po" % textDomain) + if not os.path.isfile(po_file): + translate.convert.pot2po.convertpot(file(pot_file), file(po_file,'w'), None) + else: + po2_file = po_file + '.new' + translate.convert.pot2po.convertpot(file(pot_file), file(po2_file,'w'), file(po_file)) + os.rename(po2_file, po_file) + if ld == DEFAULT_LANG: + ## set every msgstr to the respective msgid + po_data = translate.storage.po.pofile.parsefile(po_file) + for po_unit in po_data.units: + po_unit.settarget(po_unit.getsource()) + po_data.removeduplicates() + po_data.removeblanks() + po_data.savefile(po_file) + else: + po_content = translate.storage.po.pofile.parsefile(po_file) + po_content.removeduplicates() + po_content.removeblanks() + po_content.savefile(po_file) + revert_if_unchanged(po_file) + ## make it writeable for pootle + os.chmod(po_file, 0666) + ## compile po file + mo_file = po_file[:-3] + '.mo' + translate.tools.pocompile.convertmo(file(po_file), file(mo_file,'w'), file(pot_file)) @@ -192,18 +192,18 @@ def process_language_file(hdf_file, po_dir, textDomain): if __name__ == "__main__": - ## the project directory is the parent of the directory of this script - PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),os.path.pardir)) + ## the project directory is the parent of the directory of this script + PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),os.path.pardir)) - process_language_file( - os.path.join(PROJECT_DIR, 'templates', LANGUAGE_FILE), - os.path.join(PROJECT_DIR, LANG_DIR), - GETTEXT_DOMAIN) + process_language_file( + os.path.join(PROJECT_DIR, 'templates', LANGUAGE_FILE), + os.path.join(PROJECT_DIR, LANG_DIR), + GETTEXT_DOMAIN) - for root, dirs, files in os.walk(os.path.join(PROJECT_DIR, 'plugins')): - if LANGUAGE_FILE in files: - process_language_file( - os.path.join(root,LANGUAGE_FILE), - os.path.join(root,LANG_DIR), - "%s-feature-%s" % (GETTEXT_DOMAIN,os.path.basename(root))) + for root, dirs, files in os.walk(os.path.join(PROJECT_DIR, 'plugins')): + if LANGUAGE_FILE in files: + process_language_file( + os.path.join(root,LANGUAGE_FILE), + os.path.join(root,LANG_DIR), + "%s-feature-%s" % (GETTEXT_DOMAIN,os.path.basename(root))) diff --git a/setup.py b/setup.py index 0ac8829..f82d032 100644 --- a/setup.py +++ b/setup.py @@ -43,109 +43,109 @@ confdir = os.path.join(os.path.sep, 'etc', 'cryptobox-server') def listfiles(prefix, src): - """create a list of files below a directory recursively + """create a list of files below a directory recursively - If the src contains more then one path element (multiple levels), then only the - last one (basename part) is added to the prefix path (e.g.: 'dest','src1/src2' will - create a list below 'dest/src2'). - The result is a list of tuples: (destination, [srcfiles,...]). - This is the datatype expected by 'data_files' in setup.""" - ## we will not add the 'dirname' part of srcdir to the destination - src_dir, src_base = os.path.split(src) - ## add the files of this directory - result = [(os.path.join(prefix, src_base), [ os.path.join(src, f) - for f in os.listdir(src) - if os.path.isfile(os.path.join(src, f)) \ - and not f in IGNORE_FILES ])] - ## add the files in subdirectories - for d in os.listdir(src): - if os.path.isdir(os.path.join(src,d)) and not d in IGNORE_FILES: - result.extend(listfiles( - os.path.join(prefix,src_base), os.path.join(src, d))) - return result - - + If the src contains more then one path element (multiple levels), then only the + last one (basename part) is added to the prefix path (e.g.: 'dest','src1/src2' will + create a list below 'dest/src2'). + The result is a list of tuples: (destination, [srcfiles,...]). + This is the datatype expected by 'data_files' in setup.""" + ## we will not add the 'dirname' part of srcdir to the destination + src_dir, src_base = os.path.split(src) + ## add the files of this directory + result = [(os.path.join(prefix, src_base), [ os.path.join(src, f) + for f in os.listdir(src) + if os.path.isfile(os.path.join(src, f)) \ + and not f in IGNORE_FILES ])] + ## add the files in subdirectories + for d in os.listdir(src): + if os.path.isdir(os.path.join(src,d)) and not d in IGNORE_FILES: + result.extend(listfiles( + os.path.join(prefix,src_base), os.path.join(src, d))) + return result + + def getdatafiles(prefix, dirs): - filelist = [] - for d in dirs: - if os.path.isdir(d): - filelist.extend(listfiles(prefix, d)) - else: - filelist.append((prefix, [d])) - return filelist + filelist = [] + for d in dirs: + if os.path.isdir(d): + filelist.extend(listfiles(prefix, d)) + else: + filelist.append((prefix, [d])) + return filelist def get_language_files(prefix): - """return a destination-file mapping for all compiled language files (*.po) - """ - import sys - mapping = [] - ## find all language directories - intl_dirs = [] - for (root, dirs, files) in os.walk(os.getcwd()): - if 'intl' in dirs: - intl_dirs.append(os.path.join(root, 'intl')) - for i_dir in intl_dirs: - for lang_dir in [os.path.join(i_dir, e) - for e in os.listdir(i_dir) - if os.path.isdir(os.path.join(i_dir, e)) and (not e in IGNORE_FILES)]: - po_files = [ os.path.join(lang_dir, e) - for e in os.listdir(lang_dir) - if os.path.isfile(os.path.join(lang_dir, e)) \ - and (e[-3:] == '.po') ] - lang_files = [] - for po_file in po_files: - if compile_po_file(po_file): - lang_files.append(po_file[:-3] + ".mo") - else: - sys.stderr.write("Failed to compile language file: %s\n" % po_file) - mapping.append((os.path.join( - prefix, os.path.basename(lang_dir), 'LC_MESSAGES'), lang_files)) - return mapping + """return a destination-file mapping for all compiled language files (*.po) + """ + import sys + mapping = [] + ## find all language directories + intl_dirs = [] + for (root, dirs, files) in os.walk(os.getcwd()): + if 'intl' in dirs: + intl_dirs.append(os.path.join(root, 'intl')) + for i_dir in intl_dirs: + for lang_dir in [os.path.join(i_dir, e) + for e in os.listdir(i_dir) + if os.path.isdir(os.path.join(i_dir, e)) and (not e in IGNORE_FILES)]: + po_files = [ os.path.join(lang_dir, e) + for e in os.listdir(lang_dir) + if os.path.isfile(os.path.join(lang_dir, e)) \ + and (e[-3:] == '.po') ] + lang_files = [] + for po_file in po_files: + if compile_po_file(po_file): + lang_files.append(po_file[:-3] + ".mo") + else: + sys.stderr.write("Failed to compile language file: %s\n" % po_file) + mapping.append((os.path.join( + prefix, os.path.basename(lang_dir), 'LC_MESSAGES'), lang_files)) + return mapping def compile_po_file(po_file): - """compile the binary mo file out of a po file - """ - import subprocess - return subprocess.call( [ "msgfmt", "-o", po_file[:-3] + ".mo", po_file ] ) == 0 + """compile the binary mo file out of a po file + """ + import subprocess + return subprocess.call( [ "msgfmt", "-o", po_file[:-3] + ".mo", po_file ] ) == 0 setup( - name = 'cryptobox-server', - version = '0.3.0', - description = 'webinterface for handling encrypted disks', - author = 'Sense.Lab e.V.', - author_email = 'info@cryptobox.org', - maintainer = 'Lars Kruse', - maintainer_email = 'devel@sumpfralle.de', - license = 'GPL', - url = 'http://cryptobox.org', - packages = [ 'cryptobox', 'cryptobox.core', 'cryptobox.web', - 'cryptobox.plugins', 'cryptobox.tests' ], - data_files = getdatafiles(datadir, ['templates', 'www-data', 'plugins']) + - getdatafiles(confdir, [os.path.join('conf-examples', 'cryptobox.conf')]) + - getdatafiles(os.path.join(confdir, 'events.d'), [ - os.path.join('event-scripts', 'README'), - os.path.join('event-scripts', '_event_scripts_')]) + - getdatafiles(docdir, ['conf-examples', 'event-scripts', 'README', 'changelog', - 'LICENSE', 'copyright', os.path.join('doc', 'html'), 'README.davfs', - 'README.samba', 'README.proxy', 'README.ssl' ]) + - getdatafiles(os.path.join(docdir, 'conf-examples'), - [os.path.join('debian', 'cryptobox-server.init')]) + - get_language_files(os.path.join('share', 'locale')), - package_dir = { '': 'src' }, - scripts = [ os.path.join('bin', 'CryptoBoxWebserver'), - os.path.join('bin', 'CryptoBoxRootActions') ], - classifiers = [ - 'Development Status :: 4 - Beta', - 'Environment :: Web Environment', - 'Intended Audience :: End Users/Desktop', - 'Intended Audience :: System Administrators', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'Topic :: System :: Systems Administration', - 'Operating System :: POSIX', - 'Operating System :: Unix', - 'Programming Language :: Python'], - ) + name = 'cryptobox-server', + version = '0.3.0', + description = 'webinterface for handling encrypted disks', + author = 'Sense.Lab e.V.', + author_email = 'info@cryptobox.org', + maintainer = 'Lars Kruse', + maintainer_email = 'devel@sumpfralle.de', + license = 'GPL', + url = 'http://cryptobox.org', + packages = [ 'cryptobox', 'cryptobox.core', 'cryptobox.web', + 'cryptobox.plugins', 'cryptobox.tests' ], + data_files = getdatafiles(datadir, ['templates', 'www-data', 'plugins']) + + getdatafiles(confdir, [os.path.join('conf-examples', 'cryptobox.conf')]) + + getdatafiles(os.path.join(confdir, 'events.d'), [ + os.path.join('event-scripts', 'README'), + os.path.join('event-scripts', '_event_scripts_')]) + + getdatafiles(docdir, ['conf-examples', 'event-scripts', 'README', 'changelog', + 'LICENSE', 'copyright', os.path.join('doc', 'html'), 'README.davfs', + 'README.samba', 'README.proxy', 'README.ssl' ]) + + getdatafiles(os.path.join(docdir, 'conf-examples'), + [os.path.join('debian', 'cryptobox-server.init')]) + + get_language_files(os.path.join('share', 'locale')), + package_dir = { '': 'src' }, + scripts = [ os.path.join('bin', 'CryptoBoxWebserver'), + os.path.join('bin', 'CryptoBoxRootActions') ], + classifiers = [ + 'Development Status :: 4 - Beta', + 'Environment :: Web Environment', + 'Intended Audience :: End Users/Desktop', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Topic :: System :: Systems Administration', + 'Operating System :: POSIX', + 'Operating System :: Unix', + 'Programming Language :: Python'], + ) diff --git a/src/cryptobox/core/container.py b/src/cryptobox/core/container.py index 1d60199..c5f13c5 100644 --- a/src/cryptobox/core/container.py +++ b/src/cryptobox/core/container.py @@ -31,15 +31,15 @@ from cryptobox.core.exceptions import * CONTAINERTYPES = { - "unused":0, - "plain":1, - "luks":2, - "swap":3, - } + "unused":0, + "plain":1, + "luks":2, + "swap":3, + } FSTYPES = { - "plain":["ext3", "ext2", "vfat", "reiserfs", "xfs", "hfs", "jfs", "minix", "ntfs"], - "swap":["swap"]} + "plain":["ext3", "ext2", "vfat", "reiserfs", "xfs", "hfs", "jfs", "minix", "ntfs"], + "swap":["swap"]} ## we use this marker to make sure, that we do not remove a non-cryptobox directory @@ -48,754 +48,754 @@ MOUNT_DIR_MARKER = '_cryptobox_mount_dir_' class CryptoBoxContainer: - """Manage a container of the CryptoBox - """ - - __dmDir = "/dev/mapper" - - - def __init__(self, device, cbox): - """initialize the container - - "device" is a cryptobox.core.blockdevice object - """ - self.device = device - self.cbox = cbox - self.uuid = None - self.name = None - self.cont_type = None - self.fs_type = None - self.mount = None - self.umount = None - self.attributes = None - self.reset_object() - - - def get_name(self): - """Return a humanly readable name for the container. - - Available since: 0.3.0 - """ - return self.name - - - 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). - Available since: 0.3.0 - """ - try: - ## is there already an entry in the database? - self.attributes = self.cbox.prefs.volumes_db[self.get_name()] - self.attributes["uuid"] = self.uuid - except KeyError: - ## set default values - self.attributes = { "uuid": self.uuid } - self.cbox.prefs.volumes_db[self.get_name()] = self.attributes - - - def set_name(self, new_name): - """Define a human readable name of this container. - - this also manages the name database - Available since: 0.3.0 - """ - old_name = self.get_name() - if new_name == self.name: - return - ## renaming is not possible, if the volume is active, as the mountpoint name - ## is the same as the volume name - if self.is_mounted(): - raise CBVolumeIsActive("the container must not be active during renaming") - if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name): - raise CBInvalidName("the supplied new name contains illegal characters") - ## check for another partition with the same name - if self.cbox.get_container_list(filter_name=new_name): - raise CBNameIsInUse("the supplied new name is already in use for another partition") - ## maybe there a is an entry in the volumes database (but the partition is not active) - try: - ## remove possibly existing inactive database item - del self.cbox.prefs.volumes_db[new_name] - except KeyError: - ## no entry - so nothing happens - pass - ## set new name - self.name = new_name - ## remove old database entry - try: - del self.cbox.prefs.volumes_db[old_name] - except KeyError: - pass - ## set new volumes database entry - self.cbox.prefs.volumes_db[new_name] = self.attributes - try: - self.cbox.prefs.volumes_db.write() - except IOError: - self.cbox.log.warn("Failed to store volumes database after set_name") - - - def is_writeable(self): - """Return if the container is writeable - - this only affects actions like formatting or partitioning - write access for the mounted content is not considered - Available since: 0.3.3 - """ - ## symlinks are followed automatically - return os.access(self.get_device(), os.W_OK) - - - def get_device(self): - """Return the device name of the container - - e.g.: /dev/hdc1 - Available since: 0.3.0 - """ - return self.device.devnodes[0] - - - def get_type(self): - """Return the type (int) of this container. - - Available since: 0.3.0 - """ - return self.cont_type - - - def get_fs_type(self): - """Return the filesystem type of this container. - - Available since: 0.3.0 - """ - return self.fs_type - - - def is_mounted(self): - """Check if the container is currently mounted. - - Available since: 0.3.0 - """ - return os.path.ismount(self.__get_mount_point()) - - - def get_capacity(self): - """Return the current capacity state of the volume. - - the volume may not be mounted - the result is a tuple of values in megabyte: - (size, available, used) - Available since: 0.3.0 - """ - info = os.statvfs(self.__get_mount_point()) - return ( - int(info.f_bsize*info.f_blocks/1024/1024), - int(info.f_bsize*info.f_bavail/1024/1024), - int(info.f_bsize*(info.f_blocks-info.f_bavail)/1024/1024)) - - - def get_size(self): - """return the size of the block device (_not_ of the filesystem) - - the result is a value in megabyte - an error is indicated by "-1" - Available since: 0.3.0 - """ - return self.device.size - - - def reset_object(self): - """ recheck the information about this container - - this is especially useful after changing the type via 'create' - Available since: 0.3.0 - """ - self.device.reset() - self.uuid = self.__get_uuid() - self.cont_type = self.__get_type_of_partition() - self.fs_type = self.__get_fs_type() - self.name = self.__get_name_of_container() - self.__set_attributes() - if self.cont_type == CONTAINERTYPES["luks"]: - self.mount = self.__mount_luks - self.umount = self.__umount_luks - elif self.cont_type == CONTAINERTYPES["plain"]: - self.mount = self.__mount_plain - self.umount = self.__umount_plain - - - def create(self, cont_type, password=None, fs_type="ext3"): - """Format a container. - - Also set a password for encrypted container. - Available since: 0.3.0 - """ - if not fs_type in FSTYPES["plain"]: - raise CBInvalidType("invalid filesystem type supplied: %s" % str(fs_type)) - old_name = self.get_name() - if cont_type == CONTAINERTYPES["luks"]: - self.__create_luks(password, fs_type) - elif cont_type == CONTAINERTYPES["plain"]: - self.__create_plain(fs_type) - else: - raise CBInvalidType("invalid container type (%d) supplied" % (cont_type, )) - ## no exception was raised during creation -> we can continue - ## reset the properties (encryption state, ...) of the device - self.reset_object() - ## restore the old name (must be after reset_object) - try: - self.set_name(old_name) - except CBNameIsInUse: - ## failure is okay - pass - - - def change_password(self, oldpw, newpw): - """Change the password of an encrypted container. - - Raises an exception for plaintext container. - Available since: 0.3.0 - """ - if self.cont_type != CONTAINERTYPES["luks"]: - raise CBInvalidType("changing of password is possible only for luks containers") - if not oldpw: - raise CBInvalidPassword("no old password supplied for password change") - if not newpw: - raise CBInvalidPassword("no new password supplied for password change") - ## return if new and old passwords are the same - if oldpw == newpw: - return - if self.is_mounted(): - raise CBVolumeIsActive("this container is currently active") - ## remove any potential open luks mapping - self.__umount_luks() - ## create the luks header - proc = subprocess.Popen( - shell = False, - stdin = subprocess.PIPE, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "program", "cryptsetup", - "luksAddKey", - self.get_device(), - "--batch-mode"]) - proc.stdin.write("%s\n%s" % (oldpw, newpw)) - (output, errout) = proc.communicate() - if proc.returncode != 0: - error_msg = "Could not add a new luks key: %s - %s" \ - % (output.strip(), errout.strip(), ) - self.cbox.log.error(error_msg) - raise CBChangePasswordError(error_msg) - ## retrieve the key slot we used for unlocking - keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups() - if keys_found: - keyslot = int(keys_found[0]) - else: - raise CBChangePasswordError("could not get the old key slot") - ## remove the old key - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["cryptsetup"], - "--batch-mode", - "luksDelKey", - self.get_device(), - "%d" % (keyslot, )]) - proc.wait() - if proc.returncode != 0: - error_msg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), ) - self.cbox.log.error(error_msg) - raise CBChangePasswordError(error_msg) - - - def is_busy(self): - """Return the current state of the busy flag of this device. - - The busy flag is mainly used to indicate that the device may not be used - while it is being formatted or similar. - Available since: 0.3.1 - """ - return self.cbox.get_device_busy_state(self.device.name) - - - def set_busy(self, new_state, timeout=300): - """Set the busy state of this device. - - Either set or remove this flag. - The timeout is optional and defaults to five minutes. - Available since: 0.3.1 - """ - self.cbox.set_device_busy_state(self.device.name, new_state, timeout) - - - ## ****************** internal stuff ********************* - - def __get_name_of_container(self): - """retrieve the name of the container by querying the database - call this function only for the initial setup of the container object""" - found_name = None - for key in self.cbox.prefs.volumes_db.keys(): - if self.cbox.prefs.volumes_db[key]["uuid"] == self.uuid: - found_name = key - if found_name: - return found_name - ## there is no name defined for this uuid - we will propose a good one - prefix = self.cbox.prefs["Main"]["DefaultVolumePrefix"] - unused_found = False - counter = 1 - while not unused_found: - guess = prefix + str(counter) - if self.cbox.prefs.volumes_db.has_key(guess): - counter += 1 - else: - unused_found = True - return guess - - - def __get_uuid(self): - """Retrieve the uuid of the container device. - """ - guess = self.device.uuid - ## did we get a valid value? - if guess: - return guess - else: - ## emergency default value - return self.device.name - - - def __get_type_of_partition(self): - """Retrieve the type of the given partition. - - see cryptobox.core.container.CONTAINERTYPES - """ - if self.device.is_luks(): - return CONTAINERTYPES["luks"] - type_of_partition = self.__get_type_id_of_partition() - if type_of_partition in FSTYPES["plain"]: - return CONTAINERTYPES["plain"] - if type_of_partition in FSTYPES["swap"]: - return CONTAINERTYPES["swap"] - return CONTAINERTYPES["unused"] - - - def __get_type_id_of_partition(self): - "returns the type of the partition (see 'man blkid')" - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ self.cbox.prefs["Programs"]["blkid"], - "-s", "TYPE", - "-o", "value", - "-c", os.devnull, - "-w", os.devnull, - self.get_device() ]) - (stdout, stderr) = proc.communicate() - if proc.returncode == 0: - ## we found a uuid - return stdout.strip() - elif proc.returncode == 2: - ## failed to find the attribute - no problem - return None - else: - ## something strange happened - self.cbox.log.warn("retrieving of partition type via 'blkid' failed: %s" % \ - (stderr.strip(), )) - return None - - - def __get_fs_type(self): - "returns the filesystem used on a container" - ## should we handle device mapping or plain device - if self.device.is_luks() and self.name: - #TODO: replace this by self.device.holders ... - container = os.path.join(self.__dmDir, self.name) - ## can't determine fs while encrypted - if not self.is_mounted(): - return "unavailable" - else: - container = self.get_device() - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ self.cbox.prefs["Programs"]["blkid"], - "-s","TYPE", - container ]) - (stdout, stderr) = proc.communicate() - if proc.returncode == 0: - return stdout.split("TYPE=")[1] - else: - ## if something goes wrong don't dig deeper - self.cbox.log.warn("Filesystem determination (%s) failed: %s" % \ - (self.get_device(), stderr.strip())) - return "undetermined" - return - - - def __get_mount_point(self): - "return the name of the mountpoint of this volume" - return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], self.name) - - - def __mount_luks(self, password): - "mount a luks partition" - if not password: - raise CBInvalidPassword("no password supplied for luksOpen") - if self.is_mounted(): - raise CBVolumeIsActive("this container is already active") - self.__umount_luks() - self.__clean_mount_dirs() - if not os.path.exists(self.__get_mount_point()): - self.__create_mount_directory(self.__get_mount_point()) - if not os.path.exists(self.__get_mount_point()): - err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), ) - self.cbox.log.error(err_msg) - raise CBMountError(err_msg) - self.cbox.send_event_notification("premount", self.__get_event_args()) - proc = subprocess.Popen( - shell = False, - stdin = subprocess.PIPE, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "program", "cryptsetup", - "luksOpen", - self.get_device(), - self.name, - "--batch-mode"]) - proc.stdin.write(password) - (output, errout) = proc.communicate() - if proc.returncode != 0: - err_msg = "Could not open the luks mapping: %s" % (errout.strip(), ) - self.cbox.log.warn(err_msg) - raise CBMountError(err_msg) - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "program", "mount", - os.path.join(self.__dmDir, self.name), - self.__get_mount_point()]) - proc.wait() - if proc.returncode != 0: - err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) - self.cbox.log.warn(err_msg) - raise CBMountError(err_msg) - ## chmod the mount directory to 0777 - this is the easy way to avoid problems - ## this only works for ext2/3 - vfat silently ignore it - ## we mounted vfat partitions with umask=0000 - try: - os.chmod(self.__get_mount_point(), 0777) - except OSError: - self.cbox.log.warn("Failed to set write permission for the mount directory") - self.cbox.send_event_notification("postmount", self.__get_event_args()) - - - def __umount_luks(self): - "umount a luks partition" - self.cbox.send_event_notification("preumount", self.__get_event_args()) - if self.is_mounted(): - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "program", "umount", - self.__get_mount_point()]) - proc.wait() - if proc.returncode != 0: - err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) - self.cbox.log.warn(err_msg) - raise CBUmountError(err_msg) - if os.path.exists(os.path.join(self.__dmDir, self.name)): - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "program", "cryptsetup", - "luksClose", - self.name, - "--batch-mode"]) - proc.wait() - if proc.returncode != 0: - err_msg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), ) - self.cbox.log.warn(err_msg) - raise CBUmountError(err_msg) - self.cbox.send_event_notification("postumount", self.__get_event_args()) - - - def __mount_plain(self): - "mount a plaintext partition" - if self.is_mounted(): - raise CBVolumeIsActive("this container is already active") - self.__clean_mount_dirs() - if not os.path.exists(self.__get_mount_point()): - self.__create_mount_directory(self.__get_mount_point()) - if not os.path.exists(self.__get_mount_point()): - err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), ) - self.cbox.log.error(err_msg) - raise CBMountError(err_msg) - self.cbox.send_event_notification("premount", self.__get_event_args()) - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "program", "mount", - self.get_device(), - self.__get_mount_point()]) - proc.wait() - if proc.returncode != 0: - err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) - self.cbox.log.warn(err_msg) - raise CBMountError(err_msg) - ## chmod the mount directory to 0777 - this is the easy way to avoid problems - ## this only works for ext2/3 - vfat silently ignore it - ## we mounted vfat partitions with umask=0000 - try: - os.chmod(self.__get_mount_point(), 0777) - except OSError: - self.cbox.log.warn("Failed to set write permission for the mount directory") - self.cbox.send_event_notification("postmount", self.__get_event_args()) - - - def __umount_plain(self): - "umount a plaintext partition" - if not self.is_mounted(): - self.cbox.log.info("trying to umount while volume (%s) is not mounted" % \ - self.get_device()) - return - self.cbox.send_event_notification("preumount", self.__get_event_args()) - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "program", "umount", - self.__get_mount_point()]) - proc.wait() - if proc.returncode != 0: - err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) - self.cbox.log.warn(err_msg) - raise CBUmountError(err_msg) - self.cbox.send_event_notification("postumount", self.__get_event_args()) - - - def __create_plain(self, fs_type="ext3"): - "make a plaintext partition" - import threading - if self.is_mounted(): - raise CBVolumeIsActive( - "deactivate the partition before filesystem initialization") - def format(): - """This function will get called as a seperate thread. - - To avoid the non-sharing cpu distribution between the formatting thread - and the main interface, we fork and let the parent wait for the child. - This should be handled using the kernel's threading features. - """ - ## create a local object - to store different values for each thread - loc_data = threading.local() - loc_data.old_name = self.get_name() - self.set_busy(True, 600) - ## give the main thread a chance to continue - loc_data.child_pid = os.fork() - if loc_data.child_pid == 0: - loc_data.proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["nice"], - self.cbox.prefs["Programs"]["mkfs"], - "-t", fs_type, self.get_device()]) - loc_data.proc.wait() - ## wait to allow error detection - if loc_data.proc.returncode == 0: - time.sleep(5) - ## skip cleanup stuff (as common for sys.exit) - os._exit(0) - else: - os.waitpid(loc_data.child_pid, 0) - try: - self.set_name(loc_data.old_name) - except CBNameIsInUse: - pass - self.set_busy(False) - bg_task = threading.Thread(target=format) - bg_task.setDaemon(True) - bg_task.start() - time.sleep(3) - ## if the thread exited very fast, then it failed - if not bg_task.isAlive(): - raise CBCreateError("formatting of device (%s) failed out " % \ + """Manage a container of the CryptoBox + """ + + __dmDir = "/dev/mapper" + + + def __init__(self, device, cbox): + """initialize the container + + "device" is a cryptobox.core.blockdevice object + """ + self.device = device + self.cbox = cbox + self.uuid = None + self.name = None + self.cont_type = None + self.fs_type = None + self.mount = None + self.umount = None + self.attributes = None + self.reset_object() + + + def get_name(self): + """Return a humanly readable name for the container. + + Available since: 0.3.0 + """ + return self.name + + + 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). + Available since: 0.3.0 + """ + try: + ## is there already an entry in the database? + self.attributes = self.cbox.prefs.volumes_db[self.get_name()] + self.attributes["uuid"] = self.uuid + except KeyError: + ## set default values + self.attributes = { "uuid": self.uuid } + self.cbox.prefs.volumes_db[self.get_name()] = self.attributes + + + def set_name(self, new_name): + """Define a human readable name of this container. + + this also manages the name database + Available since: 0.3.0 + """ + old_name = self.get_name() + if new_name == self.name: + return + ## renaming is not possible, if the volume is active, as the mountpoint name + ## is the same as the volume name + if self.is_mounted(): + raise CBVolumeIsActive("the container must not be active during renaming") + if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name): + raise CBInvalidName("the supplied new name contains illegal characters") + ## check for another partition with the same name + if self.cbox.get_container_list(filter_name=new_name): + raise CBNameIsInUse("the supplied new name is already in use for another partition") + ## maybe there a is an entry in the volumes database (but the partition is not active) + try: + ## remove possibly existing inactive database item + del self.cbox.prefs.volumes_db[new_name] + except KeyError: + ## no entry - so nothing happens + pass + ## set new name + self.name = new_name + ## remove old database entry + try: + del self.cbox.prefs.volumes_db[old_name] + except KeyError: + pass + ## set new volumes database entry + self.cbox.prefs.volumes_db[new_name] = self.attributes + try: + self.cbox.prefs.volumes_db.write() + except IOError: + self.cbox.log.warn("Failed to store volumes database after set_name") + + + def is_writeable(self): + """Return if the container is writeable + + this only affects actions like formatting or partitioning + write access for the mounted content is not considered + Available since: 0.3.3 + """ + ## symlinks are followed automatically + return os.access(self.get_device(), os.W_OK) + + + def get_device(self): + """Return the device name of the container + + e.g.: /dev/hdc1 + Available since: 0.3.0 + """ + return self.device.devnodes[0] + + + def get_type(self): + """Return the type (int) of this container. + + Available since: 0.3.0 + """ + return self.cont_type + + + def get_fs_type(self): + """Return the filesystem type of this container. + + Available since: 0.3.0 + """ + return self.fs_type + + + def is_mounted(self): + """Check if the container is currently mounted. + + Available since: 0.3.0 + """ + return os.path.ismount(self.__get_mount_point()) + + + def get_capacity(self): + """Return the current capacity state of the volume. + + the volume may not be mounted + the result is a tuple of values in megabyte: + (size, available, used) + Available since: 0.3.0 + """ + info = os.statvfs(self.__get_mount_point()) + return ( + int(info.f_bsize*info.f_blocks/1024/1024), + int(info.f_bsize*info.f_bavail/1024/1024), + int(info.f_bsize*(info.f_blocks-info.f_bavail)/1024/1024)) + + + def get_size(self): + """return the size of the block device (_not_ of the filesystem) + + the result is a value in megabyte + an error is indicated by "-1" + Available since: 0.3.0 + """ + return self.device.size + + + def reset_object(self): + """recheck the information about this container + + this is especially useful after changing the type via 'create' + Available since: 0.3.0 + """ + self.device.reset() + self.uuid = self.__get_uuid() + self.cont_type = self.__get_type_of_partition() + self.fs_type = self.__get_fs_type() + self.name = self.__get_name_of_container() + self.__set_attributes() + if self.cont_type == CONTAINERTYPES["luks"]: + self.mount = self.__mount_luks + self.umount = self.__umount_luks + elif self.cont_type == CONTAINERTYPES["plain"]: + self.mount = self.__mount_plain + self.umount = self.__umount_plain + + + def create(self, cont_type, password=None, fs_type="ext3"): + """Format a container. + + Also set a password for encrypted container. + Available since: 0.3.0 + """ + if not fs_type in FSTYPES["plain"]: + raise CBInvalidType("invalid filesystem type supplied: %s" % str(fs_type)) + old_name = self.get_name() + if cont_type == CONTAINERTYPES["luks"]: + self.__create_luks(password, fs_type) + elif cont_type == CONTAINERTYPES["plain"]: + self.__create_plain(fs_type) + else: + raise CBInvalidType("invalid container type (%d) supplied" % (cont_type, )) + ## no exception was raised during creation -> we can continue + ## reset the properties (encryption state, ...) of the device + self.reset_object() + ## restore the old name (must be after reset_object) + try: + self.set_name(old_name) + except CBNameIsInUse: + ## failure is okay + pass + + + def change_password(self, oldpw, newpw): + """Change the password of an encrypted container. + + Raises an exception for plaintext container. + Available since: 0.3.0 + """ + if self.cont_type != CONTAINERTYPES["luks"]: + raise CBInvalidType("changing of password is possible only for luks containers") + if not oldpw: + raise CBInvalidPassword("no old password supplied for password change") + if not newpw: + raise CBInvalidPassword("no new password supplied for password change") + ## return if new and old passwords are the same + if oldpw == newpw: + return + if self.is_mounted(): + raise CBVolumeIsActive("this container is currently active") + ## remove any potential open luks mapping + self.__umount_luks() + ## create the luks header + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "program", "cryptsetup", + "luksAddKey", + self.get_device(), + "--batch-mode"]) + proc.stdin.write("%s\n%s" % (oldpw, newpw)) + (output, errout) = proc.communicate() + if proc.returncode != 0: + error_msg = "Could not add a new luks key: %s - %s" \ + % (output.strip(), errout.strip(), ) + self.cbox.log.error(error_msg) + raise CBChangePasswordError(error_msg) + ## retrieve the key slot we used for unlocking + keys_found = re.search(r'key slot (\d{1,3}) unlocked', output).groups() + if keys_found: + keyslot = int(keys_found[0]) + else: + raise CBChangePasswordError("could not get the old key slot") + ## remove the old key + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["cryptsetup"], + "--batch-mode", + "luksDelKey", + self.get_device(), + "%d" % (keyslot, )]) + proc.wait() + if proc.returncode != 0: + error_msg = "Could not remove the old luks key: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.error(error_msg) + raise CBChangePasswordError(error_msg) + + + def is_busy(self): + """Return the current state of the busy flag of this device. + + The busy flag is mainly used to indicate that the device may not be used + while it is being formatted or similar. + Available since: 0.3.1 + """ + return self.cbox.get_device_busy_state(self.device.name) + + + def set_busy(self, new_state, timeout=300): + """Set the busy state of this device. + + Either set or remove this flag. + The timeout is optional and defaults to five minutes. + Available since: 0.3.1 + """ + self.cbox.set_device_busy_state(self.device.name, new_state, timeout) + + + ## ****************** internal stuff ********************* + + def __get_name_of_container(self): + """retrieve the name of the container by querying the database + call this function only for the initial setup of the container object""" + found_name = None + for key in self.cbox.prefs.volumes_db.keys(): + if self.cbox.prefs.volumes_db[key]["uuid"] == self.uuid: + found_name = key + if found_name: + return found_name + ## there is no name defined for this uuid - we will propose a good one + prefix = self.cbox.prefs["Main"]["DefaultVolumePrefix"] + unused_found = False + counter = 1 + while not unused_found: + guess = prefix + str(counter) + if self.cbox.prefs.volumes_db.has_key(guess): + counter += 1 + else: + unused_found = True + return guess + + + def __get_uuid(self): + """Retrieve the uuid of the container device. + """ + guess = self.device.uuid + ## did we get a valid value? + if guess: + return guess + else: + ## emergency default value + return self.device.name + + + def __get_type_of_partition(self): + """Retrieve the type of the given partition. + + see cryptobox.core.container.CONTAINERTYPES + """ + if self.device.is_luks(): + return CONTAINERTYPES["luks"] + type_of_partition = self.__get_type_id_of_partition() + if type_of_partition in FSTYPES["plain"]: + return CONTAINERTYPES["plain"] + if type_of_partition in FSTYPES["swap"]: + return CONTAINERTYPES["swap"] + return CONTAINERTYPES["unused"] + + + def __get_type_id_of_partition(self): + "returns the type of the partition (see 'man blkid')" + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ self.cbox.prefs["Programs"]["blkid"], + "-s", "TYPE", + "-o", "value", + "-c", os.devnull, + "-w", os.devnull, + self.get_device() ]) + (stdout, stderr) = proc.communicate() + if proc.returncode == 0: + ## we found a uuid + return stdout.strip() + elif proc.returncode == 2: + ## failed to find the attribute - no problem + return None + else: + ## something strange happened + self.cbox.log.warn("retrieving of partition type via 'blkid' failed: %s" % \ + (stderr.strip(), )) + return None + + + def __get_fs_type(self): + "returns the filesystem used on a container" + ## should we handle device mapping or plain device + if self.device.is_luks() and self.name: + #TODO: replace this by self.device.holders ... + container = os.path.join(self.__dmDir, self.name) + ## can't determine fs while encrypted + if not self.is_mounted(): + return "unavailable" + else: + container = self.get_device() + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ self.cbox.prefs["Programs"]["blkid"], + "-s","TYPE", + container ]) + (stdout, stderr) = proc.communicate() + if proc.returncode == 0: + return stdout.split("TYPE=")[1] + else: + ## if something goes wrong don't dig deeper + self.cbox.log.warn("Filesystem determination (%s) failed: %s" % \ + (self.get_device(), stderr.strip())) + return "undetermined" + return + + + def __get_mount_point(self): + "return the name of the mountpoint of this volume" + return os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], self.name) + + + def __mount_luks(self, password): + "mount a luks partition" + if not password: + raise CBInvalidPassword("no password supplied for luksOpen") + if self.is_mounted(): + raise CBVolumeIsActive("this container is already active") + self.__umount_luks() + self.__clean_mount_dirs() + if not os.path.exists(self.__get_mount_point()): + self.__create_mount_directory(self.__get_mount_point()) + if not os.path.exists(self.__get_mount_point()): + err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), ) + self.cbox.log.error(err_msg) + raise CBMountError(err_msg) + self.cbox.send_event_notification("premount", self.__get_event_args()) + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "program", "cryptsetup", + "luksOpen", + self.get_device(), + self.name, + "--batch-mode"]) + proc.stdin.write(password) + (output, errout) = proc.communicate() + if proc.returncode != 0: + err_msg = "Could not open the luks mapping: %s" % (errout.strip(), ) + self.cbox.log.warn(err_msg) + raise CBMountError(err_msg) + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "program", "mount", + os.path.join(self.__dmDir, self.name), + self.__get_mount_point()]) + proc.wait() + if proc.returncode != 0: + err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBMountError(err_msg) + ## chmod the mount directory to 0777 - this is the easy way to avoid problems + ## this only works for ext2/3 - vfat silently ignore it + ## we mounted vfat partitions with umask=0000 + try: + os.chmod(self.__get_mount_point(), 0777) + except OSError: + self.cbox.log.warn("Failed to set write permission for the mount directory") + self.cbox.send_event_notification("postmount", self.__get_event_args()) + + + def __umount_luks(self): + "umount a luks partition" + self.cbox.send_event_notification("preumount", self.__get_event_args()) + if self.is_mounted(): + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "program", "umount", + self.__get_mount_point()]) + proc.wait() + if proc.returncode != 0: + err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBUmountError(err_msg) + if os.path.exists(os.path.join(self.__dmDir, self.name)): + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "program", "cryptsetup", + "luksClose", + self.name, + "--batch-mode"]) + proc.wait() + if proc.returncode != 0: + err_msg = "Could not remove the luks mapping: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBUmountError(err_msg) + self.cbox.send_event_notification("postumount", self.__get_event_args()) + + + def __mount_plain(self): + "mount a plaintext partition" + if self.is_mounted(): + raise CBVolumeIsActive("this container is already active") + self.__clean_mount_dirs() + if not os.path.exists(self.__get_mount_point()): + self.__create_mount_directory(self.__get_mount_point()) + if not os.path.exists(self.__get_mount_point()): + err_msg = "Could not create mountpoint (%s)" % (self.__get_mount_point(), ) + self.cbox.log.error(err_msg) + raise CBMountError(err_msg) + self.cbox.send_event_notification("premount", self.__get_event_args()) + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "program", "mount", + self.get_device(), + self.__get_mount_point()]) + proc.wait() + if proc.returncode != 0: + err_msg = "Could not mount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBMountError(err_msg) + ## chmod the mount directory to 0777 - this is the easy way to avoid problems + ## this only works for ext2/3 - vfat silently ignore it + ## we mounted vfat partitions with umask=0000 + try: + os.chmod(self.__get_mount_point(), 0777) + except OSError: + self.cbox.log.warn("Failed to set write permission for the mount directory") + self.cbox.send_event_notification("postmount", self.__get_event_args()) + + + def __umount_plain(self): + "umount a plaintext partition" + if not self.is_mounted(): + self.cbox.log.info("trying to umount while volume (%s) is not mounted" % \ + self.get_device()) + return + self.cbox.send_event_notification("preumount", self.__get_event_args()) + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "program", "umount", + self.__get_mount_point()]) + proc.wait() + if proc.returncode != 0: + err_msg = "Could not umount the filesystem: %s" % (proc.stderr.read().strip(), ) + self.cbox.log.warn(err_msg) + raise CBUmountError(err_msg) + self.cbox.send_event_notification("postumount", self.__get_event_args()) + + + def __create_plain(self, fs_type="ext3"): + "make a plaintext partition" + import threading + if self.is_mounted(): + raise CBVolumeIsActive( + "deactivate the partition before filesystem initialization") + def format(): + """This function will get called as a seperate thread. + + To avoid the non-sharing cpu distribution between the formatting thread + and the main interface, we fork and let the parent wait for the child. + This should be handled using the kernel's threading features. + """ + ## create a local object - to store different values for each thread + loc_data = threading.local() + loc_data.old_name = self.get_name() + self.set_busy(True, 600) + ## give the main thread a chance to continue + loc_data.child_pid = os.fork() + if loc_data.child_pid == 0: + loc_data.proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["nice"], + self.cbox.prefs["Programs"]["mkfs"], + "-t", fs_type, self.get_device()]) + loc_data.proc.wait() + ## wait to allow error detection + if loc_data.proc.returncode == 0: + time.sleep(5) + ## skip cleanup stuff (as common for sys.exit) + os._exit(0) + else: + os.waitpid(loc_data.child_pid, 0) + try: + self.set_name(loc_data.old_name) + except CBNameIsInUse: + pass + self.set_busy(False) + bg_task = threading.Thread(target=format) + bg_task.setDaemon(True) + bg_task.start() + time.sleep(3) + ## if the thread exited very fast, then it failed + if not bg_task.isAlive(): + raise CBCreateError("formatting of device (%s) failed out " % \ self.get_device() + "of unknown reasons") - def __create_luks(self, password, fs_type="ext3"): - """Create a luks partition. - """ - import threading - if not password: - raise CBInvalidPassword("no password supplied for new luks mapping") - if self.is_mounted(): - raise CBVolumeIsActive("deactivate the partition before filesystem initialization") - ## remove any potential open luks mapping - self.__umount_luks() - ## create the luks header - proc = subprocess.Popen( - shell = False, - stdin = subprocess.PIPE, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "program", "cryptsetup", - "luksFormat", - self.get_device(), - "--batch-mode", - "--cipher", self.cbox.prefs["Main"]["DefaultCipher"], - "--iter-time", "2000"]) - proc.stdin.write(password) - (output, errout) = proc.communicate() - if proc.returncode != 0: - err_msg = "Could not create the luks header: %s" % (errout.strip(), ) - self.cbox.log.error(err_msg) - raise CBCreateError(err_msg) - ## open the luks container for mkfs - proc = subprocess.Popen( - shell = False, - stdin = subprocess.PIPE, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["super"], - self.cbox.prefs["Programs"]["CryptoBoxRootActions"], - "program", "cryptsetup", - "luksOpen", - self.get_device(), - self.name, - "--batch-mode"]) - proc.stdin.write(password) - (output, errout) = proc.communicate() - if proc.returncode != 0: - err_msg = "Could not open the new luks mapping: %s" % (errout.strip(), ) - self.cbox.log.error(err_msg) - raise CBCreateError(err_msg) - def format_luks(): - """This function will get called as a seperate thread. + def __create_luks(self, password, fs_type="ext3"): + """Create a luks partition. + """ + import threading + if not password: + raise CBInvalidPassword("no password supplied for new luks mapping") + if self.is_mounted(): + raise CBVolumeIsActive("deactivate the partition before filesystem initialization") + ## remove any potential open luks mapping + self.__umount_luks() + ## create the luks header + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "program", "cryptsetup", + "luksFormat", + self.get_device(), + "--batch-mode", + "--cipher", self.cbox.prefs["Main"]["DefaultCipher"], + "--iter-time", "2000"]) + proc.stdin.write(password) + (output, errout) = proc.communicate() + if proc.returncode != 0: + err_msg = "Could not create the luks header: %s" % (errout.strip(), ) + self.cbox.log.error(err_msg) + raise CBCreateError(err_msg) + ## open the luks container for mkfs + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["super"], + self.cbox.prefs["Programs"]["CryptoBoxRootActions"], + "program", "cryptsetup", + "luksOpen", + self.get_device(), + self.name, + "--batch-mode"]) + proc.stdin.write(password) + (output, errout) = proc.communicate() + if proc.returncode != 0: + err_msg = "Could not open the new luks mapping: %s" % (errout.strip(), ) + self.cbox.log.error(err_msg) + raise CBCreateError(err_msg) + def format_luks(): + """This function will get called as a seperate thread. - To avoid the non-sharing cpu distribution between the formatting thread - and the main interface, we fork and let the parent wait for the child. - This should be handled using the kernel's threading features. - """ - ## create a local object - to store different values for each thread - loc_data = threading.local() - loc_data.old_name = self.get_name() - self.set_busy(True, 600) - loc_data.child_pid = os.fork() - if loc_data.child_pid == 0: - ## make the filesystem - loc_data.proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["nice"], - self.cbox.prefs["Programs"]["mkfs"], - "-t", fs_type, - os.path.join(self.__dmDir, self.name)]) - loc_data.proc.wait() - ## wait to allow error detection - if loc_data.proc.returncode == 0: - time.sleep(5) - ## skip cleanup stuff (as common for sys.exit) - os._exit(0) - else: - os.waitpid(loc_data.child_pid, 0) - self.set_name(loc_data.old_name) - self.set_busy(False) - ## remove the mapping - for every exit status - self.__umount_luks() - bg_task = threading.Thread(target=format_luks) - bg_task.setDaemon(True) - bg_task.start() - time.sleep(3) - ## if the thread exited very fast, then it failed - if not bg_task.isAlive(): - raise CBCreateError("formatting of device (%s) failed out " \ + To avoid the non-sharing cpu distribution between the formatting thread + and the main interface, we fork and let the parent wait for the child. + This should be handled using the kernel's threading features. + """ + ## create a local object - to store different values for each thread + loc_data = threading.local() + loc_data.old_name = self.get_name() + self.set_busy(True, 600) + loc_data.child_pid = os.fork() + if loc_data.child_pid == 0: + ## make the filesystem + loc_data.proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["nice"], + self.cbox.prefs["Programs"]["mkfs"], + "-t", fs_type, + os.path.join(self.__dmDir, self.name)]) + loc_data.proc.wait() + ## wait to allow error detection + if loc_data.proc.returncode == 0: + time.sleep(5) + ## skip cleanup stuff (as common for sys.exit) + os._exit(0) + else: + os.waitpid(loc_data.child_pid, 0) + self.set_name(loc_data.old_name) + self.set_busy(False) + ## remove the mapping - for every exit status + self.__umount_luks() + bg_task = threading.Thread(target=format_luks) + bg_task.setDaemon(True) + bg_task.start() + time.sleep(3) + ## if the thread exited very fast, then it failed + if not bg_task.isAlive(): + raise CBCreateError("formatting of device (%s) failed out " \ % self.get_device() + "of unknown reasons") - def __clean_mount_dirs(self): - """ remove all unnecessary subdirs of the mount parent directory - this should be called for every (u)mount """ - subdirs = os.listdir(self.cbox.prefs["Locations"]["MountParentDir"]) - for one_dir in subdirs: - abs_dir = os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], one_dir) - if (not os.path.islink(abs_dir)) \ - and os.path.isdir(abs_dir) \ - and (not os.path.ismount(abs_dir)) \ - and (os.path.isfile(os.path.join(abs_dir,MOUNT_DIR_MARKER))) \ - and (len(os.listdir(abs_dir)) == 1): - try: - os.remove(os.path.join(abs_dir, MOUNT_DIR_MARKER)) - os.rmdir(abs_dir) - except OSError, err_msg: - ## we do not care too much about unclean cleaning ... - self.cbox.log.info("failed to clean a mountpoint (%s): %s" % \ - (abs_dir, str(err_msg))) - + def __clean_mount_dirs(self): + """remove all unnecessary subdirs of the mount parent directory + this should be called for every (u)mount """ + subdirs = os.listdir(self.cbox.prefs["Locations"]["MountParentDir"]) + for one_dir in subdirs: + abs_dir = os.path.join(self.cbox.prefs["Locations"]["MountParentDir"], one_dir) + if (not os.path.islink(abs_dir)) \ + and os.path.isdir(abs_dir) \ + and (not os.path.ismount(abs_dir)) \ + and (os.path.isfile(os.path.join(abs_dir,MOUNT_DIR_MARKER))) \ + and (len(os.listdir(abs_dir)) == 1): + try: + os.remove(os.path.join(abs_dir, MOUNT_DIR_MARKER)) + os.rmdir(abs_dir) + except OSError, err_msg: + ## we do not care too much about unclean cleaning ... + self.cbox.log.info("failed to clean a mountpoint (%s): %s" % \ + (abs_dir, str(err_msg))) + - def __create_mount_directory(self, dirname): - """create and mark a mount directory - this marking helps to remove old mountdirs safely""" - os.mkdir(dirname) - try: - mark_file = file(os.path.join(dirname, MOUNT_DIR_MARKER), "w") - mark_file.close() - except OSError, err_msg: - ## we do not care too much about the marking - self.cbox.log.info("failed to mark a mountpoint (%s): %s" % (dirname, str(err_msg))) - + def __create_mount_directory(self, dirname): + """create and mark a mount directory + this marking helps to remove old mountdirs safely""" + os.mkdir(dirname) + try: + mark_file = file(os.path.join(dirname, MOUNT_DIR_MARKER), "w") + mark_file.close() + except OSError, err_msg: + ## we do not care too much about the marking + self.cbox.log.info("failed to mark a mountpoint (%s): %s" % (dirname, str(err_msg))) + - def __get_event_args(self): - """Return an array of arguments for event scripts. - - for now supported: pre/post-mount/umount events - """ - type_text = [e for e in CONTAINERTYPES.keys() - if CONTAINERTYPES[e] == self.get_type()][0] - return [self.get_device(), self.get_name(), type_text, self.__get_mount_point()] + def __get_event_args(self): + """Return an array of arguments for event scripts. + + for now supported: pre/post-mount/umount events + """ + type_text = [e for e in CONTAINERTYPES.keys() + if CONTAINERTYPES[e] == self.get_type()][0] + return [self.get_device(), self.get_name(), type_text, self.__get_mount_point()] diff --git a/src/cryptobox/core/exceptions.py b/src/cryptobox/core/exceptions.py index 53d247d..43b9561 100644 --- a/src/cryptobox/core/exceptions.py +++ b/src/cryptobox/core/exceptions.py @@ -26,127 +26,127 @@ __revision__ = "$Id$" class CBError(Exception): - """base class for exceptions of the cryptobox""" - pass + """base class for exceptions of the cryptobox""" + pass class CBConfigError(CBError): - """any kind of error related to the configuration of a cryptobox""" - pass + """any kind of error related to the configuration of a cryptobox""" + pass class CBConfigUnavailableError(CBConfigError): - """config file/input was not available at all""" - - def __init__(self, source=None): - self.source = source - - def __str__(self): - if self.source: - return "failed to access the configuration of the cryptobox: %s" % self.source - else: - return "failed to access the configuration of the cryptobox" + """config file/input was not available at all""" + + def __init__(self, source=None): + self.source = source + + def __str__(self): + if self.source: + return "failed to access the configuration of the cryptobox: %s" % self.source + else: + return "failed to access the configuration of the cryptobox" class CBConfigUndefinedError(CBConfigError): - """a specific configuration setting was not defined""" + """a specific configuration setting was not defined""" - def __init__(self, section, name=None): - self.section = section - self.name = name - - def __str__(self): - """Output the appropriate string: for a setting or a section. - """ - if self.name: - # setting - return "undefined configuration setting: [" + str(self.section) \ - + "]->" + str(self.name) + " - please check your configuration file" - else: - # section - return "undefined configuration section: [" + str(self.section) \ - + "] - please check your configuration file" - + def __init__(self, section, name=None): + self.section = section + self.name = name + + def __str__(self): + """Output the appropriate string: for a setting or a section. + """ + if self.name: + # setting + return "undefined configuration setting: [" + str(self.section) \ + + "]->" + str(self.name) + " - please check your configuration file" + else: + # section + return "undefined configuration section: [" + str(self.section) \ + + "] - please check your configuration file" + class CBConfigInvalidValueError(CBConfigError): - """a configuration setting was invalid somehow""" + """a configuration setting was invalid somehow""" - def __init__(self, section, name, value, reason): - self.section = section - self.name = name - self.value = value - self.reason = reason - - - def __str__(self): - """Return the error description. - """ - return "invalid configuration setting [%s]->%s (%s): %s" % \ - (self.section, self.name, self.value, self.reason) + def __init__(self, section, name, value, reason): + self.section = section + self.name = name + self.value = value + self.reason = reason + + + def __str__(self): + """Return the error description. + """ + return "invalid configuration setting [%s]->%s (%s): %s" % \ + (self.section, self.name, self.value, self.reason) class CBEnvironmentError(CBError): - """some part of the environment of the cryptobox is broken - e.g. the wrong version of a required program - """ + """some part of the environment of the cryptobox is broken + e.g. the wrong version of a required program + """ - def __init__(self, desc): - self.desc = desc + def __init__(self, desc): + self.desc = desc - def __str__(self): - """Return the error description. - """ - return "misconfiguration detected: %s" % self.desc + def __str__(self): + """Return the error description. + """ + return "misconfiguration detected: %s" % self.desc class CBContainerError(CBError): - """Any error raised while manipulating a cryptobox container. - """ + """Any error raised while manipulating a cryptobox container. + """ - + class CBCreateError(CBContainerError): - """Raised if a container could not be created (formatted). - """ - pass + """Raised if a container could not be created (formatted). + """ + pass class CBVolumeIsActive(CBContainerError): - """Raised if a container was active even if it may not for a specific action. - """ - pass + """Raised if a container was active even if it may not for a specific action. + """ + pass class CBInvalidName(CBContainerError): - """Raised if someone tried to set an invalid container name. - """ - pass + """Raised if someone tried to set an invalid container name. + """ + pass class CBNameIsInUse(CBContainerError): - """Raised if the new name of a container is already in use. - """ - pass + """Raised if the new name of a container is already in use. + """ + pass class CBInvalidType(CBContainerError): - """Raised if a container is of an invalid type for a choosen action. - """ - pass + """Raised if a container is of an invalid type for a choosen action. + """ + pass class CBInvalidPassword(CBContainerError): - """Someone tried to open an ecnrypted container with the wrong password. - """ - pass + """Someone tried to open an ecnrypted container with the wrong password. + """ + pass class CBChangePasswordError(CBContainerError): - """Changing of the password of an encrypted container failed. - """ - pass + """Changing of the password of an encrypted container failed. + """ + pass class CBMountError(CBContainerError): - """Failed to mount a container. - """ - pass + """Failed to mount a container. + """ + pass class CBUmountError(CBContainerError): - """Failed to umount a container. - """ - pass + """Failed to umount a container. + """ + pass diff --git a/src/cryptobox/core/main.py b/src/cryptobox/core/main.py index 7972801..619edaa 100644 --- a/src/cryptobox/core/main.py +++ b/src/cryptobox/core/main.py @@ -34,261 +34,261 @@ import threading class CryptoBox: - '''this class rules them all! + '''this class rules them all! - put things like logging, conf and other stuff in here, - that might be used by more classes, it will be passed on to them''' + put things like logging, conf and other stuff in here, + that might be used by more classes, it will be passed on to them''' - def __init__(self, config_file=None): - import cryptobox.core.settings as cbxSettings - self.log = self.__get_startup_logger() - self.prefs = cbxSettings.CryptoBoxSettings(config_file) - self.__run_tests() - self.__containers = [] - self.__busy_devices = {} - self.__busy_devices_sema = threading.BoundedSemaphore() - self.reread_container_list() + def __init__(self, config_file=None): + import cryptobox.core.settings as cbxSettings + self.log = self.__get_startup_logger() + self.prefs = cbxSettings.CryptoBoxSettings(config_file) + self.__run_tests() + self.__containers = [] + self.__busy_devices = {} + self.__busy_devices_sema = threading.BoundedSemaphore() + self.reread_container_list() - def setup(self): - """Initialize the cryptobox. - """ - self.log.info("Starting up the CryptoBox ...") + def setup(self): + """Initialize the cryptobox. + """ + self.log.info("Starting up the CryptoBox ...") - def cleanup(self): - """Umount all containers and shutdown everything safely. - """ - self.log.info("Shutting down the CryptoBox ...") - ## umount all containers - self.log.info("Umounting all volumes ...") - self.reread_container_list() - for cont in self.get_container_list(): - if cont.is_mounted(): - cont.umount() - ## save all settings - self.log.info("Storing local settings ...") - ## problems with storing are logged automatically - self.prefs.write() - # TODO: improve the configuration partition handling [a]: how? - self.prefs.umount_partition() - ## shutdown logging as the last step - try: - self.log.info("Turning off logging ...") - self.log.close() - except AttributeError: - ## there should be 'close' action - but it may fail silently - pass + def cleanup(self): + """Umount all containers and shutdown everything safely. + """ + self.log.info("Shutting down the CryptoBox ...") + ## umount all containers + self.log.info("Umounting all volumes ...") + self.reread_container_list() + for cont in self.get_container_list(): + if cont.is_mounted(): + cont.umount() + ## save all settings + self.log.info("Storing local settings ...") + ## problems with storing are logged automatically + self.prefs.write() + # TODO: improve the configuration partition handling [a]: how? + self.prefs.umount_partition() + ## shutdown logging as the last step + try: + self.log.info("Turning off logging ...") + self.log.close() + except AttributeError: + ## there should be 'close' action - but it may fail silently + pass - def __get_startup_logger(self): - """Initialize the configured logging facility of the CryptoBox. - - use it with: 'self.log.[debug|info|warning|error|critical](logmessage)' - all classes should get the logging instance during __init__: - self.log = logging.getLogger("CryptoBox") + def __get_startup_logger(self): + """Initialize the configured logging facility of the CryptoBox. + + use it with: 'self.log.[debug|info|warning|error|critical](logmessage)' + all classes should get the logging instance during __init__: + self.log = logging.getLogger("CryptoBox") - first we output all warnings/errors to stderr - as soon as we opened the config file successfully, we redirect debug output - to the configured destination - """ - import logging - ## basicConfig(...) needs python >= 2.4 - try: - log_handler = logging.getLogger("CryptoBox") - logging.basicConfig( - format = '%(asctime)s CryptoBox %(levelname)s: %(message)s', - stderr = sys.stderr) - log_handler.setLevel(logging.ERROR) - log_handler.info("loggingsystem is up'n running") - ## from now on everything can be logged via self.log... - except: - raise CBEnvironmentError("couldn't initialise the loggingsystem. I give up.") - return log_handler + first we output all warnings/errors to stderr + as soon as we opened the config file successfully, we redirect debug output + to the configured destination + """ + import logging + ## basicConfig(...) needs python >= 2.4 + try: + log_handler = logging.getLogger("CryptoBox") + logging.basicConfig( + format = '%(asctime)s CryptoBox %(levelname)s: %(message)s', + stderr = sys.stderr) + log_handler.setLevel(logging.ERROR) + log_handler.info("loggingsystem is up'n running") + ## from now on everything can be logged via self.log... + except: + raise CBEnvironmentError("couldn't initialise the loggingsystem. I give up.") + return log_handler - def __run_tests(self): - """Do some initial tests. - """ - self.__run_test_root_priv() + def __run_tests(self): + """Do some initial tests. + """ + self.__run_test_root_priv() - - def __run_test_root_priv(self): - """Try to run 'super' with 'CryptoBoxRootActions'. - """ - try: - devnull = open(os.devnull, "w") - except IOError: - raise CBEnvironmentError("could not open %s for writing!" % os.devnull) - try: - prog_super = self.prefs["Programs"]["super"] - except KeyError: - raise CBConfigUndefinedError("Programs", "super") - try: - prog_rootactions = self.prefs["Programs"]["CryptoBoxRootActions"] - except KeyError: - raise CBConfigUndefinedError("Programs", "CryptoBoxRootActions") - try: - proc = subprocess.Popen( - shell = False, - stdout = devnull, - stderr = devnull, - args = [prog_super, prog_rootactions, "check"]) - except OSError: - raise CBEnvironmentError( - "failed to execute 'super' (%s)" % self.prefs["Programs"]["super"]) - proc.wait() - if proc.returncode != 0: - raise CBEnvironmentError("failed to call CryptoBoxRootActions (" - + prog_rootactions + ") via 'super' - maybe you did not add the " - + "appropriate line to '/etc/super.tab'?") + + def __run_test_root_priv(self): + """Try to run 'super' with 'CryptoBoxRootActions'. + """ + try: + devnull = open(os.devnull, "w") + except IOError: + raise CBEnvironmentError("could not open %s for writing!" % os.devnull) + try: + prog_super = self.prefs["Programs"]["super"] + except KeyError: + raise CBConfigUndefinedError("Programs", "super") + try: + prog_rootactions = self.prefs["Programs"]["CryptoBoxRootActions"] + except KeyError: + raise CBConfigUndefinedError("Programs", "CryptoBoxRootActions") + try: + proc = subprocess.Popen( + shell = False, + stdout = devnull, + stderr = devnull, + args = [prog_super, prog_rootactions, "check"]) + except OSError: + raise CBEnvironmentError( + "failed to execute 'super' (%s)" % self.prefs["Programs"]["super"]) + proc.wait() + if proc.returncode != 0: + raise CBEnvironmentError("failed to call CryptoBoxRootActions (" + + prog_rootactions + ") via 'super' - maybe you did not add the " + + "appropriate line to '/etc/super.tab'?") - def reread_container_list(self): - """Reinitialize the list of available containers. + def reread_container_list(self): + """Reinitialize the list of available containers. - This should be called whenever the available containers may have changed. - E.g.: after partitioning and after device addition/removal - """ - self.log.debug("rereading container list") - self.__containers = [] - blockdevice.CACHE.reset() - for device in blockdevice.Blockdevices().get_storage_devices(): - if self.is_device_allowed(device) and not self.is_config_partition(device): - self.__containers.append(cbxContainer.CryptoBoxContainer(device, self)) - ## sort by container name - self.__containers.sort(cmp = lambda x, y: x.get_name() < y.get_name() and -1 or 1) - + This should be called whenever the available containers may have changed. + E.g.: after partitioning and after device addition/removal + """ + self.log.debug("rereading container list") + self.__containers = [] + blockdevice.CACHE.reset() + for device in blockdevice.Blockdevices().get_storage_devices(): + if self.is_device_allowed(device) and not self.is_config_partition(device): + self.__containers.append(cbxContainer.CryptoBoxContainer(device, self)) + ## sort by container name + self.__containers.sort(cmp = lambda x, y: x.get_name() < y.get_name() and -1 or 1) + - def get_device_busy_state(self, device): - """Return whether a device is currently marked as busy or not. + def get_device_busy_state(self, device): + """Return whether a device is currently marked as busy or not. - The busy flag can be turned off manually (recommended) or the timeout - can expire. - """ - import time - self.__busy_devices_sema.acquire() - ## not marked as busy - if not self.__busy_devices.has_key(device): - self.__busy_devices_sema.release() - return False - ## timer is expired - if time.time() > self.__busy_devices[device]: - del self.__busy_devices[device] - self.__busy_devices_sema.release() - return False - self.__busy_devices_sema.release() - return True + The busy flag can be turned off manually (recommended) or the timeout + can expire. + """ + import time + self.__busy_devices_sema.acquire() + ## not marked as busy + if not self.__busy_devices.has_key(device): + self.__busy_devices_sema.release() + return False + ## timer is expired + if time.time() > self.__busy_devices[device]: + del self.__busy_devices[device] + self.__busy_devices_sema.release() + return False + self.__busy_devices_sema.release() + return True - def set_device_busy_state(self, device, new_state, timeout=300): - """Mark a device as busy. + def set_device_busy_state(self, device, new_state, timeout=300): + """Mark a device as busy. - This is especially useful during formatting, as this may take a long time. - """ - import time - self.__busy_devices_sema.acquire() - self.log.debug("Turn busy flag %s: %s" % (new_state and "on" or "off", device)) - if new_state: - self.__busy_devices[device] = time.time() + timeout - else: - if self.__busy_devices.has_key(device): - del self.__busy_devices[device] - self.log.debug("Current busy flags: %s" % str(self.__busy_devices)) - self.__busy_devices_sema.release() + This is especially useful during formatting, as this may take a long time. + """ + import time + self.__busy_devices_sema.acquire() + self.log.debug("Turn busy flag %s: %s" % (new_state and "on" or "off", device)) + if new_state: + self.__busy_devices[device] = time.time() + timeout + else: + if self.__busy_devices.has_key(device): + del self.__busy_devices[device] + self.log.debug("Current busy flags: %s" % str(self.__busy_devices)) + self.__busy_devices_sema.release() - def is_config_partition(self, device): - """Check if a given partition contains configuration informations. + 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. - """ - return device.label == self.prefs["Main"]["ConfigVolumeLabel"] + The check is done by comparing the label of the filesystem with a string. + """ + return device.label == self.prefs["Main"]["ConfigVolumeLabel"] - def is_device_allowed(self, device): - """check if a device is white-listed for being used as cryptobox containers - - also check, if the device is readable and writeable for the current user - """ - import types - allowed = self.prefs["Main"]["AllowedDevices"] - if type(allowed) == types.StringType: - allowed = [allowed] - for devnode in device.devnodes: - if [ a_dev for a_dev in allowed if devnode.startswith(a_dev) ]: - if os.access(devnode, os.R_OK | os.W_OK): - self.log.debug("Adding valid device: %s" % devnode) - ## move the device to the first position - device.devnodes.remove(devnode) - device.devnodes.insert(0, devnode) - return True - else: - self.log.debug("Skipping device without read and write" \ - + "permissions: %s" % device.name) - self.log.debug("Skipping unusable device: %s" % device.name) - return False + def is_device_allowed(self, device): + """check if a device is white-listed for being used as cryptobox containers + + also check, if the device is readable and writeable for the current user + """ + import types + allowed = self.prefs["Main"]["AllowedDevices"] + if type(allowed) == types.StringType: + allowed = [allowed] + for devnode in device.devnodes: + if [ a_dev for a_dev in allowed if devnode.startswith(a_dev) ]: + if os.access(devnode, os.R_OK | os.W_OK): + self.log.debug("Adding valid device: %s" % devnode) + ## move the device to the first position + device.devnodes.remove(devnode) + device.devnodes.insert(0, devnode) + return True + else: + self.log.debug("Skipping device without read and write" \ + + "permissions: %s" % device.name) + self.log.debug("Skipping unusable device: %s" % device.name) + return False - def get_container_list(self, filter_type=None, filter_name=None): - "retrieve the list of all containers of this cryptobox" - try: - result = self.__containers[:] - if filter_type != None: - if filter_type in range(len(cbxContainer.CONTAINERTYPES)): - return [e for e in self.__containers if e.get_type() == filter_type] - else: - self.log.info("invalid filter_type (%d)" % filter_type) - result.clear() - if filter_name != None: - result = [e for e in self.__containers if e.get_name() == filter_name] - return result - except AttributeError: - return [] + def get_container_list(self, filter_type=None, filter_name=None): + "retrieve the list of all containers of this cryptobox" + try: + result = self.__containers[:] + if filter_type != None: + if filter_type in range(len(cbxContainer.CONTAINERTYPES)): + return [e for e in self.__containers if e.get_type() == filter_type] + else: + self.log.info("invalid filter_type (%d)" % filter_type) + result.clear() + if filter_name != None: + result = [e for e in self.__containers if e.get_name() == filter_name] + return result + except AttributeError: + return [] - def get_container(self, device): - """retrieve the container element for a device + def get_container(self, device): + """retrieve the container element for a device - "device" can be a name (e.g. "dm-0") or a blockdevice object - """ - if isinstance(device, str): - devicename = device - else: - devicename = device.name - all = [e for e in self.get_container_list() if e.device.name == devicename] - if all: - return all[0] - else: - return None + "device" can be a name (e.g. "dm-0") or a blockdevice object + """ + if isinstance(device, str): + devicename = device + else: + devicename = device.name + all = [e for e in self.get_container_list() if e.device.name == devicename] + if all: + return all[0] + else: + return None - def send_event_notification(self, event, event_infos): - """call all available scripts in the event directory with some event information""" - event_dir = self.prefs["Locations"]["EventDir"] - for fname in os.listdir(event_dir): - real_fname = os.path.join(event_dir, fname) - if os.path.isfile(real_fname) and os.access(real_fname, os.X_OK): - cmd_args = [ self.prefs["Programs"]["super"], - self.prefs["Programs"]["CryptoBoxRootActions"], - "event", real_fname, event] - cmd_args.extend(event_infos) - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = cmd_args) - (stdout, stderr) = proc.communicate() - if proc.returncode != 0: - self.log.warn( - "an event script (%s) failed (exitcode=%d) to handle an event (%s): %s" % - (real_fname, proc.returncode, event, stderr.strip())) - else: - self.log.info("event handler (%s) finished successfully: %s" % - (real_fname, event)) + def send_event_notification(self, event, event_infos): + """call all available scripts in the event directory with some event information""" + event_dir = self.prefs["Locations"]["EventDir"] + for fname in os.listdir(event_dir): + real_fname = os.path.join(event_dir, fname) + if os.path.isfile(real_fname) and os.access(real_fname, os.X_OK): + cmd_args = [ self.prefs["Programs"]["super"], + self.prefs["Programs"]["CryptoBoxRootActions"], + "event", real_fname, event] + cmd_args.extend(event_infos) + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = cmd_args) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.log.warn( + "an event script (%s) failed (exitcode=%d) to handle an event (%s): %s" % + (real_fname, proc.returncode, event, stderr.strip())) + else: + self.log.info("event handler (%s) finished successfully: %s" % + (real_fname, event)) if __name__ == "__main__": - CryptoBox() + CryptoBox() diff --git a/src/cryptobox/core/settings.py b/src/cryptobox/core/settings.py index 3b3bb69..da4cbf2 100644 --- a/src/cryptobox/core/settings.py +++ b/src/cryptobox/core/settings.py @@ -32,9 +32,9 @@ import syslog CONF_LOCATIONS = [ - "./cryptobox.conf", - "~/.cryptobox.conf", - "/etc/cryptobox-server/cryptobox.conf"] + "./cryptobox.conf", + "~/.cryptobox.conf", + "/etc/cryptobox-server/cryptobox.conf"] VOLUMESDB_FILE = "cryptobox_volumes.db" PLUGINCONF_FILE = "cryptobox_plugins.conf" @@ -45,521 +45,521 @@ CURRENT_SETTING = [] def get_current_settings(): - """return the most recently created setting object - """ - if not CURRENT_SETTING: - return None - else: - return CURRENT_SETTING[0] + """return the most recently created setting object + """ + if not CURRENT_SETTING: + return None + else: + return CURRENT_SETTING[0] class CryptoBoxSettings: - """Manage the various configuration files of the CryptoBox - """ + """Manage the various configuration files of the CryptoBox + """ - def __init__(self, config_file=None): - self.__is_initialized = False - self.log = logging.getLogger("CryptoBox") - config_file = self.__get_config_filename(config_file) - self.log.info("loading config file: %s" % config_file) - self.prefs = self.__get_preferences(config_file) - if not "PluginSettings" in self.prefs: - self.prefs["PluginSettings"] = {} - self.__validate_config() - self.__configure_log_handler() - self.__check_unknown_preferences() - self.prepare_partition() - self.volumes_db = self.__get_volumes_database() - self.plugin_conf = self.__get_plugin_config() - self.user_db = self.__get_user_db() - self.misc_files = [] - self.reload_misc_files() - self.__is_initialized = True - CURRENT_SETTING.insert(0, self) - + def __init__(self, config_file=None): + self.__is_initialized = False + self.log = logging.getLogger("CryptoBox") + config_file = self.__get_config_filename(config_file) + self.log.info("loading config file: %s" % config_file) + self.prefs = self.__get_preferences(config_file) + if not "PluginSettings" in self.prefs: + self.prefs["PluginSettings"] = {} + self.__validate_config() + self.__configure_log_handler() + self.__check_unknown_preferences() + self.prepare_partition() + self.volumes_db = self.__get_volumes_database() + self.plugin_conf = self.__get_plugin_config() + self.user_db = self.__get_user_db() + self.misc_files = [] + self.reload_misc_files() + self.__is_initialized = True + CURRENT_SETTING.insert(0, self) + - def reload_misc_files(self): - """Call this method after creating or removing a 'misc' configuration file - """ - self.misc_files = self.__get_misc_files() - + def reload_misc_files(self): + """Call this method after creating or removing a 'misc' configuration file + """ + self.misc_files = self.__get_misc_files() + - def write(self): - """ - write all local setting files including the content of the "misc" subdirectory - """ - status = True - try: - self.volumes_db.write() - except IOError: - self.log.warn("Could not save the volume database") - status = False - try: - self.plugin_conf.write() - except IOError: - self.log.warn("Could not save the plugin configuration") - status = False - try: - self.user_db.write() - except IOError: - self.log.warn("Could not save the user database") - status = False - for misc_file in self.misc_files: - if not misc_file.save(): - self.log.warn("Could not save a misc setting file (%s)" % misc_file.filename) - status = False - return status + def write(self): + """ + write all local setting files including the content of the "misc" subdirectory + """ + status = True + try: + self.volumes_db.write() + except IOError: + self.log.warn("Could not save the volume database") + status = False + try: + self.plugin_conf.write() + except IOError: + self.log.warn("Could not save the plugin configuration") + status = False + try: + self.user_db.write() + except IOError: + self.log.warn("Could not save the user database") + status = False + for misc_file in self.misc_files: + if not misc_file.save(): + self.log.warn("Could not save a misc setting file (%s)" % misc_file.filename) + status = False + return status - def get_misc_config_filename(self, name): - """Return an absolute filename for a given filename 'name' + def get_misc_config_filename(self, name): + """Return an absolute filename for a given filename 'name' - 'name' should not contain slashes (no directory part!) - """ - return os.path.join(self.prefs["Locations"]["SettingsDir"], "misc", name) - + 'name' should not contain slashes (no directory part!) + """ + return os.path.join(self.prefs["Locations"]["SettingsDir"], "misc", name) + - def create_misc_config_file(self, name, content): - """Create a new configuration file in the 'settings' directory + def create_misc_config_file(self, name, content): + """Create a new configuration file in the 'settings' directory - "name" should be the basename (without a directory) - "content" will be directly written to the file - this method may throw an IOException - """ - misc_conf_file = self.get_misc_config_filename(name) - misc_conf_dir = os.path.dirname(misc_conf_file) - if not os.path.isdir(misc_conf_dir): - try: - os.mkdir(misc_conf_dir) - except OSError, err_msg: - ## the caller expects only IOError - raise IOError, err_msg - cfile = open(misc_conf_file, "w") - try: - cfile.write(content) - except IOError: - cfile.close() - raise - cfile.close() - ## reread all misc files automatically - this should be ok - self.reload_misc_files() + "name" should be the basename (without a directory) + "content" will be directly written to the file + this method may throw an IOException + """ + misc_conf_file = self.get_misc_config_filename(name) + misc_conf_dir = os.path.dirname(misc_conf_file) + if not os.path.isdir(misc_conf_dir): + try: + os.mkdir(misc_conf_dir) + except OSError, err_msg: + ## the caller expects only IOError + raise IOError, err_msg + cfile = open(misc_conf_file, "w") + try: + cfile.write(content) + except IOError: + cfile.close() + raise + cfile.close() + ## reread all misc files automatically - this should be ok + self.reload_misc_files() - def requires_partition(self): - return bool(self.prefs["Main"]["UseConfigPartition"]) - + def requires_partition(self): + return bool(self.prefs["Main"]["UseConfigPartition"]) + - def get_active_partition(self): - """Return the currently active cnfiguration partition. - """ - settings_dir = self.prefs["Locations"]["SettingsDir"] - if not os.path.ismount(settings_dir): - return None - for line in file("/proc/mounts"): - fields = line.split(" ") - mount_dir = fields[1] - fs_type = fields[2] - if fs_type == "tmpfs": - ## skip ramdisks - these are not really "active partitions" - continue - try: - if os.path.samefile(mount_dir, settings_dir): - return fields[0] - except OSError: - pass - ## no matching entry found - return None + def get_active_partition(self): + """Return the currently active cnfiguration partition. + """ + settings_dir = self.prefs["Locations"]["SettingsDir"] + if not os.path.ismount(settings_dir): + return None + for line in file("/proc/mounts"): + fields = line.split(" ") + mount_dir = fields[1] + fs_type = fields[2] + if fs_type == "tmpfs": + ## skip ramdisks - these are not really "active partitions" + continue + try: + if os.path.samefile(mount_dir, settings_dir): + return fields[0] + except OSError: + pass + ## no matching entry found + return None - def mount_partition(self): - """Mount a config partition. - """ - self.log.debug("trying to mount configuration partition") - if not self.requires_partition(): - self.log.warn("mountConfigPartition: configuration partition is " - + "not required - mounting anyway") - if self.get_active_partition(): - self.log.warn("mountConfigPartition: configuration partition already " - + "mounted - not mounting again") - return False - conf_partitions = self.get_available_partitions() - mount_dir = self.prefs["Locations"]["SettingsDir"] - if not conf_partitions: - ## return, if tmpfs is already mounted - if os.path.ismount(mount_dir): - self.log.info("A ramdisk seems to be already mounted as a config " \ - + "partition - doing nothing ...") - ## return without any actions - return True - self.log.warn("no configuration partition found - you have to create " - + "it first") - ## mount tmpfs instead to provide a place for storing stuff - ## "_tmpfs_" as parameter for mount is interpreted as a magic word - ## by CryptoBoxRootActions - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.prefs["Programs"]["super"], - self.prefs["Programs"]["CryptoBoxRootActions"], - "program", "mount", - "_tmpfs_", - mount_dir ]) - (stdout, stderr) = proc.communicate() - if proc.returncode != 0: - self.log.error("Failed to mount a ramdisk for storing settings: %s" \ - % stderr) - return False - self.log.info("Ramdisk (tmpfs) mounted as config partition ...") - else: - partition = conf_partitions[0] - ## umount tmpfs in case it is active - if os.path.ismount(mount_dir): - self.umount_partition() - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.prefs["Programs"]["super"], - self.prefs["Programs"]["CryptoBoxRootActions"], - "program", "mount", - partition, - mount_dir ]) - (stdout, stderr) = proc.communicate() - if proc.returncode != 0: - self.log.error("Failed to mount the configuration partition (%s): %s" % \ - (partition, stderr)) - return False - self.log.info("configuration partition mounted: %s" % partition) - ## write config files (not during first initialization of this object) - if self.__is_initialized: - self.write() - return True + def mount_partition(self): + """Mount a config partition. + """ + self.log.debug("trying to mount configuration partition") + if not self.requires_partition(): + self.log.warn("mountConfigPartition: configuration partition is " + + "not required - mounting anyway") + if self.get_active_partition(): + self.log.warn("mountConfigPartition: configuration partition already " + + "mounted - not mounting again") + return False + conf_partitions = self.get_available_partitions() + mount_dir = self.prefs["Locations"]["SettingsDir"] + if not conf_partitions: + ## return, if tmpfs is already mounted + if os.path.ismount(mount_dir): + self.log.info("A ramdisk seems to be already mounted as a config " \ + + "partition - doing nothing ...") + ## return without any actions + return True + self.log.warn("no configuration partition found - you have to create " + + "it first") + ## mount tmpfs instead to provide a place for storing stuff + ## "_tmpfs_" as parameter for mount is interpreted as a magic word + ## by CryptoBoxRootActions + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.prefs["Programs"]["super"], + self.prefs["Programs"]["CryptoBoxRootActions"], + "program", "mount", + "_tmpfs_", + mount_dir ]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.log.error("Failed to mount a ramdisk for storing settings: %s" \ + % stderr) + return False + self.log.info("Ramdisk (tmpfs) mounted as config partition ...") + else: + partition = conf_partitions[0] + ## umount tmpfs in case it is active + if os.path.ismount(mount_dir): + self.umount_partition() + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.prefs["Programs"]["super"], + self.prefs["Programs"]["CryptoBoxRootActions"], + "program", "mount", + partition, + mount_dir ]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.log.error("Failed to mount the configuration partition (%s): %s" % \ + (partition, stderr)) + return False + self.log.info("configuration partition mounted: %s" % partition) + ## write config files (not during first initialization of this object) + if self.__is_initialized: + self.write() + return True - def umount_partition(self): - """Umount the currently active configuration partition. - """ - mount_dir = self.prefs["Locations"]["SettingsDir"] - if not os.path.ismount(mount_dir): - self.log.warn("umountConfigPartition: no configuration partition mounted") - return False - self.reload_misc_files() - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.prefs["Programs"]["super"], - self.prefs["Programs"]["CryptoBoxRootActions"], - "program", "umount", - mount_dir ]) - (stdout, stderr) = proc.communicate() - if proc.returncode != 0: - self.log.error("Failed to unmount the configuration partition: %s" % stderr) - return False - self.log.info("configuration partition unmounted") - return True - + def umount_partition(self): + """Umount the currently active configuration partition. + """ + mount_dir = self.prefs["Locations"]["SettingsDir"] + if not os.path.ismount(mount_dir): + self.log.warn("umountConfigPartition: no configuration partition mounted") + return False + self.reload_misc_files() + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.prefs["Programs"]["super"], + self.prefs["Programs"]["CryptoBoxRootActions"], + "program", "umount", + mount_dir ]) + (stdout, stderr) = proc.communicate() + if proc.returncode != 0: + self.log.error("Failed to unmount the configuration partition: %s" % stderr) + return False + self.log.info("configuration partition unmounted") + return True + - def get_available_partitions(self): - """returns a sequence of found config partitions""" - self.log.debug("Retrieving available configuration partitions ...") - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ - self.prefs["Programs"]["blkid"], - "-c", os.path.devnull, - "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ]) - (output, error) = proc.communicate() - if proc.returncode == 2: - self.log.info("No configuration partitions found") - return [] - elif proc.returncode == 4: - self.log.warn("Failed to call 'blkid' for unknown reasons.") - return [] - elif proc.returncode == 0: - if output: - return [e.strip().split(":", 1)[0] for e in output.splitlines()] - else: - return [] - else: - self.log.warn("Unknown exit code of 'blkid': %d - %s" \ - % (proc.returncode, error)) + def get_available_partitions(self): + """returns a sequence of found config partitions""" + self.log.debug("Retrieving available configuration partitions ...") + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.prefs["Programs"]["blkid"], + "-c", os.path.devnull, + "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ]) + (output, error) = proc.communicate() + if proc.returncode == 2: + self.log.info("No configuration partitions found") + return [] + elif proc.returncode == 4: + self.log.warn("Failed to call 'blkid' for unknown reasons.") + return [] + elif proc.returncode == 0: + if output: + return [e.strip().split(":", 1)[0] for e in output.splitlines()] + else: + return [] + else: + self.log.warn("Unknown exit code of 'blkid': %d - %s" \ + % (proc.returncode, error)) - - def prepare_partition(self): - """Mount a config partition if necessary. - """ - if self.requires_partition() and not self.get_active_partition(): - self.mount_partition() + + def prepare_partition(self): + """Mount a config partition if necessary. + """ + if self.requires_partition() and not self.get_active_partition(): + self.mount_partition() - def __getitem__(self, key): - """redirect all requests to the 'prefs' attribute""" - return self.prefs[key] + def __getitem__(self, key): + """redirect all requests to the 'prefs' attribute""" + return self.prefs[key] - def __get_preferences(self, config_file): - """Load the CryptoBox configuration. - """ - import StringIO - config_rules = StringIO.StringIO(self.validation_spec) - try: - prefs = configobj.ConfigObj(config_file, configspec=config_rules) - if prefs: - self.log.info("found config: %s" % prefs.items()) - else: - raise CBConfigUnavailableError( - "failed to load the config file: %s" % config_file) - except IOError: - raise CBConfigUnavailableError( - "unable to open the config file: %s" % config_file) - return prefs + def __get_preferences(self, config_file): + """Load the CryptoBox configuration. + """ + import StringIO + config_rules = StringIO.StringIO(self.validation_spec) + try: + prefs = configobj.ConfigObj(config_file, configspec=config_rules) + if prefs: + self.log.info("found config: %s" % prefs.items()) + else: + raise CBConfigUnavailableError( + "failed to load the config file: %s" % config_file) + except IOError: + raise CBConfigUnavailableError( + "unable to open the config file: %s" % config_file) + return prefs - def __validate_config(self): - """Check the configuration settings and cast value types. - """ - result = self.prefs.validate(CryptoBoxSettingsValidator(), preserve_errors=True) - error_list = configobj.flatten_errors(self.prefs, result) - if not error_list: - return - error_msgs = [] - for sections, key, text in error_list: - section_name = "->".join(sections) - if not text: - error_msg = "undefined configuration value (%s) in section '%s'" % \ - (key, section_name) - else: - error_msg = "invalid configuration value (%s) in section '%s': %s" % \ - (key, section_name, text) - error_msgs.append(error_msg) - raise CBConfigError, "\n".join(error_msgs) + def __validate_config(self): + """Check the configuration settings and cast value types. + """ + result = self.prefs.validate(CryptoBoxSettingsValidator(), preserve_errors=True) + error_list = configobj.flatten_errors(self.prefs, result) + if not error_list: + return + error_msgs = [] + for sections, key, text in error_list: + section_name = "->".join(sections) + if not text: + error_msg = "undefined configuration value (%s) in section '%s'" % \ + (key, section_name) + else: + error_msg = "invalid configuration value (%s) in section '%s': %s" % \ + (key, section_name, text) + error_msgs.append(error_msg) + raise CBConfigError, "\n".join(error_msgs) - def __check_unknown_preferences(self): - """Check the configuration file for unknown settings to avoid spelling mistakes. - """ - import StringIO - config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec), - list_values=False) - self.__recursive_section_check("", self.prefs, config_rules) - - - def __recursive_section_check(self, section_path, section_config, section_rules): - """should be called by '__check_unknown_preferences' for every section - sends a warning message to the logger for every undefined (see validation_spec) - configuration setting - """ - for section in section_config.keys(): - element_path = section_path + section - if section in section_rules.keys(): - if isinstance(section_config[section], configobj.Section): - if isinstance(section_rules[section], configobj.Section): - self.__recursive_section_check(element_path + "->", - section_config[section], section_rules[section]) - else: - self.log.warn("configuration setting should be a value " - + "instead of a section name: %s" % element_path) - else: - if not isinstance(section_rules[section], configobj.Section): - pass # good - the setting is valid - else: - self.log.warn("configuration setting should be a section " - + "name instead of a value: %s" % element_path) - elif element_path.startswith("PluginSettings->"): - ## ignore plugin settings - pass - else: - self.log.warn("unknown configuration setting: %s" % element_path) + def __check_unknown_preferences(self): + """Check the configuration file for unknown settings to avoid spelling mistakes. + """ + import StringIO + config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec), + list_values=False) + self.__recursive_section_check("", self.prefs, config_rules) + + + def __recursive_section_check(self, section_path, section_config, section_rules): + """should be called by '__check_unknown_preferences' for every section + sends a warning message to the logger for every undefined (see validation_spec) + configuration setting + """ + for section in section_config.keys(): + element_path = section_path + section + if section in section_rules.keys(): + if isinstance(section_config[section], configobj.Section): + if isinstance(section_rules[section], configobj.Section): + self.__recursive_section_check(element_path + "->", + section_config[section], section_rules[section]) + else: + self.log.warn("configuration setting should be a value " + + "instead of a section name: %s" % element_path) + else: + if not isinstance(section_rules[section], configobj.Section): + pass # good - the setting is valid + else: + self.log.warn("configuration setting should be a section " + + "name instead of a value: %s" % element_path) + elif element_path.startswith("PluginSettings->"): + ## ignore plugin settings + pass + else: + self.log.warn("unknown configuration setting: %s" % element_path) - def __get_plugin_config(self): - """Load the plugin configuration file if it exists. - """ - import StringIO - plugin_rules = StringIO.StringIO(self.pluginValidationSpec) - try: - try: - plugin_conf_file = os.path.join( - self.prefs["Locations"]["SettingsDir"], PLUGINCONF_FILE) - except KeyError: - raise CBConfigUndefinedError("Locations", "SettingsDir") - except SyntaxError: - raise CBConfigInvalidValueError("Locations", "SettingsDir", plugin_conf_file, - "failed to interprete the filename of the plugin config file " - + "correctly (%s)" % plugin_conf_file) - ## create plugin_conf_file if necessary - if os.path.exists(plugin_conf_file): - plugin_conf = configobj.ConfigObj(plugin_conf_file, configspec=plugin_rules) - else: - try: - plugin_conf = configobj.ConfigObj(plugin_conf_file, - configspec=plugin_rules, create_empty=True) - except IOError: - plugin_conf = configobj.ConfigObj(configspec=plugin_rules) - plugin_conf.filename = plugin_conf_file - ## validate and convert values according to the spec - plugin_conf.validate(validate.Validator()) - return plugin_conf + def __get_plugin_config(self): + """Load the plugin configuration file if it exists. + """ + import StringIO + plugin_rules = StringIO.StringIO(self.pluginValidationSpec) + try: + try: + plugin_conf_file = os.path.join( + self.prefs["Locations"]["SettingsDir"], PLUGINCONF_FILE) + except KeyError: + raise CBConfigUndefinedError("Locations", "SettingsDir") + except SyntaxError: + raise CBConfigInvalidValueError("Locations", "SettingsDir", plugin_conf_file, + "failed to interprete the filename of the plugin config file " + + "correctly (%s)" % plugin_conf_file) + ## create plugin_conf_file if necessary + if os.path.exists(plugin_conf_file): + plugin_conf = configobj.ConfigObj(plugin_conf_file, configspec=plugin_rules) + else: + try: + plugin_conf = configobj.ConfigObj(plugin_conf_file, + configspec=plugin_rules, create_empty=True) + except IOError: + plugin_conf = configobj.ConfigObj(configspec=plugin_rules) + plugin_conf.filename = plugin_conf_file + ## validate and convert values according to the spec + plugin_conf.validate(validate.Validator()) + return plugin_conf - def __get_volumes_database(self): - """Load the volume database file if it exists. - """ - #TODO: add configuration specification and validation [a]: -v - try: - try: - conf_file = os.path.join( - self.prefs["Locations"]["SettingsDir"], VOLUMESDB_FILE) - except KeyError: - raise CBConfigUndefinedError("Locations", "SettingsDir") - except SyntaxError: - raise CBConfigInvalidValueError("Locations", "SettingsDir", conf_file, - "failed to interprete the filename of the volume database " - + "correctly (%s)" % conf_file) - ## create conf_file if necessary - if os.path.exists(conf_file): - conf = configobj.ConfigObj(conf_file) - else: - try: - conf = configobj.ConfigObj(conf_file, create_empty=True) - except IOError: - conf = configobj.ConfigObj() - conf.filename = conf_file - return conf + def __get_volumes_database(self): + """Load the volume database file if it exists. + """ + #TODO: add configuration specification and validation [a]: -v + try: + try: + conf_file = os.path.join( + self.prefs["Locations"]["SettingsDir"], VOLUMESDB_FILE) + except KeyError: + raise CBConfigUndefinedError("Locations", "SettingsDir") + except SyntaxError: + raise CBConfigInvalidValueError("Locations", "SettingsDir", conf_file, + "failed to interprete the filename of the volume database " + + "correctly (%s)" % conf_file) + ## create conf_file if necessary + if os.path.exists(conf_file): + conf = configobj.ConfigObj(conf_file) + else: + try: + conf = configobj.ConfigObj(conf_file, create_empty=True) + except IOError: + conf = configobj.ConfigObj() + conf.filename = conf_file + return conf - def __get_user_db(self): - """Load the user database file if it exists. - """ - import StringIO, sha - user_db_rules = StringIO.StringIO(self.userDatabaseSpec) - try: - try: - user_db_file = os.path.join( - self.prefs["Locations"]["SettingsDir"], USERDB_FILE) - except KeyError: - raise CBConfigUndefinedError("Locations", "SettingsDir") - except SyntaxError: - raise CBConfigInvalidValueError("Locations", "SettingsDir", user_db_file, - "failed to interprete the filename of the users database file " - + "correctly (%s)" % user_db_file) - ## create user_db_file if necessary - if os.path.exists(user_db_file): - user_db = configobj.ConfigObj(user_db_file, configspec=user_db_rules) - else: - try: - user_db = configobj.ConfigObj(user_db_file, - configspec=user_db_rules, create_empty=True) - except IOError: - user_db = configobj.ConfigObj(configspec=user_db_rules) - user_db.filename = user_db_file - ## validate and set default value for "admin" user - user_db.validate(validate.Validator()) - ## define password hash function - never use "sha" directly - SPOT - user_db.get_digest = lambda password: sha.new(password).hexdigest() - return user_db + def __get_user_db(self): + """Load the user database file if it exists. + """ + import StringIO, sha + user_db_rules = StringIO.StringIO(self.userDatabaseSpec) + try: + try: + user_db_file = os.path.join( + self.prefs["Locations"]["SettingsDir"], USERDB_FILE) + except KeyError: + raise CBConfigUndefinedError("Locations", "SettingsDir") + except SyntaxError: + raise CBConfigInvalidValueError("Locations", "SettingsDir", user_db_file, + "failed to interprete the filename of the users database file " + + "correctly (%s)" % user_db_file) + ## create user_db_file if necessary + if os.path.exists(user_db_file): + user_db = configobj.ConfigObj(user_db_file, configspec=user_db_rules) + else: + try: + user_db = configobj.ConfigObj(user_db_file, + configspec=user_db_rules, create_empty=True) + except IOError: + user_db = configobj.ConfigObj(configspec=user_db_rules) + user_db.filename = user_db_file + ## validate and set default value for "admin" user + user_db.validate(validate.Validator()) + ## define password hash function - never use "sha" directly - SPOT + user_db.get_digest = lambda password: sha.new(password).hexdigest() + return user_db - def __get_misc_files(self): - """Load miscelleanous configuration files. + def __get_misc_files(self): + """Load miscelleanous configuration files. - e.g.: an ssl certificate, ... - """ - misc_dir = os.path.join(self.prefs["Locations"]["SettingsDir"], "misc") - if (not os.path.isdir(misc_dir)) or (not os.access(misc_dir, os.X_OK)): - return [] - misc_files = [] - for root, dirs, files in os.walk(misc_dir): - misc_files.extend([os.path.join(root, e) for e in files]) - return [MiscConfigFile(os.path.join(misc_dir, f), self.log) for f in misc_files] + e.g.: an ssl certificate, ... + """ + misc_dir = os.path.join(self.prefs["Locations"]["SettingsDir"], "misc") + if (not os.path.isdir(misc_dir)) or (not os.access(misc_dir, os.X_OK)): + return [] + misc_files = [] + for root, dirs, files in os.walk(misc_dir): + misc_files.extend([os.path.join(root, e) for e in files]) + return [MiscConfigFile(os.path.join(misc_dir, f), self.log) for f in misc_files] - def __get_config_filename(self, config_file): - """Search for the configuration file. - """ - import types - if config_file is None: - # no config file was specified - we will look for it in the ususal locations - conf_file_list = [os.path.expanduser(f) - for f in CONF_LOCATIONS - if os.path.exists(os.path.expanduser(f))] - if not conf_file_list: - # no possible config file found in the usual locations - raise CBConfigUnavailableError() - config_file = conf_file_list[0] - else: - # a config file was specified (e.g. via command line) - if type(config_file) != types.StringType: - raise CBConfigUnavailableError( - "invalid config file specified: %s" % config_file) - if not os.path.exists(config_file): - raise CBConfigUnavailableError( - "could not find the specified configuration file (%s)" % config_file) - return config_file + def __get_config_filename(self, config_file): + """Search for the configuration file. + """ + import types + if config_file is None: + # no config file was specified - we will look for it in the ususal locations + conf_file_list = [os.path.expanduser(f) + for f in CONF_LOCATIONS + if os.path.exists(os.path.expanduser(f))] + if not conf_file_list: + # no possible config file found in the usual locations + raise CBConfigUnavailableError() + config_file = conf_file_list[0] + else: + # a config file was specified (e.g. via command line) + if type(config_file) != types.StringType: + raise CBConfigUnavailableError( + "invalid config file specified: %s" % config_file) + if not os.path.exists(config_file): + raise CBConfigUnavailableError( + "could not find the specified configuration file (%s)" % config_file) + return config_file - def __configure_log_handler(self): - """Configure the log handler of the CryptoBox according to the config. - """ - log_level = self.prefs["Log"]["Level"].upper() - log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"] - if not log_level in log_level_avail: - raise CBConfigInvalidValueError("Log", "Level", log_level, - "invalid log level: only %s are allowed" % str(log_level_avail)) - log_destination = self.prefs["Log"]["Destination"].lower() - ## keep this in sync with the spec and the log_destination branches below - log_dest_avail = ['file', 'syslog'] - if not log_destination in log_dest_avail: - raise CBConfigInvalidValueError("Log", "Destination", log_destination, - "invalid log destination: only %s are allowed" % str(log_dest_avail)) - if log_destination == 'file': - try: - log_handler = logging.FileHandler(self.prefs["Log"]["Details"]) - except IOError: - raise CBEnvironmentError("could not write to log file (%s)" % \ - self.prefs["Log"]["Details"]) - log_handler.setFormatter( - logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) - elif log_destination == 'syslog': - log_facility = self.prefs["Log"]["Details"].upper() - log_facil_avail = ['KERN', 'USER', 'MAIL', 'DAEMON', 'AUTH', 'SYSLOG', - 'LPR', 'NEWS', 'UUCP', 'CRON', 'AUTHPRIV', 'LOCAL0', 'LOCAL1', - 'LOCAL2', 'LOCAL3', 'LOCAL4', 'LOCAL5', 'LOCAL6', 'LOCAL7'] - if not log_facility in log_facil_avail: - raise CBConfigInvalidValueError("Log", "Details", log_facility, - "invalid log details for 'syslog': only %s are allowed" % \ - str(log_facil_avail)) - ## retrive the log priority from the syslog module - log_handler = LocalSysLogHandler("CryptoBox", - getattr(syslog, 'LOG_%s' % log_facility)) - log_handler.setFormatter( - logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s')) - else: - ## this should never happen - we just have it in case someone forgets - ## to update the spec, the 'log_dest_avail' or the above branches - raise CBConfigInvalidValueError("Log", "Destination", log_destination, - "invalid log destination: only %s are allowed" % str(log_dest_avail)) - cbox_log = logging.getLogger("CryptoBox") - ## remove previous handlers (from 'basicConfig') - cbox_log.handlers = [] - ## add new one - cbox_log.addHandler(log_handler) - ## do not call parent's handlers - cbox_log.propagate = False - ## 'log_level' is a string -> use 'getattr' - cbox_log.setLevel(getattr(logging, log_level)) - ## the logger named "CryptoBox" is configured now + def __configure_log_handler(self): + """Configure the log handler of the CryptoBox according to the config. + """ + log_level = self.prefs["Log"]["Level"].upper() + log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"] + if not log_level in log_level_avail: + raise CBConfigInvalidValueError("Log", "Level", log_level, + "invalid log level: only %s are allowed" % str(log_level_avail)) + log_destination = self.prefs["Log"]["Destination"].lower() + ## keep this in sync with the spec and the log_destination branches below + log_dest_avail = ['file', 'syslog'] + if not log_destination in log_dest_avail: + raise CBConfigInvalidValueError("Log", "Destination", log_destination, + "invalid log destination: only %s are allowed" % str(log_dest_avail)) + if log_destination == 'file': + try: + log_handler = logging.FileHandler(self.prefs["Log"]["Details"]) + except IOError: + raise CBEnvironmentError("could not write to log file (%s)" % \ + self.prefs["Log"]["Details"]) + log_handler.setFormatter( + logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) + elif log_destination == 'syslog': + log_facility = self.prefs["Log"]["Details"].upper() + log_facil_avail = ['KERN', 'USER', 'MAIL', 'DAEMON', 'AUTH', 'SYSLOG', + 'LPR', 'NEWS', 'UUCP', 'CRON', 'AUTHPRIV', 'LOCAL0', 'LOCAL1', + 'LOCAL2', 'LOCAL3', 'LOCAL4', 'LOCAL5', 'LOCAL6', 'LOCAL7'] + if not log_facility in log_facil_avail: + raise CBConfigInvalidValueError("Log", "Details", log_facility, + "invalid log details for 'syslog': only %s are allowed" % \ + str(log_facil_avail)) + ## retrive the log priority from the syslog module + log_handler = LocalSysLogHandler("CryptoBox", + getattr(syslog, 'LOG_%s' % log_facility)) + log_handler.setFormatter( + logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s')) + else: + ## this should never happen - we just have it in case someone forgets + ## to update the spec, the 'log_dest_avail' or the above branches + raise CBConfigInvalidValueError("Log", "Destination", log_destination, + "invalid log destination: only %s are allowed" % str(log_dest_avail)) + cbox_log = logging.getLogger("CryptoBox") + ## remove previous handlers (from 'basicConfig') + cbox_log.handlers = [] + ## add new one + cbox_log.addHandler(log_handler) + ## do not call parent's handlers + cbox_log.propagate = False + ## 'log_level' is a string -> use 'getattr' + cbox_log.setLevel(getattr(logging, log_level)) + ## the logger named "CryptoBox" is configured now - validation_spec = """ + validation_spec = """ [Main] AllowedDevices = list(min=1) DefaultVolumePrefix = string(min=1) @@ -599,168 +599,168 @@ CryptoBoxRootActions = string(min=1) [PluginSettings] [[__many__]] - """ + """ - pluginValidationSpec = """ + pluginValidationSpec = """ [__many__] visibility = boolean(default=None) requestAuth = boolean(default=None) rank = integer(default=None) - """ - - userDatabaseSpec = """ + """ + + userDatabaseSpec = """ [admins] admin = string(default=d033e22ae348aeb5660fc2140aec35850c4da997) - """ - + """ + class CryptoBoxSettingsValidator(validate.Validator): - """Some custom configuration check functions. - """ + """Some custom configuration check functions. + """ - def __init__(self): - validate.Validator.__init__(self) - self.functions["directoryExists"] = self.check_directory_exists - self.functions["fileExecutable"] = self.check_file_executable - self.functions["fileWriteable"] = self.check_file_writeable - self.functions["listOfExistingDirectories"] = self.check_existing_directories - + def __init__(self): + validate.Validator.__init__(self) + self.functions["directoryExists"] = self.check_directory_exists + self.functions["fileExecutable"] = self.check_file_executable + self.functions["fileWriteable"] = self.check_file_writeable + self.functions["listOfExistingDirectories"] = self.check_existing_directories + - def check_directory_exists(self, value): - """Is the directory accessible? - """ - dir_path = os.path.abspath(value) - if not os.path.isdir(dir_path): - raise validate.VdtValueError("%s (not found)" % value) - if not os.access(dir_path, os.X_OK): - raise validate.VdtValueError("%s (access denied)" % value) - return dir_path - + def check_directory_exists(self, value): + """Is the directory accessible? + """ + dir_path = os.path.abspath(value) + if not os.path.isdir(dir_path): + raise validate.VdtValueError("%s (not found)" % value) + if not os.access(dir_path, os.X_OK): + raise validate.VdtValueError("%s (access denied)" % value) + return dir_path + - def check_file_executable(self, value): - """Is the file executable? - """ - file_path = os.path.abspath(value) - if not os.path.isfile(file_path): - raise validate.VdtValueError("%s (not found)" % value) - if not os.access(file_path, os.X_OK): - raise validate.VdtValueError("%s (access denied)" % value) - return file_path - + def check_file_executable(self, value): + """Is the file executable? + """ + file_path = os.path.abspath(value) + if not os.path.isfile(file_path): + raise validate.VdtValueError("%s (not found)" % value) + if not os.access(file_path, os.X_OK): + raise validate.VdtValueError("%s (access denied)" % value) + return file_path + - def check_file_writeable(self, value): - """Is the file writeable? - """ - file_path = os.path.abspath(value) - if os.path.isfile(file_path): - if not os.access(file_path, os.W_OK): - raise validate.VdtValueError("%s (not found)" % value) - else: - parent_dir = os.path.dirname(file_path) - if os.path.isdir(parent_dir) and os.access(parent_dir, os.W_OK): - return file_path - raise validate.VdtValueError("%s (directory does not exist)" % value) - return file_path + def check_file_writeable(self, value): + """Is the file writeable? + """ + file_path = os.path.abspath(value) + if os.path.isfile(file_path): + if not os.access(file_path, os.W_OK): + raise validate.VdtValueError("%s (not found)" % value) + else: + parent_dir = os.path.dirname(file_path) + if os.path.isdir(parent_dir) and os.access(parent_dir, os.W_OK): + return file_path + raise validate.VdtValueError("%s (directory does not exist)" % value) + return file_path - def check_existing_directories(self, value): - """Are these directories accessible? - """ - if not value: - raise validate.VdtValueError("no plugin directory specified") - if not isinstance(value, list): - value = [value] - result = [] - for one_dir in value: - dir_path = os.path.abspath(one_dir) - if not os.path.isdir(dir_path): - raise validate.VdtValueError( - "%s (plugin directory not found)" % one_dir) - if not os.access(dir_path, os.X_OK): - raise validate.VdtValueError( - "%s (access denied for plugin directory)" % one_dir) - result.append(dir_path) - return result + def check_existing_directories(self, value): + """Are these directories accessible? + """ + if not value: + raise validate.VdtValueError("no plugin directory specified") + if not isinstance(value, list): + value = [value] + result = [] + for one_dir in value: + dir_path = os.path.abspath(one_dir) + if not os.path.isdir(dir_path): + raise validate.VdtValueError( + "%s (plugin directory not found)" % one_dir) + if not os.access(dir_path, os.X_OK): + raise validate.VdtValueError( + "%s (access denied for plugin directory)" % one_dir) + result.append(dir_path) + return result class MiscConfigFile: - """all other config files (e.g. a ssl certificate) to be stored""" + """all other config files (e.g. a ssl certificate) to be stored""" - maxSize = 20480 + maxSize = 20480 - def __init__(self, filename, logger): - self.filename = filename - self.log = logger - self.content = None - self.load() + def __init__(self, filename, logger): + self.filename = filename + self.log = logger + self.content = None + self.load() - - def load(self): - """Load a configuration file into memory. - """ - fdesc = open(self.filename, "rb") - ## limit the maximum size - self.content = fdesc.read(self.maxSize) - if fdesc.tell() == self.maxSize: - self.log.warn("file in misc settings directory (" + str(self.filename) \ - + ") is bigger than allowed (" + str(self.maxSize) + ")") - fdesc.close() + + def load(self): + """Load a configuration file into memory. + """ + fdesc = open(self.filename, "rb") + ## limit the maximum size + self.content = fdesc.read(self.maxSize) + if fdesc.tell() == self.maxSize: + self.log.warn("file in misc settings directory (" + str(self.filename) \ + + ") is bigger than allowed (" + str(self.maxSize) + ")") + fdesc.close() - def save(self): - """Save a configuration file to disk. - """ - ## overriding of ro-files is not necessary (e.g. samba-include.conf) - if os.path.exists(self.filename) and not os.access(self.filename, os.W_OK): - return True - save_dir = os.path.dirname(self.filename) - ## create the directory, if necessary - if not os.path.isdir(save_dir): - try: - os.mkdir(save_dir) - except IOError: - return False - ## save the content of the file - try: - fdesc = open(self.filename, "wb") - except IOError: - return False - try: - fdesc.write(self.content) - fdesc.close() - return True - except IOError: - fdesc.close() - return False + def save(self): + """Save a configuration file to disk. + """ + ## overriding of ro-files is not necessary (e.g. samba-include.conf) + if os.path.exists(self.filename) and not os.access(self.filename, os.W_OK): + return True + save_dir = os.path.dirname(self.filename) + ## create the directory, if necessary + if not os.path.isdir(save_dir): + try: + os.mkdir(save_dir) + except IOError: + return False + ## save the content of the file + try: + fdesc = open(self.filename, "wb") + except IOError: + return False + try: + fdesc.write(self.content) + fdesc.close() + return True + except IOError: + fdesc.close() + return False class LocalSysLogHandler(logging.Handler): - """Pass logging messages to a local syslog server without unix sockets. + """Pass logging messages to a local syslog server without unix sockets. - derived from: logging.SysLogHandler - """ + derived from: logging.SysLogHandler + """ - def __init__(self, prepend='CryptoBox', facility=syslog.LOG_USER): - logging.Handler.__init__(self) - self.formatter = None - self.facility = facility - syslog.openlog(prepend, 0, facility) - + def __init__(self, prepend='CryptoBox', facility=syslog.LOG_USER): + logging.Handler.__init__(self) + self.formatter = None + self.facility = facility + syslog.openlog(prepend, 0, facility) + - def close(self): - """close the syslog connection - """ - syslog.closelog() - logging.Handler.close(self) + def close(self): + """close the syslog connection + """ + syslog.closelog() + logging.Handler.close(self) - def emit(self, record): - """format and send the log message - """ - msg = "%s: %s" % (record.levelname, record.getMessage()) - try: - syslog.syslog(record.levelno, msg) - except Exception: - self.handleError(record) + def emit(self, record): + """format and send the log message + """ + msg = "%s: %s" % (record.levelname, record.getMessage()) + try: + syslog.syslog(record.levelno, msg) + except Exception: + self.handleError(record) diff --git a/src/cryptobox/core/tools.py b/src/cryptobox/core/tools.py index f1500d2..3c08a70 100644 --- a/src/cryptobox/core/tools.py +++ b/src/cryptobox/core/tools.py @@ -31,218 +31,218 @@ LOGGER = logging.getLogger("CryptoBox") def get_available_partitions(): - "retrieve a list of all available containers" - ret_list = [] - try: - ## the following reads all lines of /proc/partitions and adds the mentioned devices - 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 - ## 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"): - ## for some parent devices we have to remove a 'p' (partition) - ## an example are partitionable mdadm raid devices (e.g. md1p1) - p_parent = re.sub('p?[1-9]?[0-9]$', '', p_device) - if p_parent == p_device: - if [e for e in ret_list - if re.search('^' + p_parent + 'p?[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 [ get_absolute_devicename(e) for e in ret_list ] - except IOError: - LOGGER.warning("Could not read /proc/partitions") - return [] + "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 + ## 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"): + ## for some parent devices we have to remove a 'p' (partition) + ## an example are partitionable mdadm raid devices (e.g. md1p1) + p_parent = re.sub('p?[1-9]?[0-9]$', '', p_device) + if p_parent == p_device: + if [e for e in ret_list + if re.search('^' + p_parent + 'p?[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 [ get_absolute_devicename(e) for e in ret_list ] + except IOError: + LOGGER.warning("Could not read /proc/partitions") + return [] def get_absolute_devicename(shortname): - """ returns the absolute file name of a device (e.g.: "hda1" -> "/dev/hda1") - this does also work for device mapper devices - if the result is non-unique, one arbitrary value is returned - """ - if re.search('^/', shortname): - return shortname - default = os.path.join("/dev", shortname) - if os.path.exists(default): - return default - result = find_major_minor_of_device(shortname) - ## if no valid major/minor was found -> exit - if not result: - return default - (major, minor) = result - ## for device-mapper devices (major == 254) ... - if major == 254: - result = find_major_minor_device("/dev/mapper", major, minor) - if result: - return result[0] - ## now check all files in /dev - result = find_major_minor_device("/dev", major, minor) - if result: - return result[0] - return default + """ 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 = find_major_minor_of_device(shortname) + ## if no valid major/minor was found -> exit + if not result: + return default + (major, minor) = result + ## for device-mapper devices (major == 254) ... + if major == 254: + result = find_major_minor_device("/dev/mapper", major, minor) + if result: + return result[0] + ## now check all files in /dev + result = find_major_minor_device("/dev", major, minor) + if result: + return result[0] + return default def find_major_minor_of_device(device): - """Return the major/minor numbers of a block device. - """ - if re.match("/", device) or \ - not os.path.exists(os.path.join(os.path.sep, "sys", "block", device)): - ## maybe it is an absolute device name - if not os.path.exists(device): - return None - ## okay - it seems to to a device node - rdev = os.stat(device).st_rdev - return (os.major(rdev), os.minor(rdev)) - blockdev_info_file = os.path.join(os.path.join( - os.path.sep,"sys","block", device), "dev") - 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 - return None + """Return the major/minor numbers of a block device. + """ + if re.match("/", device) or \ + not os.path.exists(os.path.join(os.path.sep, "sys", "block", device)): + ## maybe it is an absolute device name + if not os.path.exists(device): + return None + ## okay - it seems to to a device node + rdev = os.stat(device).st_rdev + return (os.major(rdev), os.minor(rdev)) + blockdev_info_file = os.path.join(os.path.join( + os.path.sep,"sys","block", device), "dev") + 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 + return None def find_major_minor_device(dirpath, major, minor): - """Returns the names of devices with the specified major and minor number. - """ - collected = [] - try: - subdirs = [os.path.join(dirpath, e) for e in os.listdir(dirpath) - if (not os.path.islink(os.path.join(dirpath, e))) and \ - os.path.isdir(os.path.join(dirpath, e))] - ## do a recursive call to parse the directory tree - for dirs in subdirs: - collected.extend(find_major_minor_device(dirs, major, minor)) - ## filter all device inodes in this directory - collected.extend([os.path.realpath(os.path.join(dirpath, e)) - for e in os.listdir(dirpath) - if (os.major(os.stat(os.path.join(dirpath, e)).st_rdev) == major) \ - and (os.minor(os.stat(os.path.join(dirpath, e)).st_rdev) == minor)]) - ## remove double entries - result = [] - for item in collected: - if item not in result: - result.append(item) - return result - except OSError: - return [] + """Returns the names of devices with the specified major and minor number. + """ + collected = [] + try: + subdirs = [os.path.join(dirpath, e) for e in os.listdir(dirpath) + if (not os.path.islink(os.path.join(dirpath, e))) and \ + os.path.isdir(os.path.join(dirpath, e))] + ## do a recursive call to parse the directory tree + for dirs in subdirs: + collected.extend(find_major_minor_device(dirs, major, minor)) + ## filter all device inodes in this directory + collected.extend([os.path.realpath(os.path.join(dirpath, e)) + for e in os.listdir(dirpath) + if (os.major(os.stat(os.path.join(dirpath, e)).st_rdev) == major) \ + and (os.minor(os.stat(os.path.join(dirpath, e)).st_rdev) == minor)]) + ## remove double entries + result = [] + for item in collected: + if item not in result: + result.append(item) + return result + except OSError: + return [] def get_parent_blockdevices(): - """Return a list of all block devices that contain other devices. - """ - devs = [] - for line in file("/proc/partitions"): - p_details = line.split() - ## we expect four values - otherwise continue with next iteration - if len(p_details) != 4: - continue - (p_major, p_minor, p_size, p_device) = p_details - ## we expect numeric values in the first two columns - if re.search(r'\D', p_major) or re.search(r'\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 [ get_absolute_devicename(e) for e in devs ] + """Return a list of all block devices that contain other devices. + """ + devs = [] + for line in file("/proc/partitions"): + p_details = line.split() + ## we expect four values - otherwise continue with next iteration + if len(p_details) != 4: + continue + (p_major, p_minor, p_size, p_device) = p_details + ## we expect numeric values in the first two columns + if re.search(r'\D', p_major) or re.search(r'\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 [ get_absolute_devicename(e) for e in devs ] 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 - """ - try: - (par_major, par_minor) = find_major_minor_of_device(parent) - (sub_major, sub_minor) = find_major_minor_of_device(subdevice) - except TypeError: - ## at least one of these devices did not return a valid major/minor combination - return False - ## search the entry below '/sys/block' belonging to the parent - root = os.path.join(os.path.sep, 'sys', 'block') - for bldev in os.listdir(root): - blpath = os.path.join(root, bldev, 'dev') - if os.access(blpath, os.R_OK): - try: - if (str(par_major), str(par_minor)) == tuple([e - for e in file(blpath)][0].strip().split(":",1)): - parent_path = os.path.join(root, bldev) - break - except (IndexError, OSError): - pass - else: - ## no block device with this major/minor combination found below '/sys/block' - return False - for subbldev in os.listdir(parent_path): - subblpath = os.path.join(parent_path, subbldev, "dev") - if os.access(subblpath, os.R_OK): - try: - if (str(sub_major), str(sub_minor)) == tuple([e - for e in file(subblpath)][0].strip().split(":",1)): - ## the name of the subdevice node is not important - we found it! - return True - except (IndexError, OSError): - pass - return False + e.g. for checking if a partition belongs to a block device + """ + try: + (par_major, par_minor) = find_major_minor_of_device(parent) + (sub_major, sub_minor) = find_major_minor_of_device(subdevice) + except TypeError: + ## at least one of these devices did not return a valid major/minor combination + return False + ## search the entry below '/sys/block' belonging to the parent + root = os.path.join(os.path.sep, 'sys', 'block') + for bldev in os.listdir(root): + blpath = os.path.join(root, bldev, 'dev') + if os.access(blpath, os.R_OK): + try: + if (str(par_major), str(par_minor)) == tuple([e + for e in file(blpath)][0].strip().split(":",1)): + parent_path = os.path.join(root, bldev) + break + except (IndexError, OSError): + pass + else: + ## no block device with this major/minor combination found below '/sys/block' + return False + for subbldev in os.listdir(parent_path): + subblpath = os.path.join(parent_path, subbldev, "dev") + if os.access(subblpath, os.R_OK): + try: + if (str(sub_major), str(sub_minor)) == tuple([e + for e in file(subblpath)][0].strip().split(":",1)): + ## the name of the subdevice node is not important - we found it! + return True + except (IndexError, OSError): + pass + return False def get_blockdevice_size(device): - """Return the size of a blockdevice in megabyte. - """ - if not device: - return -1 - try: - rdev = os.stat(device).st_rdev - except OSError: - return -1 - minor = os.minor(rdev) - major = os.major(rdev) - for line in file("/proc/partitions"): - try: - elements = line.split() - if len(elements) != 4: - continue - if (int(elements[0]) == major) and (int(elements[1]) == minor): - return int(elements[2])/1024 - except ValueError: - pass - return -1 + """Return the size of a blockdevice in megabyte. + """ + if not device: + return -1 + try: + rdev = os.stat(device).st_rdev + except OSError: + return -1 + minor = os.minor(rdev) + major = os.major(rdev) + for line in file("/proc/partitions"): + try: + elements = line.split() + if len(elements) != 4: + continue + if (int(elements[0]) == major) and (int(elements[1]) == minor): + return int(elements[2])/1024 + except ValueError: + pass + return -1 def get_blockdevice_size_humanly(device): - """Return a human readable size of a blockdevice. - """ - size = get_blockdevice_size(device) - if size > 5120: - return "%dGB" % int(size/1024) - else: - return "%dMB" % size + """Return a human readable size of a blockdevice. + """ + size = get_blockdevice_size(device) + if size > 5120: + return "%dGB" % int(size/1024) + else: + return "%dMB" % size diff --git a/src/cryptobox/plugins/base.py b/src/cryptobox/plugins/base.py index 04251f6..d71da07 100644 --- a/src/cryptobox/plugins/base.py +++ b/src/cryptobox/plugins/base.py @@ -32,282 +32,282 @@ import imp class CryptoBoxPlugin: - """The base class of all features. - """ + """The base class of all features. + """ - ## default capability is "system" - the other supported capability is: "volume" - plugin_capabilities = [ "system" ] + ## default capability is "system" - the other supported capability is: "volume" + plugin_capabilities = [ "system" ] - ## where should the plugin be visible by default? - plugin_visibility = [ "preferences" ] + ## where should the plugin be visible by default? + plugin_visibility = [ "preferences" ] - ## does this plugin require admin authentification? - request_auth = False + ## does this plugin require admin authentification? + request_auth = False - ## default rank (0..100) of the plugin in listings (lower value means higher priority) - rank = 80 + ## default rank (0..100) of the plugin in listings (lower value means higher priority) + rank = 80 - ## default icon of this plugin (relative path) - default_icon_filename = "plugin_icon" + ## default icon of this plugin (relative path) + default_icon_filename = "plugin_icon" - ## fallback icon file (in the common plugin directory) - fallback_icon_filename = "plugin_icon_unknown" + ## fallback icon file (in the common plugin directory) + fallback_icon_filename = "plugin_icon_unknown" - def __init__(self, cbox, plugin_dir, site_class=None): - if cbox: - self.cbox = cbox - else: - ## define empty dummy class as a replacement for a cbox instance - class CBoxPrefs(dict): - plugin_conf = {} - class CBoxLogger: - def debug(self, text): - pass - info = debug - warn = debug - error = debug - class CBoxMinimal: - prefs = CBoxPrefs() - prefs["PluginSettings"] = {} - log = CBoxLogger() - self.cbox = CBoxMinimal() - self.hdf = {} - self.plugin_dir = plugin_dir - self.hdf_prefix = "Data.Plugins.%s." % self.get_name() - self.site = site_class - if not self.get_name() in self.cbox.prefs.plugin_conf: - ## initialize plugin configuration - self.cbox.prefs.plugin_conf[self.get_name()] = {} - self.prefs = self.cbox.prefs.plugin_conf[self.get_name()] - self.cbox.log.debug("Plugin '%s': settings " % self.get_name() + \ - "loaded from plugin configuration file: %s" % str(self.prefs)) - if self.get_name() in self.cbox.prefs["PluginSettings"]: - self.defaults = self.cbox.prefs["PluginSettings"][self.get_name()] - else: - self.defaults = {} - self.cbox.log.debug("Plugin '%s': configuration " % self.get_name() + \ - "settings imported from global config file: %s" % str(self.defaults)) - ## load a possibly existing "root_action.py" scripts as self.root_action - if os.path.isfile(os.path.join(self.plugin_dir, "root_action.py")): - self.root_action = imp.load_source("root_action", - os.path.join(self.plugin_dir, "root_action.py")) - else: - self.root_action = None + def __init__(self, cbox, plugin_dir, site_class=None): + if cbox: + self.cbox = cbox + else: + ## define empty dummy class as a replacement for a cbox instance + class CBoxPrefs(dict): + plugin_conf = {} + class CBoxLogger: + def debug(self, text): + pass + info = debug + warn = debug + error = debug + class CBoxMinimal: + prefs = CBoxPrefs() + prefs["PluginSettings"] = {} + log = CBoxLogger() + self.cbox = CBoxMinimal() + self.hdf = {} + self.plugin_dir = plugin_dir + self.hdf_prefix = "Data.Plugins.%s." % self.get_name() + self.site = site_class + if not self.get_name() in self.cbox.prefs.plugin_conf: + ## initialize plugin configuration + self.cbox.prefs.plugin_conf[self.get_name()] = {} + self.prefs = self.cbox.prefs.plugin_conf[self.get_name()] + self.cbox.log.debug("Plugin '%s': settings " % self.get_name() + \ + "loaded from plugin configuration file: %s" % str(self.prefs)) + if self.get_name() in self.cbox.prefs["PluginSettings"]: + self.defaults = self.cbox.prefs["PluginSettings"][self.get_name()] + else: + self.defaults = {} + self.cbox.log.debug("Plugin '%s': configuration " % self.get_name() + \ + "settings imported from global config file: %s" % str(self.defaults)) + ## load a possibly existing "root_action.py" scripts as self.root_action + if os.path.isfile(os.path.join(self.plugin_dir, "root_action.py")): + self.root_action = imp.load_source("root_action", + os.path.join(self.plugin_dir, "root_action.py")) + else: + self.root_action = None - def do_action(self, **args): - """Override do_action with your plugin code - """ - raise Exception, \ - "undefined action handler ('do_action') in plugin '%s'" % self.get_name() - + def do_action(self, **args): + """Override do_action with your plugin code + """ + raise Exception, \ + "undefined action handler ('do_action') in plugin '%s'" % self.get_name() + - def get_status(self): - """you should override this, to supply useful state information - """ - raise Exception, \ - "undefined state handler ('get_status') in plugin '%s'" % self.get_name() - - def is_useful(self, device): - """Return if this plugin is useful for a specific device. + def get_status(self): + """you should override this, to supply useful state information + """ + raise Exception, \ + "undefined state handler ('get_status') in plugin '%s'" % self.get_name() + + def is_useful(self, device): + """Return if this plugin is useful for a specific device. - This should only be used for volume plugins. Nice for output filtering. - """ - return True + This should only be used for volume plugins. Nice for output filtering. + """ + return True - def get_name(self): - """the name of the python file (module) should be the name of the plugin - """ - return self.__module__ - + def get_name(self): + """the name of the python file (module) should be the name of the plugin + """ + return self.__module__ + - def handle_event(self, event_name, event_info=None): - """Any plugin that wants to define event actions may override this. + def handle_event(self, event_name, event_info=None): + """Any plugin that wants to define event actions may override this. - currently only the following events are defined: - - "bootup" (the cryptobox server is starting) - - "shutdown" (the cryptobox server is stopping) - """ - pass + currently only the following events are defined: + - "bootup" (the cryptobox server is starting) + - "shutdown" (the cryptobox server is stopping) + """ + pass - - def get_warnings(self): - """Return a priority and a warning, if the plugin detects a misconfiguration + + def get_warnings(self): + """Return a priority and a warning, if the plugin detects a misconfiguration - valid prioritie ranges are: - - 80..99 loss of data is possible - - 60..79 the cryptobox will probably not work at all - - 40..59 important features will propably not work - - 20..39 heavy security risk OR broken recommended features - - 00..19 possible mild security risk OR broken/missing optional features - """ - return [] + valid prioritie ranges are: + - 80..99 loss of data is possible + - 60..79 the cryptobox will probably not work at all + - 40..59 important features will propably not work + - 20..39 heavy security risk OR broken recommended features + - 00..19 possible mild security risk OR broken/missing optional features + """ + return [] - @cherrypy.expose - def download(self, **kargs): - """Deliver a downloadable file - by default return nothing - """ - return "" + @cherrypy.expose + def download(self, **kargs): + """Deliver a downloadable file - by default return nothing + """ + return "" - @cherrypy.expose - def get_icon(self, image=None, **kargs): - """return the image data of the icon of the plugin - - the parameter 'image' may be used for alternative image locations (relative - to the directory of the plugin) - '**kargs' is necessary, as a 'weblang' attribute may be specified (and ignored) - """ - import re - icon_ext = self.__get_default_icon_extension() - if (image is None) or (not re.match(r'[\w\-\.]*$', image)): - plugin_icon_file = os.path.join(self.plugin_dir, - "%s.%s" % (self.default_icon_filename, icon_ext)) - else: - plugin_icon_file = os.path.join(self.plugin_dir, image) - ## check if we can find the fallback plugin icon in one of the - ## plugin directories - if not os.access(plugin_icon_file, os.R_OK): - for ppath in self.cbox.prefs["Locations"]["PluginDir"]: - plugin_icon_file = os.path.join(ppath, - "%s.%s" % (self.fallback_icon_filename, icon_ext)) - if plugin_icon_file: - break - return cherrypy.lib.cptools.serveFile(plugin_icon_file) - + @cherrypy.expose + def get_icon(self, image=None, **kargs): + """return the image data of the icon of the plugin + + the parameter 'image' may be used for alternative image locations (relative + to the directory of the plugin) + '**kargs' is necessary, as a 'weblang' attribute may be specified (and ignored) + """ + import re + icon_ext = self.__get_default_icon_extension() + if (image is None) or (not re.match(r'[\w\-\.]*$', image)): + plugin_icon_file = os.path.join(self.plugin_dir, + "%s.%s" % (self.default_icon_filename, icon_ext)) + else: + plugin_icon_file = os.path.join(self.plugin_dir, image) + ## check if we can find the fallback plugin icon in one of the + ## plugin directories + if not os.access(plugin_icon_file, os.R_OK): + for ppath in self.cbox.prefs["Locations"]["PluginDir"]: + plugin_icon_file = os.path.join(ppath, + "%s.%s" % (self.fallback_icon_filename, icon_ext)) + if plugin_icon_file: + break + return cherrypy.lib.cptools.serveFile(plugin_icon_file) + - def __get_default_icon_extension(self): - """Return 'png' or 'gif' depending on the 'User-Agent' request header + def __get_default_icon_extension(self): + """Return 'png' or 'gif' depending on the 'User-Agent' request header - This is useful, as IE 5.5/6.0 does not render transparent png graphics properly - Internet Explorer 5.5/6.0: return 'gif' - everything else: return 'png' - """ - if ("User-Agent" in cherrypy.request.headers) and \ - ((cherrypy.request.headers["User-Agent"].find("MSIE 5.5;") != -1) or \ - (cherrypy.request.headers["User-Agent"].find("MSIE 6.0;") != -1)): - return "gif" - else: - return "png" + This is useful, as IE 5.5/6.0 does not render transparent png graphics properly + Internet Explorer 5.5/6.0: return 'gif' + everything else: return 'png' + """ + if ("User-Agent" in cherrypy.request.headers) and \ + ((cherrypy.request.headers["User-Agent"].find("MSIE 5.5;") != -1) or \ + (cherrypy.request.headers["User-Agent"].find("MSIE 6.0;") != -1)): + return "gif" + else: + return "png" - def get_template_filename(self, template_name): - """return the filename of the template, if it is part of this plugin + def get_template_filename(self, template_name): + """return the filename of the template, if it is part of this plugin - use this function to check, if the plugin provides the specified template - """ - result_file = os.path.join(self.plugin_dir, template_name + ".cs") - if os.access(result_file, os.R_OK) and os.path.isfile(result_file): - return result_file - else: - return None + use this function to check, if the plugin provides the specified template + """ + result_file = os.path.join(self.plugin_dir, template_name + ".cs") + if os.access(result_file, os.R_OK) and os.path.isfile(result_file): + return result_file + else: + return None - def get_language_data(self): - """Retrieve the language data of the feature. + def get_language_data(self): + """Retrieve the language data of the feature. - Typically this is the content of the language.hdf file as a HDF object. - """ - import neo_cgi, neo_util - lang_hdf = neo_util.HDF() - lang_file = os.path.join(self.plugin_dir, 'language.hdf') - try: - lang_hdf.readFile(lang_file) - except (neo_util.Error, neo_util.ParseError): - self.cbox.log.error("failed to load language file (%s) of plugin (%s):" % \ - (lang_file, self.get_name())) - return lang_hdf + Typically this is the content of the language.hdf file as a HDF object. + """ + import neo_cgi, neo_util + lang_hdf = neo_util.HDF() + lang_file = os.path.join(self.plugin_dir, 'language.hdf') + try: + lang_hdf.readFile(lang_file) + except (neo_util.Error, neo_util.ParseError): + self.cbox.log.error("failed to load language file (%s) of plugin (%s):" % \ + (lang_file, self.get_name())) + return lang_hdf - def load_dataset(self, hdf): - """Add the local values of the feature to the hdf dataset. - """ - for (key, value) in self.hdf.items(): - hdf.setValue(key, str(value)) - ## add the stylesheet file if it exists - css_file = os.path.join(self.plugin_dir, self.get_name() + ".css") - if os.path.exists(css_file): - hdf.setValue("Data.StylesheetFiles.%s" % self.get_name(), css_file) - + def load_dataset(self, hdf): + """Add the local values of the feature to the hdf dataset. + """ + for (key, value) in self.hdf.items(): + hdf.setValue(key, str(value)) + ## add the stylesheet file if it exists + css_file = os.path.join(self.plugin_dir, self.get_name() + ".css") + if os.path.exists(css_file): + hdf.setValue("Data.StylesheetFiles.%s" % self.get_name(), css_file) + - def is_auth_required(self): - """check if this plugin requires authentication - first step: check plugin configuration - second step: check default value of plugin - """ - if ("requestAuth" in self.prefs) and (not self.prefs["requestAuth"] is None): - return bool(self.prefs["requestAuth"]) - else: - return self.request_auth + def is_auth_required(self): + """check if this plugin requires authentication + first step: check plugin configuration + second step: check default value of plugin + """ + if ("requestAuth" in self.prefs) and (not self.prefs["requestAuth"] is None): + return bool(self.prefs["requestAuth"]) + else: + return self.request_auth - def is_enabled(self): - """check if this plugin is enabled - first step: check plugin configuration - second step: check default value of plugin - """ - if ("visibility" in self.prefs) and (not self.prefs["visibility"] is None): - return bool(self.prefs["visibility"]) - else: - return bool(self.plugin_visibility) + def is_enabled(self): + """check if this plugin is enabled + first step: check plugin configuration + second step: check default value of plugin + """ + if ("visibility" in self.prefs) and (not self.prefs["visibility"] is None): + return bool(self.prefs["visibility"]) + else: + return bool(self.plugin_visibility) - def get_rank(self): - """check the rank of this plugin - first step: check plugin configuration - second step: check default value of plugin - """ - if ("rank" in self.prefs) and (not self.prefs["rank"] is None): - return int(self.prefs["rank"]) - else: - return int(self.rank) - + def get_rank(self): + """check the rank of this plugin + first step: check plugin configuration + second step: check default value of plugin + """ + if ("rank" in self.prefs) and (not self.prefs["rank"] is None): + return int(self.prefs["rank"]) + else: + return int(self.rank) + - def set_rank(self, rank): - """change the current rank of the plugin in plugin_conf - 'rank' should be an integer - """ - self.prefs["rank"] = rank - + def set_rank(self, rank): + """change the current rank of the plugin in plugin_conf + 'rank' should be an integer + """ + self.prefs["rank"] = rank + - def get_visibility(self): - """Check which visibility flags of the plugin are set. - """ - try: - if self.prefs["visibility"] is None: - return self.plugin_visibility[:] - return self.prefs["visibility"] - except KeyError: - return self.plugin_visibility + def get_visibility(self): + """Check which visibility flags of the plugin are set. + """ + try: + if self.prefs["visibility"] is None: + return self.plugin_visibility[:] + return self.prefs["visibility"] + except KeyError: + return self.plugin_visibility - def reset(self): - """Reinitialize the plugin. + def reset(self): + """Reinitialize the plugin. - This function should be called before every run - """ - self.hdf = {} + This function should be called before every run + """ + self.hdf = {} - def get_test_class(self): - """Return the unittest class of the feature. - """ - import imp - pl_file = os.path.join(self.plugin_dir, "unittests.py") - if os.access(pl_file, os.R_OK) and os.path.isfile(pl_file): - try: - return imp.load_source("unittests_%s" % self.get_name(), pl_file).unittests - except AttributeError: - pass - try: - self.cbox.log.info("could not load unittests for plugin: %s" % \ - self.get_name()) - except AttributeError: - pass - return None + def get_test_class(self): + """Return the unittest class of the feature. + """ + import imp + pl_file = os.path.join(self.plugin_dir, "unittests.py") + if os.access(pl_file, os.R_OK) and os.path.isfile(pl_file): + try: + return imp.load_source("unittests_%s" % self.get_name(), pl_file).unittests + except AttributeError: + pass + try: + self.cbox.log.info("could not load unittests for plugin: %s" % \ + self.get_name()) + except AttributeError: + pass + return None diff --git a/src/cryptobox/plugins/manage.py b/src/cryptobox/plugins/manage.py index aa6b81d..ab21223 100644 --- a/src/cryptobox/plugins/manage.py +++ b/src/cryptobox/plugins/manage.py @@ -28,80 +28,80 @@ import os class PluginManager: - """manage available plugins""" + """manage available plugins""" - def __init__(self, cbox, plugin_dirs=".", site_class=None): - self.cbox = cbox - self.site = site_class - if hasattr(plugin_dirs, "__iter__"): - self.plugin_dirs = [os.path.abspath(d) for d in plugin_dirs] - else: - self.plugin_dirs = [os.path.abspath(plugin_dirs)] - self.plugin_list = self.__get_all_plugins() + def __init__(self, cbox, plugin_dirs=".", site_class=None): + self.cbox = cbox + self.site = site_class + if hasattr(plugin_dirs, "__iter__"): + self.plugin_dirs = [os.path.abspath(d) for d in plugin_dirs] + else: + self.plugin_dirs = [os.path.abspath(plugin_dirs)] + self.plugin_list = self.__get_all_plugins() - - def get_plugins(self): - """Return a list of all feature instances. - """ - return self.plugin_list[:] - + + def get_plugins(self): + """Return a list of all feature instances. + """ + return self.plugin_list[:] + - def get_plugin(self, name): - """Return the specified feature as an instance. - """ - for plugin in self.plugin_list[:]: - if plugin.get_name() == name: - return plugin - return None + def get_plugin(self, name): + """Return the specified feature as an instance. + """ + for plugin in self.plugin_list[:]: + if plugin.get_name() == name: + return plugin + return None - def __get_all_plugins(self): - """Return all available features as instances. - """ - plist = [] - for plfile in self.__get_plugin_files(): - plist.append(self.__get_plugin_class(plfile)) - return plist + def __get_all_plugins(self): + """Return all available features as instances. + """ + plist = [] + for plfile in self.__get_plugin_files(): + plist.append(self.__get_plugin_class(plfile)) + return plist - def __get_plugin_class(self, plfile): - """Return a instance object of the give feature. - """ - import imp - name = os.path.basename(plfile)[:-3] - try: - pl_class = getattr(imp.load_source(name, plfile), name) - except AttributeError: - return None - return pl_class(self.cbox, os.path.dirname(plfile), self.site) - + def __get_plugin_class(self, plfile): + """Return a instance object of the give feature. + """ + import imp + name = os.path.basename(plfile)[:-3] + try: + pl_class = getattr(imp.load_source(name, plfile), name) + except AttributeError: + return None + return pl_class(self.cbox, os.path.dirname(plfile), self.site) + - def __get_plugin_files(self): - """Retrieve all python files that may potentially be a feature. - """ - result = [] - if self.cbox and self.cbox.prefs["Main"]["DisabledPlugins"]: - disabled = self.cbox.prefs["Main"]["DisabledPlugins"] - else: - disabled = [] - for pdir in [os.path.abspath(e) for e in self.plugin_dirs - if os.access(e, os.R_OK) and os.path.isdir(e)]: - for plname in [f for f in os.listdir(pdir)]: - if plname in disabled: - if self.cbox: - self.cbox.log.info( - "Skipping plugin '%s' (disabled via config)" % plname) - continue - pldir = os.path.join(pdir, plname) - plfile = os.path.join(pldir, plname + ".py") - if os.path.isfile(plfile) and os.access(plfile, os.R_OK): - result.append(plfile) - return result + def __get_plugin_files(self): + """Retrieve all python files that may potentially be a feature. + """ + result = [] + if self.cbox and self.cbox.prefs["Main"]["DisabledPlugins"]: + disabled = self.cbox.prefs["Main"]["DisabledPlugins"] + else: + disabled = [] + for pdir in [os.path.abspath(e) for e in self.plugin_dirs + if os.access(e, os.R_OK) and os.path.isdir(e)]: + for plname in [f for f in os.listdir(pdir)]: + if plname in disabled: + if self.cbox: + self.cbox.log.info( + "Skipping plugin '%s' (disabled via config)" % plname) + continue + pldir = os.path.join(pdir, plname) + plfile = os.path.join(pldir, plname + ".py") + if os.path.isfile(plfile) and os.access(plfile, os.R_OK): + result.append(plfile) + return result if __name__ == "__main__": - MANAGER = PluginManager(None, "../plugins") - for one_plugin in MANAGER.get_plugins(): - if not one_plugin is None: - print "Plugin: %s" % one_plugin.get_name() + MANAGER = PluginManager(None, "../plugins") + for one_plugin in MANAGER.get_plugins(): + if not one_plugin is None: + print "Plugin: %s" % one_plugin.get_name() diff --git a/src/cryptobox/tests/__init__.py b/src/cryptobox/tests/__init__.py index 7194f5d..e04586d 100644 --- a/src/cryptobox/tests/__init__.py +++ b/src/cryptobox/tests/__init__.py @@ -4,5 +4,5 @@ __revision__ = "$Id$" __all__ = [ 'test.cryptobox', 'test.cryptoboxtools', 'test.plugins', 'test.websites', - 'base', 'tools' ] + 'base', 'tools' ] diff --git a/src/cryptobox/tests/base.py b/src/cryptobox/tests/base.py index 3233f2b..8cfb74f 100644 --- a/src/cryptobox/tests/base.py +++ b/src/cryptobox/tests/base.py @@ -24,16 +24,16 @@ this module contains all super classes for different tests just inherit one of its classes and add some test functions All testclasses based on the classes of this module may assume the following: - - there is one valid parent blockdevice (self.blockdevice) - - the blockdevice contains exactly two partitions: - - part1: vfat, 50MB, formatted (devicename: self.device) - - part2: ext3, 50MB, formatted - - self.blockdevice_html and self.device_html are url-escaped strings - - all databases (pluginconf, volume names, users) are empty + - there is one valid parent blockdevice (self.blockdevice) + - the blockdevice contains exactly two partitions: + - part1: vfat, 50MB, formatted (devicename: self.device) + - part2: ext3, 50MB, formatted + - self.blockdevice_html and self.device_html are url-escaped strings + - all databases (pluginconf, volume names, users) are empty Additional hints: - - if the current state of self.device is important, then you should umount - it before any of these tests: cryptobox.tests.tools.umount(self.device) + - if the current state of self.device is important, then you should umount + it before any of these tests: cryptobox.tests.tools.umount(self.device) """ __revision__ = "$Id$" @@ -56,105 +56,105 @@ CONF_FILE = 'cryptobox-unittests.conf' class CommonTestClass(unittest.TestCase): - """Super class of all tests of the CryptoBox + """Super class of all tests of the CryptoBox - prepare environment, set some values ... - """ + prepare environment, set some values ... + """ - def __init__(self, methodName='runTest'): - unittest.TestCase.__init__(self, methodName) - import cryptobox.core.settings as cbox_settings - import cryptobox.tests.tools as testtools - import os - ## search for a usable block device - ## use /dev/ubd? if possible - otherwise /dev/hd? - ## so it will be possible to use these tests inside of a uml - self.blockdevice = testtools.find_test_device() - ## umount the partitions of this device (just to be sure) - for num in range(12): - testtools.umount("%s%d" % (self.blockdevice, num)) - ## format device and partition block device if necessary - testtools.prepare_partition(self.blockdevice) - self.blockdevice = testtools.find_test_device() - self.device = self.blockdevice + "1" - self.blockdevice_html = self.blockdevice.replace("/", "%2F") - self.device_html = self.device.replace("/", "%2F") - - ## remove configuration files - ## first: retrieve the settings directory - settings_dir = cbox_settings.CryptoBoxSettings(CONF_FILE)\ - ["Locations"]["SettingsDir"] - for filename in [ - cbox_settings.VOLUMESDB_FILE, - cbox_settings.PLUGINCONF_FILE, - cbox_settings.USERDB_FILE]: - try: - os.unlink(os.path.join(settings_dir, filename)) - except OSError: - pass + def __init__(self, methodName='runTest'): + unittest.TestCase.__init__(self, methodName) + import cryptobox.core.settings as cbox_settings + import cryptobox.tests.tools as testtools + import os + ## search for a usable block device + ## use /dev/ubd? if possible - otherwise /dev/hd? + ## so it will be possible to use these tests inside of a uml + self.blockdevice = testtools.find_test_device() + ## umount the partitions of this device (just to be sure) + for num in range(12): + testtools.umount("%s%d" % (self.blockdevice, num)) + ## format device and partition block device if necessary + testtools.prepare_partition(self.blockdevice) + self.blockdevice = testtools.find_test_device() + self.device = self.blockdevice + "1" + self.blockdevice_html = self.blockdevice.replace("/", "%2F") + self.device_html = self.device.replace("/", "%2F") + + ## remove configuration files + ## first: retrieve the settings directory + settings_dir = cbox_settings.CryptoBoxSettings(CONF_FILE)\ + ["Locations"]["SettingsDir"] + for filename in [ + cbox_settings.VOLUMESDB_FILE, + cbox_settings.PLUGINCONF_FILE, + cbox_settings.USERDB_FILE]: + try: + os.unlink(os.path.join(settings_dir, filename)) + except OSError: + pass class WebInterfaceTestClass(CommonTestClass): - '''this class checks the webserver, using "twill" + '''this class checks the webserver, using "twill" - the tests in this class are from the browsers point of view, so not - really unittests. - fetch twill from: http://twill.idyll.org - ''' + the tests in this class are from the browsers point of view, so not + really unittests. + fetch twill from: http://twill.idyll.org + ''' - def __init__(self, methodName='runTest'): - CommonTestClass.__init__(self, methodName) + def __init__(self, methodName='runTest'): + CommonTestClass.__init__(self, methodName) - def setUp(self): - '''configures the cherrypy server that it works nice with twill - ''' - CommonTestClass.setUp(self) - cherrypy.config.update({ - 'server.logToScreen' : False, - 'autoreload.on': False, - 'server.threadPool': 1, - 'server.environment': 'development', - 'server.log_tracebacks': True, - 'server.log_file': WEBLOG_FILE, - }) - cherrypy.root = cryptobox.web.sites.WebInterfaceSites(CONF_FILE) + def setUp(self): + '''configures the cherrypy server that it works nice with twill + ''' + CommonTestClass.setUp(self) + cherrypy.config.update({ + 'server.logToScreen' : False, + 'autoreload.on': False, + 'server.threadPool': 1, + 'server.environment': 'development', + 'server.log_tracebacks': True, + 'server.log_file': WEBLOG_FILE, + }) + cherrypy.root = cryptobox.web.sites.WebInterfaceSites(CONF_FILE) - cherrypy.server.start(initOnly=True, serverClass=None) + cherrypy.server.start(initOnly=True, serverClass=None) - from cherrypy._cpwsgi import wsgiApp - twill.add_wsgi_intercept(CBXHOST, CBXPORT, lambda: wsgiApp) + from cherrypy._cpwsgi import wsgiApp + twill.add_wsgi_intercept(CBXHOST, CBXPORT, lambda: wsgiApp) - # grab the output of twill commands - self.output = open(LOG_FILE,"a") - twill.set_output(self.output) - self.cmd = twill.commands - self.url = CBX_URL - self.cbox = cherrypy.root.cbox - self.globals, self.locals = twill.namespaces.get_twill_glocals() + # grab the output of twill commands + self.output = open(LOG_FILE,"a") + twill.set_output(self.output) + self.cmd = twill.commands + self.url = CBX_URL + self.cbox = cherrypy.root.cbox + self.globals, self.locals = twill.namespaces.get_twill_glocals() - def tearDown(self): - '''clean up the room when leaving''' - ## remove intercept. - twill.remove_wsgi_intercept(CBXHOST, CBXPORT) - ## stop the cryptobox - cherrypy.root.cleanup() - ## shut down the cherrypy server. - cherrypy.server.stop() - self.output.close() - ## inform the parent - CommonTestClass.tearDown(self) + def tearDown(self): + '''clean up the room when leaving''' + ## remove intercept. + twill.remove_wsgi_intercept(CBXHOST, CBXPORT) + ## stop the cryptobox + cherrypy.root.cleanup() + ## shut down the cherrypy server. + cherrypy.server.stop() + self.output.close() + ## inform the parent + CommonTestClass.tearDown(self) - def __get_soup(): - browser = twill.commands.get_browser() - soup = BeautifulSoup(browser.get_html()) - return soup - + def __get_soup(): + browser = twill.commands.get_browser() + soup = BeautifulSoup(browser.get_html()) + return soup + - def register_auth(self, url, user="admin", password="admin"): - self.cmd.add_auth("CryptoBox", url, user, password) + def register_auth(self, url, user="admin", password="admin"): + self.cmd.add_auth("CryptoBox", url, user, password) diff --git a/src/cryptobox/tests/test.cryptobox.py b/src/cryptobox/tests/test.cryptobox.py index 62b051f..b2186e1 100755 --- a/src/cryptobox/tests/test.cryptobox.py +++ b/src/cryptobox/tests/test.cryptobox.py @@ -33,41 +33,41 @@ import os class CryptoBoxDeviceTests(CommonTestClass): - """Some unittests for the CryptoBox - """ + """Some unittests for the CryptoBox + """ - cb = cryptobox.core.main.CryptoBox() + cb = cryptobox.core.main.CryptoBox() - def test_allowed_devices(self): - '''is_device_allowed should accept permitted devices''' - self.assertTrue(self.cb.is_device_allowed("/dev/loop1")) - self.assertTrue(self.cb.is_device_allowed("/dev/usb/../loop1")) + def test_allowed_devices(self): + '''is_device_allowed should accept permitted devices''' + self.assertTrue(self.cb.is_device_allowed("/dev/loop1")) + self.assertTrue(self.cb.is_device_allowed("/dev/usb/../loop1")) - def test_denied_devices(self): - '''is_device_allowed should fail with not explicitly allowed devices''' - self.assertFalse(self.cb.is_device_allowed("/dev/hda")) - self.assertFalse(self.cb.is_device_allowed("/dev/loopa/../hda")) - self.assertFalse(self.cb.is_device_allowed("/")) - ## this device does not exist -> no permission check possible - self.assertFalse(self.cb.is_device_allowed("/dev/loop")) + def test_denied_devices(self): + '''is_device_allowed should fail with not explicitly allowed devices''' + self.assertFalse(self.cb.is_device_allowed("/dev/hda")) + self.assertFalse(self.cb.is_device_allowed("/dev/loopa/../hda")) + self.assertFalse(self.cb.is_device_allowed("/")) + ## this device does not exist -> no permission check possible + self.assertFalse(self.cb.is_device_allowed("/dev/loop")) class CryptoBoxConfigTests(CommonTestClass): - '''test here if everything with the config turns right''' - files = { - "configFileOK" : "cbox-test_ok.conf", - "configFileBroken" : "cbox-test_broken.conf", - "nameDBFile" : "cryptobox_volumes.db", - "pluginConf" : "cryptobox_plugins.conf", - "userDB" : "cryptobox_users.db", - "logFile" : "cryptobox.log", - "tmpdir" : "cryptobox-mnt" } - tmpdirname = "" - filenames = {} - configContentOK = """ + '''test here if everything with the config turns right''' + files = { + "configFileOK" : "cbox-test_ok.conf", + "configFileBroken" : "cbox-test_broken.conf", + "nameDBFile" : "cryptobox_volumes.db", + "pluginConf" : "cryptobox_plugins.conf", + "userDB" : "cryptobox_users.db", + "logFile" : "cryptobox.log", + "tmpdir" : "cryptobox-mnt" } + tmpdirname = "" + filenames = {} + configContentOK = """ [Main] AllowedDevices = /dev/loop DefaultVolumePrefix = "Data " @@ -94,92 +94,92 @@ CryptoBoxRootActions = CryptoBoxRootActions """ - def setUp(self): - '''prepare the test - ''' - CommonTestClass.setUp(self) - ## generate all files in tmp and remember the names - import tempfile - self.tmpdirname = tempfile.mkdtemp(prefix="cbox-") - for tfile in self.files.keys(): - self.filenames[tfile] = os.path.join(self.tmpdirname, self.files[tfile]) - self.write_config() + def setUp(self): + '''prepare the test + ''' + CommonTestClass.setUp(self) + ## generate all files in tmp and remember the names + import tempfile + self.tmpdirname = tempfile.mkdtemp(prefix="cbox-") + for tfile in self.files.keys(): + self.filenames[tfile] = os.path.join(self.tmpdirname, self.files[tfile]) + self.write_config() - def tearDown(self): - '''remove the created tmpfiles''' - # remove temp files - for tfile in self.filenames.values(): - compl_name = os.path.join(self.tmpdirname, tfile) - if os.path.exists(compl_name): - os.remove(compl_name) - # remove temp dir - os.rmdir(self.tmpdirname) - CommonTestClass.tearDown(self) + def tearDown(self): + '''remove the created tmpfiles''' + # remove temp files + for tfile in self.filenames.values(): + compl_name = os.path.join(self.tmpdirname, tfile) + if os.path.exists(compl_name): + os.remove(compl_name) + # remove temp dir + os.rmdir(self.tmpdirname) + CommonTestClass.tearDown(self) - def test_config_init(self): - '''Check various branches of config file loading''' - self.assertRaises(CBConfigUnavailableError, - cryptobox.core.main.CryptoBox,"/invalid/path/to/config/file") - self.assertRaises(CBConfigUnavailableError, - cryptobox.core.main.CryptoBox,"/etc/shadow") - ## check one of the following things: - ## 1) are we successfully using an existing config file? - ## 2) do we break, if no config file is there? - ## depending on the existence of a config file, only one of these conditions - ## can be checked - hints for more comprehensive tests are appreciated :) - for cfile in ['cryptobox.conf']: - if os.path.exists(cfile): - cryptobox.core.main.CryptoBox() - break # this skips the 'else' clause - else: - self.assertRaises(CBConfigUnavailableError, - cryptobox.core.main.CryptoBox) - self.assertRaises(CBConfigUnavailableError, - cryptobox.core.main.CryptoBox,[]) + def test_config_init(self): + '''Check various branches of config file loading''' + self.assertRaises(CBConfigUnavailableError, + cryptobox.core.main.CryptoBox,"/invalid/path/to/config/file") + self.assertRaises(CBConfigUnavailableError, + cryptobox.core.main.CryptoBox,"/etc/shadow") + ## check one of the following things: + ## 1) are we successfully using an existing config file? + ## 2) do we break, if no config file is there? + ## depending on the existence of a config file, only one of these conditions + ## can be checked - hints for more comprehensive tests are appreciated :) + for cfile in ['cryptobox.conf']: + if os.path.exists(cfile): + cryptobox.core.main.CryptoBox() + break # this skips the 'else' clause + else: + self.assertRaises(CBConfigUnavailableError, + cryptobox.core.main.CryptoBox) + self.assertRaises(CBConfigUnavailableError, + cryptobox.core.main.CryptoBox,[]) - - def test_broken_configs(self): - """Check various broken configurations - """ - self.write_config("SettingsDir", "SettingsDir=/foo/bar", - filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, - self.filenames["configFileBroken"]) - self.write_config("Level", "Level = ho", - filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, - self.filenames["configFileBroken"]) - self.write_config("Destination", "Destination = foobar", - filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, - self.filenames["configFileBroken"]) - self.write_config("super", "super=/bin/invalid/no", - filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, - self.filenames["configFileBroken"]) - self.write_config("CryptoBoxRootActions", "#not here", - filename=self.filenames["configFileBroken"]) - self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, - self.filenames["configFileBroken"]) - self.write_config("CryptoBoxRootActions", "CryptoBoxRootActions = /bin/false", - filename=self.filenames["configFileBroken"]) - self.assertRaises(CBEnvironmentError, cryptobox.core.main.CryptoBox, - self.filenames["configFileBroken"]) - + + def test_broken_configs(self): + """Check various broken configurations + """ + self.write_config("SettingsDir", "SettingsDir=/foo/bar", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("Level", "Level = ho", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("Destination", "Destination = foobar", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("super", "super=/bin/invalid/no", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("CryptoBoxRootActions", "#not here", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBConfigError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + self.write_config("CryptoBoxRootActions", "CryptoBoxRootActions = /bin/false", + filename=self.filenames["configFileBroken"]) + self.assertRaises(CBEnvironmentError, cryptobox.core.main.CryptoBox, + self.filenames["configFileBroken"]) + - def write_config(self, replace=None, newline=None, filename=None): - """write a config file and (optional) replace a line in it""" - import re - if not filename: - filename = self.filenames["configFileOK"] - content = self.configContentOK % \ - (self.tmpdirname, self.tmpdirname, self.tmpdirname) - if replace: - pattern = re.compile('^' + replace + '\\s*=.*$', flags=re.M) - content = re.sub(pattern, newline, content) - cfile = open(filename, "w") - cfile.write(content) - cfile.close() + def write_config(self, replace=None, newline=None, filename=None): + """write a config file and (optional) replace a line in it""" + import re + if not filename: + filename = self.filenames["configFileOK"] + content = self.configContentOK % \ + (self.tmpdirname, self.tmpdirname, self.tmpdirname) + if replace: + pattern = re.compile('^' + replace + '\\s*=.*$', flags=re.M) + content = re.sub(pattern, newline, content) + cfile = open(filename, "w") + cfile.write(content) + cfile.close() diff --git a/src/cryptobox/tests/test.cryptoboxtools.py b/src/cryptobox/tests/test.cryptoboxtools.py index 1579205..0a03a85 100755 --- a/src/cryptobox/tests/test.cryptoboxtools.py +++ b/src/cryptobox/tests/test.cryptoboxtools.py @@ -31,38 +31,38 @@ import os class CryptoBoxToolsTests(CommonTestClass): - """All unittests for cryptoboxtools - """ + """All unittests for cryptoboxtools + """ - def test_find_major_minor_of_device(self): - """check the find_major_minor_of_device function - """ - func = cbx_tools.find_major_minor_of_device - self.assertTrue(func(os.path.devnull) == (1, 3)) - self.assertTrue(func("/dev/nothere") is None) + def test_find_major_minor_of_device(self): + """check the find_major_minor_of_device function + """ + func = cbx_tools.find_major_minor_of_device + self.assertTrue(func(os.path.devnull) == (1, 3)) + self.assertTrue(func("/dev/nothere") is None) - def test_find_major_minor_device(self): - """check the find_major_minor_device function - """ - func = cbx_tools.find_major_minor_device - path = os.path.join(os.path.sep, "dev") - self.assertTrue(os.path.devnull in func(path, 1, 3)) - self.assertFalse(os.path.devnull in func(path, 2, 3)) - self.assertFalse(None in func(path, 17, 23)) + def test_find_major_minor_device(self): + """check the find_major_minor_device function + """ + func = cbx_tools.find_major_minor_device + path = os.path.join(os.path.sep, "dev") + self.assertTrue(os.path.devnull in func(path, 1, 3)) + self.assertFalse(os.path.devnull in func(path, 2, 3)) + self.assertFalse(None in func(path, 17, 23)) - def test_is_part_of_blockdevice(self): - """check the is_part_of_blockdevice function - """ - func = cbx_tools.is_part_of_blockdevice - self.assertTrue(func(self.blockdevice, self.device)) - self.assertFalse(func(self.blockdevice, self.blockdevice)) - self.assertFalse(func(self.device, self.blockdevice)) - self.assertFalse(func(self.device, self.device)) - self.assertFalse(func(self.blockdevice, "/dev/hde1")) - self.assertFalse(func(None, self.blockdevice)) - self.assertFalse(func(self.blockdevice, None)) - self.assertFalse(func(None, "")) - self.assertFalse(func("loop0", "loop1")) + def test_is_part_of_blockdevice(self): + """check the is_part_of_blockdevice function + """ + func = cbx_tools.is_part_of_blockdevice + self.assertTrue(func(self.blockdevice, self.device)) + self.assertFalse(func(self.blockdevice, self.blockdevice)) + self.assertFalse(func(self.device, self.blockdevice)) + self.assertFalse(func(self.device, self.device)) + self.assertFalse(func(self.blockdevice, "/dev/hde1")) + self.assertFalse(func(None, self.blockdevice)) + self.assertFalse(func(self.blockdevice, None)) + self.assertFalse(func(None, "")) + self.assertFalse(func("loop0", "loop1")) diff --git a/src/cryptobox/tests/test.plugins.py b/src/cryptobox/tests/test.plugins.py index 49353e1..27347f8 100755 --- a/src/cryptobox/tests/test.plugins.py +++ b/src/cryptobox/tests/test.plugins.py @@ -28,28 +28,28 @@ from cryptobox.tests.base import CommonTestClass import cryptobox.plugins.manage class CheckForUndefinedTestCases(CommonTestClass): - """here we will add failing test functions for every non-existing testcase""" + """here we will add failing test functions for every non-existing testcase""" def create_testcases(): - """Create functions that execute unittests for all features. - """ - plugins = cryptobox.plugins.manage.PluginManager(None, "../plugins").get_plugins() - glob_dict = globals() - loc_dict = locals() - for plugin in plugins: - test_class = plugin.get_test_class() - if test_class: - ## add the testclass to the global dictionary - glob_dict["unittest" + plugin.get_name()] = test_class - else: - subname = "test_existence_%s" % plugin.get_name() - def test_existence(self): - """check if the plugin (%s) contains tests""" % plugin.get_name() - self.fail("no tests defined for plugin: %s" % plugin.get_name()) - ## add this function to the class above - setattr(CheckForUndefinedTestCases, subname, test_existence) - #FIXME: the failure output always contains the same name for all plugins + """Create functions that execute unittests for all features. + """ + plugins = cryptobox.plugins.manage.PluginManager(None, "../plugins").get_plugins() + glob_dict = globals() + loc_dict = locals() + for plugin in plugins: + test_class = plugin.get_test_class() + if test_class: + ## add the testclass to the global dictionary + glob_dict["unittest" + plugin.get_name()] = test_class + else: + subname = "test_existence_%s" % plugin.get_name() + def test_existence(self): + """check if the plugin (%s) contains tests""" % plugin.get_name() + self.fail("no tests defined for plugin: %s" % plugin.get_name()) + ## add this function to the class above + setattr(CheckForUndefinedTestCases, subname, test_existence) + #FIXME: the failure output always contains the same name for all plugins create_testcases() diff --git a/src/cryptobox/tests/test.websites.py b/src/cryptobox/tests/test.websites.py index 81fbc02..97a3248 100755 --- a/src/cryptobox/tests/test.websites.py +++ b/src/cryptobox/tests/test.websites.py @@ -31,32 +31,32 @@ from cryptobox.tests.base import WebInterfaceTestClass class WebServer(WebInterfaceTestClass): - """Basic tests for the webserver. - """ + """Basic tests for the webserver. + """ - def test_is_server_running(self): - '''the server should run under given name and port''' - self.register_auth(self.url) - self.cmd.go(self.url) - self.cmd.find("CBOX-STATUS") - ## other URLs must not be checked, as we do not know, if they are valid + def test_is_server_running(self): + '''the server should run under given name and port''' + self.register_auth(self.url) + self.cmd.go(self.url) + self.cmd.find("CBOX-STATUS") + ## other URLs must not be checked, as we do not know, if they are valid class BuiltinPages(WebInterfaceTestClass): - """Basic test of builtin pages (no features). - """ + """Basic test of builtin pages (no features). + """ - def test_goto_index(self): - '''display all devices''' - self.register_auth(self.url) - self.cmd.go(self.url) - self.cmd.find("The CryptoBox") - self.cmd.go(self.url + "?weblang=de") - self.cmd.find("Die CryptoBox") - self.cmd.go(self.url + "?weblang=sl") - self.cmd.find("Privatnost v vsako vas") - self.cmd.go(self.url + "?weblang=fr") - self.cmd.find("La CryptoBox") + def test_goto_index(self): + '''display all devices''' + self.register_auth(self.url) + self.cmd.go(self.url) + self.cmd.find("The CryptoBox") + self.cmd.go(self.url + "?weblang=de") + self.cmd.find("Die CryptoBox") + self.cmd.go(self.url + "?weblang=sl") + self.cmd.find("Privatnost v vsako vas") + self.cmd.go(self.url + "?weblang=fr") + self.cmd.find("La CryptoBox") diff --git a/src/cryptobox/tests/tools.py b/src/cryptobox/tests/tools.py index 2c2a709..ddb201f 100644 --- a/src/cryptobox/tests/tools.py +++ b/src/cryptobox/tests/tools.py @@ -33,108 +33,108 @@ import os import subprocess def find_test_device(): - """Search for a valid test device - the data will get lost ... + """Search for a valid test device - the data will get lost ... - the result is the parent blockdevice (containing the partition table) - and the single partition - """ - for dev in TEST_DEVICE_LIST: - if os.path.exists("/dev/%s" % dev) \ - and os.access("/dev/%s" % dev, os.W_OK): - try: - ## try if it is a symlink - return os.readlink("/dev/%s" % dev) - except OSError: - ## not a symlink (usual) - return "/dev/%s" % dev - else: - raise Exception, "no valid device for testing found" - + the result is the parent blockdevice (containing the partition table) + and the single partition + """ + for dev in TEST_DEVICE_LIST: + if os.path.exists("/dev/%s" % dev) \ + and os.access("/dev/%s" % dev, os.W_OK): + try: + ## try if it is a symlink + return os.readlink("/dev/%s" % dev) + except OSError: + ## not a symlink (usual) + return "/dev/%s" % dev + else: + raise Exception, "no valid device for testing found" + def is_config_partition(device): - """Check if the device is a configuration partition. - """ - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ '/sbin/e2label', - device ]) - (stdout, stderr) = proc.communicate() - return stdout.strip() == "cbox_config" + """Check if the device is a configuration partition. + """ + 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 umount(device): - """Umount the specified device if possible - ignore errors - """ - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ '/bin/umount', '-d', device ]) - proc.wait() + """Umount the specified device if possible - ignore errors + """ + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ '/bin/umount', '-d', device ]) + proc.wait() def prepare_partition(blockdevice): - """Prepare the expected partition in the device (destroy all data) - - Check if 'device' is a vfat partition - if not, then - partition 'blockdevice' and format 'device' as vfat - """ - if (get_fs_type(blockdevice + "1") == "vfat") \ - and (get_fs_type(blockdevice + "2") == "ext3") \ - and (get_fs_type(blockdevice + "3") is None) \ - and (get_fs_type(blockdevice + "5") is None): - ## everything is fine - return - else: - ## repartitioning - proc = subprocess.Popen( - shell = False, - stdin = subprocess.PIPE, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ '/sbin/sfdisk', blockdevice ]) - proc.stdin.write(",50,0xC\n,50,L\n") - (output, error) = proc.communicate() - if proc.returncode != 0: - raise Exception, "could not partition the device (%s): %s" \ - % (blockdevice, output.strip()) - ## formatting - format_device(blockdevice + "1", "vfat") - format_device(blockdevice + "2", "ext3") + """Prepare the expected partition in the device (destroy all data) + + Check if 'device' is a vfat partition - if not, then + partition 'blockdevice' and format 'device' as vfat + """ + if (get_fs_type(blockdevice + "1") == "vfat") \ + and (get_fs_type(blockdevice + "2") == "ext3") \ + and (get_fs_type(blockdevice + "3") is None) \ + and (get_fs_type(blockdevice + "5") is None): + ## everything is fine + return + else: + ## repartitioning + proc = subprocess.Popen( + shell = False, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ '/sbin/sfdisk', blockdevice ]) + proc.stdin.write(",50,0xC\n,50,L\n") + (output, error) = proc.communicate() + if proc.returncode != 0: + raise Exception, "could not partition the device (%s): %s" \ + % (blockdevice, output.strip()) + ## formatting + format_device(blockdevice + "1", "vfat") + format_device(blockdevice + "2", "ext3") def get_fs_type(device): - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ '/sbin/blkid', - '-c', os.path.devnull, - '-w', os.path.devnull, - '-o', 'value', - '-s', 'TYPE', - device]) - (output, error) = proc.communicate() - if (proc.returncode == 0) and output.strip(): - ## everything is fine - return output.strip() - else: - return None + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ '/sbin/blkid', + '-c', os.path.devnull, + '-w', os.path.devnull, + '-o', 'value', + '-s', 'TYPE', + device]) + (output, error) = proc.communicate() + if (proc.returncode == 0) and output.strip(): + ## everything is fine + return output.strip() + else: + return None def format_device(device, fs_type="vfat"): - proc = subprocess.Popen( - shell = False, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - args = [ '/sbin/mkfs', - '-t', fs_type, - device ]) - (output, error) = proc.communicate() - if proc.returncode != 0: - raise OSError, "could not format the device (%s): %s" \ - % (device, output.strip()) - + proc = subprocess.Popen( + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ '/sbin/mkfs', + '-t', fs_type, + device ]) + (output, error) = proc.communicate() + if proc.returncode != 0: + raise OSError, "could not format the device (%s): %s" \ + % (device, output.strip()) + diff --git a/src/cryptobox/web/dataset.py b/src/cryptobox/web/dataset.py index 974a284..72a5219 100644 --- a/src/cryptobox/web/dataset.py +++ b/src/cryptobox/web/dataset.py @@ -28,199 +28,199 @@ import cryptobox.core.container as cbxContainer class WebInterfaceDataset(dict): - """this class contains all data that should be available for the clearsilver - templates - """ + """this class contains all data that should be available for the clearsilver + templates + """ - def __init__(self, cbox, prefs, plugin_manager): - """initialize the hdf dataset for the web site - """ - super(WebInterfaceDataset, self).__init__() - self.prefs = prefs - self.cbox = cbox - self.__set_config_values() - self.plugin_manager = plugin_manager - self.set_crypto_box_state() - self.set_plugin_data() - self.set_containers_state() + def __init__(self, cbox, prefs, plugin_manager): + """initialize the hdf dataset for the web site + """ + super(WebInterfaceDataset, self).__init__() + self.prefs = prefs + self.cbox = cbox + self.__set_config_values() + self.plugin_manager = plugin_manager + self.set_crypto_box_state() + self.set_plugin_data() + self.set_containers_state() - def set_crypto_box_state(self): - """Set some hdf values according to the cryptobox as a whole. - """ - import cherrypy - import cryptobox.core.main - import cryptobox.web.languages - import cryptobox - self["Data.Version"] = cryptobox.__version__ - ## first: clean the dataset up - necessary if we were called more than once - for key in self.keys(): - if key.startswith("Data.Languages."): - del self[key] - langs = self.cbox.prefs["WebSettings"]["Languages"][:] - langs.sort() - for (index, lang) in enumerate(langs): - try: - (langname, plural_info) = cryptobox.web.languages.LANGUAGE_INFO[lang] - self["Data.Languages.%d.link" % index] = langname - self["Data.Languages.%d.name" % index] = lang - self.cbox.log.info("language loaded: %s" % lang) - except KeyError: - ## language was not found - self.cbox.log.warn( - "invalid language specified in configuration: %s" % lang) + def set_crypto_box_state(self): + """Set some hdf values according to the cryptobox as a whole. + """ + import cherrypy + import cryptobox.core.main + import cryptobox.web.languages + import cryptobox + self["Data.Version"] = cryptobox.__version__ + ## first: clean the dataset up - necessary if we were called more than once + for key in self.keys(): + if key.startswith("Data.Languages."): + del self[key] + langs = self.cbox.prefs["WebSettings"]["Languages"][:] + langs.sort() + for (index, lang) in enumerate(langs): + try: + (langname, plural_info) = cryptobox.web.languages.LANGUAGE_INFO[lang] + self["Data.Languages.%d.link" % index] = langname + self["Data.Languages.%d.name" % index] = lang + self.cbox.log.info("language loaded: %s" % lang) + except KeyError: + ## language was not found + self.cbox.log.warn( + "invalid language specified in configuration: %s" % lang) - ## check the help setting - try: - if cherrypy.request.params["help"] == "1": - self["Settings.Help"] = "1" - self["Settings.LinkAttrs.help"] = "1" - except (AttributeError, KeyError): - ## no setting or first start before request - ## reset values - just in case we are called more than once - self["Settings.Help"] = "0" - if "Settings.LinkAttrs.help" in self: - del self["Settings.LinkAttrs.help"] + ## check the help setting + try: + if cherrypy.request.params["help"] == "1": + self["Settings.Help"] = "1" + self["Settings.LinkAttrs.help"] = "1" + except (AttributeError, KeyError): + ## no setting or first start before request + ## reset values - just in case we are called more than once + self["Settings.Help"] = "0" + if "Settings.LinkAttrs.help" in self: + del self["Settings.LinkAttrs.help"] - try: - self["Data.ScriptURL.Prot"] = cherrypy.request.scheme - host = cherrypy.request.headers["Host"] - self["Data.ScriptURL.Host"] = host.split(":", 1)[0] - complete_url = "%s://%s" % \ - (self["Data.ScriptURL.Prot"], self["Data.ScriptURL.Host"]) - try: - port = int(host.split(":", 1)[1]) - complete_url += ":%s" % port - except (IndexError, ValueError): - if cherrypy.request.scheme == "http": - port = 80 - elif cherrypy.request.scheme == "https": - port = 443 - else: - ## unknown scheme -> port 0 - self.cbox.log.info("unknown protocol scheme used: %s" % \ - (cherrypy.request.scheme,)) - port = 0 - self["Data.ScriptURL.Port"] = port - ## retrieve the relative address of the CGI (or the cherrypy base address) - ## remove the last part of the url and add a slash - path = "/".join(cherrypy.request.path.split("/")[:-1]) + "/" - self["Data.ScriptURL.Path"] = path - complete_url += path - self["Data.ScriptURL"] = complete_url - for (key, value) in cherrypy.request.params.items(): - if isinstance(value, list): - self["Data.ScriptParams.%s" % key] = value[0] - else: - self["Data.ScriptParams.%s" % key] = str(value) - if cherrypy.request.headers.has_key("CRYPTOBOX-Location"): - self.cbox.log.debug("ProxyLocation: %s" % \ - cherrypy.request.headers["CRYPTOBOX-Location"]) - self["Data.Proxy.ScriptPath"] = \ - cherrypy.request.headers["CRYPTOBOX-Location"] - if cherrypy.request.headers.has_key("X-Forwarded-Host"): - self.cbox.log.debug("ProxyHost: %s" % cherrypy.request.headers["X-Forwarded-Host"]) - self["Data.Proxy.Host"] = cherrypy.request.headers["X-Forwarded-Host"] - except AttributeError: - self["Data.ScriptURL"] = "" + try: + self["Data.ScriptURL.Prot"] = cherrypy.request.scheme + host = cherrypy.request.headers["Host"] + self["Data.ScriptURL.Host"] = host.split(":", 1)[0] + complete_url = "%s://%s" % \ + (self["Data.ScriptURL.Prot"], self["Data.ScriptURL.Host"]) + try: + port = int(host.split(":", 1)[1]) + complete_url += ":%s" % port + except (IndexError, ValueError): + if cherrypy.request.scheme == "http": + port = 80 + elif cherrypy.request.scheme == "https": + port = 443 + else: + ## unknown scheme -> port 0 + self.cbox.log.info("unknown protocol scheme used: %s" % \ + (cherrypy.request.scheme,)) + port = 0 + self["Data.ScriptURL.Port"] = port + ## retrieve the relative address of the CGI (or the cherrypy base address) + ## remove the last part of the url and add a slash + path = "/".join(cherrypy.request.path.split("/")[:-1]) + "/" + self["Data.ScriptURL.Path"] = path + complete_url += path + self["Data.ScriptURL"] = complete_url + for (key, value) in cherrypy.request.params.items(): + if isinstance(value, list): + self["Data.ScriptParams.%s" % key] = value[0] + else: + self["Data.ScriptParams.%s" % key] = str(value) + if cherrypy.request.headers.has_key("CRYPTOBOX-Location"): + self.cbox.log.debug("ProxyLocation: %s" % \ + cherrypy.request.headers["CRYPTOBOX-Location"]) + self["Data.Proxy.ScriptPath"] = \ + cherrypy.request.headers["CRYPTOBOX-Location"] + if cherrypy.request.headers.has_key("X-Forwarded-Host"): + self.cbox.log.debug("ProxyHost: %s" % cherrypy.request.headers["X-Forwarded-Host"]) + self["Data.Proxy.Host"] = cherrypy.request.headers["X-Forwarded-Host"] + except AttributeError: + self["Data.ScriptURL"] = "" - def set_current_disk_state(self, device): - """Set some hdf values according to the currently active disk. - """ - for container in self.cbox.get_container_list(): - if container.device == device: - is_encrypted = (container.get_type() == \ - cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0 - is_plain = (container.get_type() == \ - cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 - is_mounted = container.is_mounted() and 1 or 0 - is_busy = container.is_busy() and 1 or 0 - self["Data.CurrentDisk.device"] = device.name - self["Data.CurrentDisk.name"] = container.get_name() - self["Data.CurrentDisk.fs_type"] = container.get_fs_type() - self["Data.CurrentDisk.encryption"] = is_encrypted - self["Data.CurrentDisk.plaintext"] = is_plain - self["Data.CurrentDisk.active"] = is_mounted - self["Data.CurrentDisk.busy"] = is_busy - self["Data.CurrentDisk.size"] = device.size_human - if is_mounted: - self.cbox.log.debug("Retrieving container's data: %s" % \ - container.get_name()) - (size, avail, used) = container.get_capacity() - percent = int(used)*100 / int(size) - self.cbox.log.debug(percent) - self["Data.CurrentDisk.capacity.used"] = used - self["Data.CurrentDisk.capacity.free"] = avail - self["Data.CurrentDisk.capacity.size"] = size - self["Data.CurrentDisk.capacity.percent"] = percent - else: - for key in self.keys(): - if key.startswith("Data.CurrentDisk.capacity."): - del self[key] - break - self["Settings.LinkAttrs.device"] = device.name + def set_current_disk_state(self, device): + """Set some hdf values according to the currently active disk. + """ + for container in self.cbox.get_container_list(): + if container.device == device: + is_encrypted = (container.get_type() == \ + cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0 + is_plain = (container.get_type() == \ + cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 + is_mounted = container.is_mounted() and 1 or 0 + is_busy = container.is_busy() and 1 or 0 + self["Data.CurrentDisk.device"] = device.name + self["Data.CurrentDisk.name"] = container.get_name() + self["Data.CurrentDisk.fs_type"] = container.get_fs_type() + self["Data.CurrentDisk.encryption"] = is_encrypted + self["Data.CurrentDisk.plaintext"] = is_plain + self["Data.CurrentDisk.active"] = is_mounted + self["Data.CurrentDisk.busy"] = is_busy + self["Data.CurrentDisk.size"] = device.size_human + if is_mounted: + self.cbox.log.debug("Retrieving container's data: %s" % \ + container.get_name()) + (size, avail, used) = container.get_capacity() + percent = int(used)*100 / int(size) + self.cbox.log.debug(percent) + self["Data.CurrentDisk.capacity.used"] = used + self["Data.CurrentDisk.capacity.free"] = avail + self["Data.CurrentDisk.capacity.size"] = size + self["Data.CurrentDisk.capacity.percent"] = percent + else: + for key in self.keys(): + if key.startswith("Data.CurrentDisk.capacity."): + del self[key] + break + self["Settings.LinkAttrs.device"] = device.name - def set_containers_state(self): - """Set some hdf values according to the list of available containers. - """ - ## first: clean the dataset up - necessary if we were called more than once - for key in self.keys(): - if key.startswith("Data.Disks."): - del self[key] - avail_counter = 0 - active_counter = 0 - self.cbox.reread_container_list() - for container in self.cbox.get_container_list(): - ## useful if the container was changed during an action - container.reset_object() - is_encrypted = (container.get_type() == \ - cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0 - is_plain = (container.get_type() == \ - cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 - is_mounted = container.is_mounted() and 1 or 0 - is_busy = container.is_busy() and 1 or 0 - self["Data.Disks.%d.device" % avail_counter] = \ - container.device.name - self["Data.Disks.%d.name" % avail_counter] = container.get_name() - self["Data.Disks.%d.encryption" % avail_counter] = is_encrypted - self["Data.Disks.%d.plaintext" % avail_counter] = is_plain - self["Data.Disks.%d.busy" % avail_counter] = is_busy - self["Data.Disks.%d.active" % avail_counter] = is_mounted - self["Data.Disks.%d.size" % avail_counter] = \ - container.device.size_human - if is_mounted: - active_counter += 1 - avail_counter += 1 - self["Data.activeDisksCount"] = active_counter + def set_containers_state(self): + """Set some hdf values according to the list of available containers. + """ + ## first: clean the dataset up - necessary if we were called more than once + for key in self.keys(): + if key.startswith("Data.Disks."): + del self[key] + avail_counter = 0 + active_counter = 0 + self.cbox.reread_container_list() + for container in self.cbox.get_container_list(): + ## useful if the container was changed during an action + container.reset_object() + is_encrypted = (container.get_type() == \ + cbxContainer.CONTAINERTYPES["luks"]) and 1 or 0 + is_plain = (container.get_type() == \ + cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 + is_mounted = container.is_mounted() and 1 or 0 + is_busy = container.is_busy() and 1 or 0 + self["Data.Disks.%d.device" % avail_counter] = \ + container.device.name + self["Data.Disks.%d.name" % avail_counter] = container.get_name() + self["Data.Disks.%d.encryption" % avail_counter] = is_encrypted + self["Data.Disks.%d.plaintext" % avail_counter] = is_plain + self["Data.Disks.%d.busy" % avail_counter] = is_busy + self["Data.Disks.%d.active" % avail_counter] = is_mounted + self["Data.Disks.%d.size" % avail_counter] = \ + container.device.size_human + if is_mounted: + active_counter += 1 + avail_counter += 1 + self["Data.activeDisksCount"] = active_counter - def set_plugin_data(self): - """Set some hdf values according to the available features. - """ - ## first: clean the dataset up - necessary if we were called more than once - for key in self.keys(): - if key.startswith("Settings.PluginList."): - del self[key] - for plugin in self.plugin_manager.get_plugins(): - entry_name = "Settings.PluginList." + plugin.get_name() - self[entry_name] = plugin.get_name() - self[entry_name + ".Rank"] = plugin.get_rank() - self[entry_name + ".RequestAuth"] = plugin.is_auth_required() and "1" or "0" - for capy in plugin.plugin_capabilities: - self[entry_name + ".Types." + capy] = "1" - for visi in plugin.get_visibility(): - self[entry_name + ".Visible." + visi] = "1" + def set_plugin_data(self): + """Set some hdf values according to the available features. + """ + ## first: clean the dataset up - necessary if we were called more than once + for key in self.keys(): + if key.startswith("Settings.PluginList."): + del self[key] + for plugin in self.plugin_manager.get_plugins(): + entry_name = "Settings.PluginList." + plugin.get_name() + self[entry_name] = plugin.get_name() + self[entry_name + ".Rank"] = plugin.get_rank() + self[entry_name + ".RequestAuth"] = plugin.is_auth_required() and "1" or "0" + for capy in plugin.plugin_capabilities: + self[entry_name + ".Types." + capy] = "1" + for visi in plugin.get_visibility(): + self[entry_name + ".Visible." + visi] = "1" - def __set_config_values(self): - """Set some hdf values according to configuration settings. - """ - self["Settings.TemplateDir"] = os.path.abspath( - self.prefs["Locations"]["TemplateDir"]) - self["Settings.DocDir"] = os.path.abspath(self.prefs["Locations"]["DocDir"]) - self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"] - self["Settings.Language"] = self.prefs["WebSettings"]["Languages"][0] - self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"] + def __set_config_values(self): + """Set some hdf values according to configuration settings. + """ + self["Settings.TemplateDir"] = os.path.abspath( + self.prefs["Locations"]["TemplateDir"]) + self["Settings.DocDir"] = os.path.abspath(self.prefs["Locations"]["DocDir"]) + self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"] + self["Settings.Language"] = self.prefs["WebSettings"]["Languages"][0] + self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"] diff --git a/src/cryptobox/web/sites.py b/src/cryptobox/web/sites.py index 2c2f00d..7e80bbd 100644 --- a/src/cryptobox/web/sites.py +++ b/src/cryptobox/web/sites.py @@ -31,650 +31,650 @@ import os import sys try: - import neo_cgi, neo_util, neo_cs + import neo_cgi, neo_util, neo_cs except ImportError: - _ERRMSG = "Could not import clearsilver module. \ - Try 'apt-get install python-clearsilver'." - sys.stderr.write(_ERRMSG) - raise ImportError, _ERRMSG + _ERRMSG = "Could not import clearsilver module. \ + Try 'apt-get install python-clearsilver'." + sys.stderr.write(_ERRMSG) + raise ImportError, _ERRMSG GETTEXT_DOMAIN = 'cryptobox-server' class PluginIconHandler: - """deliver the icons of available plugins via cherrypy - - the state (enabled/disabled) and the require-auth setting is ignored to - avoid repetitive reloading""" + """deliver the icons of available plugins via cherrypy + + the state (enabled/disabled) and the require-auth setting is ignored to + avoid repetitive reloading""" - def __init__(self, plugins): - for plugin in plugins.get_plugins(): - if not plugin: - continue - plname = plugin.get_name() - ## expose the get_icon function of this plugin - setattr(self, plname, plugin.get_icon) + def __init__(self, plugins): + for plugin in plugins.get_plugins(): + if not plugin: + continue + plname = plugin.get_name() + ## expose the get_icon function of this plugin + setattr(self, plname, plugin.get_icon) class PluginDownloadHandler: - """deliver downloadable files of available plugins via cherrypy - - the state (enabled/disabled) and the require-auth setting is ignored - """ + """deliver downloadable files of available plugins via cherrypy + + the state (enabled/disabled) and the require-auth setting is ignored + """ - def __init__(self, plugins): - for plugin in plugins.get_plugins(): - if not plugin: - continue - plname = plugin.get_name() - ## expose the download function of this plugin - setattr(self, plname, plugin.download) + def __init__(self, plugins): + for plugin in plugins.get_plugins(): + if not plugin: + continue + plname = plugin.get_name() + ## expose the download function of this plugin + setattr(self, plname, plugin.download) class WebInterfaceSites: - """handle all http requests and render pages + """handle all http requests and render pages - this includes: - - filtering common arguments - - calling feature actions - - translating content + this includes: + - filtering common arguments + - calling feature actions + - translating content - all available features are dynamically exposed - """ + all available features are dynamically exposed + """ - ## this template is used under strange circumstances - defaultTemplate = "empty" + ## this template is used under strange circumstances + defaultTemplate = "empty" - def __init__(self, conf_file=None): - ## we should only use variables preceded by "__" to avoid name conflicts - ## when loading features - self.cbox = cryptobox.core.main.CryptoBox(conf_file) - self.__cached_language_data = None - self.__dataset = None - ## load the plugin manager - we will not try to detect new plugins on - ## the fly ... - self.__plugin_manager = cryptobox.plugins.manage.PluginManager( - self.cbox, self.cbox.prefs["Locations"]["PluginDir"], self) - self.__reset_dataset() - ## store the original http error handler - self._cp_on_http_error = self.new_http_error_handler - ## set initial language order - self.lang_order = self.cbox.prefs["WebSettings"]["Languages"][:] - ## publish plugin icons - self.icons = PluginIconHandler(self.__plugin_manager) - self.icons.exposed = True - ## publish plugin downloads - self.downloads = PluginDownloadHandler(self.__plugin_manager) - self.downloads.exposed = True - ## announce that the server started up - self.setup() + def __init__(self, conf_file=None): + ## we should only use variables preceded by "__" to avoid name conflicts + ## when loading features + self.cbox = cryptobox.core.main.CryptoBox(conf_file) + self.__cached_language_data = None + self.__dataset = None + ## load the plugin manager - we will not try to detect new plugins on + ## the fly ... + self.__plugin_manager = cryptobox.plugins.manage.PluginManager( + self.cbox, self.cbox.prefs["Locations"]["PluginDir"], self) + self.__reset_dataset() + ## store the original http error handler + self._cp_on_http_error = self.new_http_error_handler + ## set initial language order + self.lang_order = self.cbox.prefs["WebSettings"]["Languages"][:] + ## publish plugin icons + self.icons = PluginIconHandler(self.__plugin_manager) + self.icons.exposed = True + ## publish plugin downloads + self.downloads = PluginDownloadHandler(self.__plugin_manager) + self.downloads.exposed = True + ## announce that the server started up + self.setup() - def setup(self): - """Prepare the webinterface. - """ - self.cbox.setup() - for plugin in self.__plugin_manager.get_plugins(): - if plugin: - plugin.handle_event("bootup") + def setup(self): + """Prepare the webinterface. + """ + self.cbox.setup() + for plugin in self.__plugin_manager.get_plugins(): + if plugin: + plugin.handle_event("bootup") - def cleanup(self): - """Shutdown the webinterface safely. - """ - self.cbox.log.info("Shutting down webinterface ...") - for plugin in self.__plugin_manager.get_plugins(): - if plugin: - self.cbox.log.info("Cleaning up plugin '%s' ..." % plugin.get_name()) - plugin.handle_event("shutdown") - self.cbox.cleanup() + def cleanup(self): + """Shutdown the webinterface safely. + """ + self.cbox.log.info("Shutting down webinterface ...") + for plugin in self.__plugin_manager.get_plugins(): + if plugin: + self.cbox.log.info("Cleaning up plugin '%s' ..." % plugin.get_name()) + plugin.handle_event("shutdown") + self.cbox.cleanup() - def __reset_dataset(self): - """this method has to be called at the beginning of every "site" action - important: only at the beginning of an action (to not loose information) - important: for _every_ "site" action (cherrypy is stateful) - also take care for the plugins, as they also contain datasets - """ - self.__load_plugins() - self.__dataset = cryptobox.web.dataset.WebInterfaceDataset( - self.cbox, self.cbox.prefs, self.__plugin_manager) - ## check, if a configuration partition has become available - self.cbox.prefs.prepare_partition() + def __reset_dataset(self): + """this method has to be called at the beginning of every "site" action + important: only at the beginning of an action (to not loose information) + important: for _every_ "site" action (cherrypy is stateful) + also take care for the plugins, as they also contain datasets + """ + self.__load_plugins() + self.__dataset = cryptobox.web.dataset.WebInterfaceDataset( + self.cbox, self.cbox.prefs, self.__plugin_manager) + ## check, if a configuration partition has become available + self.cbox.prefs.prepare_partition() - def __load_plugins(self): - """reinitialize the list of available plugins + def __load_plugins(self): + """reinitialize the list of available plugins - this includes the following: - - reload all plugins and check their state (disabled or not) - - reinitilize the datasets of all plugins - """ - #TODO: in the long-term we should create a separate object that only - # contains the plugin handlers - this avoids some hassle of namespace - # conflicts - this object will be the cherrypy.server.root - # finish this for v0.4 - for plugin in self.__plugin_manager.get_plugins(): - if not plugin: - continue - plname = plugin.get_name() - ## remove the old plugin handler and attach a new one - try: - ## check if there are name conflicts: e.g. a local variable has - ## the same name as a plugin to be loaded -> skip these plugins - ## if we would not check this here, nasty effects could occour - prev_obj = getattr(self, plname) - if not callable(prev_obj) or not prev_obj.exposed: - ## name conflict - see below - raise NameError - ## remove the plugin handler - delattr(self, plname) - except AttributeError: - ## "self" does not contain the given "plname" element - ## this is ok, as we are just cleaning up - pass - except NameError: - ## the attribute "exposed" of the element self."plname" does - ## not exist - it seems, that we have a name conflict - self.cbox.log.error("Skipping feature (%s) as its" % plname - + " name conflicts with a local variable - see" - + " module cryptobox.web.sites") - ## skip this plugin - continue - ## the old attribute was cleaned up - we can reinitialize it now - 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 the stream_response feature 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) - ## nothing else has to be done + this includes the following: + - reload all plugins and check their state (disabled or not) + - reinitilize the datasets of all plugins + """ + #TODO: in the long-term we should create a separate object that only + # contains the plugin handlers - this avoids some hassle of namespace + # conflicts - this object will be the cherrypy.server.root + # finish this for v0.4 + for plugin in self.__plugin_manager.get_plugins(): + if not plugin: + continue + plname = plugin.get_name() + ## remove the old plugin handler and attach a new one + try: + ## check if there are name conflicts: e.g. a local variable has + ## the same name as a plugin to be loaded -> skip these plugins + ## if we would not check this here, nasty effects could occour + prev_obj = getattr(self, plname) + if not callable(prev_obj) or not prev_obj.exposed: + ## name conflict - see below + raise NameError + ## remove the plugin handler + delattr(self, plname) + except AttributeError: + ## "self" does not contain the given "plname" element + ## this is ok, as we are just cleaning up + pass + except NameError: + ## the attribute "exposed" of the element self."plname" does + ## not exist - it seems, that we have a name conflict + self.cbox.log.error("Skipping feature (%s) as its" % plname + + " name conflicts with a local variable - see" + + " module cryptobox.web.sites") + ## skip this plugin + continue + ## the old attribute was cleaned up - we can reinitialize it now + 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 the stream_response feature 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) + ## nothing else has to be done - ## sub pages requiring authentication may not be defined above - def __request_auth(self=None): - """ this is a function decorator to check authentication - """ - def check_credentials(site): - """ see description of _inner_wrapper - please simplify this! - """ - def _inner_wrapper(self, *args, **kargs): - """this function was necessary while trying around with the - function decorator - if someone can implement the decorator - with less effort, then any suggestions are welcome! - """ - import base64 - ## define a "non-allowed" function - user, password = None, None - try: - ## ignore the "Basic " (first six letters) part - resp = cherrypy.request.headers["Authorization"][6:] - (user, password) = base64.b64decode(resp).split(":", 1) - except KeyError: - ## no "authorization" header was sent - pass - except TypeError: - ## invalid base64 string - pass - except AttributeError: - ## no cherrypy request header defined - pass - auth_dict = self.cbox.prefs.user_db["admins"] - if user in auth_dict.keys(): - if self.cbox.prefs.user_db.get_digest(password) == auth_dict[user]: - ## ok: return the choosen page - self.cbox.log.info("access granted for: %s" % user) - return site(self, *args, **kargs) - else: - self.cbox.log.info( - "wrong password supplied for: %s" % user) - else: - self.cbox.log.info("unknown user: %s" % str(user)) - ## wrong credentials: return "access denied" - cherrypy.response.headers["WWW-Authenticate"] = \ - '''Basic realm="CryptoBox"''' - cherrypy.response.status = 401 - return self.__render("access_denied") - return _inner_wrapper - return check_credentials - + ## sub pages requiring authentication may not be defined above + def __request_auth(self=None): + """ this is a function decorator to check authentication + """ + def check_credentials(site): + """ see description of _inner_wrapper - please simplify this! + """ + def _inner_wrapper(self, *args, **kargs): + """this function was necessary while trying around with the + function decorator - if someone can implement the decorator + with less effort, then any suggestions are welcome! + """ + import base64 + ## define a "non-allowed" function + user, password = None, None + try: + ## ignore the "Basic " (first six letters) part + resp = cherrypy.request.headers["Authorization"][6:] + (user, password) = base64.b64decode(resp).split(":", 1) + except KeyError: + ## no "authorization" header was sent + pass + except TypeError: + ## invalid base64 string + pass + except AttributeError: + ## no cherrypy request header defined + pass + auth_dict = self.cbox.prefs.user_db["admins"] + if user in auth_dict.keys(): + if self.cbox.prefs.user_db.get_digest(password) == auth_dict[user]: + ## ok: return the choosen page + self.cbox.log.info("access granted for: %s" % user) + return site(self, *args, **kargs) + else: + self.cbox.log.info( + "wrong password supplied for: %s" % user) + else: + self.cbox.log.info("unknown user: %s" % str(user)) + ## wrong credentials: return "access denied" + cherrypy.response.headers["WWW-Authenticate"] = \ + '''Basic realm="CryptoBox"''' + cherrypy.response.status = 401 + return self.__render("access_denied") + return _inner_wrapper + return check_credentials + - ###################################################################### - ## put real sites down here and don't forget to expose them at the end + ###################################################################### + ## put real sites down here and don't forget to expose them at the end - @cherrypy.expose - def index(self, weblang="", help="0", device=None): - """the default page on startup - we show the list of available disks - """ - self.__reset_dataset() - self.__set_web_lang(weblang) - self.__check_environment() - ## do not forget the language! - param_dict = {"weblang":weblang} - ## render "disks" plugin by default - return self.return_plugin_action( - self.__plugin_manager.get_plugin("disks"))(**param_dict) + @cherrypy.expose + def index(self, weblang="", help="0", device=None): + """the default page on startup - we show the list of available disks + """ + self.__reset_dataset() + self.__set_web_lang(weblang) + self.__check_environment() + ## do not forget the language! + param_dict = {"weblang":weblang} + ## render "disks" plugin by default + return self.return_plugin_action( + self.__plugin_manager.get_plugin("disks"))(**param_dict) - def new_http_error_handler(self, error_code, message): - """handle http errors gracefully + def new_http_error_handler(self, error_code, message): + """handle http errors gracefully - 404 - not found errors: ignored if url is below /cryptobox-misc/ - other 404 errors: send the error code and return a nice informative page - 500 - runtime errors: return "ok" exit code and show a polite excuse - others: are there any other possible http errors? - """ - import traceback - ## we ignore uninteresting not-found errors - if (error_code == 404) and \ - (cherrypy.request.path.startswith("/cryptobox-misc/") or \ - cherrypy.request.path in ['/robots.txt','/favicon.ico']): - cherrypy.response.status = error_code - return - ## an invalid action was requested - if error_code == 404: - ## we send a not-found error (with the usual interface) - cherrypy.response.status = error_code - self.__dataset["Data.Warning"] = "InvalidAction" - cherrypy.response.body = self.__render("empty") - return - ## are there still bugs in the code? - if error_code == 500: - ## we fix the error code (200 is "OK") - cherrypy.response.status = 200 - self.cbox.log.error( - "HTTP-ERROR[500] - runtime error: %s" % str(message)) - ## add a traceback and exception information to the lo - for log_line in traceback.format_exception(*sys.exc_info()): - self.cbox.log.error("\t%s" % log_line) - self.__dataset["Data.Warning"] = "RuntimeError" - cherrypy.response.body = self.__render("empty") - return - ## unknown error type - cherrypy.response.status = error_code - self.cbox.log.warn("HTTP-ERROR[%d] - an unknown error occoured: %s" \ - % (error_code, message)) - cherrypy.response.body = self.__render("empty") + 404 - not found errors: ignored if url is below /cryptobox-misc/ + other 404 errors: send the error code and return a nice informative page + 500 - runtime errors: return "ok" exit code and show a polite excuse + others: are there any other possible http errors? + """ + import traceback + ## we ignore uninteresting not-found errors + if (error_code == 404) and \ + (cherrypy.request.path.startswith("/cryptobox-misc/") or \ + cherrypy.request.path in ['/robots.txt','/favicon.ico']): + cherrypy.response.status = error_code + return + ## an invalid action was requested + if error_code == 404: + ## we send a not-found error (with the usual interface) + cherrypy.response.status = error_code + self.__dataset["Data.Warning"] = "InvalidAction" + cherrypy.response.body = self.__render("empty") + return + ## are there still bugs in the code? + if error_code == 500: + ## we fix the error code (200 is "OK") + cherrypy.response.status = 200 + self.cbox.log.error( + "HTTP-ERROR[500] - runtime error: %s" % str(message)) + ## add a traceback and exception information to the lo + for log_line in traceback.format_exception(*sys.exc_info()): + self.cbox.log.error("\t%s" % log_line) + self.__dataset["Data.Warning"] = "RuntimeError" + cherrypy.response.body = self.__render("empty") + return + ## unknown error type + cherrypy.response.status = error_code + self.cbox.log.warn("HTTP-ERROR[%d] - an unknown error occoured: %s" \ + % (error_code, message)) + cherrypy.response.body = self.__render("empty") - def return_plugin_action(self, plugin): - """ returns a function that is suitable for handling a cherrypy - page request - """ - def handler(self, weblang="", device=None, help="0", redirect=None, - message_keep=None, **args): - """this function handles a cherrypy page request - """ - plugin.reset() - self.__reset_dataset() - self.__check_environment() - self.__set_web_lang(weblang) - ## we always read the "device" setting - otherwise volume-plugin - ## links would not work easily - ## (see "volume_props" linking to "volume_format_fs") - ## it will get ignored for non-volume plugins - plugin.device = None - if device and self.__set_device(device): - plugin.device = self.cbox.get_container(device).device - ## check the device argument of volume plugins - if "volume" in plugin.plugin_capabilities: - ## initialize the dataset of the selected device if necessary - if plugin.device: - self.__dataset.set_current_disk_state(plugin.device) - else: - ## invalid (or missing) device setting - return self.__render(self.defaultTemplate) - ## check if there is a "redirect" setting - this will override - ## the return value of the do_action function - ## (e.g. useful for umount-before-format) - override_next_template = None - if redirect: - override_next_template = { "plugin": redirect } - if "volume" in plugin.plugin_capabilities: - override_next_template["values"] = {"device":plugin.device.name} - ## check for information to be kept after the last call - if message_keep: - for (key, value) in message_keep["dataset"].items(): - self.__dataset[key] = value - ## check if the device is busy - if plugin.device and self.cbox.get_container(plugin.device).is_busy(): - return self.__render("volume_busy") - ## call the plugin handler - next_template = plugin.do_action(**args) - ## for 'volume' plugins: reread the dataset of the current disk - ## additionally: set the default template for plugins - if "volume" in plugin.plugin_capabilities: - ## maybe the state of the current volume was changed? - self.__dataset.set_current_disk_state(plugin.device) - if not next_template: - next_template = { "plugin":"volume_mount", - "values":{"device":plugin.device.name}} - else: - ## some non-volume plugins change the internal state of other - ## plugins - e.g.: plugin_manager - ## if we do not call __load_plugins now, then it is possible - ## to call a plugin directly after disabling it (only once) - self.__load_plugins() - self.__dataset.set_plugin_data() - ## default page for non-volume plugins is the disk selection - if not next_template: - next_template = { "plugin":"disks", "values":{} } - #TODO: there is a lot of piece-by-piece updating around here - # for v0.4 we should just call __reset_dataset - but this would - # require to store the currently changed dataset values (e.g. - # weblang) somewhere else to not override it - ## some non-volume plugins may change the state of containers - ## the mount plugin may change the number of active disks - for the logo - self.__dataset.set_containers_state() - ## was a redirect requested? - if override_next_template: - next_template = override_next_template - ## if another plugins was choosen for 'next_template', then do it! - if isinstance(next_template, dict) \ - and "plugin" in next_template.keys() \ - and "values" in next_template.keys() \ - and self.__plugin_manager.get_plugin(next_template["plugin"]): - value_dict = dict(next_template["values"]) - ## force the current weblang attribute - otherwise it gets lost - value_dict["weblang"] = self.lang_order[0] - ## check for warnings/success messages, that should be kept - if "Data.Success" in plugin.hdf.keys() \ - or "Data.Warning" in plugin.hdf.keys(): - value_dict["message_keep"] = {"plugin":plugin, "dataset":{}} - for keep_key in ("Data.Warning", "Data.Success"): - if keep_key in plugin.hdf.keys(): - self.cbox.log.info("keeping message: %s" % \ - plugin.hdf[keep_key]) - value_dict["message_keep"]["dataset"][keep_key] = \ - plugin.hdf[keep_key] - new_plugin = self.__plugin_manager.get_plugin(next_template["plugin"]) - return self.return_plugin_action(new_plugin)(**value_dict) - ## save the currently active plugin name - self.__dataset["Data.ActivePlugin"] = plugin.get_name() - return self.__render(next_template, plugin) - ## apply authentication? - if plugin.is_auth_required(): - return lambda **args: self.__request_auth()(handler)(self, **args) - else: - return lambda **args: handler(self, **args) + def return_plugin_action(self, plugin): + """ returns a function that is suitable for handling a cherrypy + page request + """ + def handler(self, weblang="", device=None, help="0", redirect=None, + message_keep=None, **args): + """this function handles a cherrypy page request + """ + plugin.reset() + self.__reset_dataset() + self.__check_environment() + self.__set_web_lang(weblang) + ## we always read the "device" setting - otherwise volume-plugin + ## links would not work easily + ## (see "volume_props" linking to "volume_format_fs") + ## it will get ignored for non-volume plugins + plugin.device = None + if device and self.__set_device(device): + plugin.device = self.cbox.get_container(device).device + ## check the device argument of volume plugins + if "volume" in plugin.plugin_capabilities: + ## initialize the dataset of the selected device if necessary + if plugin.device: + self.__dataset.set_current_disk_state(plugin.device) + else: + ## invalid (or missing) device setting + return self.__render(self.defaultTemplate) + ## check if there is a "redirect" setting - this will override + ## the return value of the do_action function + ## (e.g. useful for umount-before-format) + override_next_template = None + if redirect: + override_next_template = { "plugin": redirect } + if "volume" in plugin.plugin_capabilities: + override_next_template["values"] = {"device":plugin.device.name} + ## check for information to be kept after the last call + if message_keep: + for (key, value) in message_keep["dataset"].items(): + self.__dataset[key] = value + ## check if the device is busy + if plugin.device and self.cbox.get_container(plugin.device).is_busy(): + return self.__render("volume_busy") + ## call the plugin handler + next_template = plugin.do_action(**args) + ## for 'volume' plugins: reread the dataset of the current disk + ## additionally: set the default template for plugins + if "volume" in plugin.plugin_capabilities: + ## maybe the state of the current volume was changed? + self.__dataset.set_current_disk_state(plugin.device) + if not next_template: + next_template = { "plugin":"volume_mount", + "values":{"device":plugin.device.name}} + else: + ## some non-volume plugins change the internal state of other + ## plugins - e.g.: plugin_manager + ## if we do not call __load_plugins now, then it is possible + ## to call a plugin directly after disabling it (only once) + self.__load_plugins() + self.__dataset.set_plugin_data() + ## default page for non-volume plugins is the disk selection + if not next_template: + next_template = { "plugin":"disks", "values":{} } + #TODO: there is a lot of piece-by-piece updating around here + # for v0.4 we should just call __reset_dataset - but this would + # require to store the currently changed dataset values (e.g. + # weblang) somewhere else to not override it + ## some non-volume plugins may change the state of containers + ## the mount plugin may change the number of active disks - for the logo + self.__dataset.set_containers_state() + ## was a redirect requested? + if override_next_template: + next_template = override_next_template + ## if another plugins was choosen for 'next_template', then do it! + if isinstance(next_template, dict) \ + and "plugin" in next_template.keys() \ + and "values" in next_template.keys() \ + and self.__plugin_manager.get_plugin(next_template["plugin"]): + value_dict = dict(next_template["values"]) + ## force the current weblang attribute - otherwise it gets lost + value_dict["weblang"] = self.lang_order[0] + ## check for warnings/success messages, that should be kept + if "Data.Success" in plugin.hdf.keys() \ + or "Data.Warning" in plugin.hdf.keys(): + value_dict["message_keep"] = {"plugin":plugin, "dataset":{}} + for keep_key in ("Data.Warning", "Data.Success"): + if keep_key in plugin.hdf.keys(): + self.cbox.log.info("keeping message: %s" % \ + plugin.hdf[keep_key]) + value_dict["message_keep"]["dataset"][keep_key] = \ + plugin.hdf[keep_key] + new_plugin = self.__plugin_manager.get_plugin(next_template["plugin"]) + return self.return_plugin_action(new_plugin)(**value_dict) + ## save the currently active plugin name + self.__dataset["Data.ActivePlugin"] = plugin.get_name() + return self.__render(next_template, plugin) + ## apply authentication? + if plugin.is_auth_required(): + return lambda **args: self.__request_auth()(handler)(self, **args) + else: + return lambda **args: handler(self, **args) - - @cherrypy.expose - def test(self, weblang="", help="0", device=None): - """test authentication - this function may be safely removed - """ - self.__reset_dataset() - self.__set_web_lang(weblang) - self.__check_environment() - result = "Test
    " - for key in cherrypy.request.headers: - result += "
  • %s - %s
  • " % (str(key), str(cherrypy.request.headers[key])) - result += "
" - return result - + + @cherrypy.expose + def test(self, weblang="", help="0", device=None): + """test authentication - this function may be safely removed + """ + self.__reset_dataset() + self.__set_web_lang(weblang) + self.__check_environment() + result = "Test
    " + for key in cherrypy.request.headers: + result += "
  • %s - %s
  • " % (str(key), str(cherrypy.request.headers[key])) + result += "
" + return result + - @cherrypy.expose - def test_stream(self, weblang="", help="0", device=None): - """just for testing purposes - to check if the "stream_response" feature - actually works - for now (September 02006) it does not seem to be ok - """ - import time - yield "neu

    " - for num in range(10): - yield "
  • yes: %d - %s
  • " % (num, str(time.time())) - time.sleep(1) - yield "

" + @cherrypy.expose + def test_stream(self, weblang="", help="0", device=None): + """just for testing purposes - to check if the "stream_response" feature + actually works - for now (September 02006) it does not seem to be ok + """ + import time + yield "neu

    " + for num in range(10): + yield "
  • yes: %d - %s
  • " % (num, str(time.time())) + time.sleep(1) + yield "

" - + - ##################### input checker ########################## + ##################### input checker ########################## - def __check_environment(self): - """inform the user of suspicious environmental problems + def __check_environment(self): + """inform the user of suspicious environmental problems - examples are: non-https, readonly-config, ... - """ - warnings = [] - for pl in self.__plugin_manager.get_plugins(): - warnings.extend(pl.get_warnings()) - warnings.sort(reverse=True) - for (index, (warn_prio, warn_text)) in enumerate(warnings): - self.__dataset["Data.EnvironmentWarning.%d" % index] = warn_text + examples are: non-https, readonly-config, ... + """ + warnings = [] + for pl in self.__plugin_manager.get_plugins(): + warnings.extend(pl.get_warnings()) + warnings.sort(reverse=True) + for (index, (warn_prio, warn_text)) in enumerate(warnings): + self.__dataset["Data.EnvironmentWarning.%d" % index] = warn_text - def __set_web_lang(self, value): - """set the preferred priority of languages according to this order: - 1. language selected via web interface - 2. preferred browser language setting - 3. languages defined in the config file - """ - ## start with the configured language order - lang_order = self.cbox.prefs["WebSettings"]["Languages"][:] - self.cbox.log.debug( - "updating language preferences (default: %s)" % str(lang_order)) - ## put the preferred browser language in front - guess = self.__get_browser_language(lang_order) - if guess: - lang_order.remove(guess) - lang_order.insert(0, guess) - self.cbox.log.debug( - "raised priority of preferred browser language: %s" % guess) - ## check if the 'weblang' setting is necessary (does it change the result - ## of the language preference calculation?) - override_by_weblang = False - ## is the chosen language (via web interface) valid? - put it in front - if value and (value in lang_order) and (not re.search(r'\W', value)): - ## skip if the 'weblang' value is already at the top of the list - if lang_order.index(value) != 0: - override_by_weblang = True - lang_order.remove(value) - lang_order.insert(0, value) - self.cbox.log.debug( - "raised priority of selected language: %s" % value) - elif value: - self.cbox.log.info("invalid language selected: %s" % value) - ## store current language setting - self.cbox.log.info( - "current language preference: %s" % str(lang_order)) - self.lang_order = lang_order - self.__dataset["Settings.Language"] = lang_order[0] - ## we do not have to add the LinkAttr if it is irrelevant - if override_by_weblang: - self.__dataset["Settings.LinkAttrs.weblang"] = lang_order[0] + def __set_web_lang(self, value): + """set the preferred priority of languages according to this order: + 1. language selected via web interface + 2. preferred browser language setting + 3. languages defined in the config file + """ + ## start with the configured language order + lang_order = self.cbox.prefs["WebSettings"]["Languages"][:] + self.cbox.log.debug( + "updating language preferences (default: %s)" % str(lang_order)) + ## put the preferred browser language in front + guess = self.__get_browser_language(lang_order) + if guess: + lang_order.remove(guess) + lang_order.insert(0, guess) + self.cbox.log.debug( + "raised priority of preferred browser language: %s" % guess) + ## check if the 'weblang' setting is necessary (does it change the result + ## of the language preference calculation?) + override_by_weblang = False + ## is the chosen language (via web interface) valid? - put it in front + if value and (value in lang_order) and (not re.search(r'\W', value)): + ## skip if the 'weblang' value is already at the top of the list + if lang_order.index(value) != 0: + override_by_weblang = True + lang_order.remove(value) + lang_order.insert(0, value) + self.cbox.log.debug( + "raised priority of selected language: %s" % value) + elif value: + self.cbox.log.info("invalid language selected: %s" % value) + ## store current language setting + self.cbox.log.info( + "current language preference: %s" % str(lang_order)) + self.lang_order = lang_order + self.__dataset["Settings.Language"] = lang_order[0] + ## we do not have to add the LinkAttr if it is irrelevant + if override_by_weblang: + self.__dataset["Settings.LinkAttrs.weblang"] = lang_order[0] - def __get_browser_language(self, avail_langs): - """guess the preferred language of the user (as sent by the browser) - take the first language, that is part of 'avail_langs' - """ - try: - pref_lang_header = cherrypy.request.headers["Accept-Language"] - except KeyError: - ## no language header was specified - return None - ## this could be a typical 'Accept-Language' header: - ## de-de,de;q=0.8,en-us;q=0.5,en;q=0.3 - regex = re.compile(r"\w+(-\w+)?(;q=[\d\.]+)?$") - pref_langs = [e.split(";", 1)[0] - for e in pref_lang_header.split(",") - if regex.match(e)] - ## is one of these preferred languages available? - for lang in pref_langs: - if lang in avail_langs: - return lang - ## we try to be nice: also look for "de" if "de-de" was specified ... - for lang in pref_langs: - ## use only the first part of the language - short_lang = lang.split("-", 1)[0] - if short_lang in avail_langs: - return short_lang - ## we give up - return None + def __get_browser_language(self, avail_langs): + """guess the preferred language of the user (as sent by the browser) + take the first language, that is part of 'avail_langs' + """ + try: + pref_lang_header = cherrypy.request.headers["Accept-Language"] + except KeyError: + ## no language header was specified + return None + ## this could be a typical 'Accept-Language' header: + ## de-de,de;q=0.8,en-us;q=0.5,en;q=0.3 + regex = re.compile(r"\w+(-\w+)?(;q=[\d\.]+)?$") + pref_langs = [e.split(";", 1)[0] + for e in pref_lang_header.split(",") + if regex.match(e)] + ## is one of these preferred languages available? + for lang in pref_langs: + if lang in avail_langs: + return lang + ## we try to be nice: also look for "de" if "de-de" was specified ... + for lang in pref_langs: + ## use only the first part of the language + short_lang = lang.split("-", 1)[0] + if short_lang in avail_langs: + return short_lang + ## we give up + return None - def __set_device(self, device): - """check a device name that was chosen via the web interface - issue a warning if the device is invalid""" - if device and re.match(r'[\w-]+$', device) \ - and self.cbox.get_container(device): - self.cbox.log.debug("Select device: %s" % device) - return True - else: - self.cbox.log.warn("Invalid device: %s" % device) - self.__dataset["Data.Warning"] = "InvalidDevice" - return False - + def __set_device(self, device): + """check a device name that was chosen via the web interface + issue a warning if the device is invalid""" + if device and re.match(r'[\w-]+$', device) \ + and self.cbox.get_container(device): + self.cbox.log.debug("Select device: %s" % device) + return True + else: + self.cbox.log.warn("Invalid device: %s" % device) + self.__dataset["Data.Warning"] = "InvalidDevice" + return False + - def __substitute_gettext(self, languages, text_domain, hdf): - """substitute all texts in the hdf dataset with their translated - counterparts as returned by gettext - """ - import gettext - try: - translator = gettext.translation(text_domain, languages=languages) - except IOError, err_msg: - ## no translation found - self.cbox.log.warn("unable to load language file: %s" % err_msg) - return hdf - def walk_tree(parent_name, hdf_node): - """iterate through all nodes""" - def translate_node(node): - """turn one single string into unicode""" - if not node.value(): - return - for (key, value) in node.attrs(): - ## ignore all nodes with the 'LINK' attribute - ## for now clearsilver is buggy regarding attributes - ## buggy -> parsing of a hdf file fails silently - if key == "LINK": - return - ## as long as the attributes do not work, we have to rely on - ## some magic names to ignore translations - if (parent_name == "Link") and \ - (node.name() in ["Rel", "Prot", "Abs"]): - return - try: - #TODO: we should use unicode - or not? - turn it on later - #node.setValue("", translator.ugettext(node.value())) - ## quite obscure: ugettext can handle None - gettext breaks instead - node.setValue("", translator.gettext(node.value())) - except UnicodeEncodeError, err_msg: - self.cbox.log.info( - "Failed unicode encoding for gettext: %s - %s" \ - % (node.value(),err_msg)) - ## fallback to default encoding - node.setValue("", translator.gettext(node.value())) - while hdf_node: - translate_node(hdf_node) - walk_tree(hdf_node.name(), hdf_node.child()) - hdf_node = hdf_node.next() - walk_tree("", hdf) + def __substitute_gettext(self, languages, text_domain, hdf): + """substitute all texts in the hdf dataset with their translated + counterparts as returned by gettext + """ + import gettext + try: + translator = gettext.translation(text_domain, languages=languages) + except IOError, err_msg: + ## no translation found + self.cbox.log.warn("unable to load language file: %s" % err_msg) + return hdf + def walk_tree(parent_name, hdf_node): + """iterate through all nodes""" + def translate_node(node): + """turn one single string into unicode""" + if not node.value(): + return + for (key, value) in node.attrs(): + ## ignore all nodes with the 'LINK' attribute + ## for now clearsilver is buggy regarding attributes + ## buggy -> parsing of a hdf file fails silently + if key == "LINK": + return + ## as long as the attributes do not work, we have to rely on + ## some magic names to ignore translations + if (parent_name == "Link") and \ + (node.name() in ["Rel", "Prot", "Abs"]): + return + try: + #TODO: we should use unicode - or not? - turn it on later + #node.setValue("", translator.ugettext(node.value())) + ## quite obscure: ugettext can handle None - gettext breaks instead + node.setValue("", translator.gettext(node.value())) + except UnicodeEncodeError, err_msg: + self.cbox.log.info( + "Failed unicode encoding for gettext: %s - %s" \ + % (node.value(),err_msg)) + ## fallback to default encoding + node.setValue("", translator.gettext(node.value())) + while hdf_node: + translate_node(hdf_node) + walk_tree(hdf_node.name(), hdf_node.child()) + hdf_node = hdf_node.next() + walk_tree("", hdf) - def __get_language_data(self): - """return the hdf dataset of the main interface and all plugins - translations are done according to self.lang_order - """ - ## check if the language setting has changed - use cache if possible - if self.__cached_language_data and \ - self.__cached_language_data["lang_order"] == self.lang_order: - self.cbox.log.debug( - "using cached language data: %s" % str(self.lang_order)) - return self.__cached_language_data["hdf"] - self.cbox.log.debug("generating language data") - hdf = neo_util.HDF() - hdf.readFile(os.path.join( - self.cbox.prefs["Locations"]["TemplateDir"],"language.hdf")) - self.__substitute_gettext(self.lang_order, GETTEXT_DOMAIN, hdf) - ## load the language data of all plugins - for plugin in self.__plugin_manager.get_plugins(): - pl_lang = plugin.get_language_data() - self.__substitute_gettext(self.lang_order, "%s-feature-%s" % \ - (GETTEXT_DOMAIN, plugin.get_name()), pl_lang) - hdf.copy("Plugins.%s" % plugin.get_name(), pl_lang) - self.cbox.log.debug( - "language data for plugin loaded: %s" % plugin.get_name()) - ## cache result for later retrieval - self.__cached_language_data = \ - {"lang_order": self.lang_order, "hdf": hdf} - return hdf + def __get_language_data(self): + """return the hdf dataset of the main interface and all plugins + translations are done according to self.lang_order + """ + ## check if the language setting has changed - use cache if possible + if self.__cached_language_data and \ + self.__cached_language_data["lang_order"] == self.lang_order: + self.cbox.log.debug( + "using cached language data: %s" % str(self.lang_order)) + return self.__cached_language_data["hdf"] + self.cbox.log.debug("generating language data") + hdf = neo_util.HDF() + hdf.readFile(os.path.join( + self.cbox.prefs["Locations"]["TemplateDir"],"language.hdf")) + self.__substitute_gettext(self.lang_order, GETTEXT_DOMAIN, hdf) + ## load the language data of all plugins + for plugin in self.__plugin_manager.get_plugins(): + pl_lang = plugin.get_language_data() + self.__substitute_gettext(self.lang_order, "%s-feature-%s" % \ + (GETTEXT_DOMAIN, plugin.get_name()), pl_lang) + hdf.copy("Plugins.%s" % plugin.get_name(), pl_lang) + self.cbox.log.debug( + "language data for plugin loaded: %s" % plugin.get_name()) + ## cache result for later retrieval + self.__cached_language_data = \ + {"lang_order": self.lang_order, "hdf": hdf} + return hdf - def __render(self, render_info, plugin=None): - '''renders from clearsilver templates and returns the resulting html - ''' - ## is render_info a string (filename of the template) or a dictionary? - if isinstance(render_info, dict): - template = render_info["template"] - if render_info.has_key("generator"): - generator = render_info["generator"] - else: - generator = None - else: - (template, generator) = (render_info, None) + def __render(self, render_info, plugin=None): + '''renders from clearsilver templates and returns the resulting html + ''' + ## is render_info a string (filename of the template) or a dictionary? + if isinstance(render_info, dict): + template = render_info["template"] + if render_info.has_key("generator"): + generator = render_info["generator"] + else: + generator = None + else: + (template, generator) = (render_info, None) - ## load the language data - hdf = neo_util.HDF() - hdf.copy("Lang", self.__get_language_data()) + ## load the language data + hdf = neo_util.HDF() + hdf.copy("Lang", self.__get_language_data()) - ## first: assume, that the template file is in the global - ## template directory - self.__dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join( - self.cbox.prefs["Locations"]["TemplateDir"], - template + ".cs")) + ## first: assume, that the template file is in the global + ## template directory + self.__dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join( + self.cbox.prefs["Locations"]["TemplateDir"], + template + ".cs")) - if plugin: - ## check, if the plugin provides the template file -> overriding - plugin_cs_file = plugin.get_template_filename(template) - if plugin_cs_file: - self.__dataset["Settings.TemplateFile"] = plugin_cs_file + if plugin: + ## check, if the plugin provides the template file -> overriding + plugin_cs_file = plugin.get_template_filename(template) + if plugin_cs_file: + self.__dataset["Settings.TemplateFile"] = plugin_cs_file - ## add the current state of the plugins to the hdf dataset - self.__dataset["Data.Status.Plugins.%s" % plugin.get_name()] = \ - plugin.get_status() - ## load the dataset of the plugin - plugin.load_dataset(hdf) + ## add the current state of the plugins to the hdf dataset + self.__dataset["Data.Status.Plugins.%s" % plugin.get_name()] = \ + plugin.get_status() + ## load the dataset of the plugin + plugin.load_dataset(hdf) - self.cbox.log.info("rendering site: " + template) + self.cbox.log.info("rendering site: " + template) - cs_path = os.path.abspath(os.path.join( - self.cbox.prefs["Locations"]["TemplateDir"], "main.cs")) - if not os.access(cs_path, os.R_OK): - self.cbox.log.error( - "Couldn't read clearsilver file: %s" % cs_path) - yield "Couldn't read clearsilver file: %s" % cs_path - return + cs_path = os.path.abspath(os.path.join( + self.cbox.prefs["Locations"]["TemplateDir"], "main.cs")) + if not os.access(cs_path, os.R_OK): + self.cbox.log.error( + "Couldn't read clearsilver file: %s" % cs_path) + yield "Couldn't read clearsilver file: %s" % cs_path + return - self.cbox.log.debug(self.__dataset) - for key in self.__dataset.keys(): - hdf.setValue(key, str(self.__dataset[key])) - cs_data = neo_cs.CS(hdf) - cs_data.parseFile(cs_path) + self.cbox.log.debug(self.__dataset) + for key in self.__dataset.keys(): + hdf.setValue(key, str(self.__dataset[key])) + cs_data = neo_cs.CS(hdf) + cs_data.parseFile(cs_path) - ## is there a generator containing additional information? - if not generator: - ## all content in one flush - result_data = cs_data.render().splitlines() - ## remove empty leading lines (avoids html warnings) - while not result_data[0].strip(): - del result_data[0] - yield "\n".join(result_data) - else: - content_generate = generator() - dummy_line = """""" - ## now we do it linewise - checking for the content marker - for line in cs_data.render().splitlines(): - if line.find(dummy_line) != -1: - yield line.replace(dummy_line, content_generate.next()) - else: - yield line + "\n" + ## is there a generator containing additional information? + if not generator: + ## all content in one flush + result_data = cs_data.render().splitlines() + ## remove empty leading lines (avoids html warnings) + while not result_data[0].strip(): + del result_data[0] + yield "\n".join(result_data) + else: + content_generate = generator() + dummy_line = """""" + ## now we do it linewise - checking for the content marker + for line in cs_data.render().splitlines(): + if line.find(dummy_line) != -1: + yield line.replace(dummy_line, content_generate.next()) + else: + yield line + "\n"