VirtualMailManager/config.py
branchv0.6.x
changeset 353 2ae40cd0d213
parent 348 ca7575401549
child 359 7fa919dab42c
equal deleted inserted replaced
352:22d115376e4d 353:2ae40cd0d213
     9 """
     9 """
    10 
    10 
    11 from ConfigParser import \
    11 from ConfigParser import \
    12      Error, MissingSectionHeaderError, NoOptionError, NoSectionError, \
    12      Error, MissingSectionHeaderError, NoOptionError, NoSectionError, \
    13      ParsingError, RawConfigParser
    13      ParsingError, RawConfigParser
    14 from cStringIO import StringIO# TODO: move interactive stff to cli
    14 from cStringIO import StringIO
    15 
    15 
    16 from VirtualMailManager.common import VERSION_RE, \
    16 from VirtualMailManager.common import VERSION_RE, \
    17      exec_ok, expand_path, get_unicode, lisdir, version_hex
    17      exec_ok, expand_path, get_unicode, lisdir, version_hex
    18 from VirtualMailManager.constants import CONF_ERROR
    18 from VirtualMailManager.constants import CONF_ERROR
    19 from VirtualMailManager.errors import ConfigError, VMMError
    19 from VirtualMailManager.errors import ConfigError, VMMError
    20 from VirtualMailManager.maillocation import known_format
    20 from VirtualMailManager.maillocation import known_format
    21 from VirtualMailManager.password import verify_scheme as _verify_scheme
    21 from VirtualMailManager.password import verify_scheme as _verify_scheme
    22 
    22 
       
    23 DB_MUDULES = ('psycopg2', 'pypgsql')
       
    24 DB_SSL_MODES = ('allow', 'disabled', 'prefer', 'require', 'verify-ca',
       
    25                 'verify-full')
    23 
    26 
    24 _ = lambda msg: msg
    27 _ = lambda msg: msg
    25 
    28 
    26 
    29 
    27 class BadOptionError(Error):
    30 class BadOptionError(Error):
   289           path to the configuration file
   292           path to the configuration file
   290         """
   293         """
   291         LazyConfig.__init__(self)
   294         LazyConfig.__init__(self)
   292         self._cfg_filename = filename
   295         self._cfg_filename = filename
   293         self._cfg_file = None
   296         self._cfg_file = None
   294         self.__missing = {}
   297         self._missing = {}
   295 
   298 
   296         LCO = LazyConfigOption
   299         LCO = LazyConfigOption
   297         bool_t = self.bool_new
   300         bool_t = self.bool_new
   298         self._cfg = {
   301         self._cfg = {
   299             'account': {
   302             'account': {
   313                 'du': LCO(str, '/usr/bin/du', self.get, exec_ok),
   316                 'du': LCO(str, '/usr/bin/du', self.get, exec_ok),
   314                 'postconf': LCO(str, '/usr/sbin/postconf', self.get, exec_ok),
   317                 'postconf': LCO(str, '/usr/sbin/postconf', self.get, exec_ok),
   315             },
   318             },
   316             'database': {
   319             'database': {
   317                 'host': LCO(str, 'localhost', self.get),
   320                 'host': LCO(str, 'localhost', self.get),
       
   321                 'module': LCO(str, 'psycopg2', self.get, check_db_module),
   318                 'name': LCO(str, 'mailsys', self.get),
   322                 'name': LCO(str, 'mailsys', self.get),
   319                 'pass': LCO(str, None, self.get),
   323                 'pass': LCO(str, None, self.get),
       
   324                 'port': LCO(int, 5432, self.getint),
       
   325                 'sslmode': LCO(str, 'prefer', self.get, check_db_ssl_mode),
   320                 'user': LCO(str, None, self.get),
   326                 'user': LCO(str, None, self.get),
   321             },
   327             },
   322             'domain': {
   328             'domain': {
   323                 'auto_postmaster': LCO(bool_t, True, self.getboolean),
   329                 'auto_postmaster': LCO(bool_t, True, self.getboolean),
   324                 'delete_directory': LCO(bool_t, False, self.getboolean),
   330                 'delete_directory': LCO(bool_t, False, self.getboolean),
   362 
   368 
   363     def check(self):
   369     def check(self):
   364         """Performs a configuration check.
   370         """Performs a configuration check.
   365 
   371 
   366         Raises a ConfigError if settings w/o a default value are missed.
   372         Raises a ConfigError if settings w/o a default value are missed.
   367         Or a ConfigValueError if 'misc.dovecot_version' has the wrong
   373         Or some settings have a invalid value.
   368         format.
   374         """
   369         """
   375         def iter_dict():
   370         # TODO: There are only two settings w/o defaults.
   376             for section, options in self._missing.iteritems():
   371         #       So there is no need for cStringIO
   377                 errmsg.write(_(u'* Section: %s\n') % section)
   372         if not self._chk_cfg():
   378                 errmsg.writelines(u'    %s\n' % option for option in options)
       
   379             self._missing.clear()
       
   380 
       
   381         errmsg = None
       
   382         self._chk_non_default()
       
   383         miss_vers = 'misc' in self._missing and \
       
   384                     'dovecot_version' in self._missing['misc']
       
   385         if self._missing:
   373             errmsg = StringIO()
   386             errmsg = StringIO()
       
   387             errmsg.write(_(u'Check of configuration file %s failed.\n') %
       
   388                          self._cfg_filename)
   374             errmsg.write(_(u'Missing options, which have no default value.\n'))
   389             errmsg.write(_(u'Missing options, which have no default value.\n'))
   375             errmsg.write(_(u'Using configuration file: %s\n') %
   390             iter_dict()
   376                          self._cfg_filename)
   391         self._chk_possible_values(miss_vers)
   377             for section, options in self.__missing.iteritems():
   392         if self._missing:
   378                 errmsg.write(_(u'* Section: %s\n') % section)
   393             if not errmsg:
   379                 for option in options:
   394                 errmsg = StringIO()
   380                     errmsg.write((u'    %s\n') % option)
   395                 errmsg.write(_(u'Check of configuration file %s failed.\n') %
       
   396                              self._cfg_filename)
       
   397                 errmsg.write(_(u'Invalid configuration values.\n'))
       
   398             else:
       
   399                 errmsg.write('\n' + _(u'Invalid configuration values.\n'))
       
   400             iter_dict()
       
   401         if errmsg:
   381             raise ConfigError(errmsg.getvalue(), CONF_ERROR)
   402             raise ConfigError(errmsg.getvalue(), CONF_ERROR)
   382         check_version_format(self.get('misc', 'dovecot_version'))
       
   383 
   403 
   384     def hexversion(self, section, option):
   404     def hexversion(self, section, option):
   385         """Converts the version number (e.g.: 1.2.3) from the *option*'s
   405         """Converts the version number (e.g.: 1.2.3) from the *option*'s
   386         value to an int."""
   406         value to an int."""
   387         return version_hex(self.get(section, option))
   407         return version_hex(self.get(section, option))
   389     def unicode(self, section, option):
   409     def unicode(self, section, option):
   390         """Returns the value of the `option` from `section`, converted
   410         """Returns the value of the `option` from `section`, converted
   391         to Unicode."""
   411         to Unicode."""
   392         return get_unicode(self.get(section, option))
   412         return get_unicode(self.get(section, option))
   393 
   413 
   394     def _chk_cfg(self):
   414     def _chk_non_default(self):
   395         """Checks all section's options for settings w/o a default
   415         """Checks all section's options for settings w/o a default
   396         value.
   416         value. Missing items will be stored in _missing.
   397 
   417         """
   398         Returns `True` if everything is fine, else `False`.
       
   399         """
       
   400         errors = False
       
   401         for section in self._cfg.iterkeys():
   418         for section in self._cfg.iterkeys():
   402             missing = []
   419             missing = []
   403             for option, value in self._cfg[section].iteritems():
   420             for option, value in self._cfg[section].iteritems():
   404                 if (value.default is None and
   421                 if (value.default is None and
   405                     not RawConfigParser.has_option(self, section, option)):
   422                     not RawConfigParser.has_option(self, section, option)):
   406                     missing.append(option)
   423                     missing.append(option)
   407                     errors = True
       
   408             if missing:
   424             if missing:
   409                 self.__missing[section] = missing
   425                 self._missing[section] = missing
   410         return not errors
   426 
       
   427     def _chk_possible_values(self, miss_vers):
       
   428         """Check settings for which the possible values are known."""
       
   429         if not miss_vers:
       
   430             value = self.get('misc', 'dovecot_version')
       
   431             if not VERSION_RE.match(value):
       
   432                 self._missing['misc'] = ['version: ' +\
       
   433                         _(u"Not a valid Dovecot version: '%s'") % value]
       
   434         db_err = []
       
   435         value = self.dget('database.module').lower()
       
   436         if value not in DB_MUDULES:
       
   437             db_err.append('module: ' + \
       
   438                           _(u"Unsupported database module: '%s'") % value)
       
   439         if value == 'psycopg2':
       
   440             value = self.dget('database.sslmode')
       
   441             if value not in DB_SSL_MODES:
       
   442                 db_err.append('sslmode: ' + \
       
   443                               _(u"Unknown pgsql SSL mode: '%s'") % value)
       
   444         if db_err:
       
   445             self._missing['database'] = db_err
   411 
   446 
   412 
   447 
   413 def is_dir(path):
   448 def is_dir(path):
   414     """Check if the expanded path is a directory.  When the expanded path
   449     """Check if the expanded path is a directory.  When the expanded path
   415     is a directory the expanded path will be returned.  Otherwise a
   450     is a directory the expanded path will be returned.  Otherwise a
   417     """
   452     """
   418     path = expand_path(path)
   453     path = expand_path(path)
   419     if lisdir(path):
   454     if lisdir(path):
   420         return path
   455         return path
   421     raise ConfigValueError(_(u"No such directory: %s") % get_unicode(path))
   456     raise ConfigValueError(_(u"No such directory: %s") % get_unicode(path))
       
   457 
       
   458 
       
   459 def check_db_module(module):
       
   460     """Check if the *module* is a supported pgsql module."""
       
   461     if module.lower() in DB_MUDULES:
       
   462         return module
       
   463     raise ConfigValueError(_(u"Unsupported database module: '%s'") %
       
   464                            get_unicode(module))
       
   465 
       
   466 
       
   467 def check_db_ssl_mode(ssl_mode):
       
   468     """Check if the *ssl_mode* is one of the SSL modes, known by pgsql."""
       
   469     if ssl_mode in DB_SSL_MODES:
       
   470         return ssl_mode
       
   471     raise ConfigValueError(_(u"Unknown pgsql SSL mode: '%s'") %
       
   472                            get_unicode(ssl_mode))
   422 
   473 
   423 
   474 
   424 def check_mailbox_format(format):
   475 def check_mailbox_format(format):
   425     """
   476     """
   426     Check if the mailbox format *format* is supported.  When the *format*
   477     Check if the mailbox format *format* is supported.  When the *format*