1 # Copyright (C) 1998-2014 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 """Calculate the regular (i.e. non-digest) recipients of the message.
20 This module calculates the non-digest recipients for the message based on the
21 list's membership and configuration options. It places the list of recipients
22 on the `recipients' attribute of the message. This attribute is used by the
23 SendmailDeliver and BulkDeliver modules.
26 from __future__
import absolute_import
, print_function
, unicode_literals
34 from zope
.interface
import implementer
36 from mailman
.config
import config
37 from mailman
.core
import errors
38 from mailman
.core
.i18n
import _
39 from mailman
.interfaces
.handler
import IHandler
40 from mailman
.interfaces
.member
import DeliveryStatus
41 from mailman
.utilities
.string
import wrap
45 @implementer(IHandler
)
46 class MemberRecipients
:
47 """Calculate the regular (i.e. non-digest) recipients of the message."""
49 name
= 'member-recipients'
50 description
= _('Calculate the regular recipients of the message.')
52 def process(self
, mlist
, msg
, msgdata
):
54 # Short circuit if we've already calculated the recipients list,
55 # regardless of whether the list is empty or not.
56 if 'recipients' in msgdata
:
58 # Should the original sender should be included in the recipients list?
60 member
= mlist
.members
.get_member(msg
.sender
)
61 if member
and not member
.receive_own_postings
:
62 include_sender
= False
63 # Support for urgent messages, which bypasses digests and disabled
64 # delivery and forces an immediate delivery to all members Right Now.
65 # We are specifically /not/ allowing the site admins password to work
66 # here because we want to discourage the practice of sending the site
67 # admin password through email in the clear. (see also Approve.py)
71 password
= msg
.get('urgent', missing
)
72 if password
is not missing
:
73 if mlist
.Authenticate((config
.AuthListModerator
,
74 config
.AuthListAdmin
),
76 recipients
= mlist
.getMemberCPAddresses(
77 mlist
.getRegularMemberKeys() +
78 mlist
.getDigestMemberKeys())
79 msgdata
['recipients'] = recipients
82 # Bad Urgent: password, so reject it instead of passing it on.
83 # I think it's better that the sender know they screwed up
84 # than to deliver it normally.
86 Your urgent message to the $mlist.display_name mailing list was not authorized
87 for delivery. The original message as received by Mailman is attached.
89 raise errors
.RejectMessage(wrap(text
))
90 # Calculate the regular recipients of the message
91 recipients
= set(member
.address
.email
92 for member
in mlist
.regular_members
.members
93 if member
.delivery_status
== DeliveryStatus
.enabled
)
94 # Remove the sender if they don't want to receive their own posts
95 if not include_sender
and member
.address
.email
in recipients
:
96 recipients
.remove(member
.address
.email
)
97 # Handle topic classifications
98 do_topic_filters(mlist
, msg
, msgdata
, recipients
)
100 msgdata
['recipients'] = recipients
104 def do_topic_filters(mlist
, msg
, msgdata
, recipients
):
105 """Filter out recipients based on topics."""
106 if not mlist
.topics_enabled
:
107 # MAS: if topics are currently disabled for the list, send to all
108 # regardless of ReceiveNonmatchingTopics
110 hits
= msgdata
.get('topichits')
113 # The message hit some topics, so only deliver this message to those
114 # who are interested in one of the hit topics.
115 for user
in recipients
:
116 utopics
= mlist
.getMemberTopics(user
)
118 # This user is not interested in any topics, so they get all
121 # BAW: Slow, first-match, set intersection!
122 for topic
in utopics
:
124 # The user wants this message
127 # The user was interested in topics, but not any of the ones
128 # this message matched, so zap him.
129 zap_recipients
.append(user
)
131 # The semantics for a message that did not hit any of the pre-canned
132 # topics is to troll through the membership list, looking for users
133 # who selected at least one topic of interest, but turned on
134 # ReceiveNonmatchingTopics.
135 for user
in recipients
:
136 if not mlist
.getMemberTopics(user
):
137 # The user did not select any topics of interest, so he gets
138 # this message by default.
140 if not mlist
.getMemberOption(
141 user
, config
.ReceiveNonmatchingTopics
):
142 # The user has interest in some topics, but elects not to
143 # receive message that match no topics, so zap him.
144 zap_recipients
.append(user
)
145 # Otherwise, the user wants non-matching messages.
146 # Prune out the non-receiving users
147 for user
in zap_recipients
:
148 recipients
.remove(user
)