summaryrefslogtreecommitdiff
path: root/src/mailman/rules
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/rules')
-rw-r--r--src/mailman/rules/administrivia.py5
-rw-r--r--src/mailman/rules/banned_address.py6
-rw-r--r--src/mailman/rules/dmarc.py24
-rw-r--r--src/mailman/rules/emergency.py9
-rw-r--r--src/mailman/rules/implicit_dest.py5
-rw-r--r--src/mailman/rules/loop.py9
-rw-r--r--src/mailman/rules/max_recipients.py10
-rw-r--r--src/mailman/rules/max_size.py10
-rw-r--r--src/mailman/rules/moderation.py23
-rw-r--r--src/mailman/rules/news_moderation.py9
-rw-r--r--src/mailman/rules/no_senders.py10
-rw-r--r--src/mailman/rules/no_subject.py9
-rw-r--r--src/mailman/rules/suspicious.py10
-rw-r--r--src/mailman/rules/tests/test_administrivia.py51
-rw-r--r--src/mailman/rules/tests/test_banned_address.py24
-rw-r--r--src/mailman/rules/tests/test_emergency.py52
-rw-r--r--src/mailman/rules/tests/test_implicit_dest.py51
-rw-r--r--src/mailman/rules/tests/test_loop.py52
-rw-r--r--src/mailman/rules/tests/test_max_recipients.py53
-rw-r--r--src/mailman/rules/tests/test_max_size.py55
-rw-r--r--src/mailman/rules/tests/test_news_moderation.py53
-rw-r--r--src/mailman/rules/tests/test_no_senders.py5
-rw-r--r--src/mailman/rules/tests/test_no_subject.py9
-rw-r--r--src/mailman/rules/tests/test_suspicious.py13
24 files changed, 522 insertions, 35 deletions
diff --git a/src/mailman/rules/administrivia.py b/src/mailman/rules/administrivia.py
index d3d18d693..bd4602149 100644
--- a/src/mailman/rules/administrivia.py
+++ b/src/mailman/rules/administrivia.py
@@ -89,5 +89,10 @@ class Administrivia:
continue
minargs, maxargs = EMAIL_COMMANDS[words[0]]
if minargs <= len(words) - 1 <= maxargs:
+ msgdata['moderation_sender'] = msg.sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ _('Message contains administrivia'))
return True
return False
diff --git a/src/mailman/rules/banned_address.py b/src/mailman/rules/banned_address.py
index 77fb739a6..9fd011102 100644
--- a/src/mailman/rules/banned_address.py
+++ b/src/mailman/rules/banned_address.py
@@ -38,5 +38,11 @@ class BannedAddress:
ban_manager = IBanManager(mlist)
for sender in msg.senders:
if ban_manager.is_banned(sender):
+ msgdata['moderation_sender'] = sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ (_('Message sender {} is banned from this list'),
+ sender))
return True
return False
diff --git a/src/mailman/rules/dmarc.py b/src/mailman/rules/dmarc.py
index 90fff0855..9f9b11673 100644
--- a/src/mailman/rules/dmarc.py
+++ b/src/mailman/rules/dmarc.py
@@ -307,17 +307,23 @@ class DMARCMitigation:
msgdata['dmarc'] = True
if mlist.dmarc_mitigate_action is DMARCMitigateAction.discard:
msgdata['moderation_action'] = 'discard'
- msgdata['moderation_reasons'] = [_('DMARC moderation')]
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ _('DMARC moderation'))
elif mlist.dmarc_mitigate_action is DMARCMitigateAction.reject:
listowner = mlist.owner_address # noqa F841
- reason = (mlist.dmarc_moderation_notice or
- _('You are not allowed to post to this mailing '
- 'list From: a domain which publishes a DMARC '
- 'policy of reject or quarantine, and your message'
- ' has been automatically rejected. If you think '
- 'that your messages are being rejected in error, '
- 'contact the mailing list owner at ${listowner}.'))
- msgdata['moderation_reasons'] = [wrap(reason)]
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ reason = (mlist.dmarc_moderation_notice or _(
+ 'You are not allowed to post to this mailing '
+ 'list From: a domain which publishes a DMARC '
+ 'policy of reject or quarantine, and your message'
+ ' has been automatically rejected. If you think '
+ 'that your messages are being rejected in error, '
+ 'contact the mailing list owner at ${listowner}.'))
+ msgdata.setdefault('moderation_reasons', []).append(
+ wrap(reason))
msgdata['moderation_action'] = 'reject'
else:
return False
diff --git a/src/mailman/rules/emergency.py b/src/mailman/rules/emergency.py
index ac512f391..3f8da002b 100644
--- a/src/mailman/rules/emergency.py
+++ b/src/mailman/rules/emergency.py
@@ -39,4 +39,11 @@ class Emergency:
def check(self, mlist, msg, msgdata):
"""See `IRule`."""
- return mlist.emergency and not msgdata.get('moderator_approved')
+ if mlist.emergency and not msgdata.get('moderator_approved'):
+ msgdata['moderation_sender'] = msg.sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ _('Emergency moderation is in effect for this list'))
+ return True
+ return False
diff --git a/src/mailman/rules/implicit_dest.py b/src/mailman/rules/implicit_dest.py
index 7e6658d49..26729a86e 100644
--- a/src/mailman/rules/implicit_dest.py
+++ b/src/mailman/rules/implicit_dest.py
@@ -88,4 +88,9 @@ class ImplicitDestination:
if re.match(escaped, recipient, re.IGNORECASE):
return False
# Nothing matched.
+ msgdata['moderation_sender'] = msg.sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ _('Message has implicit destination'))
return True
diff --git a/src/mailman/rules/loop.py b/src/mailman/rules/loop.py
index ac4d00461..b50b55ed6 100644
--- a/src/mailman/rules/loop.py
+++ b/src/mailman/rules/loop.py
@@ -37,4 +37,11 @@ class Loop:
# Has this message already been posted to this list?
list_posts = set(value.strip().lower()
for value in msg.get_all('list-post', []))
- return mlist.posting_address in list_posts
+ if mlist.posting_address in list_posts:
+ msgdata['moderation_sender'] = msg.sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ _('Message has already been posted to this list'))
+ return True
+ return False
diff --git a/src/mailman/rules/max_recipients.py b/src/mailman/rules/max_recipients.py
index 1f95f71cc..8a90fd451 100644
--- a/src/mailman/rules/max_recipients.py
+++ b/src/mailman/rules/max_recipients.py
@@ -41,4 +41,12 @@ class MaximumRecipients:
# Figure out how many recipients there are
recipients = getaddresses(msg.get_all('to', []) +
msg.get_all('cc', []))
- return len(recipients) >= mlist.max_num_recipients
+ if len(recipients) >= mlist.max_num_recipients:
+ msgdata['moderation_sender'] = msg.sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ (_('Message has more than {} recipients'),
+ mlist.max_num_recipients))
+ return True
+ return False
diff --git a/src/mailman/rules/max_size.py b/src/mailman/rules/max_size.py
index 46c8fc5ac..2554e0db0 100644
--- a/src/mailman/rules/max_size.py
+++ b/src/mailman/rules/max_size.py
@@ -39,4 +39,12 @@ class MaximumSize:
assert hasattr(msg, 'original_size'), (
'Message was not sized on initial parsing.')
# The maximum size is specified in 1024 bytes.
- return msg.original_size / 1024.0 > mlist.max_message_size
+ if msg.original_size / 1024.0 > mlist.max_message_size:
+ msgdata['moderation_sender'] = msg.sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ (_('The message is larger than the {} KB maximum size'),
+ mlist.max_message_size))
+ return True
+ return False
diff --git a/src/mailman/rules/moderation.py b/src/mailman/rules/moderation.py
index 322216fb8..7f78d3ce7 100644
--- a/src/mailman/rules/moderation.py
+++ b/src/mailman/rules/moderation.py
@@ -84,9 +84,10 @@ class MemberModeration:
# 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')
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ _('The message comes from a moderated member'))
return True
# The sender is not a member so this rule does not match.
return False
@@ -146,10 +147,12 @@ class NonmemberModeration:
for addr in checklist:
if ((addr.startswith('^') and re.match(addr, sender))
or addr == sender): # noqa: W503
- # The reason will get translated at the point of use.
- reason = 'The sender is in the nonmember {} list'
- _record_action(msgdata, action, sender,
- reason.format(action))
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ reason = (
+ _('The sender is in the nonmember {} list'),
+ action)
+ _record_action(msgdata, action, sender, reason)
return True
action = (mlist.default_nonmember_action
if nonmember.moderation_action is None
@@ -160,9 +163,9 @@ class NonmemberModeration:
elif action is not None:
# We must stringify the moderation action so that it can be
# stored in the pending request table.
- #
- # The reason will get translated at the point of use.
- reason = 'The message is not from a list member'
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ reason = _('The message is not from a list member')
_record_action(msgdata, action.name, sender, reason)
return True
# The sender must be a member, so this rule does not match.
diff --git a/src/mailman/rules/news_moderation.py b/src/mailman/rules/news_moderation.py
index a606197f0..3d3d39dc6 100644
--- a/src/mailman/rules/news_moderation.py
+++ b/src/mailman/rules/news_moderation.py
@@ -38,4 +38,11 @@ class ModeratedNewsgroup:
def check(self, mlist, msg, msgdata):
"""See `IRule`."""
- return mlist.newsgroup_moderation == NewsgroupModeration.moderated
+ if mlist.newsgroup_moderation is NewsgroupModeration.moderated:
+ msgdata['moderation_sender'] = msg.sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ _('Post to a moderated newsgroup gateway'))
+ return True
+ return False
diff --git a/src/mailman/rules/no_senders.py b/src/mailman/rules/no_senders.py
index 7e8c78fa7..24e5fa5bd 100644
--- a/src/mailman/rules/no_senders.py
+++ b/src/mailman/rules/no_senders.py
@@ -37,9 +37,9 @@ class NoSenders:
if msg.sender:
return False
else:
- msgdata['moderation_action'] = 'discard'
- msgdata['moderation_sender'] = _('None')
- msgdata.setdefault('moderation_reasons', []).append(
- # This will get translated at the point of use.
- 'The message has no valid senders')
+ msgdata['moderation_sender'] = 'N/A'
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ _('The message has no valid senders'))
return True
diff --git a/src/mailman/rules/no_subject.py b/src/mailman/rules/no_subject.py
index f4d2f6a5d..9793ca2a5 100644
--- a/src/mailman/rules/no_subject.py
+++ b/src/mailman/rules/no_subject.py
@@ -37,4 +37,11 @@ class NoSubject:
# Convert the header value to a str because it may be an
# email.header.Header instance.
subject = str(msg.get('subject', '')).strip()
- return subject == ''
+ if subject == '':
+ msgdata['moderation_sender'] = msg.sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append(
+ _('Message has no subject'))
+ return True
+ return False
diff --git a/src/mailman/rules/suspicious.py b/src/mailman/rules/suspicious.py
index 9f0f0e922..1bf96d22a 100644
--- a/src/mailman/rules/suspicious.py
+++ b/src/mailman/rules/suspicious.py
@@ -41,7 +41,7 @@ class SuspiciousHeader:
def check(self, mlist, msg, msgdata):
"""See `IRule`."""
return (mlist.bounce_matching_headers and
- has_matching_bounce_header(mlist, msg))
+ has_matching_bounce_header(mlist, msg, msgdata))
def _parse_matching_header_opt(mlist):
@@ -77,7 +77,7 @@ bad regexp in bounce_matching_header line: %s
return all
-def has_matching_bounce_header(mlist, msg):
+def has_matching_bounce_header(mlist, msg, msgdata):
"""Does the message have a matching bounce header?
:param mlist: The mailing list the message is destined for.
@@ -90,5 +90,11 @@ def has_matching_bounce_header(mlist, msg):
# Convert the header value to a str because it may be an
# email.header.Header instance.
if cre.search(str(value)):
+ msgdata['moderation_sender'] = msg.sender
+ with _.defer_translation():
+ # This will be translated at the point of use.
+ msgdata.setdefault('moderation_reasons', []).append((_(
+ 'Header "{}" matched a bounce_matching_header line'),
+ str(value)))
return True
return False
diff --git a/src/mailman/rules/tests/test_administrivia.py b/src/mailman/rules/tests/test_administrivia.py
new file mode 100644
index 000000000..e370a0bf2
--- /dev/null
+++ b/src/mailman/rules/tests/test_administrivia.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2016-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/>.
+
+"""Test the `administrivia` rule."""
+
+import unittest
+
+from mailman.app.lifecycle import create_list
+from mailman.rules import administrivia
+from mailman.testing.helpers import specialized_message_from_string as mfs
+from mailman.testing.layers import ConfigLayer
+
+
+class TestAdministrivia(unittest.TestCase):
+ """Test the administrivia rule."""
+
+ layer = ConfigLayer
+
+ def setUp(self):
+ self._mlist = create_list('test@example.com')
+
+ def test_administrivia_returns_reason(self):
+ # Ensure administrivia rule returns a reason.
+ msg = mfs("""\
+From: anne@example.com
+To: test@example.com
+Subject: unsubscribe
+Message-ID: <ant>
+
+A message body.
+""")
+ rule = administrivia.Administrivia()
+ msgdata = {}
+ result = rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(msgdata['moderation_reasons'],
+ ['Message contains administrivia'])
diff --git a/src/mailman/rules/tests/test_banned_address.py b/src/mailman/rules/tests/test_banned_address.py
index 1ecab5b18..2d73ed3fb 100644
--- a/src/mailman/rules/tests/test_banned_address.py
+++ b/src/mailman/rules/tests/test_banned_address.py
@@ -74,6 +74,30 @@ A message body.
result = rule.check(self._mlist, msg, {})
self.assertTrue(result)
+ def test_rule_returns_reason(self):
+ # Ensure a reason is returned.
+ user_manager = getUtility(IUserManager)
+ anne = user_manager.create_user('anne@example.com')
+ set_preferred(anne)
+ IBanManager(self._mlist).ban('anne@example.com')
+ msg = mfs("""\
+From: anne@example.com
+To: test@example.com
+Subject: A test message
+Message-ID: <ant>
+MIME-Version: 1.0
+
+A message body.
+""")
+ rule = banned_address.BannedAddress()
+ msgdata = {}
+ result = rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(
+ msgdata['moderation_reasons'],
+ [('Message sender {} is banned from this list',
+ 'anne@example.com')])
+
def test_banned_address_linked_to_user(self):
# Anne is subscribed to a mailing list as a user with her preferred
# address. She also has a secondary address which is banned and which
diff --git a/src/mailman/rules/tests/test_emergency.py b/src/mailman/rules/tests/test_emergency.py
new file mode 100644
index 000000000..529742987
--- /dev/null
+++ b/src/mailman/rules/tests/test_emergency.py
@@ -0,0 +1,52 @@
+# Copyright (C) 2016-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/>.
+
+"""Test the `emergency` rule."""
+
+import unittest
+
+from mailman.app.lifecycle import create_list
+from mailman.rules import emergency
+from mailman.testing.helpers import specialized_message_from_string as mfs
+from mailman.testing.layers import ConfigLayer
+
+
+class TestEmergency(unittest.TestCase):
+ """Test the emergency rule."""
+
+ layer = ConfigLayer
+
+ def setUp(self):
+ self._mlist = create_list('test@example.com')
+
+ def test_emergency_returns_reason(self):
+ # Ensure emergency rule returns a reason.
+ msg = mfs("""\
+From: anne@example.com
+To: test@example.com
+Subject: A Subject
+Message-ID: <ant>
+
+A message body.
+""")
+ rule = emergency.Emergency()
+ self._mlist.emergency = True
+ msgdata = {}
+ result = rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(msgdata['moderation_reasons'],
+ ['Emergency moderation is in effect for this list'])
diff --git a/src/mailman/rules/tests/test_implicit_dest.py b/src/mailman/rules/tests/test_implicit_dest.py
new file mode 100644
index 000000000..aed317f57
--- /dev/null
+++ b/src/mailman/rules/tests/test_implicit_dest.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2016-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/>.
+
+"""Test the `implicit_dest` rule."""
+
+import unittest
+
+from mailman.app.lifecycle import create_list
+from mailman.rules import implicit_dest
+from mailman.testing.helpers import specialized_message_from_string as mfs
+from mailman.testing.layers import ConfigLayer
+
+
+class TestImplicitDestination(unittest.TestCase):
+ """Test the implicit_dest rule."""
+
+ layer = ConfigLayer
+
+ def setUp(self):
+ self._mlist = create_list('test@example.com')
+
+ def test_implicit_dest_returns_reason(self):
+ # Ensure implicit_dest rule returns a reason.
+ msg = mfs("""\
+From: anne@example.com
+To: bogus@example.com
+Subject: A Subject
+Message-ID: <ant>
+
+A message body.
+""")
+ rule = implicit_dest.ImplicitDestination()
+ msgdata = {}
+ result = rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(msgdata['moderation_reasons'],
+ ['Message has implicit destination'])
diff --git a/src/mailman/rules/tests/test_loop.py b/src/mailman/rules/tests/test_loop.py
new file mode 100644
index 000000000..09f9fc905
--- /dev/null
+++ b/src/mailman/rules/tests/test_loop.py
@@ -0,0 +1,52 @@
+# Copyright (C) 2016-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/>.
+
+"""Test the `loop` rule."""
+
+import unittest
+
+from mailman.app.lifecycle import create_list
+from mailman.rules import loop
+from mailman.testing.helpers import specialized_message_from_string as mfs
+from mailman.testing.layers import ConfigLayer
+
+
+class TestLoop(unittest.TestCase):
+ """Test the loop rule."""
+
+ layer = ConfigLayer
+
+ def setUp(self):
+ self._mlist = create_list('test@example.com')
+
+ def test_loop_returns_reason(self):
+ # Ensure loop rule returns a reason.
+ msg = mfs("""\
+From: anne@example.com
+To: test@example.com
+Subject: A Subject
+List-Post: test@example.com
+Message-ID: <ant>
+
+A message body.
+""")
+ rule = loop.Loop()
+ msgdata = {}
+ result = rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(msgdata['moderation_reasons'],
+ ['Message has already been posted to this list'])
diff --git a/src/mailman/rules/tests/test_max_recipients.py b/src/mailman/rules/tests/test_max_recipients.py
new file mode 100644
index 000000000..08663f1e5
--- /dev/null
+++ b/src/mailman/rules/tests/test_max_recipients.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2016-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/>.
+
+"""Test the `max_recipients` rule."""
+
+import unittest
+
+from mailman.app.lifecycle import create_list
+from mailman.rules import max_recipients
+from mailman.testing.helpers import specialized_message_from_string as mfs
+from mailman.testing.layers import ConfigLayer
+
+
+class TestMaximumRecipients(unittest.TestCase):
+ """Test the max_recipients rule."""
+
+ layer = ConfigLayer
+
+ def setUp(self):
+ self._mlist = create_list('test@example.com')
+
+ def test_max_recipients_returns_reason(self):
+ # Ensure max_recipients rule returns a reason.
+ msg = mfs("""\
+From: anne@example.com
+To: test@example.com
+Cc: anne@example.com, bill@example.com
+Subject: A Subject
+Message-ID: <ant>
+
+A message body.
+""")
+ rule = max_recipients.MaximumRecipients()
+ self._mlist.max_num_recipients = 2
+ msgdata = {}
+ result = rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(msgdata['moderation_reasons'],
+ [('Message has more than {} recipients', 2)])
diff --git a/src/mailman/rules/tests/test_max_size.py b/src/mailman/rules/tests/test_max_size.py
new file mode 100644
index 000000000..56b279a6b
--- /dev/null
+++ b/src/mailman/rules/tests/test_max_size.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2016-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/>.
+
+"""Test the `max_size` rule."""
+
+import unittest
+
+from mailman.app.lifecycle import create_list
+from mailman.rules import max_size
+from mailman.testing.helpers import specialized_message_from_string as mfs
+from mailman.testing.layers import ConfigLayer
+
+
+class TestMaximumSize(unittest.TestCase):
+ """Test the max_size rule."""
+
+ layer = ConfigLayer
+
+ def setUp(self):
+ self._mlist = create_list('test@example.com')
+
+ def test_max_size_returns_reason(self):
+ # Ensure max_size rule returns a reason.
+ msg = mfs("""\
+From: anne@example.com
+To: test@example.com
+Subject: A Subject
+Message-ID: <ant>
+
+A message body.
+""")
+ rule = max_size.MaximumSize()
+ self._mlist.max_message_size = 1
+ # Fake the size.
+ msg.original_size = 2048
+ msgdata = {}
+ result = rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(msgdata['moderation_reasons'],
+ [('The message is larger than the {} KB maximum size',
+ 1)])
diff --git a/src/mailman/rules/tests/test_news_moderation.py b/src/mailman/rules/tests/test_news_moderation.py
new file mode 100644
index 000000000..aec9e2b14
--- /dev/null
+++ b/src/mailman/rules/tests/test_news_moderation.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2016-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/>.
+
+"""Test the `news_moderation` rule."""
+
+import unittest
+
+from mailman.app.lifecycle import create_list
+from mailman.interfaces.nntp import NewsgroupModeration
+from mailman.rules import news_moderation
+from mailman.testing.helpers import specialized_message_from_string as mfs
+from mailman.testing.layers import ConfigLayer
+
+
+class TestModeratedNewsgroup(unittest.TestCase):
+ """Test the news_moderation rule."""
+
+ layer = ConfigLayer
+
+ def setUp(self):
+ self._mlist = create_list('test@example.com')
+
+ def test_news_moderation_returns_reason(self):
+ # Ensure news_moderation rule returns a reason.
+ msg = mfs("""\
+From: anne@example.com
+To: test@example.com
+Subject: A Subject
+Message-ID: <ant>
+
+A message body.
+""")
+ rule = news_moderation.ModeratedNewsgroup()
+ self._mlist.newsgroup_moderation = NewsgroupModeration.moderated
+ msgdata = {}
+ result = rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(msgdata['moderation_reasons'],
+ ['Post to a moderated newsgroup gateway'])
diff --git a/src/mailman/rules/tests/test_no_senders.py b/src/mailman/rules/tests/test_no_senders.py
index eb59dfe71..06bda5360 100644
--- a/src/mailman/rules/tests/test_no_senders.py
+++ b/src/mailman/rules/tests/test_no_senders.py
@@ -25,7 +25,7 @@ from mailman.rules import no_senders
from mailman.testing.layers import ConfigLayer
-class TestNoSubject(unittest.TestCase):
+class TestNoSender(unittest.TestCase):
"""Test the no_senders rule."""
layer = ConfigLayer
@@ -39,10 +39,9 @@ class TestNoSubject(unittest.TestCase):
msgdata = {}
result = self._rule.check(self._mlist, msg, msgdata)
self.assertTrue(result)
- self.assertEqual(msgdata['moderation_action'], 'discard')
self.assertEqual(msgdata['moderation_reasons'],
['The message has no valid senders'])
- self.assertEqual(msgdata['moderation_sender'], 'None')
+ self.assertEqual(msgdata['moderation_sender'], 'N/A')
def test_message_has_sender(self):
msg = Message()
diff --git a/src/mailman/rules/tests/test_no_subject.py b/src/mailman/rules/tests/test_no_subject.py
index 0379ea689..e80e2e4eb 100644
--- a/src/mailman/rules/tests/test_no_subject.py
+++ b/src/mailman/rules/tests/test_no_subject.py
@@ -46,3 +46,12 @@ class TestNoSubject(unittest.TestCase):
msg['Subject'] = Header('Test subject')
result = self._rule.check(self._mlist, msg, {})
self.assertFalse(result)
+
+ def test_no_subject_returns_reason(self):
+ msg = Message()
+ msg['Subject'] = Header('')
+ msgdata = {}
+ result = self._rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(msgdata['moderation_reasons'],
+ ['Message has no subject'])
diff --git a/src/mailman/rules/tests/test_suspicious.py b/src/mailman/rules/tests/test_suspicious.py
index b40292283..4012c89d5 100644
--- a/src/mailman/rules/tests/test_suspicious.py
+++ b/src/mailman/rules/tests/test_suspicious.py
@@ -42,3 +42,16 @@ class TestSuspicious(unittest.TestCase):
self._mlist.bounce_matching_headers = 'from: spam@example.com'
result = self._rule.check(self._mlist, msg, {})
self.assertFalse(result)
+
+ def test_suspicious_returns_reason(self):
+ msg = Message()
+ msg['From'] = Header('spam@example.com')
+ self._mlist.bounce_matching_headers = 'from: spam@example.com'
+ msgdata = {}
+ result = self._rule.check(self._mlist, msg, msgdata)
+ self.assertTrue(result)
+ self.assertEqual(
+ msgdata['moderation_reasons'],
+ [('Header "{}" matched a bounce_matching_header line',
+ 'spam@example.com')]
+ )