------------------------------------------------------------
revno: 6682
committer: Barry Warsaw <[email protected]>
branch nick: msg
timestamp: Mon 2009-02-09 22:19:18 -0500
message:
  Move mailman.Message to mailman.email.Message.  Rename Message.get_sender() to
  Message.sender (property) and Message.get_senders() to Message.senders
  (another property).  The semantics of .sender is slightly different too; it no
  longer consults config.mailman.use_envelope_sender.
  
  Add absolute_import and unicode_literals to Utils.py, and clean up a few
  imports.
added:
  src/mailman/email/
  src/mailman/email/__init__.py
renamed:
  src/mailman/Message.py => src/mailman/email/message.py
modified:
  src/mailman/Utils.py
  src/mailman/app/bounces.py
  src/mailman/app/membership.py
  src/mailman/app/moderator.py
  src/mailman/app/notifications.py
  src/mailman/app/registrar.py
  src/mailman/bin/inject.py
  src/mailman/chains/hold.py
  src/mailman/commands/cmd_help.py
  src/mailman/commands/docs/echo.txt
  src/mailman/commands/docs/end.txt
  src/mailman/commands/docs/join.txt
  src/mailman/commands/join.py
  src/mailman/config/schema.cfg
  src/mailman/docs/message.txt
  src/mailman/docs/registration.txt
  src/mailman/inject.py
  src/mailman/mta/smtp_direct.py
  src/mailman/pipeline/acknowledge.py
  src/mailman/pipeline/calculate_recipients.py
  src/mailman/pipeline/cook_headers.py
  src/mailman/pipeline/decorate.py
  src/mailman/pipeline/docs/cook-headers.txt
  src/mailman/pipeline/file_recipients.py
  src/mailman/pipeline/moderate.py
  src/mailman/pipeline/replybot.py
  src/mailman/pipeline/to_digest.py
  src/mailman/queue/__init__.py
  src/mailman/queue/command.py
  src/mailman/queue/docs/outgoing.txt
  src/mailman/queue/lmtp.py
  src/mailman/queue/maildir.py
  src/mailman/rules/moderation.py
  src/mailman/tests/test_documentation.py
  src/mailman/email/message.py

=== modified file 'src/mailman/Utils.py'
--- src/mailman/Utils.py        2009-01-21 01:54:22 +0000
+++ src/mailman/Utils.py        2009-02-10 03:19:18 +0000
@@ -22,6 +22,13 @@
 the mailing lists, and whatever else doesn't belong elsewhere.
 """
 
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+    ]
+
+
 import os
 import re
 import cgi
@@ -31,10 +38,9 @@
 import random
 import logging
 import htmlentitydefs
-import email.Header
-import email.Iterators
 
-from email.Errors import HeaderParseError
+from email.errors import HeaderParseError
+from email.header import decode_header, make_header
 from lazr.config import as_boolean
 from string import ascii_letters, digits, whitespace
 
@@ -609,7 +615,7 @@
 def oneline(s, cset='us-ascii', in_unicode=False):
     # Decode header string in one line and convert into specified charset
     try:
-        h = email.Header.make_header(email.Header.decode_header(s))
+        h = make_header(decode_header(s))
         ustr = h.__unicode__()
         line = UEMPTYSTRING.join(ustr.splitlines())
         if in_unicode:

=== modified file 'src/mailman/app/bounces.py'
--- src/mailman/app/bounces.py  2009-01-17 02:04:21 +0000
+++ src/mailman/app/bounces.py  2009-02-10 03:19:18 +0000
@@ -17,7 +17,7 @@
 
 """Application level bounce handling."""
 
-from __future__ import unicode_literals
+from __future__ import absolute_import, unicode_literals
 
 __metaclass__ = type
 __all__ = [
@@ -29,8 +29,8 @@
 from email.mime.message import MIMEMessage
 from email.mime.text import MIMEText
 
-from mailman import Message
 from mailman import Utils
+from mailman.email.message import Message, UserNotification
 from mailman.i18n import _
 
 log = logging.getLogger('mailman.config')
@@ -40,7 +40,10 @@
 def bounce_message(mlist, msg, e=None):
     # Bounce a message back to the sender, with an error message if provided
     # in the exception argument.
-    sender = msg.get_sender()
+    if msg.sender is None:
+        # We can't bounce the message if we don't know who it's supposed to go
+        # to.
+        return
     subject = msg.get('subject', _('(no subject)'))
     subject = Utils.oneline(subject,
                             Utils.GetCharSet(mlist.preferred_language))
@@ -49,10 +52,8 @@
     else:
         notice = _(e.notice)
     # Currently we always craft bounces as MIME messages.
-    bmsg = Message.UserNotification(msg.get_sender(),
-                                    mlist.owner_address,
-                                    subject,
-                                    lang=mlist.preferred_language)
+    bmsg = UserNotification(msg.sender, mlist.owner_address, subject,
+                            lang=mlist.preferred_language)
     # BAW: Be sure you set the type before trying to attach, or you'll get
     # a MultipartConversionError.
     bmsg.set_type('multipart/mixed')

=== modified file 'src/mailman/app/membership.py'
--- src/mailman/app/membership.py       2009-01-17 02:04:21 +0000
+++ src/mailman/app/membership.py       2009-02-10 03:19:18 +0000
@@ -28,12 +28,12 @@
 
 from email.utils import formataddr
 
-from mailman import Message
 from mailman import Utils
 from mailman import i18n
 from mailman.app.notifications import send_goodbye_message
 from mailman.config import config
 from mailman.core import errors
+from mailman.email.message import Message, OwnerNotification
 from mailman.interfaces.member import AlreadySubscribedError, MemberRole
 
 _ = i18n._
@@ -133,5 +133,5 @@
             {'listname': mlist.real_name,
              'member'  : formataddr((realname, address)),
              }, mlist=mlist)
-        msg = Message.OwnerNotification(mlist, subject, text)
+        msg = OwnerNotification(mlist, subject, text)
         msg.send(mlist)

=== modified file 'src/mailman/app/moderator.py'
--- src/mailman/app/moderator.py        2009-01-17 02:04:21 +0000
+++ src/mailman/app/moderator.py        2009-02-10 03:19:18 +0000
@@ -17,7 +17,7 @@
 
 """Application support for moderators."""
 
-from __future__ import unicode_literals
+from __future__ import absolute_import, unicode_literals
 
 __metaclass__ = type
 __all__ = [
@@ -34,7 +34,6 @@
 from datetime import datetime
 from email.utils import formataddr, formatdate, getaddresses, make_msgid
 
-from mailman import Message
 from mailman import Utils
 from mailman import i18n
 from mailman.app.membership import add_member, delete_member
@@ -42,10 +41,12 @@
     send_admin_subscription_notice, send_welcome_message)
 from mailman.config import config
 from mailman.core import errors
+from mailman.email.message import Message, UserNotification
 from mailman.interfaces import Action
 from mailman.interfaces.member import AlreadySubscribedError, DeliveryMode
 from mailman.interfaces.requests import RequestType
 
+
 _ = i18n._
 
 vlog = logging.getLogger('mailman.vette')
@@ -87,7 +88,7 @@
     # the moderation interface.
     msgdata['_mod_message_id'] = message_id
     msgdata['_mod_fqdn_listname'] = mlist.fqdn_listname
-    msgdata['_mod_sender'] = msg.get_sender()
+    msgdata['_mod_sender'] = msg.sender
     msgdata['_mod_subject'] = msg.get('subject', _('(no subject)'))
     msgdata['_mod_reason'] = reason
     msgdata['_mod_hold_date'] = datetime.now().isoformat()
@@ -165,7 +166,7 @@
             if member:
                 language = member.preferred_language
         with i18n.using_language(language):
-            fmsg = Message.UserNotification(
+            fmsg = UserNotification(
                 addresses, mlist.bounces_address,
                 _('Forward of moderated message'),
                 lang=language)
@@ -212,7 +213,7 @@
              }, mlist=mlist)
         # This message should appear to come from the <list>-owner so as
         # to avoid any useless bounce processing.
-        msg = Message.UserNotification(
+        msg = UserNotification(
             mlist.owner_address, mlist.owner_address,
             subject, text, mlist.preferred_language)
         msg.send(mlist, tomoderators=True)
@@ -284,7 +285,7 @@
              }, mlist=mlist)
         # This message should appear to come from the <list>-owner so as
         # to avoid any useless bounce processing.
-        msg = Message.UserNotification(
+        msg = UserNotification(
             mlist.owner_address, mlist.owner_address,
             subject, text, mlist.preferred_language)
         msg.send(mlist, tomoderators=True)
@@ -346,6 +347,5 @@
                  str(origmsg)
                  ])
         subject = _('Request to mailing list "$realname" rejected')
-    msg = Message.UserNotification(recip, mlist.bounces_address,
-                                   subject, text, lang)
+    msg = UserNotification(recip, mlist.bounces_address, subject, text, lang)
     msg.send(mlist)

=== modified file 'src/mailman/app/notifications.py'
--- src/mailman/app/notifications.py    2009-01-17 02:04:21 +0000
+++ src/mailman/app/notifications.py    2009-02-10 03:19:18 +0000
@@ -30,10 +30,10 @@
 from email.utils import formataddr
 from lazr.config import as_boolean
 
-from mailman import Message
 from mailman import Utils
 from mailman import i18n
 from mailman.config import config
+from mailman.email.message import Message, OwnerNotification, UserNotification
 from mailman.interfaces.member import DeliveryMode
 
 
@@ -78,7 +78,7 @@
         digmode = _(' (Digest mode)')
     else:
         digmode = ''
-    msg = Message.UserNotification(
+    msg = UserNotification(
         address, mlist.request_address,
         _('Welcome to the "$mlist.real_name" mailing list${digmode}'),
         text, language)
@@ -104,7 +104,7 @@
         goodbye = Utils.wrap(mlist.goodbye_msg) + '\n'
     else:
         goodbye = ''
-    msg = Message.UserNotification(
+    msg = UserNotification(
         address, mlist.bounces_address,
         _('You have been unsubscribed from the $mlist.real_name mailing list'),
         goodbye, language)
@@ -132,5 +132,5 @@
         {'listname' : mlist.real_name,
          'member'   : formataddr((full_name, address)),
          }, mlist=mlist)
-    msg = Message.OwnerNotification(mlist, subject, text)
+    msg = OwnerNotification(mlist, subject, text)
     msg.send(mlist)

=== modified file 'src/mailman/app/registrar.py'
--- src/mailman/app/registrar.py        2009-01-17 02:04:21 +0000
+++ src/mailman/app/registrar.py        2009-02-10 03:19:18 +0000
@@ -31,9 +31,9 @@
 from pkg_resources import resource_string
 from zope.interface import implements
 
-from mailman.Message import UserNotification
 from mailman.Utils import ValidateEmail
 from mailman.config import config
+from mailman.email.message import UserNotification
 from mailman.i18n import _
 from mailman.interfaces.domain import IDomain
 from mailman.interfaces.member import MemberRole

=== modified file 'src/mailman/bin/inject.py'
--- src/mailman/bin/inject.py   2009-01-01 22:16:51 +0000
+++ src/mailman/bin/inject.py   2009-02-10 03:19:18 +0000
@@ -21,10 +21,10 @@
 from email import message_from_string
 
 from mailman import Utils
-from mailman.Message import Message
 from mailman.configuration import config
 from mailman.i18n import _
 from mailman.inject import inject_text
+from mailman.message import Message
 from mailman.options import SingleMailingListOptions
 
 

=== modified file 'src/mailman/chains/hold.py'
--- src/mailman/chains/hold.py  2009-01-17 02:04:21 +0000
+++ src/mailman/chains/hold.py  2009-02-10 03:19:18 +0000
@@ -33,12 +33,12 @@
 from zope.interface import implements
 
 from mailman import i18n
-from mailman.Message import UserNotification
 from mailman.Utils import maketext, oneline, wrap, GetCharSet
 from mailman.app.moderator import hold_message
 from mailman.app.replybot import autorespond_to_sender, can_acknowledge
 from mailman.chains.base import TerminalChainBase
 from mailman.config import config
+from mailman.email.message import UserNotification
 from mailman.interfaces.pending import IPendable
 
 
@@ -82,8 +82,7 @@
         # Get the language to send the response in.  If the sender is a
         # member, then send it in the member's language, otherwise send it in
         # the mailing list's preferred language.
-        sender = msg.get_sender()
-        member = mlist.members.get_member(sender)
+        member = mlist.members.get_member(msg.sender)
         language = (member.preferred_language
                     if member else mlist.preferred_language)
         # A substitution dictionary for the email templates.
@@ -96,7 +95,7 @@
         substitutions = dict(
             listname    = mlist.fqdn_listname,
             subject     = original_subject,
-            sender      = sender,
+            sender      = msg.sender,
             reason      = 'XXX', #reason,
             confirmurl  = '{0}/{1}'.format(mlist.script_url('confirm'), token),
             admindb_url = mlist.script_url('admindb'),
@@ -118,7 +117,7 @@
         if (not msgdata.get('fromusenet') and
             can_acknowledge(msg) and
             mlist.respond_to_post_requests and
-            autorespond_to_sender(mlist, sender, language)):
+            autorespond_to_sender(mlist, msg.sender, language)):
             # We can respond to the sender with a message indicating their
             # posting was held.
             subject = _(
@@ -127,7 +126,7 @@
             text = maketext('postheld.txt', substitutions,
                             lang=send_language, mlist=mlist)
             adminaddr = mlist.bounces_address
-            nmsg = UserNotification(sender, adminaddr, subject, text,
+            nmsg = UserNotification(msg.sender, adminaddr, subject, text,
                                     send_language)
             nmsg.send(mlist)
         # Now the message for the list moderators.  This one should appear to
@@ -145,7 +144,8 @@
                 substitutions['subject'] = original_subject
                 # craft the admin notification message and deliver it
                 subject = _(
-                    '$mlist.fqdn_listname post from $sender requires approval')
+                    '$mlist.fqdn_listname post from $msg.sender requires '
+                    'approval')
                 nmsg = UserNotification(mlist.owner_address,
                                         mlist.owner_address,
                                         subject, lang=language)
@@ -174,5 +174,5 @@
         # XXX reason
         reason = 'n/a'
         log.info('HOLD: %s post from %s held, message-id=%s: %s',
-                 mlist.fqdn_listname, sender,
+                 mlist.fqdn_listname, msg.sender,
                  msg.get('message-id', 'n/a'), reason)

=== modified file 'src/mailman/commands/cmd_help.py'
--- src/mailman/commands/cmd_help.py    2009-01-01 22:16:51 +0000
+++ src/mailman/commands/cmd_help.py    2009-02-10 03:19:18 +0000
@@ -42,7 +42,7 @@
     # Since this message is personalized, add some useful information if the
     # address requesting help is a member of the list.
     msg = res.msg
-    for sender in  msg.get_senders():
+    for sender in  msg.senders:
         if mlist.isMember(sender):
             memberurl = mlist.GetOptionsURL(sender, absolute=1)
             urlhelp = _(

=== modified file 'src/mailman/commands/docs/echo.txt'
--- src/mailman/commands/docs/echo.txt  2008-12-28 04:29:19 +0000
+++ src/mailman/commands/docs/echo.txt  2009-02-10 03:19:18 +0000
@@ -20,7 +20,7 @@
     >>> from mailman.queue.command import Results
     >>> results = Results()
 
-    >>> from mailman.Message import Message
+    >>> from mailman.email.message import Message
     >>> print command.process(mlist, Message(), {}, ('foo', 'bar'), results)
     ContinueProcessing.yes
     >>> print unicode(results)

=== modified file 'src/mailman/commands/docs/end.txt'
--- src/mailman/commands/docs/end.txt   2008-12-28 04:29:19 +0000
+++ src/mailman/commands/docs/end.txt   2009-02-10 03:19:18 +0000
@@ -20,7 +20,7 @@
 
     >>> from mailman.app.lifecycle import create_list
     >>> mlist = create_list(u'[email protected]')
-    >>> from mailman.Message import Message
+    >>> from mailman.email.message import Message
     >>> print command.process(mlist, Message(), {}, (), None)
     ContinueProcessing.no
 

=== modified file 'src/mailman/commands/docs/join.txt'
--- src/mailman/commands/docs/join.txt  2009-01-03 10:13:41 +0000
+++ src/mailman/commands/docs/join.txt  2009-02-10 03:19:18 +0000
@@ -25,7 +25,7 @@
 No address to join
 ------------------
 
-    >>> from mailman.Message import Message
+    >>> from mailman.email.message import Message
     >>> from mailman.app.lifecycle import create_list
     >>> from mailman.queue.command import Results
     >>> mlist = create_list(u'[email protected]')

=== modified file 'src/mailman/commands/join.py'
--- src/mailman/commands/join.py        2009-01-05 00:41:05 +0000
+++ src/mailman/commands/join.py        2009-02-10 03:19:18 +0000
@@ -61,7 +61,7 @@
             real_name, address = parseaddr(msg['from'])
         # Address could be None or the empty string.
         if not address:
-            address = msg.get_sender()
+            address = msg.sender
         if not address:
             print >> results, _(
                 '$self.name: No valid address found to subscribe')

=== modified file 'src/mailman/config/schema.cfg'
--- src/mailman/config/schema.cfg       2009-01-07 00:55:59 +0000
+++ src/mailman/config/schema.cfg       2009-02-10 03:19:18 +0000
@@ -40,20 +40,6 @@
 # The default language for this server.
 default_language: en
 
-# When allowing only members to post to a mailing list, how is the sender of
-# the message determined?  If this variable is set to Yes, then first the
-# message's envelope sender is used, with a fallback to the sender if there is
-# no envelope sender.  Set this variable to No to always use the sender.
-#
-# The envelope sender is set by the SMTP delivery and is thus less easily
-# spoofed than the sender, which is typically just taken from the From: header
-# and thus easily spoofed by the end-user.  However, sometimes the envelope
-# sender isn't set correctly and this will manifest itself by postings being
-# held for approval even if they appear to come from a list member.  If you
-# are having this problem, set this variable to No, but understand that some
-# spoofed messages may get through.
-use_envelope_sender: no
-
 # Membership tests for posting purposes are usually performed by looking at a
 # set of headers, passing the test if any of their values match a member of
 # the list.  Headers are checked in the order given in this variable.  The

=== modified file 'src/mailman/docs/message.txt'
--- src/mailman/docs/message.txt        2008-12-30 04:28:56 +0000
+++ src/mailman/docs/message.txt        2009-02-10 03:19:18 +0000
@@ -12,13 +12,12 @@
 instance, and then calls the .send() method on this object.  This method
 requires a mailing list instance.
 
-    >>> mlist = config.db.list_manager.create(u'[email protected]')
-    >>> mlist.preferred_language = u'en'
+    >>> mlist = create_list(u'[email protected]')
 
 The UserNotification constructor takes the recipient address, the sender
 address, an optional subject, optional body text, and optional language.
 
-    >>> from mailman.Message import UserNotification
+    >>> from mailman.email.message import UserNotification
     >>> msg = UserNotification(
     ...     '[email protected]',
     ...     '[email protected]',

=== modified file 'src/mailman/docs/registration.txt'
--- src/mailman/docs/registration.txt   2009-01-17 02:04:21 +0000
+++ src/mailman/docs/registration.txt   2009-02-10 03:19:18 +0000
@@ -59,30 +59,30 @@
 honestly, not as much as probably should be done.  Still, some patently bad
 addresses are rejected outright.
 
-    >>> registrar.register('')
-    Traceback (most recent call last):
-    ...
-    InvalidEmailAddress: ''
-    >>> registrar.register('some [email protected]')
-    Traceback (most recent call last):
-    ...
-    InvalidEmailAddress: 'some [email protected]'
-    >>> registrar.register('<script>@example.com')
-    Traceback (most recent call last):
-    ...
-    InvalidEmailAddress: '<script>@example.com'
-    >>> registrar.register('\[email protected]')
-    Traceback (most recent call last):
-    ...
-    InvalidEmailAddress: '\[email protected]'
-    >>> registrar.register('noatsign')
-    Traceback (most recent call last):
-    ...
-    InvalidEmailAddress: 'noatsign'
-    >>> registrar.register('no...@ain')
-    Traceback (most recent call last):
-    ...
-    InvalidEmailAddress: 'no...@ain'
+    >>> registrar.register(u'')
+    Traceback (most recent call last):
+    ...
+    InvalidEmailAddress: u''
+    >>> registrar.register(u'some [email protected]')
+    Traceback (most recent call last):
+    ...
+    InvalidEmailAddress: u'some [email protected]'
+    >>> registrar.register(u'<script>@example.com')
+    Traceback (most recent call last):
+    ...
+    InvalidEmailAddress: u'<script>@example.com'
+    >>> registrar.register(u'\[email protected]')
+    Traceback (most recent call last):
+    ...
+    InvalidEmailAddress: u'\[email protected]'
+    >>> registrar.register(u'noatsign')
+    Traceback (most recent call last):
+    ...
+    InvalidEmailAddress: u'noatsign'
+    >>> registrar.register(u'no...@ain')
+    Traceback (most recent call last):
+    ...
+    InvalidEmailAddress: u'no...@ain'
 
 
 Register an email address

=== added directory 'src/mailman/email'
=== added file 'src/mailman/email/__init__.py'
=== renamed file 'src/mailman/Message.py' => 'src/mailman/email/message.py'
--- src/mailman/Message.py      2009-01-05 05:54:19 +0000
+++ src/mailman/email/message.py        2009-02-10 03:19:18 +0000
@@ -18,9 +18,19 @@
 """Standard Mailman message object.
 
 This is a subclass of email.message.Message but provides a slightly extended
-interface which is more convenient for use inside Mailman.
+interface which is more convenient for use inside Mailman.  It also supports
+safe pickle deserialization, even if the email package adds additional Message
+attributes.
 """
 
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+    'Message',
+    ]
+
+
 import re
 import email
 import email.message
@@ -30,35 +40,37 @@
 from email.header import Header
 from lazr.config import as_boolean
 
-from mailman import Utils
+from mailman.Utils import GetCharSet
 from mailman.config import config
 
+
 COMMASPACE = ', '
-
-mo = re.match(r'([\d.]+)', email.__version__)
-VERSION = tuple(int(s) for s in mo.group().split('.'))
+VERSION = tuple(int(v) for v in email.__version__.split('.'))
 
 
 
 class Message(email.message.Message):
     def __init__(self):
-        # We need a version number so that we can optimize __setstate__()
+        # We need a version number so that we can optimize __setstate__().
         self.__version__ = VERSION
         email.message.Message.__init__(self)
 
     def __getitem__(self, key):
+        # Ensure that header values are unicodes.
         value = email.message.Message.__getitem__(self, key)
         if isinstance(value, str):
             return unicode(value, 'ascii')
         return value
 
     def get(self, name, failobj=None):
+        # Ensure that header values are unicodes.
         value = email.message.Message.get(self, name, failobj)
         if isinstance(value, str):
             return unicode(value, 'ascii')
         return value
 
     def get_all(self, name, failobj=None):
+        # Ensure all header values are unicodes.
         missing = object()
         all_values = email.message.Message.get_all(self, name, missing)
         if all_values is missing:
@@ -70,133 +82,71 @@
     def __repr__(self):
         return self.__str__()
 
-    def __setstate__(self, d):
-        # The base class attributes have changed over time.  Which could
-        # affect Mailman if messages are sitting in the queue at the time of
-        # upgrading the email package.  We shouldn't burden email with this,
-        # so we handle schema updates here.
-        self.__dict__ = d
-        # We know that email 2.4.3 is up-to-date
-        version = d.get('__version__', (0, 0, 0))
-        d['__version__'] = VERSION
-        if version >= VERSION:
-            return
-        # Messages grew a _charset attribute between email version 0.97 and 1.1
-        if not d.has_key('_charset'):
-            self._charset = None
-        # Messages grew a _default_type attribute between v2.1 and v2.2
-        if not d.has_key('_default_type'):
-            # We really have no idea whether this message object is contained
-            # inside a multipart/digest or not, so I think this is the best we
-            # can do.
-            self._default_type = 'text/plain'
-        # Header instances used to allow both strings and Charsets in their
-        # _chunks, but by email 2.4.3 now it's just Charsets.
-        headers = []
-        hchanged = 0
-        for k, v in self._headers:
-            if isinstance(v, Header):
-                chunks = []
-                cchanged = 0
-                for s, charset in v._chunks:
-                    if isinstance(charset, str):
-                        charset = Charset(charset)
-                        cchanged = 1
-                    chunks.append((s, charset))
-                if cchanged:
-                    v._chunks = chunks
-                    hchanged = 1
-            headers.append((k, v))
-        if hchanged:
-            self._headers = headers
-
-    # I think this method ought to eventually be deprecated
-    def get_sender(self):
-        """Return the address considered to be the author of the email.
-
-        This can return either the From: header, the Sender: header or the
-        envelope header (a.k.a. the unixfrom header).  The first non-empty
-        header value found is returned.  However the search order is
-        determined by the following:
-
-        - If config.mailman.use_envelope_sender is true, then the search order
-          is Sender:, From:, unixfrom
-
-        - Otherwise, the search order is From:, Sender:, unixfrom
-
-        unixfrom should never be empty.  The return address is always
-        lower cased.
-
-        This method differs from get_senders() in that it returns one and only
-        one address, and uses a different search order.
+    def __setstate__(self, values):
+        # The base class has grown and changed attributes over time.  This can
+        # break messages sitting in Mailman's queues at the time of upgrading
+        # the email package.  We can't (yet) change the email package to be
+        # safer for pickling, so we handle such changes here.  Note that we're
+        # using Python 2.6's email package version 4.0.1 as a base line here.
+        self.__dict__ = values
+        # The pickled instance should have an __version__ string, but it may
+        # not if it's an email package message.
+        version = values.get('__version__', (0, 0, 0))
+        values['__version__'] = VERSION
+        # There's really nothing to check; there's nothing newer than email
+        # 4.0.1 at the moment.
+
+    @property
+    def sender(self):
+        """The address considered to be the author of the email.
+
+        This is the first non-None value in the list of senders.
+
+        :return: The email address of the first found sender, or the empty
+            string if no sender address was found.
+        :rtype: email address
         """
-        senderfirst = as_boolean(config.mailman.use_envelope_sender)
-        if senderfirst:
-            headers = ('sender', 'from')
-        else:
-            headers = ('from', 'sender')
-        for h in headers:
-            # Use only the first occurrance of Sender: or From:, although it's
-            # not likely there will be more than one.
-            fieldval = self[h]
-            if not fieldval:
-                continue
-            addrs = email.utils.getaddresses([fieldval])
-            try:
-                realname, address = addrs[0]
-            except IndexError:
-                continue
+        for address in self.senders:
+            # This could be None or the empty string.
             if address:
-                break
-        else:
-            # We didn't find a non-empty header, so let's fall back to the
-            # unixfrom address.  This should never be empty, but if it ever
-            # is, it's probably a Really Bad Thing.  Further, we just assume
-            # that if the unixfrom exists, the second field is the address.
-            unixfrom = self.get_unixfrom()
-            if unixfrom:
-                address = unixfrom.split()[1]
-            else:
-                # TBD: now what?!
-                address = ''
-        return address.lower()
+                return address
+        return ''
 
-    def get_senders(self):
+    @property
+    def senders(self):
         """Return a list of addresses representing the author of the email.
 
-        The list will contain the following addresses (in order)
-        depending on availability:
+        The list will contain email addresses in the order determined by the
+        configuration variable `sender_headers` in the `[mailman]` section.
+        By default it uses this list of headers in order:
 
         1. From:
-        2. unixfrom (From_)
+        2. envelope sender (i.e. From_, unixfrom, or RFC 2821 MAIL FROM)
         3. Reply-To:
         4. Sender:
 
-        The return addresses are always lower cased.
+        The return addresses are guaranteed to be lower case or None.  There
+        may be more than four values in the returned list, since some of the
+        originator headers above can appear multiple times in the message, or
+        contain multiple values.
+
+        :return: The list of email addresses that can be considered the sender
+            of the message.
+        :rtype: A list of email addresses or Nones
         """
-        pairs = []
+        envelope_sender = self.get_unixfrom()
+        senders = []
         for header in config.mailman.sender_headers.split():
             header = header.lower()
             if header == 'from_':
-                # get_unixfrom() returns None if there's no envelope
-                unix_from = self.get_unixfrom()
-                fieldval = (unix_from if unix_from is not None else '')
-                try:
-                    pairs.append(('', fieldval.split()[1]))
-                except IndexError:
-                    # Ignore badly formatted unixfroms
-                    pass
+                senders.append(envelope_sender.lower()
+                               if envelope_sender is not None
+                               else '')
             else:
-                fieldvals = self.get_all(header)
-                if fieldvals:
-                    pairs.extend(email.utils.getaddresses(fieldvals))
-        authors = []
-        for pair in pairs:
-            address = pair[1]
-            if address is not None:
-                address = address.lower()
-            authors.append(address)
-        return authors
+                field_values = self.get_all(header, [])
+                senders.extend(address.lower() for (real_name, address)
+                               in email.utils.getaddresses(field_values))
+        return senders
 
     def get_filename(self, failobj=None):
         """Some MUA have bugs in RFC2231 filename encoding and cause
@@ -217,7 +167,7 @@
         Message.__init__(self)
         charset = 'us-ascii'
         if lang is not None:
-            charset = Utils.GetCharSet(lang)
+            charset = GetCharSet(lang)
         if text is not None:
             self.set_payload(text.encode(charset), charset)
         if subject is None:
@@ -263,7 +213,11 @@
         if mlist is not None:
             enqueue_kws['listname'] = mlist.fqdn_listname
         enqueue_kws.update(_kws)
-        virginq.enqueue(self, **enqueue_kws)
+        # Keywords must be strings in Python 2.6.
+        str_keywords = dict()
+        for key, val in enqueue_kws.items():
+            str_keywords[str(key)] = val
+        virginq.enqueue(self, **str_keywords)
 
 
 

=== modified file 'src/mailman/inject.py'
--- src/mailman/inject.py       2009-01-17 02:04:21 +0000
+++ src/mailman/inject.py       2009-02-10 03:19:18 +0000
@@ -15,7 +15,7 @@
 # You should have received a copy of the GNU General Public License along with
 # GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
 
-from __future__ import unicode_literals
+from __future__ import absolute_import, unicode_literals
 
 __metaclass__ = type
 __all__ = [
@@ -27,8 +27,8 @@
 from email import message_from_string
 from email.utils import formatdate, make_msgid
 
-from mailman.Message import Message
 from mailman.config import config
+from mailman.email.message import Message
 
 
 

=== modified file 'src/mailman/mta/smtp_direct.py'
--- src/mailman/mta/smtp_direct.py      2009-01-17 02:04:21 +0000
+++ src/mailman/mta/smtp_direct.py      2009-02-10 03:19:18 +0000
@@ -151,7 +151,7 @@
     # VERPing, it doesn't matter because bulkdeliver is working on a copy, but
     # otherwise msg gets changed.  If the list is anonymous, the original
     # sender is long gone, but Cleanse.py has logged it.
-    origsender = msgdata.get('original_sender', msg.get_sender())
+    origsender = msgdata.get('original_sender', msg.sender)
     # `undelivered' is a copy of chunks that we pop from to do deliveries.
     # This seems like a good tradeoff between robustness and resource
     # utilization.  If delivery really fails (i.e. qfiles/shunt type

=== modified file 'src/mailman/pipeline/acknowledge.py'
--- src/mailman/pipeline/acknowledge.py 2009-01-17 02:04:21 +0000
+++ src/mailman/pipeline/acknowledge.py 2009-02-10 03:19:18 +0000
@@ -30,8 +30,8 @@
 
 from zope.interface import implements
 
-from mailman import Message
 from mailman import Utils
+from mailman.email.message import Message, UserNotification
 from mailman.i18n import _
 from mailman.interfaces.handler import IHandler
 
@@ -47,7 +47,7 @@
     def process(self, mlist, msg, msgdata):
         """See `IHandler`."""
         # Extract the sender's address and find them in the user database
-        sender = msgdata.get('original_sender', msg.get_sender())
+        sender = msgdata.get('original_sender', msg.sender)
         member = mlist.members.get_member(sender)
         if member is None or not member.acknowledge_posts:
             # Either the sender is not a member, in which case we can't know
@@ -75,6 +75,6 @@
         # necessary for general delivery.  Then enqueue it to the outgoing
         # queue.
         subject = _('$realname post acknowledgment')
-        usermsg = Message.UserNotification(sender, mlist.bounces_address,
-                                           subject, text, lang)
+        usermsg = UserNotification(sender, mlist.bounces_address,
+                                   subject, text, lang)
         usermsg.send(mlist)

=== modified file 'src/mailman/pipeline/calculate_recipients.py'
--- src/mailman/pipeline/calculate_recipients.py        2009-01-17 02:04:21 
+0000
+++ src/mailman/pipeline/calculate_recipients.py        2009-02-10 03:19:18 
+0000
@@ -56,8 +56,7 @@
             return
         # Should the original sender should be included in the recipients list?
         include_sender = True
-        sender = msg.get_sender()
-        member = mlist.members.get_member(sender)
+        member = mlist.members.get_member(msg.sender)
         if member and not member.receive_own_postings:
             include_sender = False
         # Support for urgent messages, which bypasses digests and disabled

=== modified file 'src/mailman/pipeline/cook_headers.py'
--- src/mailman/pipeline/cook_headers.py        2009-01-17 02:04:21 +0000
+++ src/mailman/pipeline/cook_headers.py        2009-02-10 03:19:18 +0000
@@ -74,7 +74,7 @@
     # message, we want to save some of the information in the msgdata
     # dictionary for later.  Specifically, the sender header will get waxed,
     # but we need it for the Acknowledge module later.
-    msgdata['original_sender'] = msg.get_sender()
+    msgdata['original_sender'] = msg.sender
     # VirginRunner sets _fasttrack for internally crafted messages.
     fasttrack = msgdata.get('_fasttrack')
     if not msgdata.get('isdigest') and not fasttrack:

=== modified file 'src/mailman/pipeline/decorate.py'
--- src/mailman/pipeline/decorate.py    2009-01-17 02:16:43 +0000
+++ src/mailman/pipeline/decorate.py    2009-02-10 03:19:18 +0000
@@ -32,8 +32,8 @@
 from zope.interface import implements
 
 from mailman import Utils
-from mailman.Message import Message
 from mailman.config import config
+from mailman.email.message import Message
 from mailman.i18n import _
 from mailman.interfaces.handler import IHandler
 from mailman.utilities.string import expand

=== modified file 'src/mailman/pipeline/docs/cook-headers.txt'
--- src/mailman/pipeline/docs/cook-headers.txt  2009-01-07 00:55:59 +0000
+++ src/mailman/pipeline/docs/cook-headers.txt  2009-02-10 03:19:18 +0000
@@ -39,8 +39,8 @@
     ... """)
     >>> msgdata = {}
     >>> process(mlist, msg, msgdata)
-    >>> msgdata['original_sender']
-    ''
+    >>> print msgdata['original_sender']
+    <BLANKLINE>
 
 
 X-BeenThere header

=== modified file 'src/mailman/pipeline/file_recipients.py'
--- src/mailman/pipeline/file_recipients.py     2009-01-17 02:04:21 +0000
+++ src/mailman/pipeline/file_recipients.py     2009-02-10 03:19:18 +0000
@@ -58,8 +58,7 @@
             return
         # If the sender is a member of the list, remove them from the file
         # recipients.
-        sender = msg.get_sender()
-        member = mlist.members.get_member(sender)
+        member = mlist.members.get_member(msg.sender)
         if member is not None:
             addrs.discard(member.address.address)
         msgdata['recips'] = addrs

=== modified file 'src/mailman/pipeline/moderate.py'
--- src/mailman/pipeline/moderate.py    2009-01-17 02:04:21 +0000
+++ src/mailman/pipeline/moderate.py    2009-02-10 03:19:18 +0000
@@ -30,10 +30,10 @@
 from email.MIMEMessage import MIMEMessage
 from email.MIMEText import MIMEText
 
-from mailman import Message
 from mailman import Utils
 from mailman.config import config
 from mailman.core import errors
+from mailman.email.message import Message
 from mailman.i18n import _
 
 
@@ -55,7 +55,7 @@
     if msgdata.get('approved') or msgdata.get('fromusenet'):
         return
     # First of all, is the poster a member or not?
-    for sender in msg.get_senders():
+    for sender in msg.senders:
         if mlist.isMember(sender):
             break
     else:
@@ -92,7 +92,7 @@
         # their own thing.
         return
     else:
-        sender = msg.get_sender()
+        sender = msg.sender
     # From here on out, we're dealing with non-members.
     if matches_p(sender, mlist.accept_these_nonmembers):
         return
@@ -154,7 +154,6 @@
 
 
 def do_discard(mlist, msg):
-    sender = msg.get_sender()
     # Do we forward auto-discards to the list owners?
     if mlist.forward_auto_discards:
         lang = mlist.preferred_language

=== modified file 'src/mailman/pipeline/replybot.py'
--- src/mailman/pipeline/replybot.py    2009-01-17 02:04:21 +0000
+++ src/mailman/pipeline/replybot.py    2009-02-10 03:19:18 +0000
@@ -31,8 +31,8 @@
 
 from zope.interface import implements
 
-from mailman import Message
 from mailman import Utils
+from mailman.email.message import Message, UserNotification
 from mailman.i18n import _
 from mailman.interfaces.handler import IHandler
 from mailman.utilities.string import expand
@@ -71,16 +71,15 @@
     # Now see if we're in the grace period for this sender.  graceperiod <= 0
     # means always autorespond, as does an "X-Ack: yes" header (useful for
     # debugging).
-    sender = msg.get_sender()
     now = time.time()
     graceperiod = mlist.autoresponse_graceperiod
     if graceperiod > NODELTA and ack <> 'yes':
         if toadmin:
-            quiet_until = mlist.admin_responses.get(sender, 0)
+            quiet_until = mlist.admin_responses.get(msg.sender, 0)
         elif torequest:
-            quiet_until = mlist.request_responses.get(sender, 0)
+            quiet_until = mlist.request_responses.get(msg.sender, 0)
         else:
-            quiet_until = mlist.postings_responses.get(sender, 0)
+            quiet_until = mlist.postings_responses.get(msg.sender, 0)
         if quiet_until > now:
             return
     # Okay, we know we're going to auto-respond to this sender, craft the
@@ -102,8 +101,8 @@
         rtext = mlist.autoresponse_postings_text
     # Interpolation and Wrap the response text.
     text = Utils.wrap(expand(rtext, d))
-    outmsg = Message.UserNotification(sender, mlist.bounces_address,
-                                      subject, text, mlist.preferred_language)
+    outmsg = UserNotification(msg.sender, mlist.bounces_address,
+                              subject, text, mlist.preferred_language)
     outmsg['X-Mailer'] = _('The Mailman Replybot')
     # prevent recursions and mail loops!
     outmsg['X-Ack'] = 'No'
@@ -113,11 +112,11 @@
         # graceperiod is in days, we need # of seconds
         quiet_until = now + graceperiod * 24 * 60 * 60
         if toadmin:
-            mlist.admin_responses[sender] = quiet_until
+            mlist.admin_responses[msg.sender] = quiet_until
         elif torequest:
-            mlist.request_responses[sender] = quiet_until
+            mlist.request_responses[msg.sender] = quiet_until
         else:
-            mlist.postings_responses[sender] = quiet_until
+            mlist.postings_responses[msg.sender] = quiet_until
 
 
 

=== modified file 'src/mailman/pipeline/to_digest.py'
--- src/mailman/pipeline/to_digest.py   2009-02-04 12:00:56 +0000
+++ src/mailman/pipeline/to_digest.py   2009-02-10 03:19:18 +0000
@@ -30,8 +30,8 @@
 
 from zope.interface import implements
 
-from mailman.Message import Message
 from mailman.config import config
+from mailman.email.message import Message
 from mailman.i18n import _
 from mailman.interfaces.handler import IHandler
 from mailman.interfaces.mailinglist import DigestFrequency

=== modified file 'src/mailman/queue/__init__.py'
--- src/mailman/queue/__init__.py       2009-02-04 12:00:56 +0000
+++ src/mailman/queue/__init__.py       2009-02-10 03:19:18 +0000
@@ -49,9 +49,9 @@
 from lazr.config import as_boolean, as_timedelta
 from zope.interface import implements
 
-from mailman import Message
 from mailman import i18n
 from mailman.config import config
+from mailman.email.message import Message
 from mailman.interfaces.runner import IRunner
 from mailman.interfaces.switchboard import ISwitchboard
 from mailman.utilities.filesystem import makedirs
@@ -186,7 +186,7 @@
             # have to generate the message later when we do size restriction
             # checking.
             original_size = len(msg)
-            msg = email.message_from_string(msg, Message.Message)
+            msg = email.message_from_string(msg, Message)
             msg.original_size = original_size
             data['original_size'] = original_size
         return msg, data
@@ -427,9 +427,8 @@
         # will be the list's preferred language.  However, we must take
         # special care to reset the defaults, otherwise subsequent messages
         # may be translated incorrectly.
-        sender = msg.get_sender()
-        if sender:
-            member = mlist.members.get_member(sender)
+        if msg.sender:
+            member = mlist.members.get_member(msg.sender)
             language = (member.preferred_language
                         if member is not None
                         else mlist.preferred_language)

=== modified file 'src/mailman/queue/command.py'
--- src/mailman/queue/command.py        2009-01-07 00:55:59 +0000
+++ src/mailman/queue/command.py        2009-02-10 03:19:18 +0000
@@ -37,14 +37,14 @@
 from email.Iterators import typed_subpart_iterator
 from zope.interface import implements
 
-from mailman import Message
 from mailman.config import config
+from mailman.email.message import Message, UserNotification
 from mailman.i18n import _
 from mailman.interfaces.command import ContinueProcessing, IEmailResults
 from mailman.queue import Runner
 
+
 NL = '\n'
-
 log = logging.getLogger('mailman.vette')
 
 
@@ -195,10 +195,9 @@
         # Send a reply, but do not attach the original message.  This is a
         # compromise because the original message is often helpful in tracking
         # down problems, but it's also a vector for backscatter spam.
-        reply = Message.UserNotification(
-            msg.get_sender(), mlist.bounces_address,
-            _('The results of your email commands'),
-            lang=msgdata['lang'])
+        reply = UserNotification(msg.sender, mlist.bounces_address,
+                                 _('The results of your email commands'),
+                                 lang=msgdata['lang'])
         # Find a charset for the response body.  Try ascii first, then
         # latin-1 and finally falling back to utf-8.
         reply_body = unicode(results)

=== modified file 'src/mailman/queue/docs/outgoing.txt'
--- src/mailman/queue/docs/outgoing.txt 2009-01-04 05:22:08 +0000
+++ src/mailman/queue/docs/outgoing.txt 2009-02-10 03:19:18 +0000
@@ -11,7 +11,6 @@
 VERP'd, etc.  The outgoing runner doesn't itself support retrying but it can
 move messages to the 'retry queue' for handling delivery failures.
 
-    >>> from mailman.app.lifecycle import create_list
     >>> mlist = create_list(u'[email protected]')
 
     >>> from mailman.app.membership import add_member

=== modified file 'src/mailman/queue/lmtp.py'
--- src/mailman/queue/lmtp.py   2009-01-07 00:55:59 +0000
+++ src/mailman/queue/lmtp.py   2009-02-10 03:19:18 +0000
@@ -38,9 +38,9 @@
 
 from email.utils import parseaddr
 
-from mailman.Message import Message
 from mailman.config import config
 from mailman.database.transaction import txn
+from mailman.email.message import Message
 from mailman.queue import Runner
 
 elog = logging.getLogger('mailman.error')

=== modified file 'src/mailman/queue/maildir.py'
--- src/mailman/queue/maildir.py        2009-01-05 00:41:05 +0000
+++ src/mailman/queue/maildir.py        2009-02-10 03:19:18 +0000
@@ -56,8 +56,8 @@
 from email.Parser import Parser
 from email.Utils import parseaddr
 
-from mailman.Message import Message
 from mailman.config import config
+from mailman.message import Message
 from mailman.queue import Runner
 
 log = logging.getLogger('mailman.error')

=== modified file 'src/mailman/rules/moderation.py'
--- src/mailman/rules/moderation.py     2009-01-17 02:04:21 +0000
+++ src/mailman/rules/moderation.py     2009-02-10 03:19:18 +0000
@@ -43,7 +43,7 @@
 
     def check(self, mlist, msg, msgdata):
         """See `IRule`."""
-        for sender in msg.get_senders():
+        for sender in msg.senders:
             member = mlist.members.get_member(sender)
             if member is not None and member.is_moderated:
                 return True
@@ -61,7 +61,7 @@
 
     def check(self, mlist, msg, msgdata):
         """See `IRule`."""
-        for sender in msg.get_senders():
+        for sender in msg.senders:
             if mlist.members.get_member(sender) is not None:
                 # The sender is a member of the mailing list.
                 return False

=== modified file 'src/mailman/tests/test_documentation.py'
--- src/mailman/tests/test_documentation.py     2009-02-04 12:00:56 +0000
+++ src/mailman/tests/test_documentation.py     2009-02-10 03:19:18 +0000
@@ -38,9 +38,9 @@
 
 import mailman
 
-from mailman.Message import Message
 from mailman.app.lifecycle import create_list
 from mailman.config import config
+from mailman.email.message import Message
 from mailman.testing.layers import SMTPLayer
 
 



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

Your team Mailman Checkins is subscribed to branch lp:mailman.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+edit-subscription.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to