VirtualMailManager/Config.py
author Pascal Volk <neverseen@users.sourceforge.net>
Mon, 18 Jan 2010 03:23:50 +0000
branchv0.6.x
changeset 169 a72908248153
parent 168 fd496561acc6
child 171 6f8ac86d1611
permissions -rw-r--r--
man: updated man/{de/,}man5/vmm.cfg.5, using reStructuredText now

# -*- coding: UTF-8 -*-
# Copyright (c) 2007 - 2010, Pascal Volk
# See COPYING for distribution information.

"""Configuration class for read, modify and write the
configuration from Virtual Mail Manager.

"""

from shutil import copy2
from ConfigParser import ConfigParser, MissingSectionHeaderError, ParsingError
from cStringIO import StringIO

from __main__ import ENCODING, ERR, w_std
from Exceptions import VMMConfigException

class Config(ConfigParser):
    """This class is for reading and modifying vmm's configuration file."""

    def __init__(self, filename):
        """Creates a new Config instance

        Arguments:
        filename -- path to the configuration file
        """
        ConfigParser.__init__(self)
        self.__cfgFileName = filename
        self.__cfgFile = None
        self.__VMMsections = ('account', 'bin', 'database', 'domain',
                              'maildir', 'misc', 'config')
        self.__changes = False
        self.__missing = {}
        self.__dbopts = [
                ['host', 'localhot'],
                ['user', 'vmm'],
                ['pass', 'your secret password'],
                ['name', 'mailsys']
                ]
        self.__mdopts = [
                ['name', 'Maildir'],
                ['folders', 'Drafts:Sent:Templates:Trash'],
                ]
        self.__accountopts = [
                ['delete_directory', 'false'],
                ['directory_mode', 448],
                ['disk_usage', 'false'],
                ['password_length', 8],
                ['random_password', 'false'],
                ['smtp', 'true'],
                ['pop3', 'true'],
                ['imap', 'true'],
                ['sieve', 'true']
                ]
        self.__domdopts = [
                ['auto_postmaster', 'true'],
                ['delete_directory', 'false'],
                ['directory_mode', 504],
                ['force_deletion', 'false'],
                ]
        self.__binopts = [
                ['dovecotpw', '/usr/sbin/dovecotpw'],
                ['du', '/usr/bin/du'],
                ['postconf', '/usr/sbin/postconf']
                ]
        self.__miscopts = [
                ['base_directory', '/srv/mail'],
                ['dovecot_version', '11'],
                ['gid_mail', 8],
                ['password_scheme', 'PLAIN'],
                ['transport', 'dovecot:'],
                ]

    def load(self):
        """Loads the configuration, read only.

        Raises a VMMConfigException if the configuration syntax is invalid.
        """
        try:
            self.__cfgFile = file(self.__cfgFileName, 'r')
            self.readfp(self.__cfgFile)
        except (MissingSectionHeaderError, ParsingError), e:
            self.__cfgFile.close()
            raise VMMConfigException(str(e), ERR.CONF_ERROR)
        self.__cfgFile.close()

    def check(self):
        """Performs a configuration check.

        Raises a VMMConfigException if the check fails.
        """
        if not self.__chkSections():
            errmsg = StringIO()
            errmsg.write(_("Using configuration file: %s\n") %\
                    self.__cfgFileName)
            for k,v in self.__missing.items():
                if v[0] is True:
                    errmsg.write(_(u"missing section: %s\n") % k)
                else:
                    errmsg.write(_(u"missing options in section %s:\n") % k)
                    for o in v:
                        errmsg.write(" * %s\n" % o)
            raise VMMConfigException(errmsg.getvalue(), ERR.CONF_ERROR)

    def getsections(self):
        """Return a list with all configurable sections."""
        return self.__VMMsections[:-1]

    def get(self, section, option, raw=False, vars=None):
        return unicode(ConfigParser.get(self, section, option, raw, vars),
                ENCODING, 'replace')

    def configure(self, sections):
        """Interactive method for configuring all options in the given sections

        Arguments:
        sections -- list of strings with section names
        """
        if not isinstance(sections, list):
            raise TypeError("Argument 'sections' is not a list.")
        # if [config] done = false (default at 1st run),
        # then set changes true
        try:
            if not self.getboolean('config', 'done'):
                self.__changes = True
        except ValueError:
            self.set('config', 'done', 'False')
            self.__changes = True
        w_std(_(u'Using configuration file: %s\n') % self.__cfgFileName)
        for s in sections:
            if s != 'config':
                w_std(_(u'* Config section: “%s”') % s )
            for opt, val in self.items(s):
                newval = raw_input(
                _('Enter new value for option %(opt)s [%(val)s]: ').encode(
                    ENCODING, 'replace') % {'opt': opt, 'val': val})
                if newval and newval != val:
                    self.set(s, opt, newval)
                    self.__changes = True
            print
        if self.__changes:
            self.__saveChanges()

    def __saveChanges(self):
        """Writes changes to the configuration file."""
        self.set('config', 'done', 'true')
        copy2(self.__cfgFileName, self.__cfgFileName+'.bak')
        self.__cfgFile = file(self.__cfgFileName, 'w')
        self.write(self.__cfgFile)
        self.__cfgFile.close()

    def __chkSections(self):
        """Checks if all configuration sections are existing."""
        errors = False
        for s in self.__VMMsections:
            if not self.has_section(s):
                self.__missing[s] = [True]
            elif not self.__chkOptions(s):
                errors = True
        return not errors

    def __chkOptions(self, section):
        """Checks if all configuration options in section are existing.

        Arguments:
        section -- the section to be checked
        """
        retval = True
        missing = []
        if section == 'database':
            opts = self.__dbopts
        elif section == 'maildir':
            opts = self.__mdopts
        elif section == 'account':
            opts = self.__accountopts
        elif section == 'domain':
            opts = self.__domdopts
        elif section == 'bin':
            opts = self.__binopts
        elif section == 'misc':
            opts = self.__miscopts
        elif section == 'config':
            opts = [['done', 'false']]
        for o, v in opts:
            if not self.has_option(section, o):
                missing.append(o)
                retval = False
        if len(missing):
            self.__missing[section] = missing
        return retval