diff options
| author | Barry Warsaw | 2008-12-20 15:41:16 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2008-12-20 15:41:16 -0500 |
| commit | dfccf2b7d2b5749c86267645aaf870f128dab2d8 (patch) | |
| tree | 205cc5253b2b5462e9e5409b09b0a7cea7f30a7b | |
| parent | 33a924c56669b12e268bce5df68f598b690cdcf0 (diff) | |
| download | mailman-dfccf2b7d2b5749c86267645aaf870f128dab2d8.tar.gz mailman-dfccf2b7d2b5749c86267645aaf870f128dab2d8.tar.zst mailman-dfccf2b7d2b5749c86267645aaf870f128dab2d8.zip | |
79 files changed, 767 insertions, 588 deletions
diff --git a/buildout.cfg b/buildout.cfg index eec2173e5..8c03f89d3 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -24,5 +24,6 @@ eggs = mailman [test] recipe = zc.recipe.testrunner -eggs = mailman +eggs = + mailman defaults = '--tests-pattern ^tests --exit-with-status'.split() diff --git a/mailman/Archiver/Archiver.py b/mailman/Archiver/Archiver.py index 313bd9a0a..261dfe206 100644 --- a/mailman/Archiver/Archiver.py +++ b/mailman/Archiver/Archiver.py @@ -34,7 +34,7 @@ from string import Template from mailman import Mailbox from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ log = logging.getLogger('mailman.error') diff --git a/mailman/Archiver/HyperArch.py b/mailman/Archiver/HyperArch.py index 11fd32cf1..0395f6482 100644 --- a/mailman/Archiver/HyperArch.py +++ b/mailman/Archiver/HyperArch.py @@ -29,6 +29,7 @@ import os import re import sys +import gzip import time import errno import urllib @@ -47,20 +48,13 @@ from mailman import i18n from mailman.Archiver import HyperDatabase from mailman.Archiver import pipermail from mailman.Mailbox import ArchiverMailbox -from mailman.configuration import config +from mailman.config import config log = logging.getLogger('mailman.error') # Set up i18n. Assume the current language has already been set in the caller. _ = i18n._ -gzip = None -if config.GZIP_ARCHIVE_TXT_FILES: - try: - import gzip - except ImportError: - pass - EMPTYSTRING = '' NL = '\n' @@ -239,7 +233,7 @@ class Article(pipermail.Article): _last_article_time = time.time() def __init__(self, message=None, sequence=0, keepHeaders=[], - lang=config.DEFAULT_SERVER_LANGUAGE, mlist=None): + lang=config.mailman.default_language, mlist=None): self.__super_init(message, sequence, keepHeaders) self.prev = None self.next = None diff --git a/mailman/Mailbox.py b/mailman/Mailbox.py index afbfb8166..cbc38585a 100644 --- a/mailman/Mailbox.py +++ b/mailman/Mailbox.py @@ -27,7 +27,7 @@ from email.Generator import Generator from email.Parser import Parser from mailman.Message import Message -from mailman.configuration import config +from mailman.config import config diff --git a/mailman/Message.py b/mailman/Message.py index f4f939568..e78678dc4 100644 --- a/mailman/Message.py +++ b/mailman/Message.py @@ -30,7 +30,7 @@ from email.charset import Charset from email.header import Header from mailman import Utils -from mailman.configuration import config +from mailman.config import config COMMASPACE = ', ' diff --git a/mailman/Utils.py b/mailman/Utils.py index 607c457d9..d7c193799 100644 --- a/mailman/Utils.py +++ b/mailman/Utils.py @@ -41,7 +41,7 @@ from string import ascii_letters, digits, whitespace, Template import mailman.templates from mailman import passwords -from mailman.configuration import config +from mailman.config import config from mailman.core import errors AT = '@' diff --git a/mailman/app/commands.py b/mailman/app/commands.py index 6ff6fdf0a..1782e46fe 100644 --- a/mailman/app/commands.py +++ b/mailman/app/commands.py @@ -23,7 +23,7 @@ __all__ = [ ] -from mailman.configuration import config +from mailman.config import config from mailman.core.plugins import get_plugins from mailman.interfaces import IEmailCommand diff --git a/mailman/app/lifecycle.py b/mailman/app/lifecycle.py index b707fdbbd..6cd3219d0 100644 --- a/mailman/app/lifecycle.py +++ b/mailman/app/lifecycle.py @@ -31,7 +31,7 @@ import logging from mailman import Utils from mailman.Utils import ValidateEmail -from mailman.configuration import config +from mailman.config import config from mailman.core import errors from mailman.core.plugins import get_plugin from mailman.core.styles import style_manager diff --git a/mailman/app/membership.py b/mailman/app/membership.py index ae14b3f25..150c2c999 100644 --- a/mailman/app/membership.py +++ b/mailman/app/membership.py @@ -30,7 +30,7 @@ from mailman import Message from mailman import Utils from mailman import i18n from mailman.app.notifications import send_goodbye_message -from mailman.configuration import config +from mailman.config import config from mailman.core import errors from mailman.interfaces import AlreadySubscribedError, DeliveryMode, MemberRole diff --git a/mailman/app/moderator.py b/mailman/app/moderator.py index 9cce5aa8a..a678f3734 100644 --- a/mailman/app/moderator.py +++ b/mailman/app/moderator.py @@ -37,7 +37,7 @@ from mailman import i18n from mailman.app.membership import add_member, delete_member from mailman.app.notifications import ( send_admin_subscription_notice, send_welcome_message) -from mailman.configuration import config +from mailman.config import config from mailman.core import errors from mailman.interfaces import Action, DeliveryMode, RequestType from mailman.interfaces.member import AlreadySubscribedError diff --git a/mailman/app/notifications.py b/mailman/app/notifications.py index 41180de7d..d3546ee83 100644 --- a/mailman/app/notifications.py +++ b/mailman/app/notifications.py @@ -30,7 +30,7 @@ from email.utils import formataddr from mailman import Message from mailman import Utils from mailman import i18n -from mailman.configuration import config +from mailman.config import config from mailman.interfaces.member import DeliveryMode _ = i18n._ diff --git a/mailman/app/registrar.py b/mailman/app/registrar.py index 320086ec7..491eb43af 100644 --- a/mailman/app/registrar.py +++ b/mailman/app/registrar.py @@ -31,7 +31,7 @@ from zope.interface import implements from mailman.Message import UserNotification from mailman.Utils import ValidateEmail -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IDomain, IPendable, IRegistrar from mailman.interfaces.member import MemberRole diff --git a/mailman/app/replybot.py b/mailman/app/replybot.py index 483f3f3cf..394a93aaa 100644 --- a/mailman/app/replybot.py +++ b/mailman/app/replybot.py @@ -31,7 +31,7 @@ import datetime from mailman import Utils from mailman import i18n -from mailman.configuration import config +from mailman.config import config log = logging.getLogger('mailman.vette') diff --git a/mailman/archiving/__init__.py b/mailman/archiving/__init__.py index 41dff720c..290eb3631 100644 --- a/mailman/archiving/__init__.py +++ b/mailman/archiving/__init__.py @@ -21,7 +21,7 @@ __all__ = [ ] -from mailman.configuration import config +from mailman.config import config from mailman.core.plugins import get_plugins diff --git a/mailman/archiving/mailarchive.py b/mailman/archiving/mailarchive.py index 30e721fc6..3edeb8c39 100644 --- a/mailman/archiving/mailarchive.py +++ b/mailman/archiving/mailarchive.py @@ -30,7 +30,7 @@ from urllib import quote from urlparse import urljoin from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.interfaces.archiver import IArchiver from mailman.queue import Switchboard diff --git a/mailman/archiving/mhonarc.py b/mailman/archiving/mhonarc.py index 7f31a6fb1..3d17ffd13 100644 --- a/mailman/archiving/mhonarc.py +++ b/mailman/archiving/mhonarc.py @@ -32,7 +32,7 @@ from string import Template from urlparse import urljoin from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.interfaces.archiver import IArchiver diff --git a/mailman/archiving/pipermail.py b/mailman/archiving/pipermail.py index df16e6e98..f42f064ed 100644 --- a/mailman/archiving/pipermail.py +++ b/mailman/archiving/pipermail.py @@ -30,7 +30,7 @@ from string import Template from zope.interface import implements from zope.interface.interface import adapter_hooks -from mailman.configuration import config +from mailman.config import config from mailman.interfaces.archiver import IArchiver, IPipermailMailingList from mailman.interfaces.mailinglist import IMailingList diff --git a/mailman/archiving/prototype.py b/mailman/archiving/prototype.py index 5e86a606f..7691c1c13 100644 --- a/mailman/archiving/prototype.py +++ b/mailman/archiving/prototype.py @@ -29,7 +29,7 @@ from base64 import b32encode from urlparse import urljoin from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.interfaces.archiver import IArchiver diff --git a/mailman/bin/master.py b/mailman/bin/master.py index 1c45e04f8..39f119725 100644 --- a/mailman/bin/master.py +++ b/mailman/bin/master.py @@ -36,8 +36,8 @@ from locknix import lockfile from munepy import Enum from mailman import Defaults -from mailman import loginit -from mailman.configuration import config +from mailman.config import config +from mailman.core.logging import reopen from mailman.i18n import _ from mailman.options import Options @@ -237,7 +237,7 @@ class Loop: signal.alarm(int(Defaults.days(1))) # SIGHUP tells the qrunners to close and reopen their log files. def sighup_handler(signum, frame): - loginit.reopen() + reopen() for pid in self._kids: os.kill(pid, signal.SIGHUP) log.info('Master watcher caught SIGHUP. Re-opening log files.') diff --git a/mailman/bin/withlist.py b/mailman/bin/withlist.py index 32bfb1206..51c3ba9d5 100644 --- a/mailman/bin/withlist.py +++ b/mailman/bin/withlist.py @@ -20,9 +20,9 @@ import sys import optparse from mailman import interact -from mailman.configuration import config +from mailman.config import config +from mailman.core.initialize import initialize from mailman.i18n import _ -from mailman.initialize import initialize from mailman.version import MAILMAN_VERSION diff --git a/mailman/chains/accept.py b/mailman/chains/accept.py index a4f02b217..0ced02d44 100644 --- a/mailman/chains/accept.py +++ b/mailman/chains/accept.py @@ -23,7 +23,7 @@ __metaclass__ = type import logging from mailman.chains.base import TerminalChainBase -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.queue import Switchboard diff --git a/mailman/chains/base.py b/mailman/chains/base.py index 39968a1fe..17c129f19 100644 --- a/mailman/chains/base.py +++ b/mailman/chains/base.py @@ -27,7 +27,7 @@ __all__ = [ from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.interfaces import ( IChain, IChainIterator, IChainLink, IMutableChain, LinkAction) diff --git a/mailman/chains/builtin.py b/mailman/chains/builtin.py index 238cf4099..0528bf769 100644 --- a/mailman/chains/builtin.py +++ b/mailman/chains/builtin.py @@ -26,7 +26,7 @@ import logging from zope.interface import implements from mailman.chains.base import Link -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IChain, LinkAction diff --git a/mailman/chains/headers.py b/mailman/chains/headers.py index d53f67e06..062c42438 100644 --- a/mailman/chains/headers.py +++ b/mailman/chains/headers.py @@ -30,7 +30,7 @@ from zope.interface import implements from mailman.interfaces import IChainIterator, IRule, LinkAction from mailman.chains.base import Chain, Link from mailman.i18n import _ -from mailman.configuration import config +from mailman.config import config log = logging.getLogger('mailman.vette') @@ -104,7 +104,7 @@ class HeaderMatchChain(Chain): self._links = [] # Initialize header check rules with those from the global # HEADER_MATCHES variable. - for entry in config.HEADER_MATCHES: + for entry in config.header_matches: self._links.append(make_link(entry)) # Keep track of how many global header matching rules we've seen. # This is so the flush() method will only delete those that were added diff --git a/mailman/chains/hold.py b/mailman/chains/hold.py index 85e8c3fec..1b44cee1b 100644 --- a/mailman/chains/hold.py +++ b/mailman/chains/hold.py @@ -36,7 +36,7 @@ from mailman.Utils import maketext, oneline, wrap, GetCharSet from mailman.app.moderator import hold_message from mailman.app.replybot import autorespond_to_sender, can_acknowledge from mailman.chains.base import TerminalChainBase -from mailman.configuration import config +from mailman.config import config from mailman.interfaces import IPendable diff --git a/mailman/commands/cmd_confirm.py b/mailman/commands/cmd_confirm.py index 20fa0169e..700b69022 100644 --- a/mailman/commands/cmd_confirm.py +++ b/mailman/commands/cmd_confirm.py @@ -23,7 +23,7 @@ from mailman import Errors from mailman import Pending -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ STOP = 1 diff --git a/mailman/commands/cmd_help.py b/mailman/commands/cmd_help.py index 4577c947e..9686bb307 100644 --- a/mailman/commands/cmd_help.py +++ b/mailman/commands/cmd_help.py @@ -24,7 +24,7 @@ import os import sys from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ EMPTYSTRING = '' diff --git a/mailman/commands/cmd_lists.py b/mailman/commands/cmd_lists.py index b7b5a3b74..df0fdcece 100644 --- a/mailman/commands/cmd_lists.py +++ b/mailman/commands/cmd_lists.py @@ -21,7 +21,7 @@ """ from mailman.MailList import MailList -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ diff --git a/mailman/commands/cmd_password.py b/mailman/commands/cmd_password.py index 0c09aa99d..75985a8a5 100644 --- a/mailman/commands/cmd_password.py +++ b/mailman/commands/cmd_password.py @@ -29,7 +29,7 @@ from email.Utils import parseaddr -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ STOP = 1 diff --git a/mailman/commands/cmd_set.py b/mailman/commands/cmd_set.py index 94ba9192f..5c89abf5c 100644 --- a/mailman/commands/cmd_set.py +++ b/mailman/commands/cmd_set.py @@ -20,7 +20,7 @@ from email.Utils import parseaddr, formatdate from mailman import Errors from mailman import MemberAdaptor from mailman import i18n -from mailman.configuration import config +from mailman.config import config def _(s): return s diff --git a/mailman/commands/cmd_who.py b/mailman/commands/cmd_who.py index 725040f4d..157b84773 100644 --- a/mailman/commands/cmd_who.py +++ b/mailman/commands/cmd_who.py @@ -18,7 +18,7 @@ from email.Utils import parseaddr from mailman import i18n -from mailman.configuration import config +from mailman.config import config STOP = 1 diff --git a/mailman/commands/join.py b/mailman/commands/join.py index 17fb071a6..a803a1e4c 100644 --- a/mailman/commands/join.py +++ b/mailman/commands/join.py @@ -29,7 +29,7 @@ from email.utils import formataddr, parseaddr from zope.interface import implements from mailman.Utils import MakeRandomPassword -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import ( ContinueProcessing, DeliveryMode, IEmailCommand) diff --git a/mailman/config/__init__.py b/mailman/config/__init__.py new file mode 100644 index 000000000..375cafa51 --- /dev/null +++ b/mailman/config/__init__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2008 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/>. + +from mailman.config.config import Configuration + +config = Configuration() diff --git a/mailman/config/config.py b/mailman/config/config.py new file mode 100644 index 000000000..031324eff --- /dev/null +++ b/mailman/config/config.py @@ -0,0 +1,202 @@ +# Copyright (C) 2006-2008 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.""" + +__metaclass__ = type +__all__ = [ + 'Configuration', + ] + + +import os +import sys +import errno +import logging + +from StringIO import StringIO +from lazr.config import ConfigSchema +from lazr.config.interfaces import NoCategoryError +from pkg_resources import resource_filename + +from mailman import Defaults +from mailman import version +from mailman.config.helpers import as_boolean +from mailman.core import errors +from mailman.domain import Domain +from mailman.languages import LanguageManager + + +SPACE = ' ' + + + +class Configuration(object): + """The core global configuration object.""" + + def __init__(self): + self.domains = {} # email host -> IDomain + self.qrunners = {} + self.qrunner_shortcuts = {} + self.QFILE_SCHEMA_VERSION = version.QFILE_SCHEMA_VERSION + self._config = None + + 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_file = resource_filename('mailman.config', 'schema.cfg') + schema = ConfigSchema(schema_file) + # If a configuration file was given, load it now too. + if filename is None: + self._config = schema.loadFile(StringIO(''), '<default>') + else: + self._config = schema.load(filename) + self.post_process() + + def post_process(self): + """Perform post-processing after loading the configuration files.""" + # Set up the domains. + try: + domains = self._config.getByCategory('domain') + except NoCategoryError: + domains = [] + 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 the queue runners. + try: + qrunners = self._config.getByCategory('qrunner') + except NoCategoryError: + qrunners = [] + for section in qrunners: + if not as_boolean(section.start): + continue + name = section['class'] + self.qrunners[name] = section.count + # Calculate the queue runner shortcut name. + classname = name.rsplit('.', 1)[1] + if classname.endswith('Runner'): + shortname = classname[:-6].lower() + else: + shortname = classname + self.qrunner_shortcuts[shortname] = section['class'] + # Set up directories. + self.BIN_DIR = os.path.abspath(os.path.dirname(sys.argv[0])) + 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.PUBLIC_ARCHIVE_FILE_DIR = join(VAR_DIR, 'archives', 'public') + self.PRIVATE_ARCHIVE_FILE_DIR = join(VAR_DIR, 'archives', 'private') + # Directories used by the qrunner subsystem + self.QUEUE_DIR = qdir = join(VAR_DIR, 'qfiles') + self.ARCHQUEUE_DIR = join(qdir, 'archive') + self.BADQUEUE_DIR = join(qdir, 'bad') + self.BOUNCEQUEUE_DIR = join(qdir, 'bounces') + self.CMDQUEUE_DIR = join(qdir, 'commands') + self.INQUEUE_DIR = join(qdir, 'in') + self.MAILDIR_DIR = join(qdir, 'maildir') + self.NEWSQUEUE_DIR = join(qdir, 'news') + self.OUTQUEUE_DIR = join(qdir, 'out') + self.PIPELINEQUEUE_DIR = join(qdir, 'pipeline') + self.RETRYQUEUE_DIR = join(qdir, 'retry') + self.SHUNTQUEUE_DIR = join(qdir, 'shunt') + self.VIRGINQUEUE_DIR = join(qdir, 'virgin') + self.MESSAGES_DIR = join(VAR_DIR, 'messages') + # 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 all the languages. + self.languages = LanguageManager() + try: + languages = self._config.getByCategory('language') + except NoCategoryError: + languages = [] + for section in languages: + code = section.name.split('.')[1] + self.languages.add_language(code, section.description, + section.charset, section.enable) + # Always enable the server default language, which must be defined. + self.languages.enable_language(self._config.mailman.default_language) + # Create various registries. + self.archivers = {} + self.chains = {} + self.rules = {} + self.handlers = {} + self.pipelines = {} + self.commands = {} + + @property + def logger_configs(self): + """Return all log config sections.""" + try: + loggers = self._config.getByCategory('logging') + except NoCategoryError: + loggers = [] + return loggers + + @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(): + try: + os.makedirs(directory, 02775) + except OSError, e: + if e.errno <> errno.EEXIST: + raise + + @property + def header_matches(self): + """Iterate over all spam matching headers. + + Values are 3-tuples of (header, pattern, chain) + """ + try: + matches = self._config.getByCategory('spam.headers') + except NoCategoryError: + matches = [] + for match in matches: + yield (matches.header, matches.pattern, matches.chain) diff --git a/mailman/config/helpers.py b/mailman/config/helpers.py new file mode 100644 index 000000000..306ef21b4 --- /dev/null +++ b/mailman/config/helpers.py @@ -0,0 +1,61 @@ +# Copyright (C) 2008 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 helpers.""" + +__metaclass__ = type +__all__ = [ + 'as_boolean', + 'as_log_level', + ] + + +import logging + + + +def as_boolean(value): + """Turn a string into a boolean. + + :param value: A string with one of the following values + (case-insensitive): true, yes, 1, on, enable, enabled (for True), or + false, no, 0, off, disable, disabled (for False). Everything else is + an error. + :type value: string + :return: True or False. + :rtype: boolean + """ + value = value.lower() + if value in ('true', 'yes', '1', 'on', 'enabled', 'enable'): + return True + if value in ('false', 'no', '0', 'off', 'disabled', 'disable'): + return False + raise ValueError('Invalid boolean value: %s' % value) + + + +def as_log_level(value): + """Turn a string into a log level. + + :param value: A string with a value (case-insensitive) equal to one of the + symbolic logging levels. + :type value: string + :return: A logging level constant. + :rtype: int + """ + value = value.upper() + return getattr(logging, value) diff --git a/mailman/config/schema.cfg b/mailman/config/schema.cfg new file mode 100644 index 000000000..181116e74 --- /dev/null +++ b/mailman/config/schema.cfg @@ -0,0 +1,235 @@ +# Copyright (C) 2008 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/>. + +# This is the GNU Mailman configuration schema. It defines the default +# configuration options for the core system and plugins. It uses ini-style +# formats under the lazr.config regime to define all system configuration +# options. See <https://launchpad.net/lazr.config> for details. You can +# override the defaults by creating a mailman.cfg file in your etc directory. +# See mailman.cfg.sample as an example. + +[mailman] +# This address is the "site owner" address. Certain messages which must be +# delivered to a human, but which can't be delivered to a list owner (e.g. a +# bounce from a list owner), will be sent to this address. It should point to +# a human. +site-owner: changeme@example.com + +# This address is used as the from address whenever a message comes from some +# entity to which there is no natural reply recipient. Set this to a real +# human or to /dev/null. It will be appended with the host name of the list +# involved. This address must not bounce and it must not point to a Mailman +# process. +noreply-address: noreply + +# Where all the runtime data will be kept. This directory must exist. +var_dir: /tmp/mailman + +# The default language for this server. +default_language: en + + +[qrunner.template] +# Define which process queue runners, and how many of them, to start. +class: mailman.queue.runner.Runner +count: 1 +start: yes + +[qrunner.archive] +class: mailman.queue.archive.ArchiveRunner + +[qrunner.bounce] +class: mailman.queue.bounce.BounceRunner + +[qrunner.command] +class: mailman.queue.command.CommandRunner + +[qrunner.incoming] +class: mailman.queue.incoming.IncomingRunner + +[qrunner.news] +class: mailman.queue.news.NewsRunner + +[qrunner.outgoing] +class: mailman.queue.outgoing.OutgoingRunner + +[qrunner.pipeline] +class: mailman.queue.pipeline.PipelineRunner + +[qrunner.retry] +class: mailman.queue.retry.RetryRunner + +[qrunner.virgin] +class: mailman.queue.virgin.VirginRunner + +[qrunner.lmtp] +class: mailman.queue.lmtp.LMTPRunner + + +[database] +# Use this to set the Storm database engine URL. You generally have one +# primary database connection for all of Mailman. List data and most rosters +# will store their data in this database, although external rosters may access +# other databases in their own way. This string supports standard +# 'configuration' substitutions. +url: sqlite:///$DATA_DIR/mailman.db +debug: no + +[logging.template] +# This defines various log settings. The options available are: +# +# - level -- Overrides the default level; this may be any of the +# standard Python logging levels, case insensitive. +# - format -- Overrides the default format string +# - datefmt -- Overrides the default date format string +# - path -- Overrides the default logger path. This may be a relative +# path name, in which case it is relative to Mailman's LOG_DIR, +# or it may be an absolute path name. You cannot change the +# handler class that will be used. +# - propagate -- Boolean specifying whether to propagate log message from this +# logger to the root "mailman" logger. You cannot override +# settings for the root logger. +# +# In this section, you can define defaults for all loggers, which will be +# prefixed by 'mailman.'. Use subsections to override settings for specific +# loggers. The names of the available loggers are: +# +# - archiver -- All archiver output +# - bounce -- All bounce processing logs go here +# - config -- Configuration issues +# - debug -- Only used for development +# - error -- All exceptions go to this log +# - fromusenet -- Information related to the Usenet to Mailman gateway +# - http -- Internal wsgi-based web interface +# - locks -- Lock state changes +# - mischief -- Various types of hostile activity +# - post -- Information about messages posted to mailing lists +# - qrunner -- qrunner start/stops +# - smtp -- Successful SMTP activity +# - smtp-failure -- Unsuccessful SMTP activity +# - subscribe -- Information about leaves/joins +# - vette -- Information related to admindb activity +format: %(asctime)s (%(process)d) %(message)s +datefmt: %b %d %H:%M:%S %Y +propagate: no +level: info +path: mailman + +[logging.root] + +[logging.archiver] + +[logging.bounce] +path: bounce + +[logging.config] + +[logging.debug] +path: debug +level: debug + +[logging.error] + +[logging.fromusenet] + +[logging.http] + +[logging.locks] + +[logging.mischief] + +[logging.qrunner] + +[logging.smtp] +path: smtp + +# The smtp logger defines additional options for handling the logging of each +# attempted delivery. These format strings specify what information is logged +# for every message, every successful delivery, every refused delivery and +# every recipient failure. To disable a status message, set the value to 'no' +# (without the quotes). +# +# These template strings accept the following set of substitution +# placeholders, if available. +# +# msgid -- the Message-ID of the message in question +# listname -- the fully-qualified list name +# sender -- the sender if available +# recip -- the recipient address if available, or the number of +# recipients being delivered to +# size -- the approximate size of the message in bytes +# seconds -- the number of seconds the operation took +# refused -- the number of refused recipients +# smtpcode -- the SMTP success or failure code +# smtpmsg -- the SMTP success or failure message + +every: [$msgid] smtp to $listname for $recip recips, completed in $time seconds +success: [$msgid] post to $listname from $sender, $size bytes +refused: [$msgid] post to $listname from $sender, $size bytes, $refused failures +failure: [$msgid] delivery to $recip failed with code $smtpcode, $smtpmsg + + +[logging.subscribe] + +[logging.vette] + + +[domain.template] +# Site-wide domain defaults. To configure an individual +# domain, add a [domain.example_com] section with the overrides. + +# This is the host name for the email interface. +email_host: example.com +# This is the base url for the domain's web interface. It must include the +# url scheme. +base_url: http://example.com +# The contact address for this domain. This is advertised as the human to +# contact when users have problems with the lists in this domain. +contact_address: postmaster@example.com +# A short description of this domain. +description: An example domain. + + +[language.template] +# Template for language definitions. The section name must be [language.xx] +# where xx is the 2-character ISO code for the language. + +# The English name for the language. +description: English (USA) +# And the default character set for the language. +charset: us-ascii +# Whether the language is enabled or not. +enable: yes + +[language.en] +description: English (USA) +charset: us-ascii + + +[spam.headers.template] +# This section defines basic header matching actions. Each spam.header +# section names a header to match (case-insensitively), a pattern to match +# against the header's value, and the chain to jump to when the match +# succeeds. +# +# The header value should not include the trailing colon. +header: X-Spam +# The pattern is always matched with re.IGNORECASE. +pattern: xyz +# The chain to jump to if the pattern matches. Maybe be any existing chain +# such as 'discard', 'reject', 'hold', or 'accept'. +chain: hold diff --git a/mailman/configuration.py b/mailman/configuration.py deleted file mode 100644 index 070f82085..000000000 --- a/mailman/configuration.py +++ /dev/null @@ -1,248 +0,0 @@ -# Copyright (C) 2006-2008 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.""" - -import os -import sys -import errno - -from mailman import Defaults -from mailman import version -from mailman.core import errors -from mailman.domain import Domain -from mailman.languages import LanguageManager - -SPACE = ' ' -_missing = object() - -DEFAULT_QRUNNERS = ( - '.archive.ArchiveRunner', - '.bounce.BounceRunner', - '.command.CommandRunner', - '.incoming.IncomingRunner', - '.news.NewsRunner', - '.outgoing.OutgoingRunner', - '.pipeline.PipelineRunner', - '.retry.RetryRunner', - '.virgin.VirginRunner', - ) - - - -class Configuration(object): - def __init__(self): - self.domains = {} # email host -> IDomain - self.qrunners = {} - self.qrunner_shortcuts = {} - self.QFILE_SCHEMA_VERSION = version.QFILE_SCHEMA_VERSION - - def load(self, filename=None): - join = os.path.join - # Set up the execfile namespace - ns = Defaults.__dict__.copy() - # Prune a few things, add a few things - del ns['__file__'] - del ns['__name__'] - del ns['__doc__'] - ns['add_domain'] = self.add_domain - ns['add_qrunner'] = self.add_qrunner - ns['del_qrunner'] = self.del_qrunner - self._languages = LanguageManager() - ns['add_language'] = self._languages.add_language - ns['enable_language'] = self._languages.enable_language - # Add all known languages, but don't enable them. - for code, (desc, charset) in Defaults._DEFAULT_LANGUAGE_DATA.items(): - self._languages.add_language(code, desc, charset, False) - # Set up the default list of qrunners so that the mailman.cfg file may - # add or delete them. - for name in DEFAULT_QRUNNERS: - self.add_qrunner(name) - # Load the configuration from the named file, or if not given, search - # around for a mailman.cfg file. Whatever you find, create a - # namespace and execfile that file in it. The values in that - # namespace are exposed as attributes on this Configuration instance. - self.filename = None - self.BIN_DIR = os.path.abspath(os.path.dirname(sys.argv[0])) - dev_dir = os.path.dirname(self.BIN_DIR) - paths = [ - # Development directories. - join(dev_dir, 'var', 'etc', 'mailman.cfg'), - join(os.getcwd(), 'var', 'etc', 'mailman.cfg'), - join(os.getcwd(), 'etc', 'mailman.cfg'), - # Standard installation directories. - join('/etc', 'mailman.cfg'), - join(Defaults.DEFAULT_VAR_DIRECTORY, 'etc', 'mailman.cfg'), - ] - if filename is not None: - paths.insert(0, filename) - for cfg_path in paths: - path = os.path.abspath(os.path.expanduser(cfg_path)) - try: - execfile(path, ns, ns) - except EnvironmentError, e: - if e.errno <> errno.ENOENT: - # It's okay if the mailman.cfg file does not exist. This - # can happen for example in the test suite. - raise - else: - self.filename = cfg_path - break - if self.filename is None: - # The logging subsystem isn't running yet, so use stderr. - print >> sys.stderr, 'No runtime configuration file file. Use -C' - sys.exit(-1) - # Based on values possibly set in mailman.cfg, add additional qrunners. - if ns['USE_MAILDIR']: - self.add_qrunner('.maildir.MaildirRunner') - if ns['USE_LMTP']: - self.add_qrunner('.lmtp.LMTPRunner') - # Pull out the defaults. - VAR_DIR = os.path.abspath(ns['VAR_DIR']) - # Now that we've loaded all the configuration files we're going to - # load, set up some useful directories. - 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.PUBLIC_ARCHIVE_FILE_DIR = join(VAR_DIR, 'archives', 'public') - self.PRIVATE_ARCHIVE_FILE_DIR = join(VAR_DIR, 'archives', 'private') - # Directories used by the qrunner subsystem - self.QUEUE_DIR = qdir = join(VAR_DIR, 'qfiles') - self.ARCHQUEUE_DIR = join(qdir, 'archive') - self.BADQUEUE_DIR = join(qdir, 'bad') - self.BOUNCEQUEUE_DIR = join(qdir, 'bounces') - self.CMDQUEUE_DIR = join(qdir, 'commands') - self.INQUEUE_DIR = join(qdir, 'in') - self.MAILDIR_DIR = join(qdir, 'maildir') - self.NEWSQUEUE_DIR = join(qdir, 'news') - self.OUTQUEUE_DIR = join(qdir, 'out') - self.PIPELINEQUEUE_DIR = join(qdir, 'pipeline') - self.RETRYQUEUE_DIR = join(qdir, 'retry') - self.SHUNTQUEUE_DIR = join(qdir, 'shunt') - self.VIRGINQUEUE_DIR = join(qdir, 'virgin') - self.MESSAGES_DIR = join(VAR_DIR, 'messages') - # 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') - # Now update our dict so attribute syntax just works - del ns['add_domain'] - del ns['add_qrunner'] - del ns['del_qrunner'] - self.__dict__.update(ns) - # Enable all specified languages, and promote the language manager to - # a public attribute. - self.languages = self._languages - del self._languages - available_languages = set(self._DEFAULT_LANGUAGE_DATA) - enabled_languages = set(self.LANGUAGES.split()) - if 'all' in enabled_languages: - languages = available_languages - else: - unknown_language = enabled_languages - available_languages - if unknown_language: - print >> sys.stderr, 'Ignoring unknown language codes:', \ - SPACE.join(unknown_language) - languages = available_languages & enabled_languages - for code in languages: - self.languages.enable_language(code) - # Always add and enable the default server language. - code = self.DEFAULT_SERVER_LANGUAGE - self.languages.enable_language(code) - # Create various registries. - self.archivers = {} - self.chains = {} - self.rules = {} - self.handlers = {} - self.pipelines = {} - self.commands = {} - - def add_domain(self, *args, **kws): - """Add a virtual domain. - - See `Domain`. - """ - domain = Domain(*args, **kws) - 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 - - # Proxy the docstring for the above method. - add_domain.__doc__ = Domain.__init__.__doc__ - - def add_qrunner(self, name, count=1): - """Convenient interface for adding additional qrunners. - - :param name: the qrunner name, which must not include the 'Runner' - suffix. E.g. 'HTTP' or 'LMTP'. - :param count: is the number of qrunner slices to create, default: 1. - """ - if name.startswith('.'): - name = 'mailman.queue' + name - self.qrunners[name] = count - # Calculate the queue runner shortcut name. - classname = name.rsplit('.', 1)[1] - if classname.endswith('Runner'): - shortname = classname[:-6].lower() - else: - shortname = classname - self.qrunner_shortcuts[shortname] = name - - def del_qrunner(self, name): - """Remove the named qrunner so that it does not start. - - :param name: the qrunner name, which must not include the 'Runner' - suffix. - """ - if name.startswith('.'): - name = 'mailman.queue' + name - self.qrunners.pop(name) - for shortname, classname in self.qrunner_shortcuts: - if name == classname: - del self.qrunner_shortcuts[shortname] - break - - @property - def paths(self): - return dict([(k, self.__dict__[k]) - for k in self.__dict__ - if k.endswith('_DIR')]) - - def ensure_directories_exist(self): - for variable, directory in self.paths.items(): - try: - os.makedirs(directory, 02775) - except OSError, e: - if e.errno <> errno.EEXIST: - raise - - - -config = Configuration() diff --git a/mailman/core/chains.py b/mailman/core/chains.py index bebbf5bee..812302d3f 100644 --- a/mailman/core/chains.py +++ b/mailman/core/chains.py @@ -17,11 +17,11 @@ """Application support for chain processing.""" +__metaclass__ = type __all__ = [ 'initialize', 'process', ] -__metaclass__ = type from mailman.chains.accept import AcceptChain @@ -30,7 +30,7 @@ from mailman.chains.discard import DiscardChain from mailman.chains.headers import HeaderMatchChain from mailman.chains.hold import HoldChain from mailman.chains.reject import RejectChain -from mailman.configuration import config +from mailman.config import config from mailman.interfaces import LinkAction diff --git a/mailman/initialize.py b/mailman/core/initialize.py index ea7c294c8..16dcecf94 100644 --- a/mailman/initialize.py +++ b/mailman/core/initialize.py @@ -29,8 +29,8 @@ import os from zope.interface.interface import adapter_hooks from zope.interface.verify import verifyObject -import mailman.configuration -import mailman.loginit +import mailman.config.config +import mailman.core.logging from mailman.core.plugins import get_plugin from mailman.interfaces import IDatabase @@ -61,10 +61,10 @@ def initialize_1(config_path, propagate_logs): # restrictive permissions in order to handle private archives, but it # handles that correctly. os.umask(007) - mailman.configuration.config.load(config_path) + mailman.config.config.load(config_path) # Create the queue and log directories if they don't already exist. - mailman.configuration.config.ensure_directories_exist() - mailman.loginit.initialize(propagate_logs) + mailman.config.config.ensure_directories_exist() + mailman.core.logging.initialize(propagate_logs) def initialize_2(debug=False): @@ -85,7 +85,7 @@ def initialize_2(debug=False): database = database_plugin() verifyObject(IDatabase, database) database.initialize(debug) - mailman.configuration.config.db = database + mailman.config.config.db = database # Initialize the rules and chains. Do the imports here so as to avoid # circular imports. from mailman.app.commands import initialize as initialize_commands diff --git a/mailman/core/logging.py b/mailman/core/logging.py new file mode 100644 index 000000000..608b59c2c --- /dev/null +++ b/mailman/core/logging.py @@ -0,0 +1,109 @@ +# Copyright (C) 2006-2008 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/>. + +"""Logging initialization, using Python's standard logging package.""" + +from __future__ import absolute_import + +import os +import sys +import codecs +import logging +import ConfigParser + +from mailman.config import config +from mailman.config.helpers import as_boolean, as_log_level + + +_handlers = [] + + + +class ReopenableFileHandler(logging.Handler): + def __init__(self, filename): + self._filename = filename + self._stream = self._open() + logging.Handler.__init__(self) + + def _open(self): + return codecs.open(self._filename, 'a', 'utf-8') + + def flush(self): + if self._stream: + self._stream.flush() + + def emit(self, record): + # It's possible for the stream to have been closed by the time we get + # here, due to the shut down semantics. This mostly happens in the + # test suite, but be defensive anyway. + stream = (self._stream if self._stream else sys.stderr) + try: + msg = self.format(record) + fs = '%s\n' + try: + stream.write(fs % msg) + except UnicodeError: + stream.write(fs % msg.encode('string-escape')) + self.flush() + except: + self.handleError(record) + + def close(self): + self.flush() + self._stream.close() + self._stream = None + logging.Handler.close(self) + + def reopen(self): + self._stream.close() + self._stream = self._open() + + + +def initialize(propagate=False): + # First, find the root logger and configure the logging subsystem. + # Initialize the root logger, then create a formatter for all the sublogs. + logging.basicConfig(format=config.logging.root.format, + datefmt=config.logging.root.datefmt, + level=as_log_level(config.logging.root.level)) + # Create the subloggers + for logger_config in config.logger_configs: + logger_name = 'mailman.' + logger_config.name.split('.')[-1] + log = logging.getLogger(logger_name) + # Get settings from log configuration file (or defaults). + log_format = logger_config.format + log_datefmt = logger_config.datefmt + # Propagation to the root logger is how we handle logging to stderr + # when the qrunners are not run as a subprocess of mailmanctl. + log.propagate = as_boolean(logger_config.propagate) + # Set the logger's level. + log.setLevel(as_log_level(logger_config.level)) + # Create a formatter for this logger, then a handler, and link the + # formatter to the handler. + formatter = logging.Formatter(fmt=log_format, datefmt=log_datefmt) + path_str = logger_config.path + path_abs = os.path.normpath(os.path.join(config.LOG_DIR, path_str)) + handler = ReopenableFileHandler(path_abs) + _handlers.append(handler) + handler.setFormatter(formatter) + log.addHandler(handler) + + + +def reopen(): + for handler in _handlers: + handler.reopen() diff --git a/mailman/core/pipelines.py b/mailman/core/pipelines.py index c790901b2..7c177b511 100644 --- a/mailman/core/pipelines.py +++ b/mailman/core/pipelines.py @@ -27,7 +27,7 @@ __all__ = [ from zope.interface import implements from zope.interface.verify import verifyObject -from mailman.configuration import config +from mailman.config import config from mailman.core.plugins import get_plugins from mailman.i18n import _ from mailman.interfaces import IHandler, IPipeline diff --git a/mailman/core/rules.py b/mailman/core/rules.py index 2c1e4fb3b..28f58d76e 100644 --- a/mailman/core/rules.py +++ b/mailman/core/rules.py @@ -26,7 +26,7 @@ __all__ = [ from zope.interface import implements from zope.interface.verify import verifyObject -from mailman.configuration import config +from mailman.config import config from mailman.core.plugins import get_plugins from mailman.interfaces import IRule diff --git a/mailman/core/styles.py b/mailman/core/styles.py index 96104c204..d264143f5 100644 --- a/mailman/core/styles.py +++ b/mailman/core/styles.py @@ -30,7 +30,7 @@ from zope.interface import implements from zope.interface.verify import verifyObject from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.core.plugins import get_plugins from mailman.i18n import _ from mailman.interfaces import ( diff --git a/mailman/database/__init__.py b/mailman/database/__init__.py index dfafe4473..429fea953 100644 --- a/mailman/database/__init__.py +++ b/mailman/database/__init__.py @@ -31,14 +31,15 @@ from zope.interface import implements import mailman.version -from mailman.configuration import config +from mailman.config import config +from mailman.config.helpers import as_boolean from mailman.database.listmanager import ListManager from mailman.database.messagestore import MessageStore from mailman.database.pending import Pendings from mailman.database.requests import Requests from mailman.database.usermanager import UserManager from mailman.database.version import Version -from mailman.interfaces import IDatabase, SchemaVersionMismatchError +from mailman.interfaces.database import IDatabase, SchemaVersionMismatchError @@ -82,8 +83,7 @@ class StockDatabase: def _create(self, debug): # Calculate the engine url. - url = Template(config.DEFAULT_DATABASE_URL).safe_substitute( - config.paths) + url = Template(config.database.url).safe_substitute(config.paths) # XXX By design of SQLite, database file creation does not honor # umask. See their ticket #1193: # http://www.sqlite.org/cvstrac/tktview?tn=1193,31 @@ -101,7 +101,7 @@ class StockDatabase: touch(url) database = create_database(url) store = Store(database) - database.DEBUG = (config.DEFAULT_DATABASE_ECHO + database.DEBUG = (as_boolean(config.database.debug) if debug is None else debug) # Check the sqlite master database to see if the version file exists. # If so, then we assume the database schema is correctly initialized. diff --git a/mailman/database/address.py b/mailman/database/address.py index 95e3007ef..272ad8dc3 100644 --- a/mailman/database/address.py +++ b/mailman/database/address.py @@ -25,7 +25,7 @@ from email.utils import formataddr from storm.locals import * from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.database.member import Member from mailman.database.model import Model from mailman.database.preferences import Preferences diff --git a/mailman/database/listmanager.py b/mailman/database/listmanager.py index c4cebce4c..45c939cd1 100644 --- a/mailman/database/listmanager.py +++ b/mailman/database/listmanager.py @@ -22,7 +22,7 @@ import datetime from zope.interface import implements from mailman.Utils import split_listname, fqdn_listname -from mailman.configuration import config +from mailman.config import config from mailman.database.mailinglist import MailingList from mailman.interfaces import IListManager, ListAlreadyExistsError diff --git a/mailman/database/mailinglist.py b/mailman/database/mailinglist.py index d5e6fd7b8..e1c6414be 100644 --- a/mailman/database/mailinglist.py +++ b/mailman/database/mailinglist.py @@ -22,7 +22,7 @@ from storm.locals import * from zope.interface import implements from mailman.Utils import fqdn_listname, makedirs, split_listname -from mailman.configuration import config +from mailman.config import config from mailman.database import roster from mailman.database.model import Model from mailman.database.types import Enum diff --git a/mailman/database/member.py b/mailman/database/member.py index a4011840a..a25f2b0f7 100644 --- a/mailman/database/member.py +++ b/mailman/database/member.py @@ -19,7 +19,7 @@ from storm.locals import * from zope.interface import implements from mailman.Utils import split_listname -from mailman.configuration import config +from mailman.config import config from mailman.constants import SystemDefaultPreferences from mailman.database.model import Model from mailman.database.types import Enum diff --git a/mailman/database/message.py b/mailman/database/message.py index 578445acf..7a4ba87d5 100644 --- a/mailman/database/message.py +++ b/mailman/database/message.py @@ -18,7 +18,7 @@ from storm.locals import * from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.database.model import Model from mailman.interfaces import IMessage diff --git a/mailman/database/messagestore.py b/mailman/database/messagestore.py index a784f89cc..3c6528105 100644 --- a/mailman/database/messagestore.py +++ b/mailman/database/messagestore.py @@ -29,7 +29,7 @@ import cPickle as pickle from zope.interface import implements from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.database.message import Message from mailman.interfaces import IMessageStore diff --git a/mailman/database/pending.py b/mailman/database/pending.py index 31cc4e285..bdfd9cc72 100644 --- a/mailman/database/pending.py +++ b/mailman/database/pending.py @@ -27,7 +27,7 @@ from storm.locals import * from zope.interface import implements from zope.interface.verify import verifyObject -from mailman.configuration import config +from mailman.config import config from mailman.database.model import Model from mailman.interfaces import ( IPendable, IPended, IPendedKeyValue, IPendings) diff --git a/mailman/database/requests.py b/mailman/database/requests.py index 963d34cc4..0569de14d 100644 --- a/mailman/database/requests.py +++ b/mailman/database/requests.py @@ -21,7 +21,7 @@ from datetime import timedelta from storm.locals import * from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.database.model import Model from mailman.database.types import Enum from mailman.interfaces import IListRequests, IPendable, IRequests, RequestType diff --git a/mailman/database/roster.py b/mailman/database/roster.py index 60087c243..4a16ebc1b 100644 --- a/mailman/database/roster.py +++ b/mailman/database/roster.py @@ -38,7 +38,7 @@ __all__ = [ from storm.locals import * from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.constants import SystemDefaultPreferences from mailman.database.address import Address from mailman.database.member import Member diff --git a/mailman/database/user.py b/mailman/database/user.py index 9d8e87a9f..9bb134734 100644 --- a/mailman/database/user.py +++ b/mailman/database/user.py @@ -19,7 +19,7 @@ from email.utils import formataddr from storm.locals import * from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.database.model import Model from mailman.database.address import Address from mailman.database.preferences import Preferences diff --git a/mailman/database/usermanager.py b/mailman/database/usermanager.py index fb97f0947..8507fa550 100644 --- a/mailman/database/usermanager.py +++ b/mailman/database/usermanager.py @@ -21,7 +21,7 @@ import os from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.database.address import Address from mailman.database.preferences import Preferences from mailman.database.user import User diff --git a/mailman/i18n.py b/mailman/i18n.py index 186965509..6abb8fa0e 100644 --- a/mailman/i18n.py +++ b/mailman/i18n.py @@ -22,7 +22,7 @@ import string import gettext import mailman.messages -from mailman.configuration import config +from mailman.config import config _translation = None _missing = object() diff --git a/mailman/loginit.py b/mailman/loginit.py deleted file mode 100644 index d3e64790d..000000000 --- a/mailman/loginit.py +++ /dev/null @@ -1,183 +0,0 @@ -# Copyright (C) 2006-2008 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/>. - -"""Logging initialization, using Python's standard logging package. - -This module cannot be called 'logging' because that would interfere with the -import below. Ah, for Python 2.5 and absolute imports. -""" - -import os -import sys -import codecs -import logging -import ConfigParser - -from mailman.configuration import config - - - -FMT = '%(asctime)s (%(process)d) %(message)s' -DATEFMT = '%b %d %H:%M:%S %Y' - -LOGGERS = ( - 'archiver', # All archiver output - 'bounce', # All bounce processing logs go here - 'config', # Configuration issues - 'debug', # Only used for development - 'error', # All exceptions go to this log - 'fromusenet', # Information related to the Usenet to Mailman gateway - 'http', # Internal wsgi-based web interface - 'locks', # Lock state changes - 'mischief', # Various types of hostile activity - 'post', # Information about messages posted to mailing lists - 'qrunner', # qrunner start/stops - 'smtp', # Successful SMTP activity - 'smtp-failure', # Unsuccessful SMTP activity - 'subscribe', # Information about leaves/joins - 'vette', # Information related to admindb activity - ) - -_handlers = [] - - - -class ReallySafeConfigParser(ConfigParser.SafeConfigParser): - """Like `SafeConfigParser` but catches `NoOptionError`.""" - - def getstring(self, section, option, default=None): - try: - return ConfigParser.SafeConfigParser.get(self, section, option) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): - # Try again with the '*' section. - try: - return ConfigParser.SafeConfigParser.get(self, '*', option) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): - return default - - def getboolean(self, section, option, default=None): - try: - return ConfigParser.SafeConfigParser.getboolean( - self, section, option) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): - # Try again with the '*' section. - try: - return ConfigParser.SafeConfigParser.getboolean( - self, '*', option) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): - return default - - def getlevel(self, section, option, default=None): - try: - level_str = ConfigParser.SafeConfigParser.get( - self, section, option) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): - # Try again with the '*' section. - try: - level_str = ConfigParser.SafeConfigParser.get( - self, '*', option) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): - level_str = default - # Convert the level string into an integer. - level_str = level_str.upper() - level_def = (logging.DEBUG if section == 'debug' else logging.INFO) - return getattr(logging, level_str, level_def) - - - -class ReopenableFileHandler(logging.Handler): - def __init__(self, filename): - self._filename = filename - self._stream = self._open() - logging.Handler.__init__(self) - - def _open(self): - return codecs.open(self._filename, 'a', 'utf-8') - - def flush(self): - if self._stream: - self._stream.flush() - - def emit(self, record): - # It's possible for the stream to have been closed by the time we get - # here, due to the shut down semantics. This mostly happens in the - # test suite, but be defensive anyway. - stream = self._stream or sys.stderr - try: - msg = self.format(record) - fs = '%s\n' - try: - stream.write(fs % msg) - except UnicodeError: - stream.write(fs % msg.encode('string-escape')) - self.flush() - except: - self.handleError(record) - - def close(self): - self.flush() - self._stream.close() - self._stream = None - logging.Handler.close(self) - - def reopen(self): - self._stream.close() - self._stream = self._open() - - - -def initialize(propagate=False): - # Initialize the root logger, then create a formatter for all the sublogs. - logging.basicConfig(format=FMT, datefmt=DATEFMT, level=logging.INFO) - # If a custom log configuration file was specified, load it now. Note - # that we don't use logging.config.fileConfig() because it requires that - # all loggers, formatters, and handlers be defined. We want to support - # minimal overloading of our logger configurations. - cp = ReallySafeConfigParser() - if config.LOG_CONFIG_FILE: - path = os.path.join(config.ETC_DIR, config.LOG_CONFIG_FILE) - cp.read(os.path.normpath(path)) - # Create the subloggers - for logger in LOGGERS: - log = logging.getLogger('mailman.' + logger) - # Get settings from log configuration file (or defaults). - log_format = cp.getstring(logger, 'format', FMT) - log_datefmt = cp.getstring(logger, 'datefmt', DATEFMT) - # Propagation to the root logger is how we handle logging to stderr - # when the qrunners are not run as a subprocess of mailmanctl. - log.propagate = cp.getboolean(logger, 'propagate', propagate) - # Set the logger's level. Note that if the log configuration file - # does not set an override, the default level will be INFO except for - # the 'debug' logger. It doesn't make much sense for the debug logger - # to ignore debug level messages! - level_int = cp.getlevel(logger, 'level', 'INFO') - log.setLevel(level_int) - # Create a formatter for this logger, then a handler, and link the - # formatter to the handler. - formatter = logging.Formatter(fmt=log_format, datefmt=log_datefmt) - path_str = cp.getstring(logger, 'path', logger) - path_abs = os.path.normpath(os.path.join(config.LOG_DIR, path_str)) - handler = ReopenableFileHandler(path_abs) - _handlers.append(handler) - handler.setFormatter(formatter) - log.addHandler(handler) - - - -def reopen(): - for handler in _handlers: - handler.reopen() diff --git a/mailman/options.py b/mailman/options.py index 94d03f657..adfe26cd9 100644 --- a/mailman/options.py +++ b/mailman/options.py @@ -29,10 +29,10 @@ import sys from copy import copy from optparse import Option, OptionParser, OptionValueError -from mailman.version import MAILMAN_VERSION -from mailman.configuration import config +from mailman.config import config +from mailman.core.initialize import initialize from mailman.i18n import _ -from mailman.initialize import initialize +from mailman.version import MAILMAN_VERSION diff --git a/mailman/pipeline/acknowledge.py b/mailman/pipeline/acknowledge.py index 41fc931ae..7421fcdd6 100644 --- a/mailman/pipeline/acknowledge.py +++ b/mailman/pipeline/acknowledge.py @@ -28,7 +28,7 @@ from zope.interface import implements from mailman import Message from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IHandler diff --git a/mailman/pipeline/avoid_duplicates.py b/mailman/pipeline/avoid_duplicates.py index d228c6ad9..604d8b162 100644 --- a/mailman/pipeline/avoid_duplicates.py +++ b/mailman/pipeline/avoid_duplicates.py @@ -30,7 +30,7 @@ __all__ = ['AvoidDuplicates'] from email.Utils import getaddresses, formataddr from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IHandler diff --git a/mailman/pipeline/calculate_recipients.py b/mailman/pipeline/calculate_recipients.py index f892435cd..3207a398e 100644 --- a/mailman/pipeline/calculate_recipients.py +++ b/mailman/pipeline/calculate_recipients.py @@ -30,7 +30,7 @@ from zope.interface import implements from mailman import Message from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.core import errors from mailman.i18n import _ from mailman.interfaces import DeliveryStatus, IHandler diff --git a/mailman/pipeline/cleanse_dkim.py b/mailman/pipeline/cleanse_dkim.py index ae51fc27c..bc76e2726 100644 --- a/mailman/pipeline/cleanse_dkim.py +++ b/mailman/pipeline/cleanse_dkim.py @@ -31,7 +31,7 @@ __all__ = ['CleanseDKIM'] from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IHandler diff --git a/mailman/pipeline/cook_headers.py b/mailman/pipeline/cook_headers.py index 362c8ecf7..bd3ad8a84 100644 --- a/mailman/pipeline/cook_headers.py +++ b/mailman/pipeline/cook_headers.py @@ -32,7 +32,7 @@ from email.utils import parseaddr, formataddr, getaddresses from zope.interface import implements from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.core.plugins import get_plugins from mailman.i18n import _ from mailman.interfaces import IHandler, Personalization, ReplyToMunging diff --git a/mailman/pipeline/decorate.py b/mailman/pipeline/decorate.py index 3e9c6360b..bc684a1b1 100644 --- a/mailman/pipeline/decorate.py +++ b/mailman/pipeline/decorate.py @@ -30,7 +30,7 @@ from zope.interface import implements from mailman import Utils from mailman.Message import Message -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IHandler diff --git a/mailman/pipeline/mime_delete.py b/mailman/pipeline/mime_delete.py index 9cb34297f..6de3d0a72 100644 --- a/mailman/pipeline/mime_delete.py +++ b/mailman/pipeline/mime_delete.py @@ -39,7 +39,7 @@ from zope.interface import implements from mailman.Message import UserNotification from mailman.Utils import oneline -from mailman.configuration import config +from mailman.config import config from mailman.core import errors from mailman.i18n import _ from mailman.interfaces import IHandler diff --git a/mailman/pipeline/moderate.py b/mailman/pipeline/moderate.py index 11471dd3a..aa78eaa36 100644 --- a/mailman/pipeline/moderate.py +++ b/mailman/pipeline/moderate.py @@ -24,7 +24,7 @@ from email.MIMEText import MIMEText from mailman import Message from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.core import errors from mailman.i18n import _ diff --git a/mailman/pipeline/scrubber.py b/mailman/pipeline/scrubber.py index 4b8d9f770..abc904613 100644 --- a/mailman/pipeline/scrubber.py +++ b/mailman/pipeline/scrubber.py @@ -39,7 +39,7 @@ from mimetypes import guess_all_extensions from zope.interface import implements from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.core.errors import DiscardMessage from mailman.core.plugins import get_plugin from mailman.i18n import _ diff --git a/mailman/pipeline/smtp_direct.py b/mailman/pipeline/smtp_direct.py index 7d9242417..ab5ca0096 100644 --- a/mailman/pipeline/smtp_direct.py +++ b/mailman/pipeline/smtp_direct.py @@ -27,7 +27,9 @@ for a threaded implementation. """ __metaclass__ = type -__all__ = ['SMTPDirect'] +__all__ = [ + 'SMTPDirect', + ] import copy @@ -44,20 +46,15 @@ from email.Utils import formataddr from zope.interface import implements from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.core import errors from mailman.i18n import _ from mailman.interfaces import IHandler, Personalization DOT = '.' - -log = logging.getLogger('mailman.smtp') -flog = logging.getLogger('mailman.smtp-failure') -every_log = logging.getLogger('mailman.' + config.SMTP_LOG_EVERY_MESSAGE[0]) -success_log = logging.getLogger('mailman.' + config.SMTP_LOG_SUCCESS[0]) -refused_log = logging.getLogger('mailman.' + config.SMTP_LOG_REFUSED[0]) -failure_log = logging.getLogger('mailman.' + config.SMTP_LOG_EACH_FAILURE[0]) +COMMA = ',' +log = logging.getLogger('mailman.smtp') @@ -101,20 +98,6 @@ class Connection: -class MessageDict(dict): - def __init__(self, message, extras): - super(MessageDict, self).__init__() - for key, value in message.items(): - self[key.lower()] = value - self.update(extras) - - -class Template(string.Template): - # Allow dashes and # signs, in addition to the standard pattern. - idpattern = '[_a-z#-][_a-z0-9#-]*' - - - def process(mlist, msg, msgdata): recips = msgdata.get('recips') if not recips: @@ -192,38 +175,39 @@ def process(mlist, msg, msgdata): msgdata['recips'] = origrecips # Log the successful post t1 = time.time() - substitutions = MessageDict(msg, { - 'time' : t1-t0, - 'size' : msg.original_size, - '#recips' : len(recips), - '#refused' : len(refused), - 'listname' : mlist.fqdn_listname, - 'sender' : origsender, - }) - if 'message-id' not in substitutions: - substitutions['message-id'] = 'n/a' - # We have to use the copy() method because extended call syntax requires a - # concrete dictionary object; it does not allow a generic mapping (XXX is - # this still true in Python 2.3?). - if config.SMTP_LOG_EVERY_MESSAGE: - template = Template(config.SMTP_LOG_EVERY_MESSAGE[1]) - every_log.info('%s', template.safe_substitute(substitutions)) - + substitutions = dict( + msgid = msg.get('message-id', 'n/a'), + listname = mlist.fqdn_listname, + sender = origsender, + recip = len(recips), + size = msg.original_size, + seconds = t1 - t0, + refused = len(refused), + smtpcode = 'n/a', + smtpmsg = 'n/a', + ) + # Log this message. + template = config.logging.smtp.every + if template != 'no': + template = Template(template) + log.info('%s', template.safe_substitute(substitutions)) if refused: - if config.SMTP_LOG_REFUSED: - template = Template(config.SMTP_LOG_REFUSED[1]) - refused_log.info('%s', template.safe_substitute(substitutions)) - - elif msgdata.get('tolist'): - # Log the successful post, but only if it really was a post to the - # mailing list. Don't log sends to the -owner, or -admin addrs. - # -request addrs should never get here. BAW: it may be useful to log - # the other messages, but in that case, we should probably have a - # separate configuration variable to control that. - if config.SMTP_LOG_SUCCESS: - template = Template(config.SMTP_LOG_SUCCESS[1]) - success_log.info('%s', template.safe_substitute(substitutions)) - + template = config.logging.smtp.refused + if template != 'no': + template = Template(template) + log.info('%s', template.safe_substitute(substitutions)) + else: + # Log the successful post, but if it was not destined to the mailing + # list (e.g. to the owner or admin), print the actual recipients + # instead of just the number. + if not msgdata.get('tolist'): + recips = msg.get_all('to', []) + recips.extend(msg.get_all('cc', [])) + substitutions['recips'] = COMMA.join(recips) + template = config.logging.smtp.success + if template != 'no': + template = Template(template) + log.info('%s', template.safe_substitute(substitutions)) # Process any failed deliveries. tempfailures = [] permfailures = [] @@ -244,14 +228,15 @@ def process(mlist, msg, msgdata): # Deal with persistent transient failures by queuing them up for # future delivery. TBD: this could generate lots of log entries! tempfailures.append(recip) - if config.SMTP_LOG_EACH_FAILURE: - substitutions.update({ - 'recipient' : recip, - 'failcode' : code, - 'failmsg' : smtpmsg, - }) - template = Template(config.SMTP_LOG_EACH_FAILURE[1]) - failure_log.info('%s', template.safe_substitute(substitutions)) + template = config.logging.smtp.failure + if template != 'no': + substitutions.update( + recip = recip, + smtpcode = code, + smtpmsg = smtpmsg, + ) + template = Template(template) + log.info('%s', template.safe_substitute(substitutions)) # Return the results if tempfailures or permfailures: raise errors.SomeRecipientsFailed(tempfailures, permfailures) diff --git a/mailman/pipeline/to_archive.py b/mailman/pipeline/to_archive.py index b693d8341..4d8c27cf1 100644 --- a/mailman/pipeline/to_archive.py +++ b/mailman/pipeline/to_archive.py @@ -23,7 +23,7 @@ __all__ = ['ToArchive'] from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IHandler from mailman.queue import Switchboard diff --git a/mailman/pipeline/to_digest.py b/mailman/pipeline/to_digest.py index cec2fa1fc..78984c92e 100644 --- a/mailman/pipeline/to_digest.py +++ b/mailman/pipeline/to_digest.py @@ -53,7 +53,7 @@ from mailman import Utils from mailman import i18n from mailman.Mailbox import Mailbox from mailman.Mailbox import Mailbox -from mailman.configuration import config +from mailman.config import config from mailman.core import errors from mailman.interfaces import DeliveryMode, DeliveryStatus, IHandler from mailman.pipeline.decorate import decorate diff --git a/mailman/pipeline/to_outgoing.py b/mailman/pipeline/to_outgoing.py index 48633da96..d8d1ec935 100644 --- a/mailman/pipeline/to_outgoing.py +++ b/mailman/pipeline/to_outgoing.py @@ -28,7 +28,7 @@ __all__ = ['ToOutgoing'] from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IHandler, Personalization from mailman.queue import Switchboard diff --git a/mailman/pipeline/to_usenet.py b/mailman/pipeline/to_usenet.py index 4ebd94bec..14e7811bb 100644 --- a/mailman/pipeline/to_usenet.py +++ b/mailman/pipeline/to_usenet.py @@ -25,7 +25,7 @@ import logging from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IHandler from mailman.queue import Switchboard diff --git a/mailman/queue/__init__.py b/mailman/queue/__init__.py index 961a46283..27ea83320 100644 --- a/mailman/queue/__init__.py +++ b/mailman/queue/__init__.py @@ -48,7 +48,7 @@ from zope.interface import implements from mailman import i18n from mailman import Message from mailman import Utils -from mailman.configuration import config +from mailman.config import config from mailman.interfaces import IRunner, ISwitchboard # 20 bytes of all bits set, maximum hashlib.sha.digest() value diff --git a/mailman/rules/administrivia.py b/mailman/rules/administrivia.py index 1c09d98e1..b4fa9b172 100644 --- a/mailman/rules/administrivia.py +++ b/mailman/rules/administrivia.py @@ -17,14 +17,16 @@ """The administrivia rule.""" -__all__ = ['Administrivia'] __metaclass__ = type +__all__ = [ + 'Administrivia', + ] from email.iterators import typed_subpart_iterator from zope.interface import implements -from mailman.configuration import config +from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IRule diff --git a/mailman/testing/helpers.py b/mailman/testing/helpers.py index 47c20ca2f..0f9c3edae 100644 --- a/mailman/testing/helpers.py +++ b/mailman/testing/helpers.py @@ -42,7 +42,7 @@ from Queue import Empty, Queue from datetime import datetime, timedelta from mailman.bin.master import Loop as Master -from mailman.configuration import config +from mailman.config import config from mailman.queue import Switchboard from mailman.testing.smtplistener import Server diff --git a/mailman/tests/test_documentation.py b/mailman/tests/test_documentation.py index cf53be245..ffb955c58 100644 --- a/mailman/tests/test_documentation.py +++ b/mailman/tests/test_documentation.py @@ -27,7 +27,7 @@ from email import message_from_string import mailman from mailman.Message import Message -from mailman.configuration import config +from mailman.config import config from mailman.core.styles import style_manager from mailman.testing.helpers import SMTPServer diff --git a/mailman/tests/test_membership.py b/mailman/tests/test_membership.py index 792681472..501b71ca3 100644 --- a/mailman/tests/test_membership.py +++ b/mailman/tests/test_membership.py @@ -23,7 +23,7 @@ import unittest from mailman import Utils from mailman import passwords -from mailman.configuration import config +from mailman.config import config from mailman.core.errors import NotAMemberError diff --git a/mailman/tests/test_security_mgr.py b/mailman/tests/test_security_mgr.py index 835530496..b02561902 100644 --- a/mailman/tests/test_security_mgr.py +++ b/mailman/tests/test_security_mgr.py @@ -27,7 +27,7 @@ from StringIO import StringIO from mailman import Utils from mailman import passwords -from mailman.configuration import config +from mailman.config import config @@ -107,6 +107,7 @@ case second `m'. Any other spelling is incorrect.""", 'mailman.handlers' : 'default = mailman.pipeline:initialize', }, install_requires = [ + 'lazr.config', 'locknix', 'munepy', 'storm', |
