summaryrefslogtreecommitdiff
path: root/Mailman/Queue/CommandRunner.py
blob: 601f8bee6468b89c134d038d935f3794e7d5aa2b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# Copyright (C) 1998,1999,2000,2001 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

"""Bounce and -request command queue runner."""

# See the delivery diagram in IncomingRunner.py.  This module handles all
# email destined for mylist-owner, mylist-admin, and mylist-request.



from Mailman import mm_cfg
from Mailman.Bouncers import BouncerAPI

from Mailman.Queue.Runner import Runner
from Mailman.Queue.sbcache import get_switchboard
from Mailman.Logging.Syslog import syslog



class CommandRunner(Runner):
    def __init__(self, slice=None, numslices=1, cachelists=1):
        Runner.__init__(self, mm_cfg.CMDQUEUE_DIR,
                        slice, numslices, cachelists)

    def _dispose(self, mlist, msg, msgdata):
        # Try to figure out whether the message was destined for the -owner or
        # -admin address.  This used to be calculated in the mailowner script,
        # but now that's too expensive, so we do it here if the message
        # metadata doesn't already tell us.
        #
        # Yes, the key really is `toauthoritah', Cartman.
        if msgdata.get('toauthoritah'):
            del msgdata['toauthoritah']
            if msg['to'].lower() == mlist.GetOwnerEmail():
                msgdata['toowner'] = 1
            else:
                msgdata['toadmin'] = 1
        # BAW: Not all the functions of this qrunner require the list to be
        # locked.  Still, it's more convenient to lock it here and now and
        # deal with lock failures in one place.
        try:
            mlist.Lock(timeout=mm_cfg.LIST_LOCK_TIMEOUT)
        except LockFile.TimeOutError:
            # Oh well, try again later
            return 1
        #
        # runner specific code
        try:
            # The message may be destined to one of three list related
            # recipients (note that posts to the list membership are handled
            # by the IncomingRunner via the qfiles/in queue):
            #
            # <list>-admin -- all errors are directed to this address, which
            # performs bounce processing.  If the bounce processor fails to
            # detect a bounce, the message is forwarded on to the <list>-owner
            # address.
            #
            # <list>-owner -- this message is directed to the human operators
            # of the list.  No bounce processing is performed, and the message
            # is forwarded to the list owners.  However, it is possible that
            # there are bogus addresses in the list owners, so if <list>-owner
            # appears to get a message from a "likely bounce sender" then it
            # simply discards the message.  BAW: should it save it some place?
            #
            # <list>-request -- this message is an emailed command, sent to
            # the command robot.  Pass it on to the command handler.
            #
            # Note that which of these subsystems the message is destined for
            # is determined by message metadata, as assigned by the front-end
            # mail filter scripts.  I've thought about adding additional
            # subsystems such as <list>-subscribe and <list>-unsubscribe as
            # shorthands for getting on and off the list.
            #
            # See the diagram in IncomingRunner.py for more information.
            if msgdata.get('toadmin'):
                if mlist.bounce_processing:
                    if BouncerAPI.ScanMessages(mlist, msg):
                        return
                # Either bounce processing isn't turned on or the bounce
                # detector found no recognizable bounce format in the message.
                # In either case, forward the dang thing off to the list
                # owners.  Be sure to munge the headers so that any bounces
                # from the list owners goes to the -owner address instead of
                # the -admin address.  This will avoid bounce loops.
                virginq = get_switchboard(mm_cfg.VIRGINQUEUE_DIR)
                virginq.enqueue(msg, msgdata,
                                recips = mlist.owner[:],
                                errorsto = mlist.GetOwnerEmail(),
                                noack = 0         # enable Replybot
                                )
                return
            elif msgdata.get('toowner'):
                # The message could have been a bounce from a broken list
                # owner address.  About the only other test we can do is to
                # see if the message is appearing to come from a well-known
                # MTA generated address.
                sender = msg.get_sender()
                i = sender.find('@')
                if i >= 0:
                    senderlhs = sender[:i].lower()
                else:
                    senderlhs = sender
                if senderlhs in mm_cfg.LIKELY_BOUNCE_SENDERS:
                    syslog('error', 'bounce loop detected from: %s' % sender)
                    return
                # Any messages to the owner address must have Errors-To: set
                # back to the owners address so bounce loops can be broken, as
                # per the code above.
                virginq = get_switchboard(mm_cfg.VIRGINQUEUE_DIR)
                virginq.enqueue(msg, msgdata,
                                recips = mlist.owner,
                                errorsto = mlist.GetOwnerEmail(),
                                noack = 0         # enable Replybot
                                )
                return
            elif msgdata.get('torequest'):
                # Just pass the message off the command handler
                mlist.ParseMailCommands(msg)
                return
        finally:
            mlist.Save()
            mlist.Unlock()