root/trunk/src/cryptobox/core/settings.py @ 916

Revision 916, 23.9 kB (checked in by lars, 3 years ago)

fixed svn:keyword tag 'Id' in various python files

  • Property svn:keywords set to Id
Line 
1#
2# Copyright 2006 sense.lab e.V.
3#
4# This file is part of the CryptoBox.
5#
6# The CryptoBox is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# The CryptoBox is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with the CryptoBox; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19#
20
21"""Manage the configuration of a CryptoBox
22"""
23
24__revision__ = "$Id$"
25
26from cryptobox.core.exceptions import *
27import logging
28import subprocess
29import os
30import configobj, validate
31import syslog
32
33
34CONF_LOCATIONS = [
35        "./cryptobox.conf",
36        "~/.cryptobox.conf",
37        "/etc/cryptobox-server/cryptobox.conf"]
38
39VOLUMESDB_FILE = "cryptobox_volumes.db"
40PLUGINCONF_FILE = "cryptobox_plugins.conf"
41USERDB_FILE = "cryptobox_users.db"
42
43
44class CryptoBoxSettings:
45        """Manage the various configuration files of the CryptoBox
46        """
47
48        def __init__(self, config_file=None):
49                self.__is_initialized = False
50                self.log = logging.getLogger("CryptoBox")
51                config_file = self.__get_config_filename(config_file)
52                self.log.info("loading config file: %s" % config_file)
53                self.prefs = self.__get_preferences(config_file)
54                if not "PluginSettings" in self.prefs:
55                        self.prefs["PluginSettings"] = {}
56                self.__validate_config()
57                self.__configure_log_handler()
58                self.__check_unknown_preferences()
59                self.prepare_partition()
60                self.volumes_db = self.__get_volumes_database()
61                self.plugin_conf = self.__get_plugin_config()
62                self.user_db = self.__get_user_db()
63                self.misc_files = []
64                self.reload_misc_files()
65                self.__is_initialized = True
66       
67
68        def reload_misc_files(self):
69                """Call this method after creating or removing a 'misc' configuration file
70                """
71                self.misc_files = self.__get_misc_files()
72       
73
74        def write(self):
75                """
76                write all local setting files including the content of the "misc" subdirectory
77                """
78                status = True
79                try:
80                        self.volumes_db.write()
81                except IOError:
82                        self.log.warn("Could not save the volume database")
83                        status = False
84                try:
85                        self.plugin_conf.write()
86                except IOError:
87                        self.log.warn("Could not save the plugin configuration")
88                        status = False
89                try:
90                        self.user_db.write()
91                except IOError:
92                        self.log.warn("Could not save the user database")
93                        status = False
94                for misc_file in self.misc_files:
95                        if not misc_file.save():
96                                self.log.warn("Could not save a misc setting file (%s)" % misc_file.filename)
97                                status = False
98                return status
99
100
101        def get_misc_config_filename(self, name):
102                """Return an absolute filename for a given filename 'name'
103
104                'name' should not contain slashes (no directory part!)
105                """
106                return os.path.join(self.prefs["Locations"]["SettingsDir"], "misc", name)
107       
108
109        def create_misc_config_file(self, name, content):
110                """Create a new configuration file in the 'settings' directory
111
112                "name" should be the basename (without a directory)
113                "content" will be directly written to the file
114                this method may throw an IOException
115                """
116                misc_conf_file = self.get_misc_config_filename(name)
117                misc_conf_dir = os.path.dirname(misc_conf_file)
118                if not os.path.isdir(misc_conf_dir):
119                        try:
120                                os.mkdir(misc_conf_dir)
121                        except OSError, err_msg:
122                                ## the caller expects only IOError
123                                raise IOError, err_msg
124                cfile = open(misc_conf_file, "w")
125                try:
126                        cfile.write(content)
127                except IOError:
128                        cfile.close()
129                        raise
130                cfile.close()
131                ## reread all misc files automatically - this should be ok
132                self.reload_misc_files()
133
134
135        def requires_partition(self):
136                return bool(self.prefs["Main"]["UseConfigPartition"])
137       
138
139        def get_active_partition(self):
140                """Return the currently active cnfiguration partition.
141                """
142                settings_dir = self.prefs["Locations"]["SettingsDir"]
143                if not os.path.ismount(settings_dir):
144                        return None
145                for line in file("/proc/mounts"):
146                        fields = line.split(" ")
147                        mount_dir = fields[1]
148                        fs_type = fields[2]
149                        if fs_type == "tmpfs":
150                                ## skip ramdisks - these are not really "active partitions"
151                                continue
152                        try:
153                                if os.path.samefile(mount_dir, settings_dir):
154                                        return fields[0]
155                        except OSError:
156                                pass
157                ## no matching entry found
158                return None
159
160
161        def mount_partition(self):
162                """Mount a config partition.
163                """
164                self.log.debug("trying to mount configuration partition")
165                if not self.requires_partition():
166                        self.log.warn("mountConfigPartition: configuration partition is "
167                                        + "not required - mounting anyway")
168                if self.get_active_partition():
169                        self.log.warn("mountConfigPartition: configuration partition already "
170                                        + "mounted - not mounting again")
171                        return False
172                conf_partitions = self.get_available_partitions()
173                mount_dir = self.prefs["Locations"]["SettingsDir"]
174                if not conf_partitions:
175                        ## return, if tmpfs is already mounted
176                        if os.path.ismount(mount_dir):
177                                self.log.info("A ramdisk seems to be already mounted as a config " \
178                                                + "partition - doing nothing ...")
179                                ## return without any actions
180                                return True
181                        self.log.warn("no configuration partition found - you have to create "
182                                        + "it first")
183                        ## mount tmpfs instead to provide a place for storing stuff
184                        ## "_tmpfs_" as parameter for mount is interpreted as a magic word
185                        ## by CryptoBoxRootActions
186                        proc = subprocess.Popen(
187                                shell = False,
188                                stdout = subprocess.PIPE,
189                                stderr = subprocess.PIPE,
190                                args = [
191                                        self.prefs["Programs"]["super"],
192                                        self.prefs["Programs"]["CryptoBoxRootActions"],
193                                        "mount",
194                                        "_tmpfs_",
195                                        mount_dir ])
196                        (stdout, stderr) = proc.communicate()
197                        if proc.returncode != 0:
198                                self.log.error("Failed to mount a ramdisk for storing settings: %s" \
199                                                % stderr)
200                                return False
201                        self.log.info("Ramdisk (tmpfs) mounted as config partition ...")
202                else:
203                        partition = conf_partitions[0]
204                        ## umount tmpfs in case it is active
205                        if os.path.ismount(mount_dir):
206                                self.umount_partition()
207                        proc = subprocess.Popen(
208                                shell = False,
209                                stdout = subprocess.PIPE,
210                                stderr = subprocess.PIPE,
211                                args = [
212                                        self.prefs["Programs"]["super"],
213                                        self.prefs["Programs"]["CryptoBoxRootActions"],
214                                        "mount",
215                                        partition,
216                                        mount_dir ])
217                        (stdout, stderr) = proc.communicate()
218                        if proc.returncode != 0:
219                                self.log.error("Failed to mount the configuration partition (%s): %s" % \
220                                                (partition, stderr))
221                                return False
222                        self.log.info("configuration partition mounted: %s" % partition)
223                ## write config files (not during first initialization of this object)
224                if self.__is_initialized:
225                        self.write()
226                return True
227
228
229        def umount_partition(self):
230                """Umount the currently active configuration partition.
231                """
232                mount_dir = self.prefs["Locations"]["SettingsDir"]
233                if not os.path.ismount(mount_dir):
234                        self.log.warn("umountConfigPartition: no configuration partition mounted")
235                        return False
236                self.reload_misc_files()
237                proc = subprocess.Popen(
238                        shell = False,
239                        stdout = subprocess.PIPE,
240                        stderr = subprocess.PIPE,
241                        args = [
242                                self.prefs["Programs"]["super"],
243                                self.prefs["Programs"]["CryptoBoxRootActions"],
244                                "umount",
245                                mount_dir ])
246                (stdout, stderr) = proc.communicate()
247                if proc.returncode != 0:
248                        self.log.error("Failed to unmount the configuration partition: %s" % stderr)
249                        return False
250                self.log.info("configuration partition unmounted")
251                return True
252               
253
254        def get_available_partitions(self):
255                """returns a sequence of found config partitions"""
256                self.log.debug("Retrieving available configuration partitions ...")
257                proc = subprocess.Popen(
258                        shell = False,
259                        stdout = subprocess.PIPE,
260                        stderr = subprocess.PIPE,
261                        args = [
262                                self.prefs["Programs"]["blkid"],
263                                "-c", os.path.devnull,
264                                "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ])
265                (output, error) = proc.communicate()
266                if proc.returncode == 2:
267                        self.log.info("No configuration partitions found")
268                        return []
269                elif proc.returncode == 4:
270                        self.log.warn("Failed to call 'blkid' for unknown reasons.")
271                        return []
272                elif proc.returncode == 0:
273                        if output:
274                                return [e.strip().split(":", 1)[0] for e in output.splitlines()]
275                        else:
276                                return []
277                else:
278                        self.log.warn("Unknown exit code of 'blkid': %d - %s" \
279                                        % (proc.returncode, error))
280
281       
282        def prepare_partition(self):
283                """Mount a config partition if necessary.
284                """
285                if self.requires_partition() and not self.get_active_partition():
286                        self.mount_partition()
287
288
289        def __getitem__(self, key):
290                """redirect all requests to the 'prefs' attribute"""
291                return self.prefs[key]
292
293
294        def __get_preferences(self, config_file):
295                """Load the CryptoBox configuration.
296                """
297                import StringIO
298                config_rules = StringIO.StringIO(self.validation_spec)
299                try:
300                        prefs = configobj.ConfigObj(config_file, configspec=config_rules)
301                        if prefs:
302                                self.log.info("found config: %s" % prefs.items())
303                        else:
304                                raise CBConfigUnavailableError(
305                                                "failed to load the config file: %s" % config_file)
306                except IOError:
307                        raise CBConfigUnavailableError(
308                                        "unable to open the config file: %s" % config_file)
309                return prefs
310
311
312        def __validate_config(self):
313                """Check the configuration settings and cast value types.
314                """
315                result = self.prefs.validate(CryptoBoxSettingsValidator(), preserve_errors=True)
316                error_list = configobj.flatten_errors(self.prefs, result)
317                if not error_list:
318                        return
319                error_msgs = []
320                for sections, key, text in error_list:
321                        section_name = "->".join(sections)
322                        if not text:
323                                error_msg = "undefined configuration value (%s) in section '%s'" % \
324                                                (key, section_name)
325                        else:
326                                error_msg = "invalid configuration value (%s) in section '%s': %s" % \
327                                                (key, section_name, text)
328                        error_msgs.append(error_msg)
329                raise CBConfigError, "\n".join(error_msgs)
330
331
332        def __check_unknown_preferences(self):
333                """Check the configuration file for unknown settings to avoid spelling mistakes.
334                """
335                import StringIO
336                config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec),
337                                list_values=False)
338                self.__recursive_section_check("", self.prefs, config_rules)
339               
340       
341        def __recursive_section_check(self, section_path, section_config, section_rules):
342                """should be called by '__check_unknown_preferences' for every section
343                sends a warning message to the logger for every undefined (see validation_spec)
344                configuration setting
345                """
346                for section in section_config.keys():
347                        element_path = section_path + section
348                        if section in section_rules.keys():
349                                if isinstance(section_config[section], configobj.Section):
350                                        if isinstance(section_rules[section], configobj.Section):
351                                                self.__recursive_section_check(element_path + "->",
352                                                                section_config[section], section_rules[section])
353                                        else:
354                                                self.log.warn("configuration setting should be a value "
355                                                                + "instead of a section name: %s" % element_path)
356                                else:
357                                        if not isinstance(section_rules[section], configobj.Section):
358                                                pass    # good - the setting is valid
359                                        else:
360                                                self.log.warn("configuration setting should be a section "
361                                                                + "name instead of a value: %s" % element_path)
362                        elif element_path.startswith("PluginSettings->"):
363                                ## ignore plugin settings
364                                pass
365                        else:
366                                self.log.warn("unknown configuration setting: %s" % element_path)
367
368
369        def __get_plugin_config(self):
370                """Load the plugin configuration file if it exists.
371                """
372                import StringIO
373                plugin_rules = StringIO.StringIO(self.pluginValidationSpec)
374                try:
375                        try:
376                                plugin_conf_file = os.path.join(
377                                                self.prefs["Locations"]["SettingsDir"], PLUGINCONF_FILE)
378                        except KeyError:
379                                raise CBConfigUndefinedError("Locations", "SettingsDir")
380                except SyntaxError:
381                        raise CBConfigInvalidValueError("Locations", "SettingsDir", plugin_conf_file,
382                                        "failed to interprete the filename of the plugin config file "
383                                        + "correctly (%s)" % plugin_conf_file)
384                ## create plugin_conf_file if necessary
385                if os.path.exists(plugin_conf_file):
386                        plugin_conf = configobj.ConfigObj(plugin_conf_file, configspec=plugin_rules)
387                else:
388                        try:
389                                plugin_conf = configobj.ConfigObj(plugin_conf_file,
390                                                configspec=plugin_rules, create_empty=True)
391                        except IOError:
392                                plugin_conf = configobj.ConfigObj(configspec=plugin_rules)
393                                plugin_conf.filename = plugin_conf_file
394                ## validate and convert values according to the spec
395                plugin_conf.validate(validate.Validator())
396                return plugin_conf
397
398
399        def __get_volumes_database(self):
400                """Load the volume database file if it exists.
401                """
402                #TODO: add configuration specification and validation [a]: -v
403                try:
404                        try:
405                                conf_file = os.path.join(
406                                                self.prefs["Locations"]["SettingsDir"], VOLUMESDB_FILE)
407                        except KeyError:
408                                raise CBConfigUndefinedError("Locations", "SettingsDir")
409                except SyntaxError:
410                        raise CBConfigInvalidValueError("Locations", "SettingsDir", conf_file,
411                                        "failed to interprete the filename of the volume database "
412                                        + "correctly (%s)" % conf_file)
413                ## create conf_file if necessary
414                if os.path.exists(conf_file):
415                        conf = configobj.ConfigObj(conf_file)
416                else:
417                        try:
418                                conf = configobj.ConfigObj(conf_file, create_empty=True)
419                        except IOError:
420                                conf = configobj.ConfigObj()
421                                conf.filename = conf_file
422                return conf
423
424
425        def __get_user_db(self):
426                """Load the user database file if it exists.
427                """
428                import StringIO, sha
429                user_db_rules = StringIO.StringIO(self.userDatabaseSpec)
430                try:
431                        try:
432                                user_db_file = os.path.join(
433                                                self.prefs["Locations"]["SettingsDir"], USERDB_FILE)
434                        except KeyError:
435                                raise CBConfigUndefinedError("Locations", "SettingsDir")
436                except SyntaxError:
437                        raise CBConfigInvalidValueError("Locations", "SettingsDir", user_db_file,
438                                        "failed to interprete the filename of the users database file "
439                                        + "correctly (%s)" % user_db_file)
440                ## create user_db_file if necessary
441                if os.path.exists(user_db_file):
442                        user_db = configobj.ConfigObj(user_db_file, configspec=user_db_rules)
443                else:
444                        try:
445                                user_db = configobj.ConfigObj(user_db_file,
446                                                configspec=user_db_rules, create_empty=True)
447                        except IOError:
448                                user_db = configobj.ConfigObj(configspec=user_db_rules)
449                                user_db.filename = user_db_file
450                ## validate and set default value for "admin" user
451                user_db.validate(validate.Validator())
452                ## define password hash function - never use "sha" directly - SPOT
453                user_db.get_digest = lambda password: sha.new(password).hexdigest()
454                return user_db
455
456
457        def __get_misc_files(self):
458                """Load miscelleanous configuration files.
459
460                e.g.: an ssl certificate, ...
461                """
462                misc_dir = os.path.join(self.prefs["Locations"]["SettingsDir"], "misc")
463                if (not os.path.isdir(misc_dir)) or (not os.access(misc_dir, os.X_OK)):
464                        return []
465                misc_files = []
466                for root, dirs, files in os.walk(misc_dir):
467                        misc_files.extend([os.path.join(root, e) for e in files])
468                return [MiscConfigFile(os.path.join(misc_dir, f), self.log) for f in misc_files]
469
470
471        def __get_config_filename(self, config_file):
472                """Search for the configuration file.
473                """
474                import types
475                if config_file is None:
476                        # no config file was specified - we will look for it in the ususal locations
477                        conf_file_list = [os.path.expanduser(f)
478                                for f in CONF_LOCATIONS
479                                if os.path.exists(os.path.expanduser(f))]
480                        if not conf_file_list:
481                                # no possible config file found in the usual locations
482                                raise CBConfigUnavailableError()
483                        config_file = conf_file_list[0]
484                else:
485                        # a config file was specified (e.g. via command line)
486                        if type(config_file) != types.StringType:
487                                raise CBConfigUnavailableError(
488                                                "invalid config file specified: %s" % config_file)
489                        if not os.path.exists(config_file):
490                                raise CBConfigUnavailableError(
491                                                "could not find the specified configuration file (%s)" % config_file)
492                return config_file
493
494
495        def __configure_log_handler(self):
496                """Configure the log handler of the CryptoBox according to the config.
497                """
498                log_level = self.prefs["Log"]["Level"].upper()
499                log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"]
500                if not log_level in log_level_avail:
501                        raise CBConfigInvalidValueError("Log", "Level", log_level,
502                                        "invalid log level: only %s are allowed" % str(log_level_avail))
503                log_destination = self.prefs["Log"]["Destination"].lower()
504                ## keep this in sync with the spec and the log_destination branches below
505                log_dest_avail = ['file', 'syslog']
506                if not log_destination in log_dest_avail:
507                        raise CBConfigInvalidValueError("Log", "Destination", log_destination,
508                                        "invalid log destination: only %s are allowed" % str(log_dest_avail))
509                if log_destination == 'file':
510                        try:
511                                log_handler = logging.FileHandler(self.prefs["Log"]["Details"])
512                        except IOError:
513                                raise CBEnvironmentError("could not write to log file (%s)" % \
514                                                self.prefs["Log"]["Details"])
515                        log_handler.setFormatter(
516                                        logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
517                elif log_destination == 'syslog':
518                        log_facility = self.prefs["Log"]["Details"].upper()
519                        log_facil_avail = ['KERN', 'USER', 'MAIL', 'DAEMON', 'AUTH', 'SYSLOG',
520                                        'LPR', 'NEWS', 'UUCP', 'CRON', 'AUTHPRIV', 'LOCAL0', 'LOCAL1',
521                                        'LOCAL2', 'LOCAL3', 'LOCAL4', 'LOCAL5', 'LOCAL6', 'LOCAL7']
522                        if not log_facility in log_facil_avail:
523                                raise CBConfigInvalidValueError("Log", "Details", log_facility,
524                                                "invalid log details for 'syslog': only %s are allowed" % \
525                                                str(log_facil_avail))
526                        ## retrive the log priority from the syslog module
527                        log_handler = LocalSysLogHandler("CryptoBox",
528                                        getattr(syslog, 'LOG_%s' % log_facility))
529                        log_handler.setFormatter(
530                                        logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s'))
531                else:
532                        ## this should never happen - we just have it in case someone forgets
533                        ## to update the spec, the 'log_dest_avail' or the above branches
534                        raise CBConfigInvalidValueError("Log", "Destination", log_destination,
535                                        "invalid log destination: only %s are allowed" % str(log_dest_avail))
536                cbox_log = logging.getLogger("CryptoBox")
537                ## remove previous handlers (from 'basicConfig')
538                cbox_log.handlers = []
539                ## add new one
540                cbox_log.addHandler(log_handler)
541                ## do not call parent's handlers
542                cbox_log.propagate = False
543                ## 'log_level' is a string -> use 'getattr'
544                cbox_log.setLevel(getattr(logging, log_level))
545                ## the logger named "CryptoBox" is configured now
546
547
548        validation_spec = """
549[Main]
550AllowedDevices = list(min=1)
551DefaultVolumePrefix = string(min=1)
552DefaultCipher = string(default="aes-cbc-essiv:sha256")
553ConfigVolumeLabel = string(min=1, default="cbox_config")
554UseConfigPartition = integer(min=0, max=1, default=0)
555DisabledPlugins = list(default=list())
556
557[Locations]
558MountParentDir = directoryExists(default="/var/cache/cryptobox-server/mnt")
559SettingsDir = directoryExists(default="/var/cache/cryptobox-server/settings")
560TemplateDir = directoryExists(default="/usr/share/cryptobox-server/template")
561DocDir = directoryExists(default="/usr/share/doc/cryptobox-server/www-data")
562PluginDir = listOfExistingDirectories(default=list("/usr/share/cryptobox-server/plugins"))
563EventDir = string(default="/etc/cryptobox-server/events.d")
564
565[Log]
566Level = option("debug", "info", "warn", "error", default="warn")
567Destination = option("file", "syslog", default="file")
568Details = string(min=1, default="/var/log/cryptobox-server/cryptobox.log")
569
570[WebSettings]
571Stylesheet = string(min=1)
572Languages = list(min=1,default=list("en"))
573
574[Programs]
575cryptsetup = fileExecutable(default="/sbin/cryptsetup")
576mkfs = fileExecutable(default="/sbin/mkfs")
577nice = fileExecutable(default="/usr/bin/nice")
578blkid = fileExecutable(default="/sbin/blkid")
579blockdev = fileExecutable(default="/sbin/blockdev")
580mount = fileExecutable(default="/bin/mount")
581umount = fileExecutable(default="/bin/umount")
582super = fileExecutable(default="/usr/bin/super")
583# this is the "program" name as defined in /etc/super.tab
584CryptoBoxRootActions = string(min=1)
585
586[PluginSettings]
587[[__many__]]
588                """
589
590        pluginValidationSpec = """
591[__many__]
592visibility = boolean(default=None)
593requestAuth = boolean(default=None)
594rank = integer(default=None)
595                """
596       
597        userDatabaseSpec = """
598[admins]
599admin = string(default=d033e22ae348aeb5660fc2140aec35850c4da997)
600                """
601       
602
603class CryptoBoxSettingsValidator(validate.Validator):
604        """Some custom configuration check functions.
605        """
606
607        def __init__(self):
608                validate.Validator.__init__(self)
609                self.functions["directoryExists"] = self.check_directory_exists
610                self.functions["fileExecutable"] = self.check_file_executable
611                self.functions["fileWriteable"] = self.check_file_writeable
612                self.functions["listOfExistingDirectories"] = self.check_existing_directories
613       
614
615        def check_directory_exists(self, value):
616                """Is the directory accessible?
617                """
618                dir_path = os.path.abspath(value)
619                if not os.path.isdir(dir_path):
620                        raise validate.VdtValueError("%s (not found)" % value)
621                if not os.access(dir_path, os.X_OK):
622                        raise validate.VdtValueError("%s (access denied)" % value)
623                return dir_path
624       
625
626        def check_file_executable(self, value):
627                """Is the file executable?
628                """
629                file_path = os.path.abspath(value)
630                if not os.path.isfile(file_path):
631                        raise validate.VdtValueError("%s (not found)" % value)
632                if not os.access(file_path, os.X_OK):
633                        raise validate.VdtValueError("%s (access denied)" % value)
634                return file_path
635       
636
637        def check_file_writeable(self, value):
638                """Is the file writeable?
639                """
640                file_path = os.path.abspath(value)
641                if os.path.isfile(file_path):
642                        if not os.access(file_path, os.W_OK):
643                                raise validate.VdtValueError("%s (not found)" % value)
644                else:
645                        parent_dir = os.path.dirname(file_path)
646                        if os.path.isdir(parent_dir) and os.access(parent_dir, os.W_OK):
647                                return file_path
648                        raise validate.VdtValueError("%s (directory does not exist)" % value)
649                return file_path
650
651
652        def check_existing_directories(self, value):
653                """Are these directories accessible?
654                """
655                if not value:
656                        raise validate.VdtValueError("no plugin directory specified")
657                if not isinstance(value, list):
658                        value = [value]
659                result = []
660                for one_dir in value:
661                        dir_path = os.path.abspath(one_dir)
662                        if not os.path.isdir(dir_path):
663                                raise validate.VdtValueError(
664                                                "%s (plugin directory not found)" % one_dir)
665                        if not os.access(dir_path, os.X_OK):
666                                raise validate.VdtValueError(
667                                                "%s (access denied for plugin directory)" % one_dir)
668                        result.append(dir_path)
669                return result
670
671
672class MiscConfigFile:
673        """all other config files (e.g. a ssl certificate) to be stored"""
674
675        maxSize = 20480
676
677        def __init__(self, filename, logger):
678                self.filename = filename
679                self.log = logger
680                self.content = None
681                self.load()
682
683       
684        def load(self):
685                """Load a configuration file into memory.
686                """
687                fdesc = open(self.filename, "rb")
688                ## limit the maximum size
689                self.content = fdesc.read(self.maxSize)
690                if fdesc.tell() == self.maxSize:
691                        self.log.warn("file in misc settings directory (" + str(self.filename) \
692                                        + ") is bigger than allowed (" + str(self.maxSize) + ")")
693                fdesc.close()
694
695
696        def save(self):
697                """Save a configuration file to disk.
698                """
699                ## overriding of ro-files is not necessary (e.g. samba-include.conf)
700                if os.path.exists(self.filename) and not os.access(self.filename, os.W_OK):
701                        return True
702                save_dir = os.path.dirname(self.filename)
703                ## create the directory, if necessary
704                if not os.path.isdir(save_dir):
705                        try:
706                                os.mkdir(save_dir)
707                        except IOError:
708                                return False
709                ## save the content of the file
710                try:
711                        fdesc = open(self.filename, "wb")
712                except IOError:
713                        return False
714                try:
715                        fdesc.write(self.content)
716                        fdesc.close()
717                        return True
718                except IOError:
719                        fdesc.close()
720                        return False
721
722
723
724class LocalSysLogHandler(logging.Handler):
725        """Pass logging messages to a local syslog server without unix sockets.
726
727        derived from: logging.SysLogHandler
728        """
729
730        def __init__(self, prepend='CryptoBox', facility=syslog.LOG_USER):
731                logging.Handler.__init__(self)
732                self.formatter = None
733                self.facility = facility
734                syslog.openlog(prepend, 0, facility)
735       
736
737        def close(self):
738                """close the syslog connection
739                """
740                syslog.closelog()
741                logging.Handler.close(self)
742
743
744        def emit(self, record):
745                """format and send the log message
746                """
747                msg = "%s: %s" % (record.levelname, record.getMessage())
748                try:
749                        syslog.syslog(record.levelno, msg)
750                except Exception:
751                        self.handleError(record)
Note: See TracBrowser for help on using the browser.