summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/bin/genaliases.py63
-rw-r--r--src/mailman/commands/cli_aliases.py64
-rw-r--r--src/mailman/commands/docs/aliases.txt62
-rw-r--r--src/mailman/interfaces/mta.py9
-rw-r--r--src/mailman/mta/null.py2
-rw-r--r--src/mailman/mta/postfix.py93
-rw-r--r--src/mailman/testing/mta.py2
7 files changed, 188 insertions, 107 deletions
diff --git a/src/mailman/bin/genaliases.py b/src/mailman/bin/genaliases.py
deleted file mode 100644
index d09dc2a22..000000000
--- a/src/mailman/bin/genaliases.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright (C) 2001-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/>.
-
-__metaclass__ = type
-__all__ = [
- 'main',
- ]
-
-
-import sys
-
-from mailman.config import config
-from mailman.core.i18n import _
-from mailman.options import Options
-from mailman.utilities.modules import call_name
-
-
-
-class ScriptOptions(Options):
- """Options for the genaliases script."""
-
- usage = _("""\
-%prog [options]
-
-Regenerate the Mailman specific MTA aliases from scratch. The actual output
-depends on the value of the 'MTA' variable in your etc/mailman.cfg file.""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '-q', '--quiet',
- default=False, action='store_true', help=_("""\
-Some MTA output can include more verbose help text. Use this to tone down the
-verbosity."""))
-
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- # Get the MTA-specific module.
- call_name(config.mta.incoming).regenerate()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/src/mailman/commands/cli_aliases.py b/src/mailman/commands/cli_aliases.py
new file mode 100644
index 000000000..088efc1bf
--- /dev/null
+++ b/src/mailman/commands/cli_aliases.py
@@ -0,0 +1,64 @@
+# Copyright (C) 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/>.
+
+"""Generate Mailman alias files for your MTA."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'Aliases',
+ ]
+
+
+import sys
+
+from zope.interface import implements
+
+from mailman.config import config
+from mailman.core.i18n import _
+from mailman.interfaces.command import ICLISubCommand
+from mailman.utilities.modules import call_name
+
+
+
+class Aliases:
+ """Regenerate the aliases appropriate for your MTA."""
+
+ implements(ICLISubCommand)
+
+ name = 'aliases'
+
+ def add(self, parser, command_parser):
+ """See `ICLISubCommand`."""
+ command_parser.add_argument(
+ '-o', '--output',
+ action='store', help=_("""\
+ File to send the output to. If not given, a file in $VAR/data is
+ used. The argument can be '-' to use standard output.."""))
+
+ def process(self, args):
+ """See `ICLISubCommand`."""
+ output = None
+ if args.output == '-':
+ output = sys.stdout
+ elif args.output is None:
+ output = None
+ else:
+ output = args.output
+ # Call the MTA-specific regeneration method.
+ call_name(config.mta.incoming).regenerate(output)
diff --git a/src/mailman/commands/docs/aliases.txt b/src/mailman/commands/docs/aliases.txt
new file mode 100644
index 000000000..252fc9979
--- /dev/null
+++ b/src/mailman/commands/docs/aliases.txt
@@ -0,0 +1,62 @@
+==================
+Generating aliases
+==================
+
+For some mail servers, Mailman must generate a data file that is used to hook
+Mailman up to the mail server. The details of this differ for each mail
+server. Generally these files are automatically kept up-to-date when mailing
+lists are created or removed, but you might occasionally need to manually
+regenerate the file. The 'bin/mailman aliases' command does this.
+
+ >>> class FakeArgs:
+ ... output = None
+
+ >>> from mailman.commands.cli_aliases import Aliases
+ >>> command = Aliases()
+
+For example, connecting Mailman to Postfix is generally done through the LMTP
+protocol. Mailman starts an LMTP server and Postfix delivers messages to
+Mailman as an LMTP client. By default this is done through Postfix transport
+maps.
+
+Selecting Postfix as the source of incoming messages enables transport map
+generation.
+
+ >>> config.push('postfix', """
+ ... [mta]
+ ... incoming: mailman.mta.postfix.LMTP
+ ... lmtp_host: lmtp.example.com
+ ... lmtp_port: 24
+ ... """)
+
+Let's create a mailing list and then display the transport map for it. We'll
+send the output to stdout.
+
+ >>> FakeArgs.output = '-'
+ >>> mlist = create_list('test@example.com')
+ >>> command.process(FakeArgs)
+ # AUTOMATICALLY GENERATED BY MAILMAN ON ...
+ #
+ # This file is generated by Mailman, and is kept in sync with the ...
+ # file. YOU SHOULD NOT MANUALLY EDIT THIS FILE unless you know what you're
+ # doing, and can keep the two files properly in sync. If you screw it up,
+ # you're on your own.
+ <BLANKLINE>
+ # Aliases which are visible only in the @example.com domain.
+ <BLANKLINE>
+ test@example.com lmtp:lmtp.example.com:24
+ test-bounces@example.com lmtp:lmtp.example.com:24
+ test-confirm@example.com lmtp:lmtp.example.com:24
+ test-join@example.com lmtp:lmtp.example.com:24
+ test-leave@example.com lmtp:lmtp.example.com:24
+ test-owner@example.com lmtp:lmtp.example.com:24
+ test-request@example.com lmtp:lmtp.example.com:24
+ test-subscribe@example.com lmtp:lmtp.example.com:24
+ test-unsubscribe@example.com lmtp:lmtp.example.com:24
+ <BLANKLINE>
+
+
+Clean up
+========
+
+ >>> config.pop('postfix')
diff --git a/src/mailman/interfaces/mta.py b/src/mailman/interfaces/mta.py
index 65696a10d..0186fa679 100644
--- a/src/mailman/interfaces/mta.py
+++ b/src/mailman/interfaces/mta.py
@@ -50,8 +50,13 @@ class IMailTransportAgentAliases(Interface):
def delete(mlist):
"""Tell the MTA that the mailing list was deleted."""
- def regenerate():
- """Regenerate the full aliases file."""
+ def regenerate(output=None):
+ """Regenerate the full aliases file.
+
+ :param output: The file name or file object to send the output to. If
+ not given or None, and MTA specific file is used.
+ :type output: string, file object, None
+ """
diff --git a/src/mailman/mta/null.py b/src/mailman/mta/null.py
index 5670d7c7f..a9c055284 100644
--- a/src/mailman/mta/null.py
+++ b/src/mailman/mta/null.py
@@ -46,6 +46,6 @@ class NullMTA:
"""See `IMailTransportAgentAliases`."""
pass
- def regenerate(self):
+ def regenerate(self, output=None):
"""See `IMailTransportAgentAliases`."""
pass
diff --git a/src/mailman/mta/postfix.py b/src/mailman/mta/postfix.py
index ca327dd1c..56ede5b45 100644
--- a/src/mailman/mta/postfix.py
+++ b/src/mailman/mta/postfix.py
@@ -60,32 +60,56 @@ class LMTP:
def create(self, mlist):
"""See `IMailTransportAgentAliases`."""
- # Acquire a lock file to prevent other processes from racing us here.
- with Lock(LOCKFILE):
- # We can ignore the mlist argument because for LMTP delivery, we
- # just generate the entire file every time.
- self._do_write_file()
+ # We can ignore the mlist argument because for LMTP delivery, we just
+ # generate the entire file every time.
+ self.regenerate()
delete = create
- def regenerate(self):
+ def regenerate(self, output=None):
"""See `IMailTransportAgentAliases`."""
# Acquire a lock file to prevent other processes from racing us here.
with Lock(LOCKFILE):
- self._do_write_file()
+ # If output is a filename, open up a backing file and write the
+ # output there, then do the atomic rename dance. First though, if
+ # it's None, we use a calculated path.
+ if output is None:
+ path = os.path.join(config.DATA_DIR, 'postfix_lmtp')
+ path_new = path + '.new'
+ elif isinstance(output, basestring):
+ path = output
+ path_new = output + '.new'
+ else:
+ path = path_new = None
+ if path_new is None:
+ self._do_write_file(output)
+ # There's nothing to rename, and we can't generate the .db
+ # file, so we're done.
+ return
+ # Write the file.
+ with open(path_new, 'w') as fp:
+ self._do_write_file(fp)
+ # Atomically rename to the intended path.
+ os.rename(path + '.new', path)
+ # Now that the new file is in place, we must tell Postfix to
+ # generate a new .db file.
+ command = config.mta.postfix_map_cmd + ' ' + path
+ status = (os.system(command) >> 8) & 0xff
+ if status:
+ msg = 'command failure: %s, %s, %s'
+ errstr = os.strerror(status)
+ log.error(msg, command, status, errstr)
+ raise RuntimeError(msg % (command, status, errstr))
- def _do_write_file(self):
+ def _do_write_file(self, fp):
"""Do the actual file writes for list creation."""
- # Open up the new alias text file.
- path = os.path.join(config.DATA_DIR, 'postfix_lmtp')
# Sort all existing mailing list names first by domain, then my local
# part. For postfix we need a dummy entry for the domain.
by_domain = {}
for mailing_list in getUtility(IListManager).mailing_lists:
by_domain.setdefault(mailing_list.host_name, []).append(
mailing_list.list_name)
- with open(path + '.new', 'w') as fp:
- print >> fp, """\
+ print >> fp, """\
# AUTOMATICALLY GENERATED BY MAILMAN ON {0}
#
# This file is generated by Mailman, and is kept in sync with the binary hash
@@ -93,33 +117,22 @@ class LMTP:
# doing, and can keep the two files properly in sync. If you screw it up,
# you're on your own.
""".format(datetime.datetime.now().replace(microsecond=0))
- for domain in sorted(by_domain):
- print >> fp, """\
+ for domain in sorted(by_domain):
+ print >> fp, """\
# Aliases which are visible only in the @{0} domain.
""".format(domain)
- for list_name in by_domain[domain]:
- # Calculate the field width of the longest alias. 10 ==
- # len('-subscribe') + '@'.
- longest = len(list_name + domain) + 10
- print >> fp, """\
-{0}@{1:{3}}lmtp:inet:{2.mta.lmtp_host}:{2.mta.lmtp_port}""".format(
- list_name, domain, config,
- # Add 1 because the bare list name has no dash.
- longest + 1)
- for destination in SUBDESTINATIONS:
- print >> fp, """\
-{0}-{1}@{2:{4}}lmtp:inet:{3.mta.lmtp_host}:{3.mta.lmtp_port}""".format(
- list_name, destination, domain, config,
- longest - len(destination))
- print >> fp
- # Move the temporary file into place, then generate the new .db file.
- os.rename(path + '.new', path)
- # Now that the new aliases file has been written, we must tell Postfix
- # to generate a new .db file.
- command = config.mta.postfix_map_cmd + ' ' + path
- status = (os.system(command) >> 8) & 0xff
- if status:
- msg = 'command failure: %s, %s, %s'
- errstr = os.strerror(status)
- log.error(msg, command, status, errstr)
- raise RuntimeError(msg % (command, status, errstr))
+ for list_name in by_domain[domain]:
+ # Calculate the field width of the longest alias. 10 ==
+ # len('-subscribe') + '@'.
+ longest = len(list_name + domain) + 10
+ print >> fp, """\
+{0}@{1:{3}}lmtp:{2.mta.lmtp_host}:{2.mta.lmtp_port}""".format(
+ list_name, domain, config,
+ # Add 1 because the bare list name has no dash.
+ longest + 1)
+ for destination in SUBDESTINATIONS:
+ print >> fp, """\
+{0}-{1}@{2:{4}}lmtp:{3.mta.lmtp_host}:{3.mta.lmtp_port}""".format(
+ list_name, destination, domain, config,
+ longest - len(destination))
+ print >> fp
diff --git a/src/mailman/testing/mta.py b/src/mailman/testing/mta.py
index d16b9f955..6887f74a4 100644
--- a/src/mailman/testing/mta.py
+++ b/src/mailman/testing/mta.py
@@ -51,7 +51,7 @@ class FakeMTA:
def delete(self, mlist):
pass
- def regenerate(self):
+ def regenerate(self, output=None):
pass