summaryrefslogtreecommitdiff
path: root/src/mailman/config
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/config')
-rw-r--r--src/mailman/config/__init__.py30
-rw-r--r--src/mailman/config/config.py206
-rw-r--r--src/mailman/config/mailman.cfg69
-rw-r--r--src/mailman/config/schema.cfg589
4 files changed, 894 insertions, 0 deletions
diff --git a/src/mailman/config/__init__.py b/src/mailman/config/__init__.py
new file mode 100644
index 000000000..11f4f0c80
--- /dev/null
+++ b/src/mailman/config/__init__.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2008-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/>.
+
+"""Mailman configuration package."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'config',
+ ]
+
+
+from mailman.config.config import Configuration
+
+config = Configuration()
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)
diff --git a/src/mailman/config/mailman.cfg b/src/mailman/config/mailman.cfg
new file mode 100644
index 000000000..2bf528bea
--- /dev/null
+++ b/src/mailman/config/mailman.cfg
@@ -0,0 +1,69 @@
+# Copyright (C) 2008-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/>.
+
+# This is the absolute bare minimum base configuration file. User supplied
+# configurations are pushed onto this.
+
+[language.en]
+
+[qrunner.archive]
+class: mailman.queue.archive.ArchiveRunner
+
+[qrunner.bad]
+class: mailman.queue.fake.BadRunner
+# The shunt runner is just a placeholder for its switchboard.
+start: no
+
+[qrunner.bounces]
+class: mailman.queue.bounce.BounceRunner
+
+[qrunner.command]
+class: mailman.queue.command.CommandRunner
+
+[qrunner.in]
+class: mailman.queue.incoming.IncomingRunner
+
+[qrunner.lmtp]
+class: mailman.queue.lmtp.LMTPRunner
+
+[qrunner.maildir]
+class: mailman.queue.maildir.MaildirRunner
+# This is still experimental.
+start: no
+
+[qrunner.news]
+class: mailman.queue.news.NewsRunner
+
+[qrunner.out]
+class: mailman.queue.outgoing.OutgoingRunner
+
+[qrunner.pipeline]
+class: mailman.queue.pipeline.PipelineRunner
+
+[qrunner.retry]
+class: mailman.queue.retry.RetryRunner
+sleep_time: 15m
+
+[qrunner.shunt]
+class: mailman.queue.fake.ShuntRunner
+# The shunt runner is just a placeholder for its switchboard.
+start: no
+
+[qrunner.virgin]
+class: mailman.queue.virgin.VirginRunner
+
+[style.default]
diff --git a/src/mailman/config/schema.cfg b/src/mailman/config/schema.cfg
new file mode 100644
index 000000000..df20a7370
--- /dev/null
+++ b/src/mailman/config/schema.cfg
@@ -0,0 +1,589 @@
+# Copyright (C) 2008-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/>.
+
+# 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.
+
+[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
+
+# When allowing only members to post to a mailing list, how is the sender of
+# the message determined? If this variable is set to Yes, then first the
+# message's envelope sender is used, with a fallback to the sender if there is
+# no envelope sender. Set this variable to No to always use the sender.
+#
+# The envelope sender is set by the SMTP delivery and is thus less easily
+# spoofed than the sender, which is typically just taken from the From: header
+# and thus easily spoofed by the end-user. However, sometimes the envelope
+# sender isn't set correctly and this will manifest itself by postings being
+# held for approval even if they appear to come from a list member. If you
+# are having this problem, set this variable to No, but understand that some
+# spoofed messages may get through.
+use_envelope_sender: no
+
+# Membership tests for posting purposes are usually performed by looking at a
+# set of headers, passing the test if any of their values match a member of
+# the list. Headers are checked in the order given in this variable. The
+# value From_ means to use the envelope sender. Field names are case
+# insensitive. This is a space separate list of headers.
+sender_headers: from from_ reply-to sender
+
+# Mail command processor will ignore mail command lines after designated max.
+email_commands_max_lines: 10
+
+# Default length of time a pending request is live before it is evicted from
+# the pending database.
+pending_request_life: 3d
+
+
+[passwords]
+# When Mailman generates them, this is the default length of member passwords.
+member_password_length: 8
+
+# Specify the type of passwords to use, when Mailman generates the passwords
+# itself, as would be the case for membership requests where the user did not
+# fill in a password, or during list creation, when auto-generation of admin
+# passwords was selected.
+#
+# Set this value to 'yes' for classic Mailman user-friendly(er) passwords.
+# These generate semi-pronounceable passwords which are easier to remember.
+# Set this value to 'no' to use more cryptographically secure, but harder to
+# remember, passwords -- if your operating system and Python version support
+# the necessary feature (specifically that /dev/urandom be available).
+user_friendly_passwords: yes
+
+
+[qrunner.master]
+# Define which process queue runners, and how many of them, to start.
+
+# The full import path to the class for this queue runner.
+class: mailman.queue.runner.Runner
+
+# The directory path that this queue runner scans.
+path: $VAR_DIR/qfiles/$name
+
+# The number of parallel queue runners. This must be a power of 2.
+instances: 1
+
+# Whether to start this queue runner or not.
+start: yes
+
+# The maximum number of restarts for this queue runner. When the runner exits
+# because of an error or other unexpected problem, it is automatically
+# restarted, until the maximum number of restarts has been reached.
+max_restarts: 10
+
+# The sleep interval for the queue runner. It wakes up once every interval to
+# process the files in its slice of the queue directory.
+sleep_time: 1s
+
+[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.master]
+# 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.master]
+# 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.
+enabled: yes
+
+
+[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
+
+
+[mta]
+# The class defining the interface to the incoming mail transport agent.
+incoming: mailman.mta.postfix.LMTP
+
+# The class defining the interface to the outgoing mail transport agent.
+outgoing: mailman.mta.smtp_direct.process
+
+# How to connect to the outgoing MTA.
+smtp_host: localhost
+smtp_port: 25
+
+# Where the LMTP server listens for connections.
+lmtp_host: localhost
+lmtp_port: 8025
+
+# Ceiling on the number of recipients that can be specified in a single SMTP
+# transaction. Set to 0 to submit the entire recipient list in one
+# transaction.
+max_recipients: 500
+
+# Ceiling on the number of SMTP sessions to perform on a single socket
+# connection. Some MTAs have limits. Set this to 0 to do as many as we like
+# (i.e. your MTA has no limits). Set this to some number great than 0 and
+# Mailman will close the SMTP connection and re-open it after this number of
+# consecutive sessions.
+max_sessions_per_connection: 0
+
+# Maximum number of simultaneous subthreads that will be used for SMTP
+# delivery. After the recipients list is chunked according to max_recipients,
+# each chunk is handed off to the SMTP server by a separate such thread. If
+# your Python interpreter was not built for threads, this feature is disabled.
+# You can explicitly disable it in all cases by setting max_delivery_threads
+# to 0.
+max_delivery_threads: 0
+
+# How long should messages which have delivery failures continue to be
+# retried? After this period of time, a message that has failed recipients
+# will be dequeued and those recipients will never receive the message.
+delivery_retry_period: 5d
+
+# These variables control the format and frequency of VERP-like delivery for
+# better bounce detection. VERP is Variable Envelope Return Path, defined
+# here:
+#
+# http://cr.yp.to/proto/verp.txt
+#
+# This involves encoding the address of the recipient as we (Mailman) know it
+# into the envelope sender address (i.e. the SMTP `MAIL FROM:' address).
+# Thus, no matter what kind of forwarding the recipient has in place, should
+# it eventually bounce, we will receive an unambiguous notice of the bouncing
+# address.
+#
+# However, we're technically only "VERP-like" because we're doing the envelope
+# sender encoding in Mailman, not in the MTA. We do require cooperation from
+# the MTA, so you must be sure your MTA can be configured for extended address
+# semantics.
+#
+# The first variable describes how to encode VERP envelopes. It must contain
+# these three string interpolations:
+#
+# $bounces -- the list-bounces mailbox will be set here
+# $mailbox -- the recipient's mailbox will be set here
+# $host -- the recipient's host name will be set here
+#
+# This example uses the default below.
+#
+# FQDN list address is: mylist@dom.ain
+# Recipient is: aperson@a.nother.dom
+#
+# The envelope sender will be mylist-bounces+aperson=a.nother.dom@dom.ain
+#
+# Note that your MTA /must/ be configured to deliver such an addressed message
+# to mylist-bounces!
+verp_delimiter: +
+verp_format: ${bounces}+${mailbox}=${host}
+
+# For nicer confirmation emails, use a VERP-like format which encodes the
+# confirmation cookie in the reply address. This lets us put a more user
+# friendly Subject: on the message, but requires cooperation from the MTA.
+# Format is like verp_format, but with the following substitutions:
+#
+# $address -- the list-confirm address
+# $cookie -- the confirmation cookie
+verp_confirm_format: $address+$cookie
+
+# This is analogous to verp_regexp, but for splitting apart the
+# verp_confirm_format. MUAs have been observed that mung
+#
+# From: local_part@host
+#
+# into
+#
+# To: "local_part" <local_part@host>
+#
+# when replying, so we skip everything up to '<' if any.
+verp_confirm_regexp: ^(.*<)?(?P<addr>[^+]+?)\+(?P<cookie>[^@]+)@.*$
+
+# Set this to 'yes' to enable VERP-like (more user friendly) confirmations.
+verp_confirmations: no
+
+# Another good opportunity is when regular delivery is personalized. Here
+# again, we're already incurring the performance hit for addressing each
+# individual recipient. Set this to 'yes' to enable VERPs on all personalized
+# regular deliveries (personalized digests aren't supported yet).
+verp_personalized_deliveries: no
+
+# And finally, we can VERP normal, non-personalized deliveries. However,
+# because it can be a significant performance hit, we allow you to decide how
+# often to VERP regular deliveries. This is the interval, in number of
+# messages, to do a VERP recipient address. The same variable controls both
+# regular and digest deliveries. Set to 0 to disable occasional VERPs, set to
+# 1 to VERP every delivery, or to some number > 1 for only occasional VERPs.
+verp_delivery_interval: 0
+
+# VERP format and regexp for probe messages.
+verp_probe_format: %(bounces)s+%(token)s
+verp_probe_regexp: ^(?P<bounces>[^+]+?)\+(?P<token>[^@]+)@.*$
+# Set this 'yes' to activate VERP probe for disabling by bounce.
+verp_probes: no
+
+# This is the maximum number of automatic responses sent to an address because
+# of -request messages or posting hold messages. This limit prevents response
+# loops between Mailman and misconfigured remote email robots. Mailman
+# already inhibits automatic replies to any message labeled with a header
+# "Precendence: bulk|list|junk". This is a fallback safety valve so it should
+# be set fairly high. Set to 0 for no limit (probably useful only for
+# debugging).
+max_autoresponses_per_day: 10
+
+# Some list posts and mail to the -owner address may contain DomainKey or
+# DomainKeys Identified Mail (DKIM) signature headers <http://www.dkim.org/>.
+# Various list transformations to the message such as adding a list header or
+# footer or scrubbing attachments or even reply-to munging can break these
+# signatures. It is generally felt that these signatures have value, even if
+# broken and even if the outgoing message is resigned. However, some sites
+# may wish to remove these headers by setting this to 'yes'.
+remove_dkim_headers: no
+
+# This variable describe the program to use for regenerating the transport map
+# db file, from the associated plain text files. The file being updated will
+# be appended to this string (with a separating space), so it must be
+# appropriate for os.system().
+postfix_map_cmd: /usr/sbin/postmap
+
+
+[bounces]
+# How often should the bounce qrunner process queued detected bounces?
+register_bounces_every: 15m
+
+
+[archiver.master]
+# To add new archivers, define a new section based on this one, overriding the
+# following values.
+
+# The class implementing the IArchiver interface.
+class: mailman.archiving.prototype.Prototype
+
+# Set this to 'yes' to enable the archiver.
+enable: no
+
+# The base url for the archiver. This is used to to calculate links to
+# individual messages in the archive.
+base_url: http://archive.example.com/
+
+# If the archiver works by getting a copy of the message, this is the address
+# to send the copy to.
+recipient: archive@archive.example.com
+
+# If the archiver works by calling a command on the local machine, this is the
+# command to call.
+command: /bin/echo
+
+
+[archiver.mhonarc]
+# This is the stock MHonArc archiver.
+class: mailman.archiving.mhonarc.MHonArc
+
+base_url: http://$hostname/archives/$fqdn_listname
+
+
+[archiver.mail_archive]
+# This is the stock mail-archive.com archiver.
+class: mailman.archiving.mailarchive.MailArchive
+
+[archiver.pipermail]
+# This is the stock Pipermail archiver.
+class: mailman.archiving.pipermail.Pipermail
+
+# This sets the default `clobber date' policy for the archiver. When a
+# message is to be archived either by Pipermail or an external archiver,
+# Mailman can modify the Date: header to be the date the message was received
+# instead of the Date: in the original message. This is useful if you
+# typically receive messages with outrageous dates. Set this to 0 to retain
+# the date of the original message, or to 1 to always clobber the date. Set
+# it to 2 to perform `smart overrides' on the date; when the date is outside
+# allowable_sane_date_skew (either too early or too late), then the received
+# date is substituted instead.
+clobber_date_policy: 2
+allowable_sane_date_skew: 15d
+
+# Pipermail archives contain the raw email addresses of the posting authors.
+# Some view this as a goldmine for spam harvesters. Set this to 'yes' to
+# moderately obscure email addresses, but note that this breaks mailto: URLs
+# in the archives too.
+obscure_email_addresses: yes
+
+# When the archive is public, should Pipermail also make the raw Unix mbox
+# file publically available?
+public_mbox: no
+
+
+[archiver.prototype]
+# This is a prototypical sample archiver.
+class: mailman.archiving.prototype.Prototype
+
+
+[style.master]
+# The style's priority, with 0 being the lowest priority.
+priority: 0
+
+# The class implementing the IStyle interface, which applies the style.
+class: mailman.styles.default.DefaultStyle
+
+
+[scrubber]
+# A filter module that converts from multipart messages to "flat" messages
+# (i.e. containing a single payload). This is required for Pipermail, and you
+# may want to set it to 0 for external archivers. You can also replace it
+# with your own module as long as it contains a process() function that takes
+# a MailList object and a Message object. It should raise
+# Errors.DiscardMessage if it wants to throw the message away. Otherwise it
+# should modify the Message object as necessary.
+archive_scrubber: mailman.pipeline.scrubber
+
+# This variable defines what happens to text/html subparts. They can be
+# stripped completely, escaped, or filtered through an external program. The
+# legal values are:
+# 0 - Strip out text/html parts completely, leaving a notice of the removal in
+# the message. If the outer part is text/html, the entire message is
+# discarded.
+# 1 - Remove any embedded text/html parts, leaving them as HTML-escaped
+# attachments which can be separately viewed. Outer text/html parts are
+# simply HTML-escaped.
+# 2 - Leave it inline, but HTML-escape it
+# 3 - Remove text/html as attachments but don't HTML-escape them. Note: this
+# is very dangerous because it essentially means anybody can send an HTML
+# email to your site containing evil JavaScript or web bugs, or other
+# nasty things, and folks viewing your archives will be susceptible. You
+# should only consider this option if you do heavy moderation of your list
+# postings.
+#
+# Note: given the current archiving code, it is not possible to leave
+# text/html parts inline and un-escaped. I wouldn't think it'd be a good idea
+# to do anyway.
+#
+# The value can also be a string, in which case it is the name of a command to
+# filter the HTML page through. The resulting output is left in an attachment
+# or as the entirety of the message when the outer part is text/html. The
+# format of the string must include a $filename substitution variable which
+# will contain the name of the temporary file that the program should operate
+# on. It should write the processed message to stdout. Set this to
+# HTML_TO_PLAIN_TEXT_COMMAND to specify an HTML to plain text conversion
+# program.
+archive_html_sanitizer: 1
+
+# Control parameter whether the scrubber should use the message attachment's
+# filename as is indicated by the filename parameter or use 'attachement-xxx'
+# instead. The default is set 'no' because the applications on PC and Mac
+# begin to use longer non-ascii filenames.
+use_attachment_filename: no
+
+# Use of attachment filename extension per se is may be dangerous because
+# viruses fakes it. You can set this 'yes' if you filter the attachment by
+# filename extension.
+use_attachment_filename_extension: no
+
+
+[digests]
+# Headers which should be kept in both RFC 1153 (plain) and MIME digests. RFC
+# 1153 also specifies these headers in this exact order, so order matters.
+# These are space separated and case insensitive.
+mime_digest_keep_headers:
+ Date From To Cc Subject Message-ID Keywords
+ In-Reply-To References Content-Type MIME-Version
+ Content-Transfer-Encoding Precedence Reply-To
+ Message
+
+plain_digest_keep_headers:
+ Message Date From
+ Subject To Cc
+ Message-ID Keywords
+ Content-Type
+
+
+[nntp]
+# Set these variables if you need to authenticate to your NNTP server for
+# Usenet posting or reading. If no authentication is necessary, specify None
+# for both variables.
+username:
+password:
+
+# Set this if you have an NNTP server you prefer gatewayed lists to use.
+host:
+
+# This controls how headers must be cleansed in order to be accepted by your
+# NNTP server. Some servers like INN reject messages containing prohibited
+# headers, or duplicate headers. The NNTP server may reject the message for
+# other reasons, but there's little that can be programmatically done about
+# that.
+#
+# These headers (case ignored) are removed from the original message. This is
+# a whitespace separate list of headers.
+remove_headers:
+ nntp-posting-host nntp-posting-date x-trace
+ x-complaints-to xref date-received posted
+ posting-version relay-version received
+
+# These headers are left alone, unless there are duplicates in the original
+# message. Any second and subsequent headers are rewritten to the second
+# named header (case preserved). This is a list of header pairs, one pair per
+# line.
+rewrite_duplicate_headers:
+ To X-Original-To
+ CC X-Original-CC
+ Content-Transfer-Encoding X-Original-Content-Transfer-Encoding
+ MIME-Version X-MIME-Version