"""Bounce queue runner."""
import re
from email.MIMEText import MIMEText
from email.MIMEMessage import MIMEMessage
from email.Utils import parseaddr
from Mailman import mm_cfg
from Mailman import Utils
from Mailman import LockFile
from Mailman.Message import UserNotification
from Mailman.Bouncers import BouncerAPI
from Mailman.Queue.Runner import Runner
from Mailman.Queue.sbcache import get_switchboard
from Mailman.Logging.Syslog import syslog
from Mailman.i18n import _
COMMASPACE = ', '
class BounceRunner(Runner):
QDIR = mm_cfg.BOUNCEQUEUE_DIR
SLEEPTIME = mm_cfg.minutes(1)
def _dispose(self, mlist, msg, msgdata):
mlist.Load()
outq = get_switchboard(mm_cfg.OUTQUEUE_DIR)
if msg.get('to', '') == Utils.get_site_email(extra='-owner'):
outq.enqueue(msg, msgdata,
recips=[Utils.get_site_email()],
envsender=Utils.get_site_email(extra='loop'),
)
if not mlist.bounce_processing:
return
addrs = verp_bounce(mlist, msg)
if not addrs:
addrs = BouncerAPI.ScanMessages(mlist, msg)
if not addrs:
syslog('bounce', 'bounce message w/no discernable addresses: %s',
msg.get('message-id'))
maybe_forward(mlist, msg)
return
addrs = filter(None, addrs)
foundp = 0
listname = mlist.internal_name()
if listname == mm_cfg.MAILMAN_SITE_LIST:
foundp = 1
for listname in Utils.list_names():
xlist = self._open_list(listname)
xlist.Load()
for addr in addrs:
if xlist.isMember(addr):
unlockp = 0
if not xlist.Locked():
try:
xlist.Lock(timeout=mm_cfg.LIST_LOCK_TIMEOUT)
except LockFile.TimeOutError:
continue
unlockp = 1
try:
xlist.registerBounce(addr, msg)
foundp = 1
xlist.Save()
finally:
if unlockp:
xlist.Unlock()
else:
try:
mlist.Lock(timeout=mm_cfg.LIST_LOCK_TIMEOUT)
except LockFile.TimeOutError:
syslog('bounce', "%s: couldn't get list lock", listname)
return 1
else:
try:
for addr in addrs:
if mlist.isMember(addr):
mlist.registerBounce(addr, msg)
foundp = 1
mlist.Save()
finally:
mlist.Unlock()
if not foundp:
syslog('bounce', 'bounce message with non-members of %s: %s',
listname, COMMASPACE.join(addrs))
def verp_bounce(mlist, msg):
bmailbox, bdomain = Utils.ParseEmail(mlist.GetBouncesEmail())
vals = []
for header in ('to', 'delivered-to', 'envelope-to', 'apparently-to'):
vals.extend(msg.get_all(header, []))
for field in vals:
to = parseaddr(field)[1]
if not to:
continue mo = re.search(mm_cfg.VERP_REGEXP, to)
if not mo:
continue try:
if bmailbox <> mo.group('bounces'):
continue addr = '%s@%s' % mo.group('mailbox', 'host')
except IndexError:
syslog('error',
"VERP_REGEXP doesn't yield the right match groups: %s",
mm_cfg.VERP_REGEXP)
return []
return [addr]
def maybe_forward(mlist, msg):
if mlist.bounce_unrecognized_goes_to_list_owner:
adminurl = mlist.GetScriptURL('admin', absolute=1) + '/bounce'
mlist.ForwardMessage(msg,
text=_("""\
The attached message was received as a bounce, but either the bounce format
was not recognized, or no member addresses could be extracted from it. This
mailing list has been configured to send all unrecognized bounce messages to
the list administrator(s).
For more information see:
%(adminurl)s
"""),
subject=_('Uncaught bounce notification'),
tomoderators=0)
syslog('bounce', 'forwarding unrecognized, message-id: %s',
msg.get('message-id', 'n/a'))
else:
syslog('bounce', 'discarding unrecognized, message-id: %s',
msg.get('message-id', 'n/a'))