handle 'quoted-printable' shorthand version as Transfer-Encoding
This commit is contained in:
parent
49d3b02b9c
commit
bb07a6b641
2 changed files with 62 additions and 0 deletions
|
@ -43,6 +43,11 @@ def unescape_and_decode_base64(s):
|
||||||
return decoded
|
return decoded
|
||||||
|
|
||||||
|
|
||||||
|
def unescape_simplified_quoted_printable(s):
|
||||||
|
import quopri
|
||||||
|
return quopri.decodestring(s).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def collect_references(issue_thread):
|
def collect_references(issue_thread):
|
||||||
mails = issue_thread.emails.order_by('timestamp')
|
mails = issue_thread.emails.order_by('timestamp')
|
||||||
references = []
|
references = []
|
||||||
|
@ -127,6 +132,8 @@ def parse_email_body(raw, log=None):
|
||||||
continue
|
continue
|
||||||
segment = unescape_and_decode_quoted_printable(segment)
|
segment = unescape_and_decode_quoted_printable(segment)
|
||||||
segment = unescape_and_decode_base64(segment)
|
segment = unescape_and_decode_base64(segment)
|
||||||
|
if part.get('Content-Transfer-Encoding') == 'quoted-printable':
|
||||||
|
segment = unescape_simplified_quoted_printable(segment)
|
||||||
log.debug(segment)
|
log.debug(segment)
|
||||||
body = body + segment
|
body = body + segment
|
||||||
elif 'attachment' in cdispo or 'inline' in cdispo:
|
elif 'attachment' in cdispo or 'inline' in cdispo:
|
||||||
|
@ -158,6 +165,8 @@ def parse_email_body(raw, log=None):
|
||||||
body = "Unknown content type"
|
body = "Unknown content type"
|
||||||
body = unescape_and_decode_quoted_printable(body)
|
body = unescape_and_decode_quoted_printable(body)
|
||||||
body = unescape_and_decode_base64(body)
|
body = unescape_and_decode_base64(body)
|
||||||
|
if parsed.get('Content-Transfer-Encoding') == 'quoted-printable':
|
||||||
|
body = unescape_simplified_quoted_printable(body)
|
||||||
log.debug(body)
|
log.debug(body)
|
||||||
|
|
||||||
return parsed, body, attachments
|
return parsed, body, attachments
|
||||||
|
|
|
@ -880,3 +880,56 @@ hello \xe4\xf6\xfc'''
|
||||||
states = StateChange.objects.filter(issue_thread=IssueThread.objects.all()[0])
|
states = StateChange.objects.filter(issue_thread=IssueThread.objects.all()[0])
|
||||||
self.assertEqual(1, len(states))
|
self.assertEqual(1, len(states))
|
||||||
self.assertEqual('pending_new', states[0].state)
|
self.assertEqual('pending_new', states[0].state)
|
||||||
|
|
||||||
|
def test_mail_quoted_printable_transfer_encoding(self):
|
||||||
|
from aiosmtpd.smtp import Envelope
|
||||||
|
from asgiref.sync import async_to_sync
|
||||||
|
import aiosmtplib
|
||||||
|
|
||||||
|
aiosmtplib.send = make_mocked_coro()
|
||||||
|
|
||||||
|
handler = LMTPHandler()
|
||||||
|
server = mock.Mock()
|
||||||
|
session = mock.Mock()
|
||||||
|
envelope = Envelope()
|
||||||
|
|
||||||
|
envelope.mail_from = 'test1@test'
|
||||||
|
envelope.rcpt_tos = ['test2@test']
|
||||||
|
|
||||||
|
envelope.content = b'''Subject: test
|
||||||
|
From: test1@test
|
||||||
|
To: test2@test
|
||||||
|
Message-ID: <1@test>
|
||||||
|
Content-Type: text/html; charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
|
hello =C3=A4=C3=B6=C3=BC'''
|
||||||
|
|
||||||
|
result = async_to_sync(handler.handle_DATA)(server, session, envelope)
|
||||||
|
self.assertEqual('250 Message accepted for delivery', result)
|
||||||
|
self.assertEqual(2, len(Email.objects.all()))
|
||||||
|
self.assertEqual(1, len(IssueThread.objects.all()))
|
||||||
|
aiosmtplib.send.assert_called_once()
|
||||||
|
self.assertEqual('test', Email.objects.all()[0].subject)
|
||||||
|
self.assertEqual('test1@test', Email.objects.all()[0].sender)
|
||||||
|
self.assertEqual('test2@test', Email.objects.all()[0].recipient)
|
||||||
|
self.assertEqual('hello äöü', Email.objects.all()[0].body)
|
||||||
|
self.assertEqual(IssueThread.objects.all()[0], Email.objects.all()[0].issue_thread)
|
||||||
|
self.assertEqual('<1@test>', Email.objects.all()[0].reference)
|
||||||
|
self.assertEqual(None, Email.objects.all()[0].in_reply_to)
|
||||||
|
self.assertEqual(expected_auto_reply_subject.format('test', IssueThread.objects.all()[0].short_uuid()),
|
||||||
|
Email.objects.all()[1].subject)
|
||||||
|
self.assertEqual('test2@test', Email.objects.all()[1].sender)
|
||||||
|
self.assertEqual('test1@test', Email.objects.all()[1].recipient)
|
||||||
|
self.assertEqual(expected_auto_reply.format(IssueThread.objects.all()[0].short_uuid()),
|
||||||
|
Email.objects.all()[1].body)
|
||||||
|
self.assertEqual(IssueThread.objects.all()[0], Email.objects.all()[1].issue_thread)
|
||||||
|
self.assertTrue(Email.objects.all()[1].reference.startswith("<"))
|
||||||
|
self.assertTrue(Email.objects.all()[1].reference.endswith("@localhost>"))
|
||||||
|
self.assertEqual("<1@test>", Email.objects.all()[1].in_reply_to)
|
||||||
|
self.assertEqual('test', IssueThread.objects.all()[0].name)
|
||||||
|
self.assertEqual('pending_new', IssueThread.objects.all()[0].state)
|
||||||
|
self.assertEqual(None, IssueThread.objects.all()[0].assigned_to)
|
||||||
|
states = StateChange.objects.filter(issue_thread=IssueThread.objects.all()[0])
|
||||||
|
self.assertEqual(1, len(states))
|
||||||
|
self.assertEqual('pending_new', states[0].state)
|
||||||
|
|
Loading…
Reference in a new issue