Skip to content

Commit

Permalink
Mailgun: handle x.y.z status code in webhooks
Browse files Browse the repository at this point in the history
Mailgun sometimes (though not usually) gives the 'code' event
field as an RFC-3463 extended SMTP status code. Handle either
format.

Fixes #62
  • Loading branch information
medmunds committed May 22, 2017
1 parent bb54806 commit ba6ccdb
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 0 deletions.
9 changes: 9 additions & 0 deletions anymail/webhooks/mailgun.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ def esp_to_anymail_event(self, esp_event):
mta_status = int(esp_event['code'])
except (KeyError, TypeError):
pass
except ValueError:
# RFC-3463 extended SMTP status code (class.subject.detail, where class is "2", "4" or "5")
try:
status_class = esp_event['code'].split('.')[0]
except (TypeError, IndexError):
# illegal SMTP status code format
pass
else:
reject_reason = RejectReason.BOUNCED if status_class in ("4", "5") else RejectReason.OTHER
else:
reject_reason = self.reject_reasons.get(
mta_status,
Expand Down
19 changes: 19 additions & 0 deletions tests/test_mailgun_webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,25 @@ def test_invalid_mailbox(self):
self.assertEqual(event.reject_reason, "bounced")
self.assertIn("The email account that you tried to reach does not exist", event.mta_response)

def test_alt_smtp_code(self):
# In some cases, Mailgun uses RFC-3463 extended SMTP status codes (x.y.z, rather than nnn).
# See issue #62.
raw_event = mailgun_sign({
'code': '5.1.1',
'error': 'smtp;550 5.1.1 RESOLVER.ADR.RecipNotFound; not found',
'event': 'bounced',
'recipient': 'noreply@example.com',
# (omitting some fields that aren't relevant to the test)
})
response = self.client.post('/anymail/mailgun/tracking/', data=raw_event)
self.assertEqual(response.status_code, 200)
kwargs = self.assert_handler_called_once_with(self.tracking_handler, sender=MailgunTrackingWebhookView,
event=ANY, esp_name='Mailgun')
event = kwargs['event']
self.assertEqual(event.event_type, "bounced")
self.assertEqual(event.reject_reason, "bounced")
self.assertIn("RecipNotFound", event.mta_response)

def test_metadata(self):
# Metadata fields are interspersed with other data, but also in message-headers
raw_event = mailgun_sign({
Expand Down

0 comments on commit ba6ccdb

Please sign in to comment.