summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/rules/moderation.py119
1 files changed, 53 insertions, 66 deletions
diff --git a/src/mailman/rules/moderation.py b/src/mailman/rules/moderation.py
index 3f10e31bf..b6b33f720 100644
--- a/src/mailman/rules/moderation.py
+++ b/src/mailman/rules/moderation.py
@@ -30,6 +30,29 @@ from zope.component import getUtility
from zope.interface import implementer
+def _find_sender_member(mlist, msg):
+ # For every sender email in the message, try to find a member associated
+ # with that email.
+ #
+ # First, check the sender email directly. If it's a member, we're done.
+ #
+ # Next, check to see if the sender email is linked to an existing user,
+ # and if so, check to see if any of the addresses linked to that user is a
+ # member.
+ user_manager = getUtility(IUserManager)
+ for sender in msg.senders:
+ member = mlist.members.get_member(sender)
+ if member is not None:
+ return member
+ user = user_manager.get_user(sender)
+ if user is not None:
+ for address in user.addresses:
+ member = mlist.members.get_member(address.email)
+ if member is not None:
+ return member
+ return None
+
+
@public
@implementer(IRule)
class MemberModeration:
@@ -44,47 +67,27 @@ class MemberModeration:
# The MemberModeration rule misses unconditionally if any of the
# senders are banned.
ban_manager = IBanManager(mlist)
- user_manager = getUtility(IUserManager)
for sender in msg.senders:
if ban_manager.is_banned(sender):
return False
- # For each sender address, try to find a member associated with the
- # email address. Start by checking the sender email directly. If the
- # sender email is not a member, try to find the user linked to the
- # email, and then check to see if *that* user, or any of the addresses
- # linked to that user is a member. This rule hits of we find a member
- # and their moderation action is not to defer.
- for sender in msg.senders:
- # Is the sender email itself a member?
- member = mlist.members.get_member(sender)
- if member is None:
- # Is the sender email linked to a user?
- user = user_manager.get_user(sender)
- if user is not None:
- # Are any of the emails linked to this user a member?
- for address in user.addresses:
- member = mlist.members.get_member(address.email)
- if member is not None:
- # We found a member, so we don't need to check any
- # of the other linked addresses.
- break
- if member is None:
- return False
- action = (mlist.default_member_action
- if member.moderation_action is None
- else member.moderation_action)
- if action is Action.defer:
- # The regular moderation rules apply.
- return False
- elif action is not None:
- # We must stringify the moderation action so that it can be
- # stored in the pending request table.
- msgdata['moderation_action'] = action.name
- msgdata['moderation_sender'] = sender
- msgdata.setdefault('moderation_reasons', []).append(
- # This will get translated at the point of use.
- 'The message comes from a moderated member')
- return True
+ member = _find_sender_member(mlist, msg)
+ if member is None:
+ return False
+ action = (mlist.default_member_action
+ if member.moderation_action is None
+ else member.moderation_action)
+ if action is Action.defer:
+ # The regular moderation rules apply.
+ return False
+ elif action is not None:
+ # We must stringify the moderation action so that it can be
+ # stored in the pending request table.
+ msgdata['moderation_action'] = action.name
+ msgdata['moderation_sender'] = sender
+ msgdata.setdefault('moderation_reasons', []).append(
+ # This will get translated at the point of use.
+ 'The message comes from a moderated member')
+ return True
# The sender is not a member so this rule does not match.
return False
@@ -113,43 +116,27 @@ class NonmemberModeration:
for sender in msg.senders:
if ban_manager.is_banned(sender):
return False
- # Every sender must somehow be a member or nonmember. The sender
- # email can have one of those roles directly, or a user that the email
- # is linked to can have one of those roles indirectly, or any address
- # linked to one of those users can have one of those roles.
- #
- # If the sender is not somehow a member or nonmember, make them a
- # nonmember. We maintain a record of which senders are members, and
- # then the ones that aren't are made nonmembers.
- found_linked_membership = set()
+ # Every sender email must be a member or nonmember directly. If it is
+ # neither, make the email a nonmembers.
for sender in msg.senders:
- member = mlist.members.get_member(sender)
- if member is None:
- user = user_manager.get_user(sender)
- if user is not None:
- for address in user.addresses:
- if mlist.members.get_member(address.email) is not None:
- found_linked_membership.add(sender)
- else:
- found_linked_membership.add(sender)
- # Now we know whether the sender is somehow linked to a member or
- # not. If not, and the email also isn't already a nonmember, make
- # them a nonmember.
- if (mlist.nonmembers.get_member(sender) is None
- and sender not in found_linked_membership): # noqa
+ if (mlist.members.get_member(sender) is None
+ and mlist.nonmembers.get_member(sender) is None): # noqa
+ # The email must already be registered, since this happens in
+ # the incoming runner itself.
address = user_manager.get_address(sender)
assert address is not None, (
'Posting address is not registered: {}'.format(sender))
mlist.subscribe(address, MemberRole.nonmember)
- # If a membership is found, the MemberModeration rule takes precedence.
- for sender in msg.senders:
- if sender in found_linked_membership:
- return False
+ # Check to see if any of the sender emails is already a member. If
+ # so, then this rule misses.
+ member = _find_sender_member(mlist, msg)
+ if member is not None:
+ return False
# Do nonmember moderation check.
for sender in msg.senders:
nonmember = mlist.nonmembers.get_member(sender)
assert nonmember is not None, (
- 'Sender not added to the nonmembers: {}'.format(sender))
+ "sender didn't get subscribed as a nonmember".format(sender))
# Check the '*_these_nonmembers' properties first. XXX These are
# legacy attributes from MM2.1; their database type is 'pickle' and
# they should eventually get replaced.