Clean up the mta directory.
[mailman.git] / src / mailman / runners / bounce.py
blobba05b400df17c066709291474b622639bdf4b8f8
1 # Copyright (C) 2001-2016 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)
8 # any later version.
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
13 # more details.
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 """Bounce runner."""
20 import logging
22 from flufl.bounce import all_failures, scan_message
23 from mailman.app.bounces import ProbeVERP, StandardVERP, maybe_forward
24 from mailman.core.runner import Runner
25 from mailman.interfaces.bounce import BounceContext, IBounceProcessor
26 from zope.component import getUtility
29 COMMASPACE = ', '
31 log = logging.getLogger('mailman.bounce')
32 elog = logging.getLogger('mailman.error')
36 class BounceRunner(Runner):
37 """The bounce runner."""
39 def __init__(self, name, slice=None):
40 super().__init__(name, slice)
41 self._processor = getUtility(IBounceProcessor)
43 def _dispose(self, mlist, msg, msgdata):
44 # List isn't doing bounce processing?
45 if not mlist.process_bounces:
46 return False
47 # Try VERP detection first, since it's quick and easy
48 context = BounceContext.normal
49 addresses = StandardVERP().get_verp(mlist, msg)
50 if len(addresses) > 0:
51 # Scan the message to see if it contained permanent or temporary
52 # failures. We'll ignore temporary failures, but even if there
53 # are no permanent failures, we'll assume VERP bounces are
54 # permanent.
55 temporary, permanent = all_failures(msg)
56 if len(temporary) > 0:
57 # This was a temporary failure, so just ignore it.
58 return False
59 else:
60 # See if this was a probe message.
61 addresses = ProbeVERP().get_verp(mlist, msg)
62 if len(addresses) > 0:
63 context = BounceContext.probe
64 else:
65 # That didn't give us anything useful, so try the old fashion
66 # bounce matching modules. This returns only the permanently
67 # failing addresses. Since Mailman currently doesn't score
68 # temporary failures, if we get no permanent failures, we're
69 # done.s
70 addresses = scan_message(msg)
71 # If that still didn't return us any useful addresses, then send it on
72 # or discard it. The addresses will come back from flufl.bounce as
73 # bytes/8-bit strings, but we must store them as unicodes in the
74 # database. Assume utf-8 encoding, but be cautious.
75 if len(addresses) > 0:
76 for address in addresses:
77 if isinstance(address, bytes):
78 try:
79 address = address.decode('utf-8')
80 except UnicodeError:
81 log.exception('Ignoring non-UTF-8 encoded '
82 'address: {0}'.format(address))
83 continue
84 self._processor.register(mlist, address, msg, context)
85 else:
86 log.info('Bounce message w/no discernable addresses: %s',
87 msg.get('message-id', 'n/a'))
88 maybe_forward(mlist, msg)
89 # Dequeue this message.
90 return False