summaryrefslogtreecommitdiff
path: root/Mailman/queue/incoming.py
diff options
context:
space:
mode:
authorBarry Warsaw2007-09-29 14:55:25 -0400
committerBarry Warsaw2007-09-29 14:55:25 -0400
commit3e9ed398b6a05c69daca14c8226ca7f57c164c21 (patch)
treeb0ae2e9771a80f9e0e6218871bbe2d281120202c /Mailman/queue/incoming.py
parentcbf2967239163e42cc2b25eece7bb5cb71b197fe (diff)
downloadmailman-3e9ed398b6a05c69daca14c8226ca7f57c164c21.tar.gz
mailman-3e9ed398b6a05c69daca14c8226ca7f57c164c21.tar.zst
mailman-3e9ed398b6a05c69daca14c8226ca7f57c164c21.zip
Diffstat (limited to 'Mailman/queue/incoming.py')
-rw-r--r--Mailman/queue/incoming.py183
1 files changed, 183 insertions, 0 deletions
diff --git a/Mailman/queue/incoming.py b/Mailman/queue/incoming.py
new file mode 100644
index 000000000..05ab924e6
--- /dev/null
+++ b/Mailman/queue/incoming.py
@@ -0,0 +1,183 @@
+# Copyright (C) 1998-2007 by the Free Software Foundation, Inc.
+#
+# This program 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 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+"""Incoming queue runner."""
+
+# A typical Mailman list exposes nine aliases which point to seven different
+# wrapped scripts. E.g. for a list named `mylist', you'd have:
+#
+# mylist-bounces -> bounces (-admin is a deprecated alias)
+# mylist-confirm -> confirm
+# mylist-join -> join (-subscribe is an alias)
+# mylist-leave -> leave (-unsubscribe is an alias)
+# mylist-owner -> owner
+# mylist -> post
+# mylist-request -> request
+#
+# -request, -join, and -leave are a robot addresses; their sole purpose is to
+# process emailed commands in a Majordomo-like fashion (although the latter
+# two are hardcoded to subscription and unsubscription requests). -bounces is
+# the automated bounce processor, and all messages to list members have their
+# return address set to -bounces. If the bounce processor fails to extract a
+# bouncing member address, it can optionally forward the message on to the
+# list owners.
+#
+# -owner is for reaching a human operator with minimal list interaction
+# (i.e. no bounce processing). -confirm is another robot address which
+# processes replies to VERP-like confirmation notices.
+#
+# So delivery flow of messages look like this:
+#
+# joerandom ---> mylist ---> list members
+# | |
+# | |[bounces]
+# | mylist-bounces <---+ <-------------------------------+
+# | | |
+# | +--->[internal bounce processing] |
+# | ^ | |
+# | | | [bounce found] |
+# | [bounces *] +--->[register and discard] |
+# | | | | |
+# | | | |[*] |
+# | [list owners] |[no bounce found] | |
+# | ^ | | |
+# | | | | |
+# +-------> mylist-owner <--------+ | |
+# | | |
+# | data/owner-bounces.mbox <--[site list] <---+ |
+# | |
+# +-------> mylist-join--+ |
+# | | |
+# +------> mylist-leave--+ |
+# | | |
+# | v |
+# +-------> mylist-request |
+# | | |
+# | +---> [command processor] |
+# | | |
+# +-----> mylist-confirm ----> +---> joerandom |
+# | |
+# |[bounces] |
+# +----------------------+
+#
+# A person can send an email to the list address (for posting), the -owner
+# address (to reach the human operator), or the -confirm, -join, -leave, and
+# -request mailbots. Message to the list address are then forwarded on to the
+# list membership, with bounces directed to the -bounces address.
+#
+# [*] Messages sent to the -owner address are forwarded on to the list
+# owner/moderators. All -owner destined messages have their bounces directed
+# to the site list -bounces address, regardless of whether a human sent the
+# message or the message was crafted internally. The intention here is that
+# the site owners want to be notified when one of their list owners' addresses
+# starts bouncing (yes, the will be automated in a future release).
+#
+# Any messages to site owners has their bounces directed to a special
+# "loop-killer" address, which just dumps the message into
+# data/owners-bounces.mbox.
+#
+# Finally, message to any of the mailbots causes the requested action to be
+# performed. Results notifications are sent to the author of the message,
+# which all bounces pointing back to the -bounces address.
+
+
+
+import os
+import sys
+import logging
+
+from cStringIO import StringIO
+
+from Mailman import Errors
+from Mailman import LockFile
+from Mailman.configuration import config
+from Mailman.queue import Runner
+
+log = logging.getLogger('mailman.error')
+vlog = logging.getLogger('mailman.vette')
+
+
+
+class IncomingRunner(Runner):
+ QDIR = config.INQUEUE_DIR
+
+ def _dispose(self, mlist, msg, msgdata):
+ if msgdata.get('envsender') is None:
+ msg['envsender'] = mlist.no_reply_address
+ # Try to get the list lock.
+ try:
+ mlist.Lock(timeout=config.LIST_LOCK_TIMEOUT)
+ except LockFile.TimeOutError:
+ # Oh well, try again later
+ return 1
+ # Process the message through a handler pipeline. The handler
+ # pipeline can actually come from one of three places: the message
+ # metadata, the mlist, or the global pipeline.
+ #
+ # If a message was requeued due to an uncaught exception, its metadata
+ # will contain the retry pipeline. Use this above all else.
+ # Otherwise, if the mlist has a `pipeline' attribute, it should be
+ # used. Final fallback is the global pipeline.
+ try:
+ pipeline = self._get_pipeline(mlist, msg, msgdata)
+ msgdata['pipeline'] = pipeline
+ more = self._dopipeline(mlist, msg, msgdata, pipeline)
+ if not more:
+ del msgdata['pipeline']
+ mlist.Save()
+ return more
+ finally:
+ mlist.Unlock()
+
+ # Overridable
+ def _get_pipeline(self, mlist, msg, msgdata):
+ # We must return a copy of the list, otherwise, the first message that
+ # flows through the pipeline will empty it out!
+ return msgdata.get('pipeline',
+ getattr(mlist, 'pipeline',
+ config.GLOBAL_PIPELINE))[:]
+
+ def _dopipeline(self, mlist, msg, msgdata, pipeline):
+ while pipeline:
+ handler = pipeline.pop(0)
+ modname = 'Mailman.Handlers.' + handler
+ __import__(modname)
+ try:
+ pid = os.getpid()
+ sys.modules[modname].process(mlist, msg, msgdata)
+ # Failsafe -- a child may have leaked through.
+ if pid <> os.getpid():
+ log.error('child process leaked thru: %s', modname)
+ os._exit(1)
+ except Errors.DiscardMessage:
+ # Throw the message away; we need do nothing else with it.
+ vlog.info('Message discarded, msgid: %s',
+ msg.get('message-id', 'n/a'))
+ return 0
+ except Errors.HoldMessage:
+ # Let the approval process take it from here. The message no
+ # longer needs to be queued.
+ return 0
+ except Errors.RejectMessage, e:
+ mlist.bounce_message(msg, e)
+ return 0
+ except:
+ # Push this pipeline module back on the stack, then re-raise
+ # the exception.
+ pipeline.insert(0, handler)
+ raise
+ # We've successfully completed handling of this message
+ return 0