diff options
Diffstat (limited to 'Mailman/Queue/LMTPRunner.py')
| -rw-r--r-- | Mailman/Queue/LMTPRunner.py | 194 |
1 files changed, 0 insertions, 194 deletions
diff --git a/Mailman/Queue/LMTPRunner.py b/Mailman/Queue/LMTPRunner.py deleted file mode 100644 index 81c912653..000000000 --- a/Mailman/Queue/LMTPRunner.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright (C) 2006-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. - -"""Mailman LMTP runner (server). - -Most mail servers can be configured to deliver local messages via 'LMTP'[1]. -This module is actually an LMTP server rather than a standard queue runner. -Once it enters its main asyncore loop, it does not respond to mailmanctl -signals the same way as other runners do. All signals will kill this process, -but the normal mailmanctl watchdog will restart it upon exit. - -The LMTP runner opens a local TCP port and waits for the mail server to -connect to it. The messages it receives over LMTP are very minimally parsed -for sanity and if they look okay, they are accepted and injected into -Mailman's incoming queue for processing through the normal pipeline. If they -don't look good, or are destined for a bogus sub-queue address, they are -rejected right away, hopefully so that the peer mail server can provide better -diagnostics. - -[1] RFC 2033 Local Mail Transport Protocol - http://www.faqs.org/rfcs/rfc2033.html - -See the variable USE_LMTP in Defaults.py.in for enabling this delivery -mechanism. -""" - -# NOTE: LMTP delivery is experimental in Mailman 2.2. - -import os -import email -import smtpd -import logging -import asyncore - -from email.utils import parseaddr - -from Mailman.Message import Message -from Mailman.Queue.Runner import Runner -from Mailman.Queue.sbcache import get_switchboard -from Mailman.configuration import config - -elog = logging.getLogger('mailman.error') -qlog = logging.getLogger('mailman.qrunner') - -# We only care about the listname and the subq as in listname@ or -# listname-request@ -subqnames = ( - 'bounces', 'confirm', 'join', ' leave', - 'owner', 'request', 'subscribe', 'unsubscribe', - ) - -DASH = '-' -CRLF = '\r\n' -ERR_451 = '451 Requested action aborted: error in processing' -ERR_502 = '502 Error: command HELO not implemented' -ERR_550 = config.LMTP_ERR_550 - -# XXX Blech -smtpd.__version__ = 'Python LMTP queue runner 1.0' - - - -def getlistq(address): - localpart, domain = address.split('@', 1) - localpart = localpart.split(config.VERP_DELIMITER, 1)[0] - l = localpart.split(DASH) - if l[-1] in subqnames: - listname = DASH.join(l[:-1]) - subq = l[-1] - else: - listname = localpart - subq = None - return listname, subq, domain - - - -class SMTPChannel(smtpd.SMTPChannel): - # Override smtpd.SMTPChannel don't can't change the class name so that we - # don't have to reverse engineer Python's name mangling scheme. - # - # LMTP greeting is LHLO and no HELO/EHLO - - def smtp_LHLO(self, arg): - smtpd.SMTPChannel.smtp_HELO(self, arg) - - def smtp_HELO(self, arg): - self.push(ERR_502) - - - -class LMTPRunner(Runner, smtpd.SMTPServer): - # Only __init__ is called on startup. Asyncore is responsible for later - # connections from MTA. slice and numslices are ignored and are - # necessary only to satisfy the API. - def __init__(self, slice=None, numslices=1): - localaddr = config.LMTP_HOST, config.LMTP_PORT - # Do not call Runner's constructor because there's no QDIR to create - smtpd.SMTPServer.__init__(self, localaddr, remoteaddr=None) - - def handle_accept(self): - conn, addr = self.accept() - channel = SMTPChannel(self, conn, addr) - - def process_message(self, peer, mailfrom, rcpttos, data): - try: - # Refresh the list of list names every time we process a message - # since the set of mailing lists could have changed. However, on - # a big site this could be fairly expensive, so we may need to - # cache this in some way. - listnames = set(config.list_manager.names) - # Parse the message data. XXX Should we reject the message - # immediately if it has defects? Usually only spam has defects. - msg = email.message_from_string(data, Message) - msg['X-MailFrom'] = mailfrom - except Exception, e: - elog.error('%s', e) - return CRLF.join([ERR_451 for to in rcpttos]) - # RFC 2033 requires us to return a status code for every recipient. - status = [] - # Now for each address in the recipients, parse the address to first - # see if it's destined for a valid mailing list. If so, then queue - # the message to the appropriate place and record a 250 status for - # that recipient. If not, record a failure status for that recipient. - for to in rcpttos: - try: - to = parseaddr(to)[1].lower() - listname, subq, domain = getlistq(to) - listname += '@' + domain - if listname not in listnames: - status.append(ERR_550) - continue - # The recipient is a valid mailing list; see if it's a valid - # sub-queue, and if so, enqueue it. - msgdata = dict(listname=listname) - if subq in ('bounces', 'admin'): - queue = get_switchboard(config.BOUNCEQUEUE_DIR) - elif subq == 'confirm': - msgdata['toconfirm'] = True - queue = get_switchboard(config.CMDQUEUE_DIR) - elif subq in ('join', 'subscribe'): - msgdata['tojoin'] = True - queue = get_switchboard(config.CMDQUEUE_DIR) - elif subq in ('leave', 'unsubscribe'): - msgdata['toleave'] = True - queue = get_switchboard(config.CMDQUEUE_DIR) - elif subq == 'owner': - msgdata.update({ - 'toowner' : True, - 'envsender' : config.SITE_OWNER_ADDRESS, - 'pipeline' : config.OWNER_PIPELINE, - }) - queue = get_switchboard(config.INQUEUE_DIR) - elif subq is None: - msgdata['tolist'] = True - queue = get_switchboard(config.INQUEUE_DIR) - elif subq == 'request': - msgdata['torequest'] = True - queue = get_switchboard(config.CMDQUEUE_DIR) - else: - elog.error('Unknown sub-queue: %s', subq) - status.append(ERR_550) - continue - queue.enqueue(msg, msgdata) - status.append('250 Ok') - except Exception, e: - elog.error('%s', e) - status.append(ERR_550) - # All done; returning this big status string should give the expected - # response to the LMTP client. - return CRLF.join(status) - - def _cleanup(self): - pass - - -server = LMTPRunner() -qlog.info('LMTPRunner qrunner started.') -asyncore.loop() -# We'll never get here, but just in case... -qlog.info('LMTPRunner qrunner exiting.') |
