summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Mailman/Handlers/Hold.py21
-rw-r--r--Mailman/ListAdmin.py8
-rw-r--r--Mailman/MailList.py9
-rw-r--r--Mailman/database/model/mailinglist.py7
-rw-r--r--Mailman/docs/hold.txt373
-rw-r--r--Mailman/testing/test_handlers.py160
6 files changed, 397 insertions, 181 deletions
diff --git a/Mailman/Handlers/Hold.py b/Mailman/Handlers/Hold.py
index d13babba6..2eae08fc1 100644
--- a/Mailman/Handlers/Hold.py
+++ b/Mailman/Handlers/Hold.py
@@ -84,7 +84,7 @@ class Administrivia(Errors.HoldMessage):
def rejection_notice(self, mlist):
listurl = mlist.GetScriptURL('listinfo', absolute=1)
- request = mlist.GetRequestEmail()
+ request = mlist.request_address
return _("""Please do *not* post administrative requests to the mailing
list. If you wish to subscribe, visit $listurl or send a message with the
word `help' in it to the request address, $request, for further
@@ -133,7 +133,7 @@ def process(mlist, msg, msgdata):
if msgdata.get('approved'):
return
# Get the sender of the message
- listname = mlist.internal_name()
+ listname = mlist.list_name
adminaddr = listname + '-admin'
sender = msg.get_sender()
# Special case an ugly sendmail feature: If there exists an alias of the
@@ -204,7 +204,7 @@ def hold_for_approval(mlist, msg, msgdata, exc):
if isinstance(exc, ClassType) or isinstance(exc, type):
# Go ahead and instantiate it now.
exc = exc()
- listname = mlist.real_name
+ listname = mlist.list_name
sender = msgdata.get('sender', msg.get_sender())
usersubject = msg.get('subject')
charset = Utils.GetCharSet(mlist.preferred_language)
@@ -213,9 +213,9 @@ def hold_for_approval(mlist, msg, msgdata, exc):
else:
usersubject = _('(no subject)')
message_id = msg.get('message-id', 'n/a')
- owneraddr = mlist.GetOwnerEmail()
- adminaddr = mlist.GetBouncesEmail()
- requestaddr = mlist.GetRequestEmail()
+ owneraddr = mlist.owner_address
+ adminaddr = mlist.bounces_address
+ requestaddr = mlist.request_address
# We need to send both the reason and the rejection notice through the
# translator again, because of the games we play above
reason = Utils.wrap(exc.reason_notice())
@@ -240,12 +240,17 @@ def hold_for_approval(mlist, msg, msgdata, exc):
# This message should appear to come from <list>-admin so as to handle any
# bounce processing that might be needed.
cookie = mlist.pend_new(Pending.HELD_MESSAGE, id)
+ # 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.
+ member = mlist.members.get_member(sender)
+ lang = (member.preferred_language if member else mlist.preferred_language)
if not fromusenet and ackp(msg) and mlist.respond_to_post_requests and \
- mlist.autorespondToSender(sender, mlist.getMemberLanguage(sender)):
+ mlist.autorespondToSender(sender, lang):
# Get a confirmation cookie
d['confirmurl'] = '%s/%s' % (mlist.GetScriptURL('confirm', absolute=1),
cookie)
- lang = msgdata.get('lang', mlist.getMemberLanguage(sender))
+ lang = msgdata.get('lang', lang)
subject = _('Your message to $listname awaits moderator approval')
text = Utils.maketext('postheld.txt', d, lang=lang, mlist=mlist)
nmsg = Message.UserNotification(sender, adminaddr, subject, text, lang)
diff --git a/Mailman/ListAdmin.py b/Mailman/ListAdmin.py
index cadf0146e..f90b192ce 100644
--- a/Mailman/ListAdmin.py
+++ b/Mailman/ListAdmin.py
@@ -68,10 +68,6 @@ log = logging.getLogger('mailman.vette')
class ListAdmin:
- def InitVars(self):
- # non-configurable data
- self.next_request_id = 1
-
def InitTempVars(self):
self._db = None
self._filename = os.path.join(self.full_path, 'request.pck')
@@ -114,7 +110,7 @@ class ListAdmin:
next = self.next_request_id
self.next_request_id += 1
if self._db.setdefault(next, missing) is missing:
- yield next
+ return next
def SaveRequestsDb(self):
self._closedb()
@@ -183,7 +179,7 @@ class ListAdmin:
ext = 'pck'
else:
ext = 'txt'
- filename = 'heldmsg-%s-%d.%s' % (self.internal_name(), id, ext)
+ filename = 'heldmsg-%s-%d.%s' % (self.fqdn_listname, id, ext)
with open(os.path.join(config.DATA_DIR, filename), 'w') as fp:
if config.HOLD_MESSAGES_AS_PICKLES:
cPickle.dump(msg, fp, 1)
diff --git a/Mailman/MailList.py b/Mailman/MailList.py
index 6f08ca674..9d784de51 100644
--- a/Mailman/MailList.py
+++ b/Mailman/MailList.py
@@ -155,19 +155,17 @@ class MailList(object, HTMLFormatter, Deliverer, ListAdmin,
self._lock.lock()
def Lock(self, timeout=0):
- database.lock(self)
self._lock.lock(timeout)
self._memberadaptor.lock()
# Must reload our database for consistency. Watch out for lists that
# don't exist.
try:
- self.Load(self.fqdn_listname)
+ self.Load()
except Exception:
self.Unlock()
raise
def Unlock(self):
- database.unlock(self)
self._lock.unlock(unconditionally=True)
self._memberadaptor.unlock()
@@ -551,15 +549,12 @@ class MailList(object, HTMLFormatter, Deliverer, ListAdmin,
# the lock (which is a serious problem!). TBD: do we need to be more
# defensive?
self._lock.refresh()
- # Commit the database transaction
- database.save(self)
# The member adaptor may have its own save operation
self._memberadaptor.save()
self.SaveRequestsDb()
self.CheckHTMLArchiveDir()
def Load(self):
- database.load(self)
self._memberadaptor.load()
@@ -1235,7 +1230,7 @@ class MailList(object, HTMLFormatter, Deliverer, ListAdmin,
addr = addr.lower()
localpart = addr.split('@')[0]
if (# TBD: backwards compatibility: deprecated
- localpart == self.internal_name() or
+ localpart == self.list_name or
# exact match against the complete list address
addr == self.fqdn_listname):
return True
diff --git a/Mailman/database/model/mailinglist.py b/Mailman/database/model/mailinglist.py
index 4feb64db4..71007b83f 100644
--- a/Mailman/database/model/mailinglist.py
+++ b/Mailman/database/model/mailinglist.py
@@ -40,6 +40,7 @@ class MailingList(Entity):
# Attributes not directly modifiable via the web u/i
has_field('web_page_url', Unicode),
has_field('admin_member_chunksize', Integer),
+ has_field('hold_and_cmd_autoresponses', PickleType),
# Attributes which are directly modifiable via the web u/i. The more
# complicated attributes are currently stored as pickles, though that
# will change as the schema and implementation is developed.
@@ -167,6 +168,8 @@ class MailingList(Entity):
listname, hostname = split_listname(fqdn_listname)
self.list_name = listname
self.host_name = hostname
+ # For the pending database
+ self.next_request_id = 1
# Create several rosters for filtering out or querying the membership
# table.
from Mailman.database.model import roster
@@ -176,6 +179,10 @@ class MailingList(Entity):
self.members = roster.MemberRoster(self)
self.regular_members = roster.RegularMemberRoster(self)
self.digest_members = roster.DigestMemberRoster(self)
+ # Max autoresponses per day. A mapping between addresses and a
+ # 2-tuple of the date of the last autoresponse and the number of
+ # autoresponses sent on that date.
+ self.hold_and_cmd_autoresponses = {}
@property
def fqdn_listname(self):
diff --git a/Mailman/docs/hold.txt b/Mailman/docs/hold.txt
new file mode 100644
index 000000000..7a06c4b81
--- /dev/null
+++ b/Mailman/docs/hold.txt
@@ -0,0 +1,373 @@
+Holding messages
+================
+
+One of the most important functions of Mailman is to moderate messages by
+holding some for approval before they will post to the mailing list. Messages
+are held when they meet any of a number of criteria.
+
+ >>> import os
+ >>> import errno
+ >>> from Mailman.Handlers.Hold import process
+ >>> from Mailman.Queue.Switchboard import Switchboard
+ >>> from Mailman.configuration import config
+ >>> from Mailman.database import flush
+ >>> mlist = config.list_manager.create('_xtest@example.com')
+ >>> mlist.preferred_language = 'en'
+ >>> mlist.real_name = '_XTest'
+ >>> # XXX This will almost certainly change once we've worked out the web
+ >>> # space layout for mailing lists now.
+ >>> mlist._data.web_page_url = 'http://lists.example.com/'
+ >>> flush()
+
+XXX The Hold handler requires that the mailing list be locked because it
+touches the pending database. Eventually the pending database should be moved
+into the real database so that the lock is no longer necessary.
+
+ >>> mlist.Lock()
+
+Here's a helper function used when we don't care about what's in the virgin
+queue or in the pending database.
+
+ >>> switchboard = Switchboard(config.VIRGINQUEUE_DIR)
+ >>> def clear():
+ ... for filebase in switchboard.files:
+ ... msg, msgdata = switchboard.dequeue(filebase)
+ ... switchboard.finish(filebase)
+ ... for holdfile in os.listdir(config.DATA_DIR):
+ ... if holdfile.startswith('heldmsg-'):
+ ... os.unlink(os.path.join(config.DATA_DIR, holdfile))
+ ... try:
+ ... os.unlink(os.path.join(config.DATA_DIR, 'pending.db'))
+ ... except OSError, e:
+ ... if e.errno <> errno.ENOENT:
+ ... raise
+
+
+Short circuiting
+----------------
+
+If the message metadata indicates that the message is pre-approved, then the
+handler returns immediately.
+
+ >>> from email import message_from_string
+ >>> from Mailman.Message import Message
+ >>> msg = message_from_string("""\
+ ... From: aperson@example.com
+ ...
+ ... An important message.
+ ... """, Message)
+ >>> msgdata = {'approved': True}
+ >>> process(mlist, msg, msgdata)
+ >>> print msg.as_string()
+ From: aperson@example.com
+ <BLANKLINE>
+ An important message.
+ <BLANKLINE>
+ >>> msgdata
+ {'approved': True}
+
+
+Administrivia
+-------------
+
+Mailman scans parts of the message for administrivia, meaning text that looks
+like an email command. This is to prevent people sending 'help' or
+'subscribe' message, etc. to the list members. First, we enable the scanning
+of administrivia for the list.
+
+ >>> mlist.administrivia = True
+ >>> flush()
+ >>> msg = message_from_string("""\
+ ... From: aperson@example.com
+ ... Subject: unsubscribe
+ ...
+ ... """, Message)
+ >>> process(mlist, msg, {})
+ Traceback (most recent call last):
+ ...
+ Administrivia
+ >>> clear()
+
+
+Maximum number of recipients
+----------------------------
+
+Mailman will hold messages that have more than a specified number of explicit
+recipients.
+
+ >>> mlist.max_num_recipients = 5
+ >>> flush()
+ >>> msg = message_from_string("""\
+ ... From: aperson@example.com
+ ... To: _xtest@example.com, bperson@example.com
+ ... Cc: cperson@example.com
+ ... Cc: dperson@example.com (Dan Person)
+ ... To: Elly Q. Person <eperson@example.com>
+ ...
+ ... Hey folks!
+ ... """, Message)
+ >>> process(mlist, msg, {})
+ Traceback (most recent call last):
+ ...
+ TooManyRecipients
+ >>> clear()
+
+
+Implicit destination
+--------------------
+
+Mailman will hold messages that have implicit destination, meaning that the
+mailing list's posting address isn't included in the explicit recipients.
+
+ >>> mlist.require_explicit_destination = True
+ >>> mlist.acceptable_aliases = ''
+ >>> flush()
+ >>> msg = message_from_string("""\
+ ... From: aperson@example.org
+ ... Subject: An implicit message
+ ...
+ ... """, Message)
+ >>> process(mlist, msg, {})
+ Traceback (most recent call last):
+ ...
+ ImplicitDestination
+ >>> clear()
+
+A message gated from NNTP will obviously have an implicit destination. Such
+gated messages will not be held for implicit destination because it's assumed
+that Mailman pulled it from the appropriate news group.
+
+ >>> msgdata = {'fromusenet': True}
+ >>> process(mlist, msg, msgdata)
+ >>> print msg.as_string()
+ From: aperson@example.org
+ Subject: An implicit message
+ <BLANKLINE>
+ >>> print msgdata
+ {'fromusenet': True}
+
+
+Suspicious headers
+------------------
+
+Suspicious headers are a way for Mailman to hold messages that match a
+particular regular expression. This mostly historical feature is fairly
+confusing to users, and the list attribute that controls this is misnamed.
+
+ >>> mlist.bounce_matching_headers = 'From: .*person@(blah.)?example.com'
+ >>> flush()
+ >>> msg = message_from_string("""\
+ ... From: aperson@example.com
+ ... To: _xtest@example.com
+ ... Subject: An implicit message
+ ...
+ ... """, Message)
+ >>> process(mlist, msg, {})
+ Traceback (most recent call last):
+ ...
+ SuspiciousHeaders
+ >>> clear()
+
+But if the header doesn't match the regular expression, it'll get posted just
+fine. This one comes from a .org address.
+
+ >>> msg = message_from_string("""\
+ ... From: aperson@example.org
+ ... To: _xtest@example.com
+ ... Subject: An implicit message
+ ...
+ ... """, Message)
+ >>> msgdata = {}
+ >>> process(mlist, msg, msgdata)
+ >>> print msgdata
+ {}
+
+Just a bit of clean up.
+
+ >>> mlist.bounce_matching_headers = None
+ >>> flush()
+
+
+Message size
+------------
+
+Mailman can hold messages that are bigger than a given size. Generally this
+is used to prevent huge attachments from getting posted to the list. This
+value is calculated in terms of KB (1024 bytes).
+
+ >>> mlist.max_message_size = 1
+ >>> flush()
+ >>> one_line = 'x' * 79
+ >>> big_body = '\n'.join([one_line] * 15)
+ >>> msg = message_from_string("""\
+ ... From: aperson@example.com
+ ... To: _xtest@example.com
+ ...
+ ... """ + big_body, Message)
+ >>> process(mlist, msg, {})
+ Traceback (most recent call last):
+ ...
+ MessageTooBig
+ >>> clear()
+
+
+Hold Notifications
+------------------
+
+Whenever Mailman holds a message, it sends notifications both to the list
+owner and to the original sender, as long as it is configured to do so. We
+can show this by first holding a message.
+
+ >>> mlist.respond_to_post_requests = True
+ >>> mlist.admin_immed_notify = True
+ >>> flush()
+ >>> msg = message_from_string("""\
+ ... From: aperson@example.com
+ ...
+ ... """, Message)
+ >>> process(mlist, msg, {})
+ Traceback (most recent call last):
+ ...
+ ImplicitDestination
+
+There should be two messages in the virgin queue, one to the list owner and
+one to the original author.
+
+ >>> len(switchboard.files)
+ 2
+ >>> qfiles = {}
+ >>> for filebase in switchboard.files:
+ ... qmsg, qdata = switchboard.dequeue(filebase)
+ ... switchboard.finish(filebase)
+ ... qfiles[qmsg['to']] = qmsg, qdata
+ >>> qmsg, qdata = qfiles['_xtest-owner@example.com']
+ >>> print qmsg.as_string()
+ Subject: _xtest post from aperson@example.com requires approval
+ From: _xtest-owner@example.com
+ To: _xtest-owner@example.com
+ MIME-Version: 1.0
+ Content-Type: multipart/mixed; boundary="..."
+ Message-ID: ...
+ Date: ...
+ Precedence: bulk
+ <BLANKLINE>
+ --...
+ Content-Type: text/plain; charset="us-ascii"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ <BLANKLINE>
+ As list administrator, your authorization is requested for the
+ following mailing list posting:
+ <BLANKLINE>
+ List: _xtest@example.com
+ From: aperson@example.com
+ Subject: (no subject)
+ Reason: Message has implicit destination
+ <BLANKLINE>
+ At your convenience, visit:
+ <BLANKLINE>
+ http://lists.example.com/admindb/_xtest@example.com
+ <BLANKLINE>
+ to approve or deny the request.
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ From: aperson@example.com
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ Content-Type: text/plain; charset="us-ascii"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ Subject: confirm ...
+ Sender: _xtest-request@example.com
+ From: _xtest-request@example.com
+ Date: ...
+ Message-ID: ...
+ <BLANKLINE>
+ If you reply to this message, keeping the Subject: header intact,
+ Mailman will discard the held message. Do this if the message is
+ spam. If you reply to this message and include an Approved: header
+ with the list password in it, the message will be approved for posting
+ to the list. The Approved: header can also appear in the first line
+ of the body of the reply.
+ --...
+ >>> sorted(qdata.items())
+ [('_parsemsg', False), ('listname', '_xtest@example.com'),
+ ('nodecorate', True), ('received_time', ...),
+ ('recips', ['_xtest-owner@example.com']),
+ ('reduced_list_headers', True),
+ ('tomoderators', 1), ('version', 3)]
+ >>> qmsg, qdata = qfiles['aperson@example.com']
+ >>> print qmsg.as_string()
+ MIME-Version: 1.0
+ Content-Type: text/plain; charset="us-ascii"
+ Content-Transfer-Encoding: 7bit
+ Subject: Your message to _xtest awaits moderator approval
+ From: _xtest-bounces@example.com
+ To: aperson@example.com
+ Message-ID: ...
+ Date: ...
+ Precedence: bulk
+ <BLANKLINE>
+ Your mail to '_xtest' with the subject
+ <BLANKLINE>
+ (no subject)
+ <BLANKLINE>
+ Is being held until the list moderator can review it for approval.
+ <BLANKLINE>
+ The reason it is being held:
+ <BLANKLINE>
+ Message has implicit destination
+ <BLANKLINE>
+ Either the message will get posted to the list, or you will receive
+ notification of the moderator's decision. If you would like to cancel
+ this posting, please visit the following URL:
+ <BLANKLINE>
+ http://lists.example.com/confirm/_xtest@example.com/...
+ <BLANKLINE>
+ <BLANKLINE>
+ >>> sorted(qdata.items())
+ [('_parsemsg', False), ('listname', '_xtest@example.com'),
+ ('nodecorate', True), ('received_time', ...),
+ ('recips', ['aperson@example.com']),
+ ('reduced_list_headers', True), ('version', 3)]
+
+In addition, the pending database is holding the original messages, waiting
+for them to be disposed of by the original author or the list moderators. The
+database is essentially a dictionary, with the keys being the randomly
+selected tokens included in the urls and the values being a 2-tuple where the
+first item is a type code and the second item is a message id.
+
+ >>> import re
+ >>> cookie = None
+ >>> qmsg, qdata = qfiles['aperson@example.com']
+ >>> for line in qmsg.get_payload().splitlines():
+ ... mo = re.search('confirm/[^/]+/(?P<cookie>.*)$', line)
+ ... if mo:
+ ... cookie = mo.group('cookie')
+ ... break
+ >>> data = mlist.pend_confirm(cookie)
+ >>> data
+ ('H', ...)
+ >>> filename = 'heldmsg-_xtest@example.com-%d.pck' % data[1]
+ >>> heldmsg = os.path.join(config.DATA_DIR, filename)
+
+Use helper function to read the held message.
+
+ >>> from Mailman.ListAdmin import readMessage
+ >>> msg = readMessage(heldmsg)
+ >>> print msg.as_string()
+ From: aperson@example.com
+ <BLANKLINE>
+ <BLANKLINE>
+
+Clean up.
+
+ >>> clear()
+ >>> mlist.Unlock()
diff --git a/Mailman/testing/test_handlers.py b/Mailman/testing/test_handlers.py
index bf6b883fc..840d62e55 100644
--- a/Mailman/testing/test_handlers.py
+++ b/Mailman/testing/test_handlers.py
@@ -39,7 +39,6 @@ from Mailman.testing.base import TestBase
from Mailman.Handlers import Acknowledge
from Mailman.Handlers import AfterDelivery
from Mailman.Handlers import Approve
-from Mailman.Handlers import Hold
from Mailman.Handlers import MimeDel
from Mailman.Handlers import Moderate
from Mailman.Handlers import Scrubber
@@ -134,164 +133,6 @@ X-BeenThere: %s
-class TestHold(TestBase):
- def setUp(self):
- TestBase.setUp(self)
- self._mlist.administrivia = 1
- self._mlist.respond_to_post_requests = 0
- self._mlist.admin_immed_notify = 0
- # We're going to want to inspect this queue directory
- self._sb = Switchboard(config.VIRGINQUEUE_DIR)
-
- def tearDown(self):
- for f in os.listdir(config.VIRGINQUEUE_DIR):
- os.unlink(os.path.join(config.VIRGINQUEUE_DIR, f))
- TestBase.tearDown(self)
- try:
- os.unlink(os.path.join(config.DATA_DIR, 'pending.db'))
- except OSError, e:
- if e.errno <> errno.ENOENT: raise
- for f in [holdfile for holdfile in os.listdir(config.DATA_DIR)
- if holdfile.startswith('heldmsg-')]:
- os.unlink(os.path.join(config.DATA_DIR, f))
-
- def test_short_circuit(self):
- msgdata = {'approved': 1}
- rtn = Hold.process(self._mlist, None, msgdata)
- # Not really a great test, but there's little else to assert
- self.assertEqual(rtn, None)
-
- def test_administrivia(self):
- msg = email.message_from_string("""\
-From: aperson@example.org
-Subject: unsubscribe
-
-""", Message.Message)
- self.assertRaises(Hold.Administrivia, Hold.process,
- self._mlist, msg, {})
-
- def test_max_recips(self):
- self._mlist.max_num_recipients = 5
- msg = email.message_from_string("""\
-From: aperson@example.org
-To: _xtest@example.com, bperson@example.com
-Cc: cperson@example.com
-Cc: dperson@example.com (Jimmy D. Person)
-To: Billy E. Person <eperson@example.com>
-
-Hey folks!
-""", Message.Message)
- self.assertRaises(Hold.TooManyRecipients, Hold.process,
- self._mlist, msg, {})
-
- def test_implicit_destination(self):
- self._mlist.require_explicit_destination = 1
- msg = email.message_from_string("""\
-From: aperson@example.org
-Subject: An implicit message
-
-""", Message.Message)
- self.assertRaises(Hold.ImplicitDestination, Hold.process,
- self._mlist, msg, {})
-
- def test_implicit_destination_fromusenet(self):
- self._mlist.require_explicit_destination = 1
- msg = email.message_from_string("""\
-From: aperson@example.org
-Subject: An implicit message
-
-""", Message.Message)
- rtn = Hold.process(self._mlist, msg, {'fromusenet': 1})
- self.assertEqual(rtn, None)
-
- def test_suspicious_header(self):
- self._mlist.bounce_matching_headers = 'From: .*person@(blah.)?example.org'
- msg = email.message_from_string("""\
-From: aperson@example.org
-To: _xtest@example.net
-Subject: An implicit message
-
-""", Message.Message)
- self.assertRaises(Hold.SuspiciousHeaders, Hold.process,
- self._mlist, msg, {})
-
- def test_suspicious_header_ok(self):
- self._mlist.bounce_matching_headers = 'From: .*person@blah.example.com'
- msg = email.message_from_string("""\
-From: aperson@example.org
-To: _xtest@example.com
-Subject: An implicit message
-
-""", Message.Message)
- rtn = Hold.process(self._mlist, msg, {})
- self.assertEqual(rtn, None)
-
- def test_max_message_size(self):
- self._mlist.max_message_size = 1
- msg = email.message_from_string("""\
-From: aperson@example.org
-To: _xtest@example.com
-
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-""", Message.Message)
- self.assertRaises(Hold.MessageTooBig, Hold.process,
- self._mlist, msg, {})
-
- def test_hold_notifications(self):
- eq = self.assertEqual
- self._mlist.respond_to_post_requests = 1
- self._mlist.admin_immed_notify = 1
- # Now cause an implicit destination hold
- msg = email.message_from_string("""\
-From: aperson@example.org
-
-""", Message.Message)
- self.assertRaises(Hold.ImplicitDestination, Hold.process,
- self._mlist, msg, {})
- # Now we have to make sure there are two messages in the virgin queue,
- # one to the sender and one to the list owners.
- qfiles = {}
- files = self._sb.files()
- eq(len(files), 2)
- for filebase in files:
- qmsg, qdata = self._sb.dequeue(filebase)
- to = qmsg['to']
- qfiles[to] = qmsg, qdata
- # BAW: We could be testing many other attributes of either the
- # messages or the metadata files...
- keys = qfiles.keys()
- keys.sort()
- eq(keys, ['_xtest-owner@example.com', 'aperson@example.org'])
- # Get the pending cookie from the message to the sender
- pmsg, pdata = qfiles['aperson@example.org']
- confirmlines = pmsg.get_payload().split('\n')
- cookie = confirmlines[-3].split('/')[-1]
- # We also need to make sure there's an entry in the Pending database
- # for the hold message.
- data = self._mlist.pend_confirm(cookie)
- eq(data, ('H', 1))
- heldmsg = os.path.join(config.DATA_DIR, 'heldmsg-_xtest-1.pck')
- self.failUnless(os.path.exists(heldmsg))
- os.unlink(heldmsg)
- holdfiles = [f for f in os.listdir(config.DATA_DIR)
- if f.startswith('heldmsg-')]
- eq(len(holdfiles), 0)
-
-
-
class TestMimeDel(TestBase):
def setUp(self):
TestBase.setUp(self)
@@ -968,7 +809,6 @@ Mailman rocks!
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestApprove))
- suite.addTest(unittest.makeSuite(TestHold))
suite.addTest(unittest.makeSuite(TestMimeDel))
suite.addTest(unittest.makeSuite(TestModerate))
suite.addTest(unittest.makeSuite(TestScrubber))