diff options
| -rw-r--r-- | src/mailman/commands/cli_inject.py | 2 | ||||
| -rw-r--r-- | src/mailman/commands/cli_withlist.py | 187 | ||||
| -rw-r--r-- | src/mailman/mta/base.py | 1 | ||||
| -rw-r--r-- | src/mailman/mta/deliver.py | 36 | ||||
| -rw-r--r-- | src/mailman/mta/verp.py | 1 | ||||
| -rw-r--r-- | src/mailman/pipeline/to_outgoing.py | 24 |
6 files changed, 226 insertions, 25 deletions
diff --git a/src/mailman/commands/cli_inject.py b/src/mailman/commands/cli_inject.py index 8b7570e0a..cb5ef51bd 100644 --- a/src/mailman/commands/cli_inject.py +++ b/src/mailman/commands/cli_inject.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Module stuff.""" +"""bin/mailman inject""" from __future__ import absolute_import, unicode_literals diff --git a/src/mailman/commands/cli_withlist.py b/src/mailman/commands/cli_withlist.py new file mode 100644 index 000000000..e13b0970a --- /dev/null +++ b/src/mailman/commands/cli_withlist.py @@ -0,0 +1,187 @@ +# 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/>. + +"""bin/mailman withlist""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'Withlist', + ] + + +import sys + +from zope.component import getUtility +from zope.interface import implements + +from mailman.config import config +from mailman.core.i18n import _ +from mailman.interact import DEFAULT_BANNER, interact +from mailman.interfaces.command import ICLISubCommand +from mailman.interfaces.listmanager import IListManager +from mailman.utilities.modules import call_name + +# Global holding onto the open mailing list. +m = None +# Global holding the results of --run. +r = None + + + +class Withlist: + """Operate on a mailing list. + + For detailed help, see --details + """ + + implements(ICLISubCommand) + + name = 'withlist' + + def add(self, parser, command_parser): + """See `ICLISubCommand`.""" + self.parser = parser + command_parser.add_argument( + '-i', '--interactive', + default=None, action='store_true', help=_("""\ + Leaves you at an interactive prompt after all other processing is + complete. This is the default unless the --run option is + given.""")) + command_parser.add_argument( + '-r', '--run', + help=_("""\ + Run a script on a mailing list. The argument is the module path + to a callable. This callable will be imported and then called + with the mailing list as the first argument. If additional + arguments are given at the end of the command line, they are + passed as subsequent positional arguments to the callable. For + additional help, see --details. + """)) + command_parser.add_argument( + '--details', + default=False, action='store_true', + help=_('Print detailed instructions on using this command.')) + # Optional positional argument. + command_parser.add_argument( + 'listname', metavar='LISTNAME', nargs='?', + help=_("""\ + The 'fully qualified list name', i.e. the posting address of the + mailing list to inject the message into.""")) + + def process(self, args): + """See `ICLISubCommand`.""" + global m, r + banner = DEFAULT_BANNER + # Detailed help wanted? + if args.details: + self._details() + sys.exit(0) + # Interactive is the default unless --run was given. + if args.interactive is None: + interactive = (args.run is None) + else: + interactive = args.interactive + # If a listname was given, open it. + if args.listname is not None: + fqdn_listname = args.listname + mlist = getUtility(IListManager).get(fqdn_listname) + if mlist is None: + self.parser.error(_('No such list: $fqdn_listname')) + return + m = mlist + banner = _("The variable 'm' is the $fqdn_listname mailing list") + # Handle --run + if args.run: + # When the module and the callable have the same name, a shorthand + # without the dot is allowed. + dotted_name = (args.run if '.' in args.run + else '{0}.{0}'.format(args.run)) + r = call_name(dotted_name, m) + # All other processing is finished; maybe go into interactive mode. + if interactive: + overrides = dict( + m=m, + commit=config.db.commit, + abort=config.db.abort, + config=config, + ) + interact(upframe=False, banner=banner, overrides=overrides) + + def _details(self): + """Print detailed usage.""" + # Split this up into paragraphs for easier translation. + print _("""\ +This script provides you with a general framework for interacting with a +mailing list.""") + print + print _("""\ +There are two ways to use this script: interactively or programmatically. +Using it interactively allows you to play with, examine and modify a mailing +list from Python's interactive interpreter. When running interactively, the +variable 'm' will be available in the global namespace. It will reference the +mailing list object.""") + print + print _("""\ +Programmatically, you can write a function to operate on a mailing list, and +this script will take care of the housekeeping (see below for examples). In +that case, the general usage syntax is: + + % bin/mailman withlist [options] listname [args ...]""") + print + print _("""\ +Here's an example of how to use the --run option. Say you have a file in the +Mailman installation directory called 'listaddr.py', with the following two +functions: + + def listaddr(mlist): + print mlist.posting_address + + def requestaddr(mlist): + print mlist.request_address""") + print + print _("""\ +You can print the list's posting address by running the following from the +command line: + + % bin/mailman withlist -r listaddr mylist@example.com + Importing listaddr ... + Running listaddr.listaddr() ... + mylist@example.com""") + print + print _("""\ +And you can print the list's request address by running: + + % bin/mailman withlist -r listaddr.requestaddr mylist + Importing listaddr ... + Running listaddr.requestaddr() ... + mylist-request@example.com""") + print + print _("""\ +As another example, say you wanted to change the display name for a particular +mailing list. You could put the following function in a file called +'change.pw': + + def change(mlist, real_name): + mlist.real_name = real_name + # Required to save changes to the database. + commit() + +and run this from the command line: + + % bin/mailman withlist -r change mylist@example.com 'My List'""") diff --git a/src/mailman/mta/base.py b/src/mailman/mta/base.py index ff56094ac..38ed0d836 100644 --- a/src/mailman/mta/base.py +++ b/src/mailman/mta/base.py @@ -159,6 +159,7 @@ class IndividualDelivery(BaseDelivery): refused = {} recipients = msgdata.get('recipients', set()) for recipient in recipients: + log.debug('IndividualDelivery to: %s', recipient) # Make a copy of the original messages and operator on it, since # we're going to munge it repeatedly for each recipient. message_copy = copy.deepcopy(msg) diff --git a/src/mailman/mta/deliver.py b/src/mailman/mta/deliver.py index 696770894..85bb09991 100644 --- a/src/mailman/mta/deliver.py +++ b/src/mailman/mta/deliver.py @@ -28,6 +28,8 @@ __all__ = [ import time import logging +from lazr.config import as_boolean + from mailman.config import config from mailman.interfaces.mailinglist import Personalization from mailman.interfaces.mta import SomeRecipientsFailed @@ -73,6 +75,9 @@ def deliver(mlist, msg, msgdata): if not recipients: # Could be None, could be an empty sequence. return + # Calculate whether we should VERP this message or not. The results of + # this set the 'verp' key in the message metadata. + _calculate_verp(mlist, msg, msgdata) # Which delivery agent should we use? Several situations can cause us to # use individual delivery. If not specified, use bulk delivery. See the # to-outgoing handler for when the 'verp' key is set in the metadata. @@ -82,6 +87,7 @@ def deliver(mlist, msg, msgdata): agent = Deliver() else: agent = BulkDelivery(int(config.mta.max_recipients)) + log.debug('Using agent: %s', agent) # Keep track of the original recipients and the original sender for # logging purposes. original_recipients = msgdata['recipients'] @@ -152,3 +158,33 @@ def deliver(mlist, msg, msgdata): # Return the results if temporary_failures or permanent_failures: raise SomeRecipientsFailed(temporary_failures, permanent_failures) + + + +def _calculate_verp(mlist, msg, msgdata): + """Calculate whether this message should be VERP'd or not. + + This function works by side-effect. If the message should be VERP'd, then + the 'verp' key in msgdata is set to True, otherwise it is set to False. + """ + if 'verp' in msgdata: + # Honor existing settings. + return + # If personalization is enabled for this list and we've configured Mailman + # to always VERP personalized deliveries, then yes we VERP it. Also, if + # personalization is /not/ enabled, but verp_delivery_interval is set (and + # we've hit this interval), then again, this message should be + # VERP'd. Otherwise, no. + interval = int(config.mta.verp_delivery_interval) + if mlist.personalize <> Personalization.none: + if as_boolean(config.mta.verp_personalized_deliveries): + msgdata['verp'] = True + elif interval == 0: + # Never VERP. + msgdata['verp'] = False + elif interval == 1: + # VERP every time. + msgdata['verp'] = True + else: + # VERP every 'interval' number of times. + msgdata['verp'] = (int(mlist.post_id) % interval == 0) diff --git a/src/mailman/mta/verp.py b/src/mailman/mta/verp.py index c53276bdc..269eca4dc 100644 --- a/src/mailman/mta/verp.py +++ b/src/mailman/mta/verp.py @@ -58,6 +58,7 @@ class VERPMixin: """ sender = super(VERPMixin, self)._get_sender(mlist, msg, msgdata) if msgdata.get('verp', False): + log.debug('VERPing %s', msg.get('message-id')) recipient = msgdata['recipient'] sender_mailbox, sender_domain = split_email(sender) # Encode the recipient's address for VERP. diff --git a/src/mailman/pipeline/to_outgoing.py b/src/mailman/pipeline/to_outgoing.py index d9894238b..935d099c5 100644 --- a/src/mailman/pipeline/to_outgoing.py +++ b/src/mailman/pipeline/to_outgoing.py @@ -50,29 +50,5 @@ class ToOutgoing: def process(self, mlist, msg, msgdata): """See `IHandler`.""" - interval = int(config.mta.verp_delivery_interval) - # Should we VERP this message? If personalization is enabled for this - # list and VERP_PERSONALIZED_DELIVERIES is true, then yes we VERP it. - # Also, if personalization is /not/ enabled, but - # VERP_DELIVERY_INTERVAL is set (and we've hit this interval), then - # again, this message should be VERPed. Otherwise, no. - # - # Note that the verp flag may already be set, e.g. by mailpasswds - # using VERP_PASSWORD_REMINDERS. Preserve any existing verp flag. - if 'verp' in msgdata: - pass - elif mlist.personalize <> Personalization.none: - if as_boolean(config.mta.verp_personalized_deliveries): - msgdata['verp'] = True - elif interval == 0: - # Never VERP - pass - elif interval == 1: - # VERP every time - msgdata['verp'] = True - else: - # VERP every `interval' number of times - msgdata['verp'] = (int(mlist.post_id) % interval == 0) - # And now drop the message in qfiles/out config.switchboards['out'].enqueue( msg, msgdata, listname=mlist.fqdn_listname) |
