diff options
| author | Barry Warsaw | 2011-05-13 10:25:38 +0200 |
|---|---|---|
| committer | Barry Warsaw | 2011-05-13 10:25:38 +0200 |
| commit | e116cfac469673fad8446d93cddecdfdf6344039 (patch) | |
| tree | 306fde9494c1a4fab2173e9242e917bdea84ddab /src | |
| parent | 091917126e7c58657310524882743e8391166fc3 (diff) | |
| download | mailman-e116cfac469673fad8446d93cddecdfdf6344039.tar.gz mailman-e116cfac469673fad8446d93cddecdfdf6344039.tar.zst mailman-e116cfac469673fad8446d93cddecdfdf6344039.zip | |
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/config/configure.zcml | 23 | ||||
| -rw-r--r-- | src/mailman/database/mailman.sql | 11 | ||||
| -rw-r--r-- | src/mailman/interfaces/bounce.py | 48 | ||||
| -rw-r--r-- | src/mailman/model/bounce.py | 64 | ||||
| -rw-r--r-- | src/mailman/model/docs/bounce.rst | 76 | ||||
| -rw-r--r-- | src/mailman/model/tests/test_bounce.py | 46 |
6 files changed, 257 insertions, 11 deletions
diff --git a/src/mailman/config/configure.zcml b/src/mailman/config/configure.zcml index 3b4497ab8..299a0ce67 100644 --- a/src/mailman/config/configure.zcml +++ b/src/mailman/config/configure.zcml @@ -22,6 +22,11 @@ /> <utility + factory="mailman.model.bounce.BounceProcessor" + provides="mailman.interfaces.bounce.IBounceProcessor" + /> + + <utility factory="mailman.model.domain.DomainManager" provides="mailman.interfaces.domain.IDomainManager" /> @@ -37,11 +42,6 @@ /> <utility - factory="mailman.model.usermanager.UserManager" - provides="mailman.interfaces.usermanager.IUserManager" - /> - - <utility factory="mailman.model.messagestore.MessageStore" provides="mailman.interfaces.messages.IMessageStore" /> @@ -52,13 +52,13 @@ /> <utility - factory="mailman.model.requests.Requests" - provides="mailman.interfaces.requests.IRequests" + factory="mailman.app.registrar.Registrar" + provides="mailman.interfaces.registrar.IRegistrar" /> <utility - factory="mailman.app.registrar.Registrar" - provides="mailman.interfaces.registrar.IRegistrar" + factory="mailman.model.requests.Requests" + provides="mailman.interfaces.requests.IRequests" /> <utility @@ -67,6 +67,11 @@ /> <utility + factory="mailman.model.usermanager.UserManager" + provides="mailman.interfaces.usermanager.IUserManager" + /> + + <utility factory="mailman.email.validate.Validator" provides="mailman.interfaces.address.IEmailValidator" /> diff --git a/src/mailman/database/mailman.sql b/src/mailman/database/mailman.sql index 5d07c607a..016328194 100644 --- a/src/mailman/database/mailman.sql +++ b/src/mailman/database/mailman.sql @@ -54,6 +54,17 @@ CREATE INDEX ix_autoresponserecord_address_id CREATE INDEX ix_autoresponserecord_mailing_list_id ON autoresponserecord (mailing_list_id); +CREATE TABLE bounceevent ( + id INTEGER NOT NULL, + list_name TEXT, + email TEXT, + 'timestamp' TIMESTAMP, + message_id TEXT, + 'where' TEXT, + processed BOOLEAN, + PRIMARY KEY (id) + ); + CREATE TABLE contentfilter ( id INTEGER NOT NULL, mailing_list_id INTEGER, diff --git a/src/mailman/interfaces/bounce.py b/src/mailman/interfaces/bounce.py index 22e2467b8..e6d9e8ccd 100644 --- a/src/mailman/interfaces/bounce.py +++ b/src/mailman/interfaces/bounce.py @@ -22,12 +22,13 @@ from __future__ import absolute_import, unicode_literals __metaclass__ = type __all__ = [ 'IBounceDetector', + 'IBounceEvent', + 'IBounceProcessor', 'Stop', ] -from flufl.enum import Enum -from zope.interface import Interface +from zope.interface import Attribute, Interface @@ -52,3 +53,46 @@ class IBounceDetector(Interface): returned to halt any bounce processing pipeline. :rtype: A set strings, or `Stop` """ + + + +class IBounceEvent(Interface): + """Registration record for a single bounce event.""" + + list_name = Attribute( + """The name of the mailing list that received this bounce.""") + + email = Attribute( + """The email address that bounced.""") + + timestamp = Attribute( + """The timestamp for when the bounce was received.""") + + message_id = Attribute( + """The Message-ID of the bounce message.""") + + where = Attribute( + """Where was the bounce detected?""") + + processed = Attribute( + """Has this bounce event been processed?""") + + + +class IBounceProcessor(Interface): + """Manager/processor of bounce events.""" + + def register(mlist, email, msg, where=None): + """Register a bounce event. + + :param mlist: The mailing list that the bounce occurred on. + :type mlist: IMailingList + :param email: The email address that is bouncing. + :type email: str + :param msg: The bounce message. + :type msg: email.message.Message + :param where: A description of where the bounce was detected. + :type where: str + :return: The registered bounce event. + :rtype: IBounceEvent + """ diff --git a/src/mailman/model/bounce.py b/src/mailman/model/bounce.py new file mode 100644 index 000000000..855fb472f --- /dev/null +++ b/src/mailman/model/bounce.py @@ -0,0 +1,64 @@ +# Copyright (C) 2011 by the Free Software Foundation, Inc. +# +# This file is part of GNU Mailman. +# +# GNU Mailman is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. + +"""Bounce support.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'BounceEvent', + 'BounceProcessor', + ] + + +from storm.locals import Bool, Int, DateTime, Unicode +from zope.interface import implements + +from mailman.interfaces.bounce import IBounceEvent, IBounceProcessor +from mailman.database.model import Model +from mailman.utilities.datetime import now + + + +class BounceEvent(Model): + implements(IBounceEvent) + + id = Int(primary=True) + list_name = Unicode() + email = Unicode() + timestamp = DateTime() + message_id = Unicode() + where = Unicode() + processed = Bool() + + def __init__(self, list_name, email, msg, where): + self.list_name = list_name + self.email = email + self.timestamp = now() + self.message_id = msg['message-id'] + self.where = where + self.processed = False + + + +class BounceProcessor: + implements(IBounceProcessor) + + def register(self, mlist, email, msg, where=None): + """See `IBounceProcessor`.""" + return BounceEvent(mlist.fqdn_listname, email, msg, where) diff --git a/src/mailman/model/docs/bounce.rst b/src/mailman/model/docs/bounce.rst new file mode 100644 index 000000000..6c59a68ee --- /dev/null +++ b/src/mailman/model/docs/bounce.rst @@ -0,0 +1,76 @@ +======= +Bounces +======= + +When a message to an email address bounces, Mailman's bounce runner will +register a bounce event. This registration is done through a utility. + + >>> from zope.component import getUtility + >>> from zope.interface.verify import verifyObject + >>> from mailman.interfaces.bounce import IBounceProcessor + >>> processor = getUtility(IBounceProcessor) + >>> verifyObject(IBounceProcessor, processor) + True + + +Registration +============ + +When a bounce occurs, it's always within the context of a specific mailing +list. + + >>> mlist = create_list('test@example.com') + +The bouncing email contains useful information that will be registered as +well. In particular, the Message-ID is a key piece of data that needs to be +recorded. + + >>> msg = message_from_string("""\ + ... From: mail-daemon@example.org + ... To: test-bounces@example.com + ... Message-ID: <first> + ... + ... """) + +There is a suite of bounce detectors that are used to heuristically extract +the bouncing email addresses. Various techniques are employed including VERP, +DSN, and magic. It is the bounce queue's responsibility to extract the set of +bouncing email addrsses. These are passed one-by-one to the registration +interface. + + >>> event = processor.register(mlist, 'anne@example.com', msg) + >>> print event.list_name + test@example.com + >>> print event.email + anne@example.com + >>> print event.message_id + <first> + +Bounce events have a timestamp. + + >>> print event.timestamp + 2005-08-01 07:49:23 + +Bounce events have a flag indicating whether they've been processed or not. + + >>> event.processed + False + +When a bounce is registered, you can also include an informative string which +indicates where the bounce was detected. This is essentially a semantics-free +field. +:: + + >>> msg = message_from_string("""\ + ... From: mail-daemon@example.org + ... To: test-bounces@example.com + ... Message-ID: <second> + ... + ... """) + + >>> event = processor.register( + ... mlist, 'bart@example.com', msg, 'Some place') + >>> print event.message_id + <second> + >>> print event.where + Some place diff --git a/src/mailman/model/tests/test_bounce.py b/src/mailman/model/tests/test_bounce.py new file mode 100644 index 000000000..fb0bf0875 --- /dev/null +++ b/src/mailman/model/tests/test_bounce.py @@ -0,0 +1,46 @@ +# Copyright (C) 2011 by the Free Software Foundation, Inc. +# +# This file is part of GNU Mailman. +# +# GNU Mailman is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. + +"""Test bounce model objects.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'test_suite', + ] + + +import unittest + +from mailman.model.bounce import BounceEvent +from mailman.testing.layers import ConfigLayer + + + +class TestBounceEvents(unittest.TestCase): + layer = ConfigLayer + + + + + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestBounceEvents)) + return suite |
