summaryrefslogtreecommitdiff
path: root/Mailman/pipeline/avoid_duplicates.py
blob: 2c52a7781db6c4831f56cc5859039e4df171e01b (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) 2002-2008 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.

"""If the user wishes it, do not send duplicates of the same message.

This module keeps an in-memory dictionary of Message-ID: and recipient pairs.
If a message with an identical Message-ID: is about to be sent to someone who
has already received a copy, we either drop the message, add a duplicate
warning header, or pass it through, depending on the user's preferences.
"""

from email.Utils import getaddresses, formataddr
from Mailman.configuration import config

COMMASPACE = ', '



def process(mlist, msg, msgdata):
    recips = msgdata.get('recips')
    # Short circuit
    if not recips:
        return
    # Seed this set with addresses we don't care about dup avoiding.
    listaddrs = set((mlist.posting_address,
                     mlist.bounces_address,
                     mlist.owner_address,
                     mlist.request_address))
    explicit_recips = listaddrs.copy()
    # Figure out the set of explicit recipients
    cc_addresses = {}
    for header in ('to', 'cc', 'resent-to', 'resent-cc'):
        addrs = getaddresses(msg.get_all(header, []))
        header_addresses = dict((addr, formataddr((name, addr)))
                                for name, addr in addrs
                                if addr)
        if header == 'cc':
            # Yes, it's possible that an address is mentioned in multiple CC
            # headers using different names.  In that case, the last real name
            # will win, but that doesn't seem like such a big deal.  Besides,
            # how else would you chose?
            cc_addresses.update(header_addresses)
        # Ignore the list addresses for purposes of dup avoidance.
        explicit_recips |= set(header_addresses)
    # Now strip out the list addresses
    explicit_recips -= listaddrs
    if not explicit_recips:
        # No one was explicitly addressed, so we can't do any dup collapsing
        return
    newrecips = set()
    for r in recips:
        # If this recipient is explicitly addressed...
        if r in explicit_recips:
            send_duplicate = True
            # If the member wants to receive duplicates, or if the recipient
            # is not a member at all, they will get a copy.
            # header.
            member = mlist.members.get_member(r)
            if member and not member.receive_list_copy:
                send_duplicate = False
            # We'll send a duplicate unless the user doesn't wish it.  If
            # personalization is enabled, the add-dupe-header flag will add a
            # X-Mailman-Duplicate: yes header for this user's message.
            if send_duplicate:
                msgdata.setdefault('add-dup-header', set()).add(r)
                newrecips.add(r)
            elif r in cc_addresses:
                del cc_addresses[r]
        else:
            # Otherwise, this is the first time they've been in the recips
            # list.  Add them to the newrecips list and flag them as having
            # received this message.
            newrecips.add(r)
    # Set the new list of recipients.  XXX recips should always be a set.
    msgdata['recips'] = list(newrecips)
    # RFC 2822 specifies zero or one CC header
    if cc_addresses:
        del msg['cc']
        msg['CC'] = COMMASPACE.join(cc_addresses.values())