Commit d5bc3eba authored by Raphaël Hertzog's avatar Raphaël Hertzog

mail: ignore bounces generated by spam messages

The regex are not exhaustive and we should really parse better
the non-delivery notification that we get back but this is enough
to cover the gmail bounces we get that are triggering many
unsubscriptions currently.
parent 5d01c9e5
......@@ -28,6 +28,7 @@ from distro_tracker.core.models import PackageName
from distro_tracker.core.models import Keyword
from distro_tracker.core.models import Team
from distro_tracker.core.utils import extract_email_address_from_header
from distro_tracker.core.utils import get_decoded_message_payload
from distro_tracker.core.utils import get_or_none
from distro_tracker.core.utils import distro_tracker_render_to_string
from distro_tracker.core.utils import verp
......@@ -377,6 +378,34 @@ def prepare_message(received_message, to_email, date):
return message
def bounce_is_for_spam(message):
spam_bounce_re = [
# Google blocks executables files
# 552-5.7.0 This message was blocked because its content presents a[...]
# 552-5.7.0 security issue. Please visit
# 552-5.7.0 https://support.google.com/mail/?p=BlockedMessage to [...]
# 552 5.7.0 message content and attachment content guidelines. [...]
r"552-5.7.0 This message was blocked",
# host ...: 550 High probability of spam
r"55[0-9] .*spam",
# 550 Executable files are not allowed in compressed files.
r"55[0-9] .*[Ee]xecutable files",
]
# XXX: Handle delivery report properly
for part in message.walk():
if not part or part.is_multipart():
continue
text = get_decoded_message_payload(part)
if text is None:
continue
for line in text.splitlines()[0:15]:
for rule in spam_bounce_re:
if re.search(rule, line):
return True
return False
def handle_bounces(sent_to_address, message):
"""
Handles a received bounce message.
......@@ -403,6 +432,11 @@ def handle_bounces(sent_to_address, message):
logger.warning('bounces :: unknown user email %s', user_email)
return
if bounce_is_for_spam(message):
logger.info('bounces :: discarded spam bounce for %s/%s',
user_email, date)
return
UserEmailBounceStats.objects.add_bounce_for_user(email=user_email,
date=date)
......
......@@ -507,6 +507,22 @@ class BounceMessagesTest(TestCase, DispatchTestHelperMixin):
self.assertEqual(bounce_stats[0].mails_bounced, 1)
self.assertEqual(self.user.emailsettings.subscription_set.count(), 1)
def test_spam_bounce_ignored(self):
self.assertEqual(self.user.bouncestats_set.count(), 0)
self.message.set_payload(
"""
someone@example.net
SMTP error from remote mail server after end of data:
host aspmx.l.google.com [2607:f8b0:400e:c02::1a]:
552-5.7.0 This message was blocked because its content presents a potential
552-5.7.0 security issue. Please visit
552-5.7.0 https://support.google.com/mail/?p=BlockedMessage to review our
552 5.7.0 message content and attachment content guidelines.
""")
dispatch.handle_bounces(self.create_bounce_address(self.user.email),
self.message)
self.assertEqual(self.user.bouncestats_set.count(), 0)
def test_bounce_over_limit(self):
"""
Tests that all the user's subscriptions are dropped when too many
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment