------------------------------------------------------------
revno: 6606
committer: Barry Warsaw <[EMAIL PROTECTED]>
branch nick: 3.0
timestamp: Thu 2008-03-13 22:39:06 -0500
message:
  merging thread
renamed:
  mailman/database/txnsupport.py => mailman/database/transaction.py
modified:
  mailman/queue/docs/lmtp.txt
  mailman/queue/lmtp.py
  mailman/database/transaction.py
    ------------------------------------------------------------
    revno: 6603.2.3
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: lmtp
    timestamp: Thu 2008-03-13 09:00:20 -0400
    message:
      Move mailman.database.txnsupport to mailman.database.transaction and 
update
      the txn() decorator.  It no longer requires a _withtxn() method since it 
knows
      how to commit and abort the current transaction using the global database.
      
      Flesh out the lmtp.txt doctest with documentation for both bogus and valid
      subaddresses.
    renamed:
      mailman/database/txnsupport.py => mailman/database/transaction.py
    modified:
      mailman/queue/docs/lmtp.txt
      mailman/queue/lmtp.py
      mailman/database/transaction.py
    ------------------------------------------------------------
    revno: 6603.2.2
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: lmtp
    timestamp: Wed 2008-03-12 21:54:27 -0400
    message:
      merge down thread
    modified:
      mailman/bin/testall.py
      mailman/database/__init__.py
      mailman/queue/__init__.py
      mailman/tests/test_documentation.py
    ------------------------------------------------------------
    revno: 6603.2.1
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: lmtp
    timestamp: Wed 2008-03-12 20:35:27 -0400
    message:
      merge down-thread
    modified:
      mailman/database/__init__.py
      mailman/interfaces/database.py
      mailman/tests/test_documentation.py

=== renamed file 'mailman/database/txnsupport.py' => 
'mailman/database/transaction.py'
--- a/mailman/database/txnsupport.py    2008-02-08 04:01:48 +0000
+++ b/mailman/database/transaction.py   2008-03-13 13:00:20 +0000
@@ -15,20 +15,37 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 # USA.
 
-# A transaction wrapping decorator. The basic idea is that methods in the
-# DBContext that need to operate on transaction boundaries can be written to
-# be transaction naive.  By wrapping them in this decorator, they
-# automatically become transaction safe.
-
+"""Transactional support."""
+
+__metaclass__ = type
+__all__ = [
+    'txn',
+    ]
+
+
+from mailman.configuration import config
+
+
+
 class txn(object):
-    def __init__(self, func):
-        # func is a function object, not a method (even an unbound method).
-        self._func = func
+    """Decorator for transactional support.
+
+    When the function this decorator wraps exits cleanly, the current
+    transaction is committed.  When it exits uncleanly (i.e. because of an
+    exception, the transaction is aborted.
+
+    Either way, the current transaction is completed.
+    """
+    def __init__(self, function):
+        self._function = function
 
     def __get__(self, obj, type=None):
-        # Return a wrapper function that creates a bound method from the
-        # function, then calls it wrapped in a transaction boundary.  Uses a
-        # non-public method called _withtxn() in the object's class.
         def wrapper(*args, **kws):
-            return obj._withtxn(self._func.__get__(obj), *args, **kws)
+            try:
+                rtn = self._function(obj, *args, **kws)
+                config.db.commit()
+                return rtn
+            except:
+                config.db.abort()
+                raise
         return wrapper

=== modified file 'mailman/queue/docs/lmtp.txt'
--- a/mailman/queue/docs/lmtp.txt       2008-03-12 22:35:16 +0000
+++ b/mailman/queue/docs/lmtp.txt       2008-03-13 13:00:20 +0000
@@ -22,8 +22,8 @@
     (250, ...)
 
 
-Invalid mailing list
---------------------
+Posting address
+---------------
 
 If the mail server tries to send a message to a non-existant mailing list, it
 will get a 550 error.
@@ -42,6 +42,60 @@
     ...
     SMTPDataError: (550, 'Requested action not taken: mailbox unavailable')
 
+Once the mailing list is created, the posting address is valid.
+
+    >>> from mailman.app.lifecycle import create_list
+    >>> create_list(u'[EMAIL PROTECTED]')
+    <mailing list "[EMAIL PROTECTED]" at ...>
+    >>> commit()
+    >>> lmtp.sendmail(
+    ...     '[EMAIL PROTECTED]',
+    ...     ['[EMAIL PROTECTED]'], """\
+    ... From: [EMAIL PROTECTED]
+    ... To: [EMAIL PROTECTED]
+    ... Subject: An interesting message
+    ... Message-ID: <badger>
+    ...
+    ... This is an interesting message.
+    ... """)
+    {}
+
+
+Sub-addresses
+-------------
+
+The LMTP server understands each of the list's sub-addreses, such as -join,
+-leave, -request and so on.  If the message is posted to an invalid
+sub-address though, it is rejected.
+
+    >>> lmtp.sendmail(
+    ...     '[EMAIL PROTECTED]',
+    ...     ['[EMAIL PROTECTED]'], """\
+    ... From: [EMAIL PROTECTED]
+    ... To: [EMAIL PROTECTED]
+    ... Subject: Help
+    ... Message-ID: <cow>
+    ...
+    ... Please help me.
+    ... """)
+    Traceback (most recent call last):
+    ...
+    SMTPDataError: (550, 'Requested action not taken: mailbox unavailable')
+
+But the message is accepted if posted to a valid sub-address.
+
+    >>> lmtp.sendmail(
+    ...     '[EMAIL PROTECTED]',
+    ...     ['[EMAIL PROTECTED]'], """\
+    ... From: [EMAIL PROTECTED]
+    ... To: [EMAIL PROTECTED]
+    ... Subject: Help
+    ... Message-ID: <dog>
+    ...
+    ... Please help me.
+    ... """)
+    {}
+
 
 Clean up
 --------

=== modified file 'mailman/queue/lmtp.py'
--- a/mailman/queue/lmtp.py     2008-03-12 22:35:16 +0000
+++ b/mailman/queue/lmtp.py     2008-03-13 13:00:20 +0000
@@ -24,8 +24,8 @@
 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-queue address, they are rejected right away,
-hopefully so that the peer mail server can provide better diagnostics.
+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
@@ -44,15 +44,16 @@
 
 from mailman.Message import Message
 from mailman.configuration import config
+from mailman.database.transaction import txn
 from mailman.queue import Runner, Switchboard
 
 elog = logging.getLogger('mailman.error')
 qlog = logging.getLogger('mailman.qrunner')
 
 
-# We only care about the listname and the subqueue as in listname@ or
+# We only care about the listname and the sub-addresses as in listname@ or
 # listname-request@
-SUBQUEUE_NAMES = (
+SUBADDRESS_NAMES = (
     'bounces',  'confirm',  'join', '       leave',
     'owner',    'request',  'subscribe',    'unsubscribe',
     )
@@ -70,16 +71,30 @@
 
 
 def split_recipient(address):
+    """Split an address into listname, subaddress and domain parts.
+
+    For example:
+
+    >>> split_recipient('[EMAIL PROTECTED]')
+    ('mylist', None, 'example.com')
+
+    >>> split_recipient('[EMAIL PROTECTED]')
+    ('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(config.VERP_DELIMITER, 1)[0]
     parts = localpart.split(DASH)
-    if parts[-1] in SUBQUEUE_NAMES:
+    if parts[-1] in SUBADDRESS_NAMES:
         listname = DASH.join(parts[:-1])
-        subq = l[-1]
+        subaddress = parts[-1]
     else:
         listname = localpart
-        subq = None
-    return listname, subq, domain
+        subaddress = None
+    return listname, subaddress, domain
 
 
 
@@ -103,7 +118,7 @@
 
 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
+    # connections from the 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
@@ -114,12 +129,11 @@
         conn, addr = self.accept()
         channel = Channel(self, conn, addr)
 
+    @txn
     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.
+            # since the set of mailing lists could have changed.
             listnames = set(config.db.list_manager.names)
             # Parse the message data.  If there are any defects in the
             # message, reject it right away; it's probably spam. 
@@ -128,7 +142,8 @@
                 return ERR_501
             msg['X-MailFrom'] = mailfrom
         except Exception, e:
-            elog.error('%s', e)
+            elog.exception('LMTP message parsing')
+            config.db.abort()
             return CRLF.join([ERR_451 for to in rcpttos])
         # RFC 2033 requires us to return a status code for every recipient.
         status = []
@@ -139,46 +154,53 @@
         for to in rcpttos:
             try:
                 to = parseaddr(to)[1].lower()
-                listname, subq, domain = split_recipient(to)
+                listname, subaddress, domain = split_recipient(to)
+                qlog.debug('to: %s, list: %s, sub: %s, dom: %s',
+                           to, listname, subaddress, domain)
                 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.
+                # sub-address, and if so, enqueue it.
+                queue = None
                 msgdata = dict(listname=listname)
-                if subq in ('bounces', 'admin'):
+                if subaddress in ('bounces', 'admin'):
                     queue = Switchboard(config.BOUNCEQUEUE_DIR)
-                elif subq == 'confirm':
+                elif subaddress == 'confirm':
                     msgdata['toconfirm'] = True
                     queue = Switchboard(config.CMDQUEUE_DIR)
-                elif subq in ('join', 'subscribe'):
+                elif subaddress in ('join', 'subscribe'):
                     msgdata['tojoin'] = True
                     queue = Switchboard(config.CMDQUEUE_DIR)
-                elif subq in ('leave', 'unsubscribe'):
+                elif subaddress in ('leave', 'unsubscribe'):
                     msgdata['toleave'] = True
                     queue = Switchboard(config.CMDQUEUE_DIR)
-                elif subq == 'owner':
-                    msgdata.update({
-                        'toowner'   : True,
-                        'envsender' : config.SITE_OWNER_ADDRESS,
-                        'pipeline'  : config.OWNER_PIPELINE,
-                        })
+                elif subaddress == 'owner':
+                    msgdata.update(dict(
+                        toowner=True,
+                        envsender=config.SITE_OWNER_ADDRESS,
+                        pipeline=config.OWNER_PIPELINE,
+                        ))
                     queue = Switchboard(config.INQUEUE_DIR)
-                elif subq is None:
+                elif subaddress is None:
                     msgdata['tolist'] = True
                     queue = Switchboard(config.INQUEUE_DIR)
-                elif subq == 'request':
+                elif subaddress == 'request':
                      msgdata['torequest'] = True
                      queue = Switchboard(config.CMDQUEUE_DIR)
                 else:
-                    elog.error('Unknown sub-queue: %s', subq)
+                    elog.error('Unknown sub-address: %s', subaddress)
                     status.append(ERR_550)
                     continue
-                queue.enqueue(msg, msgdata)
-                status.append('250 Ok')
+                # 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 Ok')
             except Exception, e:
-                elog.error('%s', e)
+                elog.exception('Queue detection: %s', msg['message-id'])
+                config.db.abort()
                 status.append(ERR_550)
         # All done; returning this big status string should give the expected
         # response to the LMTP client.



--
Primary development focus
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to