aboutsummaryrefslogtreecommitdiffstats
path: root/Mailman/Queue
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/Queue')
-rw-r--r--Mailman/Queue/ArchRunner.py6
-rw-r--r--Mailman/Queue/BounceRunner.py33
-rw-r--r--Mailman/Queue/CommandRunner.py32
-rw-r--r--Mailman/Queue/IncomingRunner.py17
-rw-r--r--Mailman/Queue/MaildirRunner.py1
-rw-r--r--Mailman/Queue/NewsRunner.py46
-rw-r--r--Mailman/Queue/Switchboard.py6
7 files changed, 101 insertions, 40 deletions
diff --git a/Mailman/Queue/ArchRunner.py b/Mailman/Queue/ArchRunner.py
index 62714537..17056e29 100644
--- a/Mailman/Queue/ArchRunner.py
+++ b/Mailman/Queue/ArchRunner.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2000,2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 2000-2016 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -19,6 +19,7 @@
import time
from email.Utils import parsedate_tz, mktime_tz, formatdate
+from Mailman import i18n
from Mailman import mm_cfg
from Mailman import LockFile
from Mailman.Queue.Runner import Runner
@@ -70,6 +71,9 @@ class ArchRunner(Runner):
# oh well, try again later
return 1
try:
+ # Archiving should be done in the list's preferred language, not
+ # the sender's language.
+ i18n.set_language(mlist.preferred_language)
mlist.ArchiveMail(msg)
mlist.Save()
finally:
diff --git a/Mailman/Queue/BounceRunner.py b/Mailman/Queue/BounceRunner.py
index d219d6e9..2d14f284 100644
--- a/Mailman/Queue/BounceRunner.py
+++ b/Mailman/Queue/BounceRunner.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2008 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -29,7 +29,9 @@ from email.Utils import parseaddr
from Mailman import mm_cfg
from Mailman import Utils
from Mailman import LockFile
+from Mailman.Errors import NotAMemberError
from Mailman.Message import UserNotification
+from Mailman.Bouncer import _BounceInfo
from Mailman.Bouncers import BouncerAPI
from Mailman.Queue.Runner import Runner
from Mailman.Queue.sbcache import get_switchboard
@@ -150,11 +152,26 @@ class BounceMixin:
mlist.Lock()
try:
op, addr, bmsg = mlist.pend_confirm(token)
- info = mlist.getBounceInfo(addr)
- mlist.disableBouncingMember(addr, info, bmsg)
- # Only save the list if we're unlocking it
- if not locked:
- mlist.Save()
+ # For Python 2.4 compatibility we need an inner try because
+ # try: ... except: ... finally: requires Python 2.5+
+ try:
+ info = mlist.getBounceInfo(addr)
+ if not info:
+ # info was deleted before probe bounce was received.
+ # Just create a new info.
+ info = _BounceInfo(addr,
+ 0.0,
+ time.localtime()[:3],
+ mlist.bounce_you_are_disabled_warnings
+ )
+ mlist.disableBouncingMember(addr, info, bmsg)
+ # Only save the list if we're unlocking it
+ if not locked:
+ mlist.Save()
+ except NotAMemberError:
+ # Member was removed before probe bounce returned.
+ # Just ignore it.
+ pass
finally:
if not locked:
mlist.Unlock()
@@ -244,6 +261,7 @@ class BounceRunner(Runner, BounceMixin):
return
# If that still didn't return us any useful addresses, then send it on
# or discard it.
+ addrs = filter(None, addrs)
if not addrs:
syslog('bounce',
'%s: bounce message w/no discernable addresses: %s',
@@ -254,7 +272,8 @@ class BounceRunner(Runner, BounceMixin):
# BAW: It's possible that there are None's in the list of addresses,
# although I'm unsure how that could happen. Possibly ScanMessages()
# can let None's sneak through. In any event, this will kill them.
- addrs = filter(None, addrs)
+ # addrs = filter(None, addrs)
+ # MAS above filter moved up so we don't try to queue an empty list.
self._queue_bounces(mlist.internal_name(), addrs, msg)
_doperiodic = BounceMixin._doperiodic
diff --git a/Mailman/Queue/CommandRunner.py b/Mailman/Queue/CommandRunner.py
index b63b050c..a9f6f000 100644
--- a/Mailman/Queue/CommandRunner.py
+++ b/Mailman/Queue/CommandRunner.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2011 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -12,7 +12,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
"""-request robot command queue runner."""
@@ -132,22 +133,35 @@ class Results:
__import__(modname)
handler = sys.modules[modname]
# ValueError can be raised if cmd has dots in it.
- except (ImportError, ValueError):
+ # and KeyError if cmd is otherwise good but ends with a dot.
+ # and TypeError if cmd has a null byte.
+ except (ImportError, ValueError, KeyError, TypeError):
# If we're on line zero, it was the Subject: header that didn't
# contain a command. It's possible there's a Re: prefix (or
# localized version thereof) on the Subject: line that's messing
# things up. Pop the prefix off and try again... once.
#
+ # At least one MUA (163.com web mail) has been observed that
+ # inserts 'Re:' with no following space, so try to account for
+ # that too.
+ #
# If that still didn't work it isn't enough to stop processing.
# BAW: should we include a message that the Subject: was ignored?
- if not self.subjcmdretried and args:
+ #
+ # But first, be sure we're looking at the Subject: and not past
+ # it already.
+ if self.lineno != 0:
+ return BADCMD
+ if self.subjcmdretried < 1:
+ self.subjcmdretried += 1
+ if re.search('^.*:.+', cmd):
+ cmd = re.sub('.*:', '', cmd).lower()
+ return self.do_command(cmd, args)
+ if self.subjcmdretried < 2 and args:
self.subjcmdretried += 1
- cmd = args.pop(0)
+ cmd = args.pop(0).lower()
return self.do_command(cmd, args)
- if self.lineno <> 0:
- return BADCMD
- else:
- return BADSUBJ
+ return BADSUBJ
if handler.process(self, args):
return STOP
else:
diff --git a/Mailman/Queue/IncomingRunner.py b/Mailman/Queue/IncomingRunner.py
index bcc51209..2c6c2815 100644
--- a/Mailman/Queue/IncomingRunner.py
+++ b/Mailman/Queue/IncomingRunner.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2003 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -157,8 +157,15 @@ class IncomingRunner(Runner):
os._exit(1)
except Errors.DiscardMessage:
# Throw the message away; we need do nothing else with it.
- syslog('vette', 'Message discarded, msgid: %s',
- msg.get('message-id', 'n/a'))
+ # We do need to push the current handler back in the pipeline
+ # just in case the syslog call throws an exception and the
+ # message is shunted.
+ pipeline.insert(0, handler)
+ syslog('vette', """Message discarded, msgid: %s'
+ list: %s,
+ handler: %s""",
+ msg.get('message-id', 'n/a'),
+ mlist.real_name, handler)
return 0
except Errors.HoldMessage:
# Let the approval process take it from here. The message no
@@ -166,6 +173,10 @@ class IncomingRunner(Runner):
return 0
except Errors.RejectMessage, e:
# Log this.
+ # We do need to push the current handler back in the pipeline
+ # just in case the syslog call or BounceMessage throws an
+ # exception and the message is shunted.
+ pipeline.insert(0, handler)
syslog('vette', """Message rejected, msgid: %s
list: %s,
handler: %s,
diff --git a/Mailman/Queue/MaildirRunner.py b/Mailman/Queue/MaildirRunner.py
index 5959dcd6..d9fe02cb 100644
--- a/Mailman/Queue/MaildirRunner.py
+++ b/Mailman/Queue/MaildirRunner.py
@@ -172,6 +172,7 @@ class MaildirRunner(Runner):
elif subq == 'owner':
msgdata.update({
'toowner': 1,
+ 'envsender': Utils.get_site_email(extra='bounces'),
'pipeline': mm_cfg.OWNER_PIPELINE,
})
queue = get_switchboard(mm_cfg.INQUEUE_DIR)
diff --git a/Mailman/Queue/NewsRunner.py b/Mailman/Queue/NewsRunner.py
index 44850063..fe693f28 100644
--- a/Mailman/Queue/NewsRunner.py
+++ b/Mailman/Queue/NewsRunner.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2000-2005 by the Free Software Foundation, Inc.
+# Copyright (C) 2000-2016 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -12,7 +12,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
"""NNTP queue runner."""
@@ -109,22 +110,18 @@ def prepare_message(mlist, msg, msgdata):
or msgdata.get('origsubj')
if not mlist.news_prefix_subject_too and stripped_subject is not None:
del msg['subject']
- msg['subject'] = stripped_subject
+ msg['Subject'] = stripped_subject
+ # Make sure we have a non-blank subject.
+ if not msg.get('subject', ''):
+ del msg['subject']
+ msg['Subject'] = '(no subject)'
# Add the appropriate Newsgroups: header
- ngheader = msg['newsgroups']
- if ngheader is not None:
- # See if the Newsgroups: header already contains our linked_newsgroup.
- # If so, don't add it again. If not, append our linked_newsgroup to
- # the end of the header list
- ngroups = [s.strip() for s in ngheader.split(',')]
- if mlist.linked_newsgroup not in ngroups:
- ngroups.append(mlist.linked_newsgroup)
- # Subtitute our new header for the old one.
- del msg['newsgroups']
- msg['Newsgroups'] = COMMASPACE.join(ngroups)
- else:
- # Newsgroups: isn't in the message
- msg['Newsgroups'] = mlist.linked_newsgroup
+ if msg['newsgroups'] is not None:
+ # This message is gated from our list to it's associated usnet group.
+ # If it has a Newsgroups: header mentioning other groups, it's not
+ # up to us to post it to those groups.
+ del msg['newsgroups']
+ msg['Newsgroups'] = mlist.linked_newsgroup
# Note: We need to be sure two messages aren't ever sent to the same list
# in the same process, since message ids need to be unique. Further, if
# messages are crossposted to two Usenet-gated mailing lists, they each
@@ -133,6 +130,9 @@ def prepare_message(mlist, msg, msgdata):
# isn't ours with one of ours, so we need to parse it to be sure we're not
# looping.
#
+ # We also add the original Message-ID: to References: to try to help with
+ # threading issues and create another header for documentation.
+ #
# Our Message-ID format is <mailman.secs.pid.listname@hostname>
msgid = msg['message-id']
hackmsgid = True
@@ -145,6 +145,18 @@ def prepare_message(mlist, msg, msgdata):
if hackmsgid:
del msg['message-id']
msg['Message-ID'] = Utils.unique_message_id(mlist)
+ if msgid:
+ msg['X-Mailman-Original-Message-ID'] = msgid
+ refs = msg['references']
+ del msg['references']
+ if not refs:
+ refs = msg.get('in-reply-to', '')
+ else:
+ msg['X-Mailman-Original-References'] = refs
+ if refs:
+ msg['References'] = '\n '.join([refs, msgid])
+ else:
+ msg['References'] = msgid
# Lines: is useful
if msg['Lines'] is None:
# BAW: is there a better way?
diff --git a/Mailman/Queue/Switchboard.py b/Mailman/Queue/Switchboard.py
index bd1cd357..a2c31263 100644
--- a/Mailman/Queue/Switchboard.py
+++ b/Mailman/Queue/Switchboard.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2008 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2013 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -184,8 +184,8 @@ class Switchboard:
else:
os.unlink(bakfile)
except EnvironmentError, e:
- syslog('error', 'Failed to unlink/preserve backup file: %s',
- bakfile)
+ syslog('error', 'Failed to unlink/preserve backup file: %s\n%s',
+ bakfile, e)
def files(self, extension='.pck'):
times = {}