diff options
Diffstat (limited to 'Mailman/Queue')
-rw-r--r-- | Mailman/Queue/ArchRunner.py | 6 | ||||
-rw-r--r-- | Mailman/Queue/BounceRunner.py | 33 | ||||
-rw-r--r-- | Mailman/Queue/CommandRunner.py | 32 | ||||
-rw-r--r-- | Mailman/Queue/IncomingRunner.py | 17 | ||||
-rw-r--r-- | Mailman/Queue/MaildirRunner.py | 1 | ||||
-rw-r--r-- | Mailman/Queue/NewsRunner.py | 46 | ||||
-rw-r--r-- | Mailman/Queue/Switchboard.py | 6 |
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 = {} |