summaryrefslogtreecommitdiff
path: root/src/mailman/core/chains.py
blob: c251d38bc8bb7fedae0d2cd2b0d8d788fb5d8b50 (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
# Copyright (C) 2007-2017 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
# GNU Mailman 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 3 of the License, or (at your option)
# any later version.
#
# GNU Mailman 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
# GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.

"""Application support for chain processing."""

from mailman.config import config
from mailman.interfaces.chain import IChain, LinkAction
from mailman.utilities.modules import add_components
from public import public


@public
def process(mlist, msg, msgdata, start_chain='default-posting-chain'):
    """Process the message through a chain.

    :param mlist: the IMailingList for this message.
    :param msg: The Message object.
    :param msgdata: The message metadata dictionary.
    :param start_chain: The name of the chain to start the processing with.
    """
    # Set up some bookkeeping.
    chain_stack = []
    msgdata['rule_hits'] = hits = []
    msgdata['rule_misses'] = misses = []
    # Find the starting chain and begin iterating through its links.
    chain = config.chains[start_chain]
    chain_iter = chain.get_links(mlist, msg, msgdata)
    # Loop until we've reached the end of all processing chains.
    while chain:
        # Iterate over all links in the chain.  Do this outside a for-loop so
        # we can capture a chain's link iterator in mid-flight.  This supports
        # the 'detour' link action.
        try:
            link = next(chain_iter)
        except StopIteration:
            # This chain is exhausted.  Pop the last chain on the stack and
            # continue iterating through it.  If there's nothing left on the
            # chain stack then we're completely finished processing.
            if len(chain_stack) == 0:
                return
            chain, chain_iter = chain_stack.pop()
            continue
        if link.rule.check(mlist, msg, msgdata):
            if link.rule.record:
                hits.append(link.rule.name)
            # The rule matched so run its action.
            if link.action is LinkAction.jump:
                chain = link.chain
                chain_iter = chain.get_links(mlist, msg, msgdata)
                continue
            elif link.action is LinkAction.detour:
                # Push the current chain so that we can return to it when
                # the next chain is finished.
                chain_stack.append((chain, chain_iter))
                chain = link.chain
                chain_iter = chain.get_links(mlist, msg, msgdata)
                continue
            elif link.action is LinkAction.stop:
                # Stop all processing.
                return
            elif link.action is LinkAction.defer:
                # Just process the next link in the chain.
                pass
            elif link.action is LinkAction.run:
                link.function(mlist, msg, msgdata)
            else:
                raise AssertionError(
                    'Bad link action: {}'.format(link.action))
        else:
            # The rule did not match; keep going.
            if link.rule.record:
                misses.append(link.rule.name)


@public
def initialize():
    """Set up chains, both built-in and from the database."""
    add_components('mailman.chains', IChain, config.chains)
    # XXX Read chains from the database and initialize them.