VirtualMailManager/ext/postconf.py
author martin f. krafft <madduck@madduck.net>
Fri, 13 Apr 2012 23:24:12 +0200
branchv0.6.x
changeset 527 e09139525580
parent 422 33a45e4c80c4
child 568 14abdd04ddf5
permissions -rw-r--r--
Modify userinfo output to indicate when domain defaults are displayed When Account instances reference NULL tid/qid/ssid, the data must come from the associated domain, and this should be indicated. For transport and services, this is easy to do as the string passed in the info dict can simply be modified. For quotalimit, however, another method must be used due to the CLI-side formatting. All approaches use a common formatter outsourced to the common.py file.

# -*- coding: UTF-8 -*-
# Copyright (c) 2008 - 2011, Pascal Volk
# See COPYING for distribution information.
"""
    VirtualMailManager.ext.postconf
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Wrapper class for Postfix's postconf.
    Postconf instances can be used to read actual values of configuration
    parameters or edit the value of a configuration parameter.

    postconf.read(parameter) -> value
    postconf.edit(parameter, value)
"""

import re
from subprocess import Popen, PIPE

from VirtualMailManager.errors import VMMError
from VirtualMailManager.constants import VMM_ERROR

_ = lambda msg: msg


class Postconf(object):
    """Wrapper class for Postfix's postconf."""
    __slots__ = ('_bin', '_val')
    _parameter_re = re.compile(r'^\w+$')
    _variables_re = re.compile(r'\$\b\w+\b')

    def __init__(self, postconf_bin):
        """Creates a new Postconf instance.

        Argument:

        `postconf_bin` : str
          absolute path to the Postfix postconf binary.
        """
        self._bin = postconf_bin
        self._val = ''

    def edit(self, parameter, value):
        """Set the `parameter`'s value to `value`.

        Arguments:

        `parameter` : str
          the name of a Postfix configuration parameter
        `value` : str
          the parameter's new value.
        """
        self._check_parameter(parameter)
        stderr = Popen((self._bin, '-e', parameter + '=' + str(value)),
                       stderr=PIPE).communicate()[1]
        if stderr:
            raise VMMError(stderr.strip(), VMM_ERROR)

    def read(self, parameter, expand_vars=True):
        """Returns the parameters value.

        If expand_vars is True (default), all variables in the value will be
        expanded:
        e.g. mydestination: mail.example.com, localhost.example.com, localhost
        Otherwise the value may contain one or more variables.
        e.g. mydestination: $myhostname, localhost.$mydomain, localhost

        Arguments:

        `parameter` : str
          the name of a Postfix configuration parameter.
        `expand_vars` : bool
          indicates if variables should be expanded or not, default True
        """
        self._check_parameter(parameter)
        self._val = self._read(parameter)
        if expand_vars:
            self._expand_vars()
        return self._val

    def _check_parameter(self, parameter):
        """Check that the `parameter` looks like a configuration parameter.
        If not, a VMMError will be raised."""
        if not self.__class__._parameter_re.match(parameter):
            raise VMMError(_(u"The value '%s' does not look like a valid "
                             u"postfix configuration parameter name.") %
                           parameter, VMM_ERROR)

    def _expand_vars(self):
        """Expand the $variables in self._val to their values."""
        while True:
            pvars = set(self.__class__._variables_re.findall(self._val))
            if not pvars:
                break
            if len(pvars) > 1:
                self._expand_multi_vars(self._read_multi(pvars))
                continue
            pvars = pvars.pop()
            self._val = self._val.replace(pvars, self._read(pvars[1:]))

    def _expand_multi_vars(self, old_new):
        """Replace all $vars in self._val with their values."""
        for old, new in old_new.iteritems():
            self._val = self._val.replace('$' + old, new)

    def _read(self, parameter):
        """Ask postconf for the value of a single configuration parameter."""
        stdout, stderr = Popen([self._bin, '-h', parameter], stdout=PIPE,
                               stderr=PIPE).communicate()
        if stderr:
            raise VMMError(stderr.strip(), VMM_ERROR)
        return stdout.strip()

    def _read_multi(self, parameters):
        """Ask postconf for multiple configuration parameters. Returns a dict
        parameter: value items."""
        cmd = [self._bin]
        cmd.extend(parameter[1:] for parameter in parameters)
        stdout, stderr = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate()
        if stderr:
            raise VMMError(stderr.strip(), VMM_ERROR)
        par_val = {}
        for line in stdout.splitlines():
            par, val = line.split(' = ')
            par_val[par] = val
        return par_val

del _