diff --git a/Mailman/Queue/LMTPRunner.py b/Mailman/Queue/LMTPRunner.py
new file mode 100644
index 0000000000000000000000000000000000000000..b2fdde02c1e7ac8989d76b462ded3437942c04db
--- /dev/null
+++ b/Mailman/Queue/LMTPRunner.py
@@ -0,0 +1,490 @@
+# Copyright (C) 2006-2008 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.
+
+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 normal processing.  If they don't look good, or
+are destined for a bogus sub-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"""
+
+
+import os
+import email
+import smtpd
+import logging
+import asyncore
+import asynchat
+import socket
+
+
+from Mailman.Message import Message
+from Mailman import mm_cfg
+from Mailman.Queue.Runner import Runner
+from Mailman.Queue.Switchboard import Switchboard
+from Mailman.Handlers.Moderate import matches_p
+from Mailman import Utils
+from Mailman.Utils import ValidateEmail
+from Mailman.Utils import list_exists
+from Mailman.Utils import get_domain
+from Mailman import MemberAdaptor
+from Mailman.OldStyleMemberships import OldStyleMemberships
+from Mailman import MailList
+from email.Utils import parseaddr
+from email._parseaddr import AddressList as _AddressList
+
+
+# We only care about the listname and the sub-addresses as in listname@ or listname-request@
+SUBADDRESS_NAMES = (
+    'bounces',  'confirm',  'join',         'leave',
+    'owner',    'request',  'subscribe',    'unsubscribe',
+    )
+
+EMPTYSTRING = ''
+NEWLINE = '\n'
+DASH    = '-'
+CRLF    = '\r\n'
+# These error codes are now obsolete. The LMTP protocol now uses enhanced error codes 
+#ERR_451 = '451 Requested action aborted: error in processing'
+#ERR_501 = '501 Message has defects'
+#ERR_502 = '502 Error: command HELO not implemented'
+#ERR_550 = config.LMTP_ERR_550
+
+# Enhanced error codes
+EERR_200 = '2.0.0'
+EERR_450 = '4.5.0 Other or undefined protocol status'
+EERR_511 = '5.1.1 Bad destination mailbox address'
+EERR_513 = '5.1.3 Bad destination list address syntax'
+EERR_551 = '5.5.1 Invalid command'
+EERR_554 = '5.5.4 Invalid command arguments'
+EERR_572 = '5.7.2 The sender is not authorized to send a message to the intended mailing list'
+
+
+# XXX Blech
+__version__ = 'Python LMTP queue runner 1.1'
+
+
+
+def split_recipient(address):
+    """Split an address into listname, subaddress and domain parts.
+
+    For example:
+
+    >>> split_recipient('mylist@example.com')
+    ('mylist', None, 'example.com')
+
+    >>> split_recipient('mylist-request@example.com')
+    ('mylist', 'request', 'example.com')
+
+    :param address: The destination address.
+    :return: A 3-tuple of the form (list-shortname, subaddress, domain).
+        subaddress may be None if this is the list's posting address.
+    """
+    localpart, domain = address.split('@', 1)
+    localpart = localpart.split('+', 1)[0]
+    parts = localpart.split(DASH)
+    if parts[-1] in SUBADDRESS_NAMES:
+        listname = DASH.join(parts[:-1])
+        subaddress = parts[-1]
+    else:
+        listname = localpart
+        subaddress = None
+    return listname, subaddress, domain
+
+
+class Channel(asynchat.async_chat):
+    """An LMTP channel."""
+    # The LMTP channel is not dependent on the SMTP channel found in Python smtpd, 
+    # It is a complete working LMTP channel, based on smtpd in Python
+    
+    COMMAND = 0
+    DATA = 1
+    # The LHLO boolean determines if the LHLO command has been used or not during a session
+    # False = LHLO has not been used
+    # True = LHLO has been used
+    LHLO = False
+    
+    # Don't forget to add:
+    # QRUNNERS.extend([('LMTPRunner', 1),])
+    # to mm_cfg so that the LMTP queue runner gets started with the other queue runners
+
+    def __init__(self, server, conn, addr):
+        asynchat.async_chat.__init__(self, conn)
+        self._server = server
+        self._conn = conn
+        self._addr = addr
+        self._line = []
+        self._state = self.COMMAND
+        self._greeting = 0
+        self._mailfrom = None
+        self._rcpttos = []
+        self._data = ''
+        self._fqdn = socket.getfqdn()
+        self._peer = conn.getpeername()
+        self.set_terminator('\r\n')
+        self.push('220 %s %s' % (self._fqdn, __version__))
+
+
+    # smtp_HELO pushs an error if the HELO command is used
+    def smtp_HELO(self, arg):
+        self.push('501 '+EERR_551+' Use: LHLO command')
+        return
+
+    # smtp_EHLO pushs an error if the EHLO command is used
+    def smtp_EHLO(self, arg):
+        self.push('501 '+EERR_551+' Use: LHLO command')
+        return
+
+    def smtp_LHLO(self, arg):
+        """HELO is not a valid LMTP command."""
+        if not arg:
+            self.push('501 '+EERR_554+' Syntax: lhlo hostname')
+            return
+        if self._greeting:
+            self.push('503 '+EERR_551+' Duplicate LHLO')
+        else:
+            self.LHLO = True
+            self._greeting = arg
+            # Don't forget '-' after the status code on each line 
+            # except for the last line, if there is a multiline response 
+            self.push('250-%s' % self._fqdn)
+            # Use following line when Pipelining is supported
+            #self.push('250-PIPELINING')
+            self.push('250 ENHANCEDSTATUSCODES')
+
+    def smtp_MAIL(self, arg):
+         # RFC 2033 requires the client to say LHLO to the server before mail can be sent
+        if self.LHLO == False:
+            self.push('503 '+EERR_551+' Need LHLO command')
+            return
+        address = self._getaddr('FROM:', arg) if arg else None
+        if not address:
+            self.push('501 '+EERR_554+' Syntax: MAIL FROM:<address>')
+            return
+        if self._mailfrom:
+            self.push('503 '+EERR_551+' Nested MAIL command')
+            return
+        self._mailfrom = address
+        self.push('250 '+EERR_200+' Ok Sender address accepted')
+
+
+    def smtp_RCPT(self, arg):
+        if not self._mailfrom:
+            self.push('503 '+EERR_551+' Need MAIL command')
+            return
+        address = self._getaddr('TO:', arg) if arg else None
+        if not address:
+            self.push('501 '+EERR_554+' Syntax: RCPT TO:<address>')
+            return
+        # Call rcpttocheck to check if list address has syntax errors
+        if self.rcpttocheck(address) == 'EERR_513':
+            self.push('550 '+EERR_513+' Syntax: list@domain')
+            return
+        # Call rcpttocheck to check if list address is a known address.
+        if self.rcpttocheck(address) == 'EERR_511':
+            self.push('550 '+EERR_511+': '+address)
+            return
+        # get subaddress and listname
+        listname = self.mlistname(address)
+        subaddress = self.subaddress(address)
+        # Check if sender is authorised to post to list 
+        if not subaddress in SUBADDRESS_NAMES:
+            if self.listmembercheck(self._mailfrom, listname) == 'EERR_572':
+                self.push('550 '+EERR_572+': '+address)
+                return   
+        if subaddress in SUBADDRESS_NAMES:
+            if self.listmembercheck(self._mailfrom, listname) == 'EERR_572':
+                if subaddress == 'leave' or subaddress == 'unsubscribe': 
+                    self.push('550 '+EERR_572+', the subaddresses -leave and -unsubscribe can not be used by unauthorised senders')
+                    return
+        self._rcpttos.append(address)
+        self.push('250 '+EERR_200+' Ok Recipient address accepted') 
+
+    def smtp_DATA(self, arg):
+        if not self._rcpttos:
+            self.push('503 '+EERR_551+' Need a valid recipient')
+            return
+        if arg:
+            self.push('501 '+EERR_554+' Syntax: DATA')
+            return
+        self._state = self.DATA
+        self.set_terminator('\r\n.\r\n')
+        self.push('354 '+EERR_200+' End data with <CR><LF>.<CR><LF>')
+
+    def smtp_RSET(self, arg):
+        if arg:
+            self.push('501 '+EERR_554+' Syntax: RSET')
+            return
+        # Resets the sender, recipients, and data, but not the greeting
+        self._mailfrom = None
+        self._rcpttos = []
+        self._data = ''
+        self._state = self.COMMAND
+        self.push('250 '+EERR_200+' Ok Reset')
+
+    def smtp_NOOP(self, arg):
+        if arg:
+            self.push('501 '+EERR_554+' Syntax: NOOP')
+        else:
+            self.push('250 '+EERR_200+' Ok')
+
+    def smtp_QUIT(self, arg):
+        # args is ignored
+        self.push('221 '+EERR_200+' Goodbye')
+        self.close_when_done()
+
+
+    # Overrides base class for convenience
+    def push(self, msg):
+        asynchat.async_chat.push(self, msg + '\r\n')
+
+    # Implementation of base class abstract method
+    def collect_incoming_data(self, data):
+        self._line.append(data)
+
+    # factored
+    def _getaddr(self, keyword, arg):
+        address = None
+        keylen = len(keyword)
+        if arg[:keylen].upper() == keyword:
+            address = arg[keylen:].strip()
+            if not address:
+                pass
+            elif address[0] == '<' and address[-1] == '>' and address != '<>':
+                # Addresses can be in the form <person@dom.com> but watch out
+                # for null address, e.g. <>
+                address = address[1:-1]
+        return address
+
+    # Implementation of base class abstract method
+    def found_terminator(self):
+        line = EMPTYSTRING.join(self._line)
+        self._line = []
+        if self._state == self.COMMAND:
+            if not line:
+                self.push('500 '+EERR_551+' Bad syntax')
+                return
+            method = None
+            i = line.find(' ')
+            if i < 0:
+                command = line.upper()
+                arg = None
+            else:
+                command = line[:i].upper()
+                arg = line[i+1:].strip()
+            method = getattr(self, 'smtp_' + command, None)
+            if not method:
+                self.push('500 '+EERR_551+' Command "%s" not implemented' % command)
+                return
+            method(arg)
+            return
+        else:
+            if self._state != self.DATA:
+                self.push('451 '+EERR_450+' Internal confusion')
+                return
+            # Remove extraneous carriage returns and de-transparency according
+            # to RFC 821, Section 4.5.2.
+            data = []
+            for text in line.split('\r\n'):
+                if text and text[0] == '.':
+                    data.append(text[1:])
+                else:
+                    data.append(text)
+            self._data = NEWLINE.join(data)
+            status = self._server.process_message(self._peer,
+                                                   self._mailfrom,
+                                                   self._rcpttos,
+                                                   self._data)
+            self._rcpttos = []
+            self._mailfrom = None
+            self._state = self.COMMAND
+            self.set_terminator('\r\n')
+            if not status:
+                self.push('250 '+EERR_200+' Ok')
+            else:
+                self.push(status)
+
+    # listname parses the given address and returns the name of the list
+    def mlistname(self, to):
+        try:
+            to = parseaddr(to)[1].lower()
+            listname, subaddress, domain = split_recipient(to)
+            return listname
+        except:
+            return 'Unknown Error'
+
+    # subaddress parses the given address and returns the sub-address of the list
+    def subaddress(self, to):
+        try:
+            to = parseaddr(to)[1].lower()
+            listname, subaddress, domain = split_recipient(to)
+            return subaddress
+        except:
+            return 'Unknown Error'
+    # domain parses the given address and returns the domain of the list        
+    def domain(self, to):
+        try:
+            to = parseaddr(to)[1].lower()
+            listname, subaddress, domain = split_recipient(to)
+            return domain
+        except:
+            return 'Unknown Error'
+    
+    # rcpttocheck checks if list is known
+    def rcpttocheck(self, to):
+        try:
+            if '@' not in to:
+                return 'EERR_513'
+            name = self.mlistname(to)
+            domain = self.domain(to)
+            sitedomain = get_domain()
+            if domain == sitedomain:
+                if list_exists(name):
+                    return
+            else:
+                return 'EERR_511'
+        except:
+            return 'Unknown Error rcpt'
+
+    # listmembercheck checks if sender is authorised to post to intended mailing list   
+    def listmembercheck(self, mailfrom, to):
+        try:
+            name = self.mlistname(to)
+            mlist = MailList.MailList(to, lock=False)
+            if mlist.isMember(mailfrom):
+                return True
+            if mlist.generic_nonmember_action != 2:
+                return True
+            if matches_p(mailfrom, mlist.accept_these_nonmembers, to): 
+                return True
+            else:
+                return 'EERR_572'
+        except:
+            return 'Unknown Error'
+        
+class LMTPRunner(Runner, smtpd.SMTPServer):
+    # Only __init__ is called on startup. Asyncore is responsible for later
+    # connections from the MTA.  slice and numslices are ignored and are
+    # necessary only to satisfy the API.
+    def __init__(self, slice=None, numslices=1):
+        # Don't forget to add:
+        # LMTP_HOST = 'host'
+        # LMTP_PORT = port
+        # to mm_cfg so that we know which host and port to use 
+        localaddr = mm_cfg.LMTP_HOST, mm_cfg.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 = Channel(self, conn, addr)
+
+    def process_message(self, peer, mailfrom, rcpttos, data):
+    
+        #The following code is no longer in use due to .defects not being implemeted yet.
+        ###########################################################################################################
+        #try:
+            # Refresh the list of list names every time we process a message
+            # since the set of mailing lists could have changed.
+            # Parse the message data.  If there are any defects in the
+            # message, reject it right away; it's probably spam. 
+            #msg = email.message_from_string(data, Message)
+            #if msg.defects:
+            #    return ('501 '+EERR_554+'. Message has defects')
+            #msg['X-MailFrom'] = mailfrom
+        #except Exception, e:
+            #config.db.abort()
+            #return CRLF.join(['451 '+EERR_450+' Requested action aborted: error in processing' for to in rcpttos])
+        ###########################################################################################################
+        
+        # Need a msg object
+        # Copied from above section of code
+        msg = email.message_from_string(data, Message)
+        # 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, subaddress, domain = split_recipient(to)
+                if list_exists(listname) == False:
+                    status.append('550 '+EERR_511)
+                    continue
+                # The recipient is a valid mailing list; see if it's a valid
+                # sub-address, and if so, enqueue it.
+                queue = None
+                msgdata = dict(listname=listname)
+                if subaddress == 'bounces':
+                    queue = Switchboard(mm_cfg.BOUNCEQUEUE_DIR)
+                elif subaddress == 'confirm':
+                    msgdata['toconfirm'] = True
+                    queue = Switchboard(mm_cfg.CMDQUEUE_DIR)
+                elif subaddress in ('join', 'subscribe'):
+                    msgdata['tojoin'] = True
+                    queue = Switchboard(mm_cfg.CMDQUEUE_DIR)
+                elif subaddress in ('leave', 'unsubscribe'):
+                    msgdata['toleave'] = True
+                    queue = Switchboard(mm_cfg.CMDQUEUE_DIR)
+                elif subaddress == 'owner':
+                    msgdata.update(dict(
+                        toowner=True,
+                        # SITE_OWNER_ADDRESS does not exist in Defaults.py
+                        # Add it to mm_cfg 
+                        envsender=mm_cfg.SITE_OWNER_ADDRESS,  
+                        pipeline=mm_cfg.OWNER_PIPELINE,
+                        ))
+                    queue = Switchboard(mm_cfg.INQUEUE_DIR)
+                elif subaddress is None:
+                    msgdata['tolist'] = True
+                    queue = Switchboard(mm_cfg.INQUEUE_DIR)
+                elif subaddress == 'request':
+                     msgdata['torequest'] = True
+                     queue = Switchboard(mm_cfg.CMDQUEUE_DIR)
+                else:
+                    status.append('550 '+EERR_511)
+                    continue
+                # If we found a valid subaddress, enqueue the message and add
+                # a success status for this recipient.
+                if queue is not None:
+                    queue.enqueue(msg, msgdata)
+                    status.append('250 '+EERR_200+' Ok Message enqueued for '+to)
+            except Exception, e:
+                status.append('550 '+EERR_513)
+        # All done; returning this big status string should give the expected
+        # response to the LMTP client.
+        return CRLF.join(status)
+
+    def run(self):
+        """See `IRunner`."""
+        asyncore.loop()
+
+    def stop(self):
+        """See `IRunner`."""
+        asyncore.socket_map.clear()
+        asyncore.close_all()
+        self.close()