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
-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()