summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Warsaw2008-03-06 00:29:11 -0500
committerBarry Warsaw2008-03-06 00:29:11 -0500
commit5ca899a81b547dd46197b8d51c7f51538ecde397 (patch)
tree43f1ce1d7b94ff46d8e546ab9abf92e4dd9e494e
parenta1c73f6c305c7f74987d99855ba59d8fa823c253 (diff)
downloadmailman-5ca899a81b547dd46197b8d51c7f51538ecde397.tar.gz
mailman-5ca899a81b547dd46197b8d51c7f51538ecde397.tar.zst
mailman-5ca899a81b547dd46197b8d51c7f51538ecde397.zip
-rw-r--r--mailman/database/roster.py2
-rw-r--r--mailman/pipeline/docs/to-outgoing.txt (renamed from mailman/queue/docs/outgoing.txt)0
-rw-r--r--mailman/queue/lmtp.py2
-rw-r--r--mailman/tests/helpers.py55
-rw-r--r--mailman/tests/smtplistener.py87
5 files changed, 71 insertions, 75 deletions
diff --git a/mailman/database/roster.py b/mailman/database/roster.py
index 15191a7c9..2b237df97 100644
--- a/mailman/database/roster.py
+++ b/mailman/database/roster.py
@@ -181,7 +181,7 @@ _digest_modes = (
class DigestMemberRoster(AbstractRoster):
"""Return all the regular delivery members of a list."""
- name = 'regular_members'
+ name = 'digest_members'
@property
def members(self):
diff --git a/mailman/queue/docs/outgoing.txt b/mailman/pipeline/docs/to-outgoing.txt
index 3840b71ee..3840b71ee 100644
--- a/mailman/queue/docs/outgoing.txt
+++ b/mailman/pipeline/docs/to-outgoing.txt
diff --git a/mailman/queue/lmtp.py b/mailman/queue/lmtp.py
index 23aa9b360..71ae2b9cc 100644
--- a/mailman/queue/lmtp.py
+++ b/mailman/queue/lmtp.py
@@ -38,8 +38,6 @@ See the variable USE_LMTP in Defaults.py.in for enabling this delivery
mechanism.
"""
-# NOTE: LMTP delivery is experimental in Mailman 2.2.
-
import os
import email
import smtpd
diff --git a/mailman/tests/helpers.py b/mailman/tests/helpers.py
index 92d652236..4dc289458 100644
--- a/mailman/tests/helpers.py
+++ b/mailman/tests/helpers.py
@@ -32,16 +32,17 @@ import os
import time
import errno
import mailbox
-import subprocess
+import smtplib
+import tempfile
+import threading
+from Queue import Empty, Queue
from datetime import datetime, timedelta
from mailman.bin.master import Loop as Master
from mailman.configuration import config
from mailman.queue import Switchboard
-
-
-WAIT_INTERVAL = timedelta(seconds=3)
+from mailman.tests.smtplistener import Server
@@ -132,3 +133,49 @@ class TestableMaster(Master):
"""The pids of all the child qrunner processes."""
for pid in self._started_kids:
yield pid
+
+
+
+class SMTPServer:
+ """An smtp server for testing."""
+
+ host = 'localhost'
+ port = 9025
+
+ def __init__(self):
+ self._messages = []
+ self._queue = Queue()
+ self._server = Server((self.host, self.port), self._queue)
+ self._thread = threading.Thread(target=self._server.start)
+
+ def start(self):
+ """Start the smtp server in a thread."""
+ self._thread.start()
+
+ def stop(self):
+ """Stop the smtp server."""
+ smtpd = smtplib.SMTP()
+ smtpd.connect(self.host, self.port)
+ smtpd.docmd('EXIT')
+ self.clear()
+ # Wait for the thread to exit.
+ self._thread.join()
+
+ @property
+ def messages(self):
+ """Return all the messages received by the smtp server."""
+ for message in self._messages:
+ # See if there's anything waiting in the queue.
+ try:
+ message = self._queue.get_nowait()
+ except Empty:
+ pass
+ else:
+ self._messages.append(message)
+ yield message
+
+ def clear(self):
+ """Clear all messages from the queue."""
+ # Just throw these away.
+ list(self._messages)
+ self._messages = []
diff --git a/mailman/tests/smtplistener.py b/mailman/tests/smtplistener.py
index 977726247..3a5b870b7 100644
--- a/mailman/tests/smtplistener.py
+++ b/mailman/tests/smtplistener.py
@@ -17,17 +17,12 @@
"""A test SMTP listener."""
-import sys
import smtpd
-import signal
-import mailbox
import asyncore
-import optparse
from email import message_from_string
COMMASPACE = ', '
-DEFAULT_PORT = 9025
@@ -39,10 +34,10 @@ class Channel(smtpd.SMTPChannel):
# Stash this here since the subclass uses private attributes. :(
self._server = server
- def smtp_RSET(self, arg):
- """Respond to RSET and clear the mailbox."""
- self._server.clear_mailbox()
- smtpd.SMTPChannel.smtp_RSET(self, arg)
+ def smtp_EXIT(self, arg):
+ """Respond to a new command EXIT by exiting the server."""
+ self.push('250 Ok')
+ self._server.stop()
def send(self, data):
"""Silence the bloody asynchat/asyncore broken pipe errors!"""
@@ -58,9 +53,9 @@ class Channel(smtpd.SMTPChannel):
class Server(smtpd.SMTPServer):
"""An SMTP server that stores messages to a mailbox."""
- def __init__(self, localaddr, mailbox_path):
+ def __init__(self, localaddr, queue):
smtpd.SMTPServer.__init__(self, localaddr, None)
- self._mailbox = mailbox.Maildir(mailbox_path)
+ self._queue = queue
def handle_accept(self):
"""Handle connections by creating our own Channel object."""
@@ -69,62 +64,18 @@ class Server(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data):
"""Process a message by adding it to the mailbox."""
- msg = message_from_string(data)
- msg['X-Peer'] = peer
- msg['X-MailFrom'] = mailfrom
- msg['X-RcptTo'] = COMMASPACE.join(rcpttos)
- self._mailbox.add(msg)
- self._mailbox.clean()
+ message = message_from_string(data)
+ message['X-Peer'] = peer
+ message['X-MailFrom'] = mailfrom
+ message['X-RcptTo'] = COMMASPACE.join(rcpttos)
+ self._queue.put(message)
+ def start(self):
+ """Start the asyncore loop."""
+ asyncore.loop()
-
-def handle_signal(*ignore):
- """Handle signal sent by parent to kill the process."""
- asyncore.socket_map.clear()
-
-
-
-def main():
- parser = optparse.OptionParser(usage="""\
-%prog [options] mboxfile
-
-This starts a process listening on a specified host and port (by default
-localhost:9025) for SMTP conversations. All messages this process receives
-are stored in a specified mbox file for the parent process to investigate.
-
-This SMTP server responds to RSET commands by clearing the mbox file.
-""")
- parser.add_option('-a', '--address',
- type='string', default=None,
- help='host:port to listen on')
- opts, args = parser.parse_args()
- if len(args) == 0:
- parser.error('Missing mbox file')
- elif len(args) > 1:
- parser.error('Unexpected arguments')
-
- mboxfile = args[0]
- if opts.address is None:
- host = 'localhost'
- port = DEFAULT_PORT
- elif ':' not in opts.address:
- host = opts.address
- port = DEFAULT_PORT
- else:
- host, port = opts.address.split(':', 1)
- port = int(port)
-
- # Catch the parent's exit signal, and also C-c.
- signal.signal(signal.SIGTERM, handle_signal)
- signal.signal(signal.SIGINT, handle_signal)
-
- server = Server((host, port), mboxfile)
- asyncore.loop()
- asyncore.close_all()
- server.close()
- return 0
-
-
-
-if __name__ == '__main__':
- sys.exit(main())
+ def stop(self):
+ """Stop the asyncore loop."""
+ asyncore.socket_map.clear()
+ asyncore.close_all()
+ self.close()