diff options
Diffstat (limited to 'src/mailman/config/config.py')
| -rw-r--r-- | src/mailman/config/config.py | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/src/mailman/config/config.py b/src/mailman/config/config.py new file mode 100644 index 000000000..fa359a6f5 --- /dev/null +++ b/src/mailman/config/config.py @@ -0,0 +1,206 @@ +# Copyright (C) 2006-2009 by the Free Software Foundation, Inc. +# +# This file is part of GNU Mailman. +# +# GNU Mailman is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. + +"""Configuration file loading and management.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'Configuration', + ] + + +import os +import sys +import errno +import logging + +from StringIO import StringIO +from lazr.config import ConfigSchema, as_boolean +from pkg_resources import resource_string + +from mailman import version +from mailman.core import errors +from mailman.domain import Domain +from mailman.languages import LanguageManager +from mailman.styles.manager import StyleManager +from mailman.utilities.filesystem import makedirs + + +SPACE = ' ' + + + +class Configuration(object): + """The core global configuration object.""" + + def __init__(self): + self.domains = {} # email host -> IDomain + self.switchboards = {} + self.languages = LanguageManager() + self.style_manager = StyleManager() + self.QFILE_SCHEMA_VERSION = version.QFILE_SCHEMA_VERSION + self._config = None + self.filename = None + # Create various registries. + self.chains = {} + self.rules = {} + self.handlers = {} + self.pipelines = {} + self.commands = {} + + def _clear(self): + """Clear the cached configuration variables.""" + self.domains.clear() + self.switchboards.clear() + self.languages = LanguageManager() + + def __getattr__(self, name): + """Delegate to the configuration object.""" + return getattr(self._config, name) + + def load(self, filename=None): + """Load the configuration from the schema and config files.""" + schema_string = resource_string('mailman.config', 'schema.cfg') + schema = ConfigSchema('schema.cfg', StringIO(schema_string)) + # If a configuration file was given, load it now too. First, load the + # absolute minimum default configuration, then if a configuration + # filename was given by the user, push it. + config_string = resource_string('mailman.config', 'mailman.cfg') + self._config = schema.loadFile(StringIO(config_string), 'mailman.cfg') + if filename is not None: + self.filename = filename + with open(filename) as user_config: + self._config.push(filename, user_config.read()) + self._post_process() + + def push(self, config_name, config_string): + """Push a new configuration onto the stack.""" + self._clear() + self._config.push(config_name, config_string) + self._post_process() + + def pop(self, config_name): + """Pop a configuration from the stack.""" + self._clear() + self._config.pop(config_name) + self._post_process() + + def _post_process(self): + """Perform post-processing after loading the configuration files.""" + # Set up the domains. + domains = self._config.getByCategory('domain', []) + for section in domains: + domain = Domain(section.email_host, section.base_url, + section.description, section.contact_address) + if domain.email_host in self.domains: + raise errors.BadDomainSpecificationError( + 'Duplicate email host: %s' % domain.email_host) + # Make sure there's only one mapping for the url_host + if domain.url_host in self.domains.values(): + raise errors.BadDomainSpecificationError( + 'Duplicate url host: %s' % domain.url_host) + # We'll do the reverse mappings on-demand. There shouldn't be too + # many virtual hosts that it will really matter that much. + self.domains[domain.email_host] = domain + # Set up directories. + self.BIN_DIR = os.path.abspath(os.path.dirname(sys.argv[0])) + self.VAR_DIR = var_dir = self._config.mailman.var_dir + # Now that we've loaded all the configuration files we're going to + # load, set up some useful directories. + join = os.path.join + self.LIST_DATA_DIR = join(var_dir, 'lists') + self.LOG_DIR = join(var_dir, 'logs') + self.LOCK_DIR = lockdir = join(var_dir, 'locks') + self.DATA_DIR = datadir = join(var_dir, 'data') + self.ETC_DIR = etcdir = join(var_dir, 'etc') + self.SPAM_DIR = join(var_dir, 'spam') + self.EXT_DIR = join(var_dir, 'ext') + self.QUEUE_DIR = join(var_dir, 'qfiles') + self.MESSAGES_DIR = join(var_dir, 'messages') + self.PUBLIC_ARCHIVE_FILE_DIR = join(var_dir, 'archives', 'public') + self.PRIVATE_ARCHIVE_FILE_DIR = join(var_dir, 'archives', 'private') + # Other useful files + self.PIDFILE = join(datadir, 'master-qrunner.pid') + self.SITE_PW_FILE = join(datadir, 'adm.pw') + self.LISTCREATOR_PW_FILE = join(datadir, 'creator.pw') + self.CONFIG_FILE = join(etcdir, 'mailman.cfg') + self.LOCK_FILE = join(lockdir, 'master-qrunner') + # Set up the switchboards. + from mailman.queue import Switchboard + Switchboard.initialize() + # Set up all the languages. + languages = self._config.getByCategory('language', []) + for language in languages: + code = language.name.split('.')[1] + self.languages.add_language(code, language.description, + language.charset, language.enabled) + # Always enable the server default language, which must be defined. + self.languages.enable_language(self._config.mailman.default_language) + self.ensure_directories_exist() + self.style_manager.populate() + + @property + def logger_configs(self): + """Return all log config sections.""" + return self._config.getByCategory('logging', []) + + @property + def paths(self): + """Return a substitution dictionary of all path variables.""" + return dict((k, self.__dict__[k]) + for k in self.__dict__ + if k.endswith('_DIR')) + + def ensure_directories_exist(self): + """Create all path directories if they do not exist.""" + for variable, directory in self.paths.items(): + makedirs(directory) + + @property + def qrunner_configs(self): + """Iterate over all the qrunner configuration sections.""" + for section in self._config.getByCategory('qrunner', []): + yield section + + @property + def archivers(self): + """Iterate over all the enabled archivers.""" + for section in self._config.getByCategory('archiver', []): + if not as_boolean(section.enable): + continue + class_path = section['class'] + module_name, class_name = class_path.rsplit('.', 1) + __import__(module_name) + yield getattr(sys.modules[module_name], class_name)() + + @property + def style_configs(self): + """Iterate over all the style configuration sections.""" + for section in self._config.getByCategory('style', []): + yield section + + @property + def header_matches(self): + """Iterate over all spam matching headers. + + Values are 3-tuples of (header, pattern, chain) + """ + matches = self._config.getByCategory('spam.headers', []) + for match in matches: + yield (matches.header, matches.pattern, matches.chain) |
