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)
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/>.
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
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
:
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
55 temporary
, permanent
= all_failures(msg
)
56 if len(temporary
) > 0:
57 # This was a temporary failure, so just ignore it.
60 # See if this was a probe message.
61 addresses
= ProbeVERP().get_verp(mlist
, msg
)
62 if len(addresses
) > 0:
63 context
= BounceContext
.probe
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
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
):
79 address
= address
.decode('utf-8')
81 log
.exception('Ignoring non-UTF-8 encoded '
82 'address: {0}'.format(address
))
84 self
._processor
.register(mlist
, address
, msg
, context
)
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.