------------------------------------------------------------
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