1
# Copyright (C) 1998-2011 by the Free Software Foundation, Inc.
3
# This file is part of GNU Mailman.
5
# GNU Mailman is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free
7
# Software Foundation, either version 3 of the License, or (at your option)
10
# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15
# You should have received a copy of the GNU General Public License along with
16
# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18
"""Parse bounce messages generated by qmail.
20
Qmail actually has a standard, called QSBMF (qmail-send bounce message
21
format), as described in
23
http://cr.yp.to/proto/qsbmf.txt
25
This module should be conformant.
29
from __future__ import absolute_import, unicode_literals
39
from email.iterators import body_line_iterator
40
from flufl.enum import Enum
41
from zope.interface import implements
43
from mailman.interfaces.bounce import IBounceDetector
46
# Other (non-standard?) intros have been observed in the wild.
48
"We're sorry. There's a problem",
49
'Check your send e-mail address.',
50
'Hi. The MTA program at',
52
'This is the mail delivery agent at',
53
'Unfortunately, your mail was not delivered',
55
acre = re.compile(r'<(?P<addr>[^>]*)>:')
58
class ParseState(Enum):
60
intro_paragraph_seen = 1
61
recip_paragraph_seen = 2
66
"""Parse QSBMF format bounces."""
68
implements(IBounceDetector)
70
def process(self, msg):
71
"""See `IBounceDetector`."""
73
state = ParseState.start
74
for line in body_line_iterator(msg):
76
if state is ParseState.start:
77
for introtag in introtags:
78
if line.startswith(introtag):
79
state = ParseState.intro_paragraph_seen
81
elif state is ParseState.intro_paragraph_seen and not line:
82
# Looking for the end of the intro paragraph.
83
state = ParseState.recip_paragraph_seen
84
elif state is ParseState.recip_paragraph_seen:
85
if line.startswith('-'):
86
# We're looking at the break paragraph, so we're done.
88
# At this point we know we must be looking at a recipient
92
addresses.add(mo.group('addr'))
93
# Otherwise, it must be a continuation line, so just ignore it.
95
# We're not looking at anything in particular.