summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbwarsaw2002-05-02 03:03:14 +0000
committerbwarsaw2002-05-02 03:03:14 +0000
commit386d9559901196aa6e107daa4eadc469525f80a4 (patch)
treee3e76e5eb6e4d13f82828c7de217f1897f662a94
parentbfe15e9954a8df780ce6d27f3c953649f9d35756 (diff)
downloadmailman-386d9559901196aa6e107daa4eadc469525f80a4.tar.gz
mailman-386d9559901196aa6e107daa4eadc469525f80a4.tar.zst
mailman-386d9559901196aa6e107daa4eadc469525f80a4.zip
Support the new email commands framework. We now no longer use
MailCommandHandler.py. Specific changes include: class Results: Basically a bag to hold state during the processing of a particular email message loaded with commands. This makes it easy to pass data around (like the mailing list object and the message). It also contains the results of the command processing and gloms up the response email. _dispose(): This method actually gets simplified because it just needs to pass things to the Results instance and let it do the job of processing the email commands.
-rw-r--r--Mailman/Queue/CommandRunner.py149
1 files changed, 115 insertions, 34 deletions
diff --git a/Mailman/Queue/CommandRunner.py b/Mailman/Queue/CommandRunner.py
index 759ebd101..3310b1cd4 100644
--- a/Mailman/Queue/CommandRunner.py
+++ b/Mailman/Queue/CommandRunner.py
@@ -26,23 +26,127 @@
# BAW: get rid of this when we Python 2.2 is a minimum requirement.
from __future__ import nested_scopes
+import sys
import re
+from types import StringType, UnicodeType
from Mailman import mm_cfg
-from Mailman.Bouncers import BouncerAPI
-from Mailman.Handlers import SpamDetect
-
+from Mailman import Utils
+from Mailman import Message
+from Mailman.i18n import _
from Mailman.Queue.Runner import Runner
-from Mailman.Queue.sbcache import get_switchboard
from Mailman.Logging.Syslog import syslog
from Mailman import LockFile
+from email.MIMEText import MIMEText
+from email.MIMEMessage import MIMEMessage
+from email.Iterators import typed_subpart_iterator
+
+NL = '\n'
+
+
+
+class Results:
+ def __init__(self, mlist, msg, msgdata):
+ self.mlist = mlist
+ self.msg = msg
+ self.msgdata = msgdata
+ # Only set returnaddr if the response is to go to someone other than
+ # the address specified in the From: header (e.g. for the password
+ # command).
+ self.returnaddr = None
+ self.commands = []
+ self.results = []
+ self.ignored = []
+ self.lineno = 0
+ # Always process the Subject: header first
+ self.commands.append(msg['subject'])
+ # Find the first text/plain part
+ part = None
+ for part in typed_subpart_iterator(msg, 'text', 'plain'):
+ break
+ if part is None or part is not msg:
+ # Either there was no text/plain part or we ignored some
+ # non-text/plain parts.
+ self.results.append(_('Ignoring non-text/plain MIME parts'))
+ body = part.get_payload()
+ # text/plain parts better have string payloads
+ assert isinstance(body, StringType) or isinstance(body, UnicodeType)
+ lines = body.splitlines()
+ # Use no more lines than specified
+ self.commands.extend(lines[:mm_cfg.DEFAULT_MAIL_COMMANDS_MAX_LINES])
+ self.ignored.extend(lines[mm_cfg.DEFAULT_MAIL_COMMANDS_MAX_LINES:])
+
+ def process(self):
+ # Now, process each line until we find an error. The first
+ # non-command line found stops processing.
+ stop = 0
+ for line in self.commands:
+ if line and line.strip():
+ args = line.split()
+ cmd = args.pop(0).lower()
+ stop = self.do_command(cmd, args)
+ self.lineno += 1
+ if stop:
+ break
+
+ def do_command(self, cmd, args=None):
+ if args is None:
+ args = ()
+ # Try to import a command handler module for this command
+ modname = 'Mailman.Commands.cmd_' + cmd
+ try:
+ __import__(modname)
+ handler = sys.modules[modname]
+ except ImportError:
+ # If we're on line zero, it was the Subject: header that
+ # didn't contain a command. This isn't enough to stop
+ # processing. BAW: should we include a message that the
+ # Subject: was ignored?
+ return self.lineno <> 0
+ return handler.process(self, args)
+
+ def make_response(self):
+ def indent(lines):
+ return [' ' + line for line in lines]
+
+ resp = [Utils.wrap(_("""\
+The results of your email command are provided below.
+Attached is your original message.
+"""))]
+ if self.results:
+ resp.append(_('- Results:'))
+ resp.extend(indent(self.results))
+ # Ignore empty lines
+ unprocessed = [line for line in self.commands[self.lineno:]
+ if line.strip()]
+ if unprocessed:
+ resp.append(_('\n- Unprocessed:'))
+ resp.extend(indent(unprocessed))
+ if self.ignored:
+ resp.append(_('\n- Ignored:'))
+ resp.extend(indent(self.ignored))
+ resp.append(_('\n- Done.\n\n'))
+ results = MIMEText(
+ NL.join(resp),
+ _charset=Utils.GetCharSet(self.mlist.preferred_language))
+ msg = Message.UserNotification(
+ self.returnaddr or self.msg.get_sender(),
+ self.mlist.GetBouncesEmail(),
+ _('The results of your email commands'))
+ msg.set_type('multipart/mixed')
+ msg.attach(results)
+ orig = MIMEMessage(self.msg)
+ msg.attach(orig)
+ return msg
+
class CommandRunner(Runner):
QDIR = mm_cfg.CMDQUEUE_DIR
def _dispose(self, mlist, msg, msgdata):
+ res = Results(mlist, msg, msgdata)
# BAW: Not all the functions of this qrunner require the list to be
# locked. Still, it's more convenient to lock it here and now and
# deal with lock failures in one place.
@@ -51,45 +155,22 @@ class CommandRunner(Runner):
except LockFile.TimeOutError:
# Oh well, try again later
return 1
- # runner specific code
- #
# This message will have been delivered to one of mylist-request,
# mylist-join, or mylist-leave, and the message metadata will contain
- # a key to which one was used. BAW: The tojoin and toleave actions
- # are hacks!
- def parsecmd():
- try:
- mlist.ParseMailCommands(msg, msgdata)
- except LockFile.TimeOutError:
- # We probably could not get the lock on the pending
- # database. That's okay, we'll just try again later.
- return 1
- return 0
+ # a key to which one was used.
try:
- status = 0
if msgdata.get('torequest'):
- # Just pass the message off the command handler
- status = parsecmd()
+ res.process()
elif msgdata.get('tojoin'):
- del msg['subject']
- msg['Subject'] = 'join'
- msg.set_payload('')
- status = parsecmd()
+ res.do_command('join')
elif msgdata.get('toleave'):
- del msg['subject']
- msg['Subject'] = 'leave'
- msg.set_payload('')
- status = parsecmd()
+ res.do_command('leave')
elif msgdata.get('toconfirm'):
mo = re.match(mm_cfg.VERP_CONFIRM_REGEXP, msg.get('to', ''))
if mo:
- # BAW: blech, this should not hack the Subject: header,
- # but this is quick and dirty until we rewrite email
- # command handling.
- del msg['subject']
- msg['Subject'] = 'confirm ' + mo.group('cookie')
- status = parsecmd()
+ res.do_command('confirm', (mo.group('cookie'),))
+ msg = res.make_response()
+ msg.send(mlist)
mlist.Save()
- return status
finally:
mlist.Unlock()