diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/bin/genaliases.py | 63 | ||||
| -rw-r--r-- | src/mailman/commands/cli_aliases.py | 64 | ||||
| -rw-r--r-- | src/mailman/commands/docs/aliases.txt | 62 | ||||
| -rw-r--r-- | src/mailman/interfaces/mta.py | 9 | ||||
| -rw-r--r-- | src/mailman/mta/null.py | 2 | ||||
| -rw-r--r-- | src/mailman/mta/postfix.py | 93 | ||||
| -rw-r--r-- | src/mailman/testing/mta.py | 2 |
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 |
