VirtualMailManager/Config.py
branchv0.6.x
changeset 174 974bafa59330
parent 173 c0e2c7687dd3
child 175 b241272eb1bd
equal deleted inserted replaced
173:c0e2c7687dd3 174:974bafa59330
     1 # -*- coding: UTF-8 -*-
     1 # -*- coding: UTF-8 -*-
     2 # Copyright (c) 2007 - 2010, Pascal Volk
     2 # Copyright (c) 2007 - 2010, Pascal Volk
     3 # See COPYING for distribution information.
     3 # See COPYING for distribution information.
     4 
     4 
     5 """Configuration class for read, modify and write the
     5 """vmm's configuration module for simplified configuration access.
     6 configuration from Virtual Mail Manager.
     6 
     7 
     7 This module defines a few classes:
       
     8 
       
     9 ``LazyConfig``
       
    10     This class provides the following additonal methods
       
    11 
       
    12     * `LazyConfig.pget()`
       
    13         polymorphic getter which returns the value with the appropriate
       
    14         type.
       
    15     * `LazyConfig.dget()`
       
    16         like *pget()*, but checks additonal for default values in
       
    17         `LazyConfig._cfg`.
       
    18     * `LazyConfig.set()`
       
    19         like `RawConfigParser.set()`, but converts the new value to the
       
    20         appropriate type/class and optional validates the new value.
       
    21     * `LazyConfig.bool_new()`
       
    22         converts data from raw_input into boolean values.
       
    23     * `LazyConfig.get_boolean()`
       
    24         like `RawConfigParser.getboolean()`, but doesn't fail on real
       
    25         `bool` values.
       
    26 
       
    27 ``Config``
       
    28     The Config class used by vmm.
       
    29 
       
    30 ``LazyConfigOption``
       
    31     The class for the configuration objects in the ``Config`` class'
       
    32     ``_cfg`` dictionary.
     8 """
    33 """
     9 
    34 
       
    35 
    10 from shutil import copy2
    36 from shutil import copy2
    11 from ConfigParser import ConfigParser, MissingSectionHeaderError, ParsingError
    37 from ConfigParser import (Error, MissingSectionHeaderError, NoOptionError,
       
    38                           NoSectionError, ParsingError, RawConfigParser)
    12 from cStringIO import StringIO
    39 from cStringIO import StringIO
    13 
    40 
    14 from __main__ import ENCODING, ERR, w_std
    41 from __main__ import os, ENCODING, ERR, get_unicode, w_std
    15 from Exceptions import VMMConfigException
    42 from Exceptions import VMMConfigException
    16 
    43 
    17 class Config(ConfigParser):
    44 
       
    45 class BadOptionError(Error):
       
    46     """Raised when a option isn't in the format 'section.option'."""
       
    47     pass
       
    48 
       
    49 
       
    50 class ConfigValueError(Error):
       
    51     """Raised when creating or validating of new values fails."""
       
    52     pass
       
    53 
       
    54 
       
    55 class NoDefaultError(Error):
       
    56     """Raised when the requested option has no default value."""
       
    57 
       
    58     def __init__(self, section, option):
       
    59         Error.__init__(self, 'Option %r in section %r has no default value' %(
       
    60                        option, section))
       
    61 
       
    62 
       
    63 class LazyConfig(RawConfigParser):
       
    64     """The **lazy** derivate of the `RawConfigParser`.
       
    65     
       
    66     There are two additional getters:
       
    67     
       
    68     `LazyConfig.pget()`
       
    69         The polymorphic getter, which returns a option's value with the
       
    70         appropriate type.
       
    71     `LazyConfig.dget()`
       
    72         Like `LazyConfig.pget()`, but returns the option's default, from
       
    73         `LazyConfig._cfg['sectionname']['optionname'].default`, if the
       
    74         option is not configured in a ini-like configuration file.
       
    75 
       
    76 
       
    77     `LazyConfig.set()` differs from ``RawConfigParser``'s ``set()`` method.
       
    78     ``LazyConfig.set()`` takes the ``section`` and ``option`` arguments
       
    79     combined to a single string in the form
       
    80     "``section``\ **.**\ ``option``".
       
    81     """
       
    82     def __init__(self):
       
    83         RawConfigParser.__init__(self)
       
    84         self._modified = False
       
    85         self._cfg = {
       
    86             'sectionname': {
       
    87                 'optionname': LazyConfigOption(int, 1, self.getint)
       
    88             }
       
    89         }
       
    90         """sample _cfg dictionary. Create your own in your derived class."""
       
    91 
       
    92     def bool_new(self, value):
       
    93         """Converts the string `value` into a `bool` and returns it.
       
    94 
       
    95         | '1', 'on', 'yes' and 'true' will become ``True``
       
    96         | '0', 'off', 'no' and 'false' will become ``False``
       
    97 
       
    98         Throws a `ConfigValueError` for all other values, except ``bool``\ s.
       
    99         """
       
   100         if isinstance(value, bool):
       
   101             return value
       
   102         if value.lower() in self._boolean_states:
       
   103             return self._boolean_states[value.lower()]
       
   104         else:
       
   105             raise ConfigValueError(_(u'Not a boolean: “%s”') % \
       
   106                                    get_unicode(value))
       
   107 
       
   108     def get_boolean(self, section, option):
       
   109         # if the setting was not written to the configuration file, it may
       
   110         # be still a boolean value - lets see
       
   111         if self._modified:
       
   112            tmp = self.get(section, option)
       
   113            if isinstance(tmp, bool):
       
   114                return tmp
       
   115         return self.getboolean(section, option)
       
   116 
       
   117     def __get_section_option(self, section_option):
       
   118         """splits ``section_option`` (section\ **.**\ option) in two parts
       
   119         and returns them as list ``[section, option]``, if:
       
   120 
       
   121             * it likes the format of ``section_option``
       
   122             * the ``section`` is known
       
   123             * the ``option`` is known
       
   124 
       
   125         Else one of the following exceptions will be thrown:
       
   126 
       
   127             * `BadOptionError`
       
   128             * `NoSectionError`
       
   129             * `NoOptionError`
       
   130         """ 
       
   131         sect_opt = section_option.lower().split('.')
       
   132         if len(sect_opt) != 2:# do we need a regexp to check the format?
       
   133             raise BadOptionError(
       
   134                         _(u'Bad format: “%s” - expected: section.option') % \
       
   135                         get_unicode(section_option))
       
   136         if not sect_opt[0] in self._cfg:
       
   137             raise NoSectionError(sect_opt[0])
       
   138         if not sect_opt[1] in self._cfg[sect_opt[0]]:
       
   139             raise NoOptionError(sect_opt[1], sect_opt[0])
       
   140         return sect_opt
       
   141 
       
   142     def items(self, section):
       
   143         """returns a ``list`` with key, value ``tuples`` from the given
       
   144         ``section``: ``[(key, value), …]``"""
       
   145         if section in self._sections:# check if the section was parsed
       
   146             d2 = self._sections[section]
       
   147         elif not section in self._cfg:
       
   148             raise NoSectionError(section)
       
   149         else:
       
   150             return ((k, self._cfg[section][k].default) \
       
   151                     for k in self._cfg[section].iterkeys())
       
   152         # still here? Get defaults and merge defaults with configured setting
       
   153         d = dict((k, self._cfg[section][k].default) \
       
   154                  for k in self._cfg[section].iterkeys())
       
   155         d.update(d2)
       
   156         if '__name__' in d: del d['__name__']
       
   157         return d.iteritems()
       
   158 
       
   159     def dget(self, option):
       
   160         """Returns the value of the `option`.
       
   161         
       
   162         If the option could not be found in the configuration file, the
       
   163         configured default value, from ``LazyConfig._cfg`` will be
       
   164         returned.
       
   165         
       
   166         Arguments:
       
   167         
       
   168         `option` : string
       
   169             the configuration option in the form
       
   170             "``section``\ **.**\ ``option``"
       
   171 
       
   172         Throws a `NoDefaultError`, if no default value was passed to
       
   173         `LazyConfigOption.__init__()` for the `option`.
       
   174         """
       
   175         section, option = self.__get_section_option(option)
       
   176         try:
       
   177             return self._cfg[section][option].getter(section, option)
       
   178         except (NoSectionError, NoOptionError):
       
   179             if not self._cfg[section][option].default is None:
       
   180                 return self._cfg[section][option].default
       
   181             else:
       
   182                 raise NoDefaultError(section, option)
       
   183 
       
   184     def pget(self, option):
       
   185         """Returns the value of the `option`."""
       
   186         section, option = self.__get_section_option(option)
       
   187         return self._cfg[section][option].getter(section, option)
       
   188 
       
   189     def set(self, option, value):
       
   190         """Set the value of an option.
       
   191         
       
   192         Throws a ``ValueError`` if `value` couldn't be converted to
       
   193         ``LazyConfigOption.cls``"""
       
   194         section, option = self.__get_section_option(option)
       
   195         val = self._cfg[section][option].cls(value)
       
   196         if not self._cfg[section][option].validate is None:
       
   197             val = self._cfg[section][option].validate(val)
       
   198         if not RawConfigParser.has_section(self, section):
       
   199             self.add_section(section)
       
   200         RawConfigParser.set(self, section, option, val)
       
   201         self._modified = True
       
   202 
       
   203     def has_section(self, section):
       
   204         """Checks if ``section`` is a known configuration section."""
       
   205         return section.lower() in self._cfg 
       
   206 
       
   207     def has_option(self, option):
       
   208         """Checks if the option (section\ **.**\ option) is a known
       
   209         configuration option."""
       
   210         try:
       
   211             self.__get_section_option(option)
       
   212             return True
       
   213         except(BadOptionError, NoSectionError, NoOptionError):
       
   214             return False
       
   215 
       
   216 
       
   217 
       
   218 class LazyConfigOption(object):
       
   219     """A simple container class for configuration settings.
       
   220 
       
   221    ``LazyConfigOption`` instances are required by `LazyConfig` instances,
       
   222    and instances of classes derived from ``LazyConfig``, like the
       
   223    `Config` class.
       
   224     """
       
   225     __slots__ = ('cls', 'default', 'getter', 'validate')
       
   226 
       
   227     def __init__(self, cls, default, getter, validate=None):
       
   228         """Creates a new ``LazyConfigOption`` instance.
       
   229 
       
   230         Arguments:
       
   231 
       
   232         ``cls`` : type
       
   233             The class/type of the option's value
       
   234         ``default``
       
   235             Default value of the option. Use ``None`` if the option should
       
   236             not have a default value.
       
   237         ``getter`` : callable
       
   238             A method's name of `RawConfigParser` and derived classes, to
       
   239             get a option's value, e.g. `self.getint`.
       
   240         ``validate`` : NoneType or a callable
       
   241             None or any method, that takes one argument, in order to check
       
   242             the value, when `LazyConfig.set()` is called.
       
   243         """
       
   244         self.cls = cls
       
   245         """The class of the option's value e.g. `str`, `unicode` or `bool`"""
       
   246         self.default = default
       
   247         """The option's default value, may be ``None``"""
       
   248         if callable(getter):
       
   249             self.getter = getter
       
   250             """The getter method to get the option's value"""
       
   251         else:
       
   252             raise TypeError('getter has to be a callable, got a %r'\
       
   253                             % getter.__class__.__name__)
       
   254         if validate is None or callable(validate):
       
   255             self.validate = validate
       
   256             """A method to validate the value"""
       
   257         else:
       
   258             raise TypeError('validate has to be callable or None, got a %r'\
       
   259                             % validate.__class__.__name__)
       
   260 
       
   261 
       
   262 class Config(LazyConfig):
    18     """This class is for reading and modifying vmm's configuration file."""
   263     """This class is for reading and modifying vmm's configuration file."""
    19 
   264 
    20     def __init__(self, filename):
   265     def __init__(self, filename):
    21         """Creates a new Config instance
   266         """Creates a new Config instance
    22 
   267 
    23         Arguments:
   268         Arguments:
    24         filename -- path to the configuration file
   269      
    25         """
   270         ``filename``
    26         ConfigParser.__init__(self)
   271             path to the configuration file
       
   272         """
       
   273         LazyConfig.__init__(self)
    27         self.__cfgFileName = filename
   274         self.__cfgFileName = filename
    28         self.__cfgFile = None
   275         self.__cfgFile = None
    29         self.__VMMsections = ('account', 'bin', 'database', 'domain',
       
    30                               'maildir', 'misc', 'config')
       
    31         self.__changes = False
       
    32         self.__missing = {}
   276         self.__missing = {}
    33         self.__dbopts = [
   277 
    34                 ['host', 'localhot'],
   278         LCO = LazyConfigOption
    35                 ['user', 'vmm'],
   279         bool_t = self.bool_new
    36                 ['pass', 'your secret password'],
   280         self._cfg = {
    37                 ['name', 'mailsys']
   281             'account': {
    38                 ]
   282                 'delete_directory': LCO(bool_t, False, self.get_boolean),
    39         self.__mdopts = [
   283                 'directory_mode':   LCO(int,    448,   self.getint),
    40                 ['name', 'Maildir'],
   284                 'disk_usage':       LCO(bool_t, False, self.get_boolean),
    41                 ['folders', 'Drafts:Sent:Templates:Trash'],
   285                 'password_length':  LCO(int,    8,     self.getint),
    42                 ]
   286                 'random_password':  LCO(bool_t, False, self.get_boolean),
    43         self.__accountopts = [
   287                 'imap' :            LCO(bool_t, True,  self.get_boolean),
    44                 ['delete_directory', 'false'],
   288                 'pop3' :            LCO(bool_t, True,  self.get_boolean),
    45                 ['directory_mode', 448],
   289                 'sieve':            LCO(bool_t, True,  self.get_boolean),
    46                 ['disk_usage', 'false'],
   290                 'smtp' :            LCO(bool_t, True,  self.get_boolean),
    47                 ['password_length', 8],
   291             },
    48                 ['random_password', 'false'],
   292             'bin': {
    49                 ['smtp', 'true'],
   293                 'dovecotpw': LCO(str, '/usr/sbin/dovecotpw', self.get,
    50                 ['pop3', 'true'],
   294                                  self.exec_ok),
    51                 ['imap', 'true'],
   295                 'du':        LCO(str, '/usr/bin/du', self.get, self.exec_ok),
    52                 ['sieve', 'true']
   296                 'postconf':  LCO(str, '/usr/sbin/postconf', self.get,
    53                 ]
   297                                  self.exec_ok),
    54         self.__domdopts = [
   298             },
    55                 ['auto_postmaster', 'true'],
   299             'database': {
    56                 ['delete_directory', 'false'],
   300                 'host': LCO(str, 'localhost', self.get),
    57                 ['directory_mode', 504],
   301                 'name': LCO(str, 'mailsys',   self.get),
    58                 ['force_deletion', 'false'],
   302                 'pass': LCO(str, None,        self.get),
    59                 ]
   303                 'user': LCO(str, None,        self.get),
    60         self.__binopts = [
   304             },
    61                 ['dovecotpw', '/usr/sbin/dovecotpw'],
   305             'domain': {
    62                 ['du', '/usr/bin/du'],
   306                 'auto_postmaster':  LCO(bool_t, True,  self.get_boolean),
    63                 ['postconf', '/usr/sbin/postconf']
   307                 'delete_directory': LCO(bool_t, False, self.get_boolean),
    64                 ]
   308                 'directory_mode':   LCO(int,    504,   self.getint),
    65         self.__miscopts = [
   309                 'force_deletion':   LCO(bool_t, False, self.get_boolean),
    66                 ['base_directory', '/srv/mail'],
   310             },
    67                 ['dovecot_version', '11'],
   311             'maildir': {
    68                 ['gid_mail', 8],
   312                 'folders': LCO(str, 'Drafts:Sent:Templates:Trash', self.get),
    69                 ['password_scheme', 'PLAIN'],
   313                 'name':    LCO(str, 'Maildir',                     self.get),
    70                 ['transport', 'dovecot:'],
   314             },
    71                 ]
   315             'misc': {
       
   316                 'base_directory':  LCO(str, '/srv/mail', self.get, self.is_dir),
       
   317                 'dovecot_version': LCO(int, 12,          self.getint),
       
   318                 'gid_mail':        LCO(int, 8,           self.getint),
       
   319                 'password_scheme': LCO(str, 'CRAM-MD5',  self.get,
       
   320                                        self.known_scheme),
       
   321                 'transport':       LCO(str, 'dovecot:',  self.get),
       
   322             },
       
   323             'config': {'done': LCO(bool_t, False, self.get_boolean)}
       
   324         }
    72 
   325 
    73     def load(self):
   326     def load(self):
    74         """Loads the configuration, read only.
   327         """Loads the configuration, read only.
    75 
   328 
    76         Raises a VMMConfigException if the configuration syntax is invalid.
   329         Raises a VMMConfigException if the configuration syntax is invalid.
    77         """
   330         """
    78         try:
   331         try:
    79             self.__cfgFile = file(self.__cfgFileName, 'r')
   332             self.__cfgFile = open(self.__cfgFileName, 'r')
    80             self.readfp(self.__cfgFile)
   333             self.readfp(self.__cfgFile)
    81         except (MissingSectionHeaderError, ParsingError), e:
   334         except (MissingSectionHeaderError, ParsingError), e:
    82             self.__cfgFile.close()
   335             self.__cfgFile.close()
    83             raise VMMConfigException(str(e), ERR.CONF_ERROR)
   336             raise VMMConfigException(str(e), ERR.CONF_ERROR)
    84         self.__cfgFile.close()
   337         self.__cfgFile.close()
    86     def check(self):
   339     def check(self):
    87         """Performs a configuration check.
   340         """Performs a configuration check.
    88 
   341 
    89         Raises a VMMConfigException if the check fails.
   342         Raises a VMMConfigException if the check fails.
    90         """
   343         """
    91         if not self.__chkSections():
   344         if not self.__chkCfg():
    92             errmsg = StringIO()
   345             errmsg = StringIO()
    93             errmsg.write(_("Using configuration file: %s\n") %\
   346             errmsg.write(_(u'Missing options, which have no default value.\n'))
    94                     self.__cfgFileName)
   347             errmsg.write(_(u'Using configuration file: %s\n') %\
    95             for k,v in self.__missing.items():
   348                          self.__cfgFileName)
    96                 if v[0] is True:
   349             for section, options in self.__missing.iteritems():
    97                     errmsg.write(_(u"missing section: %s\n") % k)
   350                 errmsg.write(_(u'* Section: %s\n') % section)
    98                 else:
   351                 for option in options:
    99                     errmsg.write(_(u"missing options in section %s:\n") % k)
   352                     errmsg.write((u'    %s\n') % option)
   100                     for o in v:
       
   101                         errmsg.write(" * %s\n" % o)
       
   102             raise VMMConfigException(errmsg.getvalue(), ERR.CONF_ERROR)
   353             raise VMMConfigException(errmsg.getvalue(), ERR.CONF_ERROR)
   103 
   354 
   104     def getsections(self):
   355     def getsections(self):
   105         """Return a list with all configurable sections."""
   356         """Returns a generator object for all configurable sections."""
   106         return self.__VMMsections[:-1]
   357         return (s for s in self._cfg.iterkeys() if s != 'config')
   107 
   358 
   108     def get(self, section, option, raw=False, vars=None):
   359     def is_dir(self, path):
   109         return unicode(ConfigParser.get(self, section, option, raw, vars),
   360         """Checks if ``path`` is a directory.
   110                 ENCODING, 'replace')
   361         
       
   362         Throws a `ConfigValueError` if ``path`` is not a directory.
       
   363         """
       
   364         path = self.__expand_path(path)
       
   365         if not os.path.isdir(path):
       
   366             raise ConfigValueError(_(u'“%s” is not a directory') % \
       
   367                                    get_unicode(path))
       
   368         return path
       
   369 
       
   370     def exec_ok(self, binary):
       
   371         """Checks if the ``binary`` exists and if it is executable.
       
   372         
       
   373         Throws a `ConfigValueError` if the ``binary`` isn't a file or is
       
   374         not executable.
       
   375         """
       
   376         binary = self.__expand_path(binary)
       
   377         if not os.path.isfile(binary):
       
   378             raise ConfigValueError(_(u'“%s” is not a file') % \
       
   379                                    get_unicode(binary))
       
   380         if not os.access(binary, os.X_OK):
       
   381             raise ConfigValueError(_(u'File is not executable: “%s”') % \
       
   382                                    get_unicode(binary))
       
   383         return binary
       
   384 
       
   385     def known_scheme(self, scheme):
       
   386         """Converts ``scheme`` to upper case and checks if is known by
       
   387         Dovecot (listed in VirtualMailManager.SCHEMES).
       
   388         
       
   389         Throws a `ConfigValueError` if the scheme is not listed in
       
   390         VirtualMailManager.SCHEMES.
       
   391         """
       
   392         scheme = scheme.upper()
       
   393         # TODO: VMM.SCHEMES
       
   394 
       
   395     def unicode(self, section, option):
       
   396         """Returns the value of the ``option`` from ``section``, converted
       
   397         to Unicode.
       
   398         """
       
   399         return get_unicode(self.get(section, option))
   111 
   400 
   112     def configure(self, sections):
   401     def configure(self, sections):
   113         """Interactive method for configuring all options in the given sections
   402         """Interactive method for configuring all options in the given sections
   114 
   403 
   115         Arguments:
   404         Arguments:
   116         sections -- list of strings with section names
   405         sections -- list of strings with section names
   117         """
   406         """
   118         if not isinstance(sections, list):
   407         input_fmt = _(u'Enter new value for option %(option)s \
   119             raise TypeError("Argument 'sections' is not a list.")
   408 [%(current_value)s]: ')
   120         # if [config] done = false (default at 1st run),
   409         failures = 0
       
   410 
       
   411         # if config.done == false (default at 1st run),
   121         # then set changes true
   412         # then set changes true
   122         try:
   413         if not self.dget('config.done'):
   123             if not self.getboolean('config', 'done'):
   414             self._modified = True
   124                 self.__changes = True
       
   125         except ValueError:
       
   126             self.set('config', 'done', 'False')
       
   127             self.__changes = True
       
   128         w_std(_(u'Using configuration file: %s\n') % self.__cfgFileName)
   415         w_std(_(u'Using configuration file: %s\n') % self.__cfgFileName)
   129         for s in sections:
   416         for s in sections:
   130             if s != 'config':
   417             w_std(_(u'* Config section: “%s”') % s )
   131                 w_std(_(u'* Config section: “%s”') % s )
       
   132             for opt, val in self.items(s):
   418             for opt, val in self.items(s):
   133                 newval = raw_input(
   419                 failures = 0
   134                 _('Enter new value for option %(opt)s [%(val)s]: ').encode(
   420                 while True:
   135                     ENCODING, 'replace') % {'opt': opt, 'val': val})
   421                     newval = raw_input(input_fmt.encode(ENCODING,'replace') %{
   136                 if newval and newval != val:
   422                                        'option': opt, 'current_value': val})
   137                     self.set(s, opt, newval)
   423                     if newval and newval != val:
   138                     self.__changes = True
   424                         try:
       
   425                             self.set('%s.%s' % (s, opt), newval)
       
   426                             break
       
   427                         except (ValueError, ConfigValueError), e:
       
   428                             w_std(_(u'Warning: %s') % e)
       
   429                             failures += 1
       
   430                             if failures > 2:
       
   431                                 raise VMMConfigException(
       
   432                                     _(u'Too many failures - try again later.'),
       
   433                                     ERR.VMM_TOO_MANY_FAILURES)
       
   434                     else:
       
   435                         break
   139             print
   436             print
   140         if self.__changes:
   437         if self._modified:
   141             self.__saveChanges()
   438             self.__saveChanges()
   142 
   439 
   143     def __saveChanges(self):
   440     def __saveChanges(self):
   144         """Writes changes to the configuration file."""
   441         """Writes changes to the configuration file."""
   145         self.set('config', 'done', 'true')
   442         self.set('config.done', True)
   146         copy2(self.__cfgFileName, self.__cfgFileName+'.bak')
   443         copy2(self.__cfgFileName, self.__cfgFileName+'.bak')
   147         self.__cfgFile = file(self.__cfgFileName, 'w')
   444         self.__cfgFile = open(self.__cfgFileName, 'w')
   148         self.write(self.__cfgFile)
   445         self.write(self.__cfgFile)
   149         self.__cfgFile.close()
   446         self.__cfgFile.close()
   150 
   447 
   151     def __chkSections(self):
   448     def __chkCfg(self):
   152         """Checks if all configuration sections are existing."""
   449         """Checks all section's options for settings w/o default values.
       
   450         
       
   451         Returns ``True`` if everything is fine, else ``False``."""
   153         errors = False
   452         errors = False
   154         for s in self.__VMMsections:
   453         for section in self._cfg.iterkeys():
   155             if not self.has_section(s):
   454             missing = []
   156                 self.__missing[s] = [True]
   455             for option, value in self._cfg[section].iteritems():
   157                 errors = True
   456                 if (value.default is None
   158             elif not self.__chkOptions(s):
   457                 and not RawConfigParser.has_option(self, section, option)):
   159                 errors = True
   458                     missing.append(option)
       
   459                     errors = True
       
   460             if len(missing):
       
   461                 self.__missing[section] = missing
   160         return not errors
   462         return not errors
   161 
   463 
   162     def __chkOptions(self, section):
   464     def __expand_path(self, path):
   163         """Checks if all configuration options in section are existing.
   465         """Expands paths, starting with ``.`` or ``~``, to an absolute path."""
   164 
   466         if path.startswith('.'):
   165         Arguments:
   467             return os.path.abspath(path)
   166         section -- the section to be checked
   468         if path.startswith('~'):
   167         """
   469             return os.path.expanduser(path)
   168         retval = True
   470         return path
   169         missing = []
       
   170         if section == 'database':
       
   171             opts = self.__dbopts
       
   172         elif section == 'maildir':
       
   173             opts = self.__mdopts
       
   174         elif section == 'account':
       
   175             opts = self.__accountopts
       
   176         elif section == 'domain':
       
   177             opts = self.__domdopts
       
   178         elif section == 'bin':
       
   179             opts = self.__binopts
       
   180         elif section == 'misc':
       
   181             opts = self.__miscopts
       
   182         elif section == 'config':
       
   183             opts = [['done', 'false']]
       
   184         for o, v in opts:
       
   185             if not self.has_option(section, o):
       
   186                 missing.append(o)
       
   187                 retval = False
       
   188         if len(missing):
       
   189             self.__missing[section] = missing
       
   190         return retval