Skip to content

Commit f241de2

Browse files
committed
Merged [7673] from rjsparks@nostrum.com: Made several changes to better handle non-ascii UTF-8 in email messages.
Used multipart mime when including the original message in an error message to the secretariat. Modified the way IANA review email is parsed. Added non-ascii UTF-8 tests to the IANA review email parser and to the SMTP Exception handling code. - Legacy-Id: 7676 Note: SVN reference [7673] has been migrated to Git commit bf4f3ad
2 parents 3e0e103 + bf4f3ad commit f241de2

4 files changed

Lines changed: 77 additions & 62 deletions

File tree

ietf/sync/iana.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ def update_history_with_changes(changes, send_email=True):
216216

217217

218218
def parse_review_email(text):
219-
msg = email.message_from_string(text.encode("utf-8"))
219+
msg = email.message_from_string(text)
220220

221221
# doc
222222
doc_name = ""

ietf/sync/tests.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
import os
23
import json
34
import datetime
@@ -130,7 +131,7 @@ def test_changes_sync_errors(self):
130131
def test_iana_review_mail(self):
131132
draft = make_test_data()
132133

133-
msg = """From: "%(person)s via RT" <drafts-lastcall@iana.org>
134+
msg = u"""From: "%(person)s via RT" <drafts-lastcall@iana.org>
134135
Date: Thu, 10 May 2012 12:00:00 +0000
135136
Subject: [IANA #12345] Last Call: <%(draft)s-%(rev)s.txt> (Long text) to Informational RFC
136137
@@ -147,7 +148,7 @@ def test_iana_review_mail(self):
147148
Thanks,
148149
149150
%(person)s
150-
IANA Fake Test Person
151+
IANA Fake Test Person
151152
ICANN
152153
153154
(END IANA LAST CALL COMMENTS)
@@ -156,8 +157,8 @@ def test_iana_review_mail(self):
156157
msg = msg % dict(person=Person.objects.get(user__username="iana").name,
157158
draft=draft.name,
158159
rev=draft.rev)
159-
160-
doc_name, review_time, by, comment = iana.parse_review_email(msg)
160+
161+
doc_name, review_time, by, comment = iana.parse_review_email(msg.encode('utf-8'))
161162

162163
self.assertEqual(doc_name, draft.name)
163164
# self.assertEqual(review_time, datetime.datetime(2012, 5, 10, 5, 0, 0))

ietf/utils/mail.py

Lines changed: 39 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -304,17 +304,20 @@ def smtp_error_user_warning(thing,request):
304304
except smtplib.SMTPException as e:
305305
(extype, value, tb) = log_smtp_exception(e)
306306

307-
warning = "An error occured while sending email with\n"
308-
warning += "Subject: %s\n" % e.original_msg.get('Subject','[no subject]')
309-
warning += "To: %s\n" % e.original_msg.get('To','[no to]')
310-
warning += "Cc: %s\n" % e.original_msg.get('Cc','[no cc]')
311-
if isinstance(e,SMTPSomeRefusedRecipients):
312-
warning += e.detailed_refusals()
313-
else:
314-
warning += "SMTP Exception: %s\n"%extype
315-
warning += "Error Message: %s\n\n"%value
316-
warning += "The message was not delivered to anyone."
317-
messages.warning(request,warning,extra_tags='preformatted',fail_silently=True)
307+
if request:
308+
warning = "An error occured while sending email:\n"
309+
if (e.original_msg):
310+
warning += "Subject: %s\n" % e.original_msg.get('Subject','[no subject]')
311+
warning += "To: %s\n" % e.original_msg.get('To','[no to]')
312+
warning += "Cc: %s\n" % e.original_msg.get('Cc','[no cc]')
313+
if isinstance(e,SMTPSomeRefusedRecipients):
314+
warning += e.detailed_refusals()
315+
else:
316+
warning += "SMTP Exception: %s\n"%extype
317+
warning += "Error Message: %s\n\n"%value
318+
warning += "The message was not delivered to anyone."
319+
messages.warning(request,warning,extra_tags='preformatted',fail_silently=True)
320+
318321
raise
319322

320323
@contextmanager
@@ -324,68 +327,52 @@ def smtp_error_logging(thing):
324327
except smtplib.SMTPException as e:
325328
(extype, value, tb) = log_smtp_exception(e)
326329

327-
msg = textwrap.dedent("""\
328-
To: <action@ietf.org>
329-
From: %s
330-
""") % settings.SERVER_EMAIL
330+
msg = MIMEMultipart()
331+
msg['To'] = '<action@ietf.org>'
332+
msg['From'] = settings.SERVER_EMAIL
331333
if isinstance(e,SMTPSomeRefusedRecipients):
332-
msg += textwrap.dedent("""\
333-
Subject: Some recipients were refused while sending mail with Subject: %s
334+
msg['Subject'] = 'Subject: Some recipients were refused while sending mail with Subject: %s' % e.original_msg.get('Subject','[no subject]')
335+
textpart = textwrap.dedent("""\
336+
This is a message from the datatracker to IETF-Action about an email
337+
delivery failure, when sending email from the datatracker.
334338
335-
This is a message from the datatracker to IETF-Action about an email
336-
delivery failure, when sending email from the datatracker.
339+
%s
337340
338-
%s
339-
340-
The original message follows:
341-
-------- BEGIN ORIGINAL MESSAGE --------
342-
%s
343-
--------- END ORIGINAL MESSAGE ---------
344-
""") % (e.original_msg.get('Subject', '[no subject]'),e.detailed_refusals(),e.original_msg.as_string())
341+
""") % e.detailed_refusals()
345342
else:
346-
msg += textwrap.dedent("""\
347-
Subject: Datatracker error while sending email
348-
349-
This is a message from the datatracker to IETF-Action about an email
350-
delivery failure, when sending email from the datatracker.
343+
msg['Subject'] = 'Datatracker error while sending email'
344+
textpart = textwrap.dedent("""\
345+
This is a message from the datatracker to IETF-Action about an email
346+
delivery failure, when sending email from the datatracker.
351347
352-
The original message was not delivered to anyone.
348+
The original message was not delivered to anyone.
353349
354-
SMTP Exception: %s
350+
SMTP Exception: %s
355351
356-
Error Message: %s
352+
Error Message: %s
357353
358-
""") % (extype,value)
359-
if hasattr(e,'original_msg'):
360-
msg += textwrap.dedent("""\
361-
The original message follows:
362-
-------- BEGIN ORIGINAL MESSAGE --------
363-
%s
364-
--------- END ORIGINAL MESSAGE ---------
365-
""") % e.original_msg.as_string()
354+
""") % (extype,value)
355+
if hasattr(e,'original_msg'):
356+
textpart += "The original message follows:\n"
357+
msg.attach(MIMEText(textpart,_charset='utf-8'))
358+
if hasattr(e,'original_msg'):
359+
msg.attach(MIMEMessage(e.original_msg))
366360

367361
send_error_to_secretariat(msg)
368362

369-
def send_error_to_secretariat(raw_msg):
370-
371-
(parsed_msg , headers , bcc) = parse_preformatted(raw_msg)
372-
send_msg = encode_message(parsed_msg.get_payload())
373-
cc = None
374-
condition_message(parsed_msg['To'], parsed_msg['From'], parsed_msg['Subject'], send_msg, cc, headers)
363+
def send_error_to_secretariat(msg):
375364

376365
debugging = getattr(settings, "USING_DEBUG_EMAIL_SERVER", False) and settings.EMAIL_HOST == 'localhost' and settings.EMAIL_PORT == 2025
377366

378367
try:
379368
if test_mode or debugging or settings.SERVER_MODE == 'production':
380-
send_smtp(send_msg, bcc)
369+
send_smtp(msg, bcc=None)
381370
try:
382371
copy_to = settings.EMAIL_COPY_TO
383372
except AttributeError:
384373
copy_to = "ietf.tracker.archive+%s@gmail.com" % settings.SERVER_MODE
385374
if copy_to and not test_mode and not debugging: # if we're running automated tests, this copy is just annoying
386-
if bcc:
387-
send_msg['X-Tracker-Bcc']=bcc
388-
copy_email(send_msg, copy_to,originalBcc=bcc)
375+
copy_email(msg, copy_to,originalBcc=None)
389376
except smtplib.SMTPException:
390377
log("Exception encountered while sending a ticket to the secretariat")
391378
(extype,value) = sys.exc_info()[:2]

ietf/utils/tests.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
# -*- coding: utf-8 -*-
12
import os.path
23

4+
from textwrap import dedent
5+
from email.mime.text import MIMEText
6+
from email.mime.image import MIMEImage
7+
from email.mime.multipart import MIMEMultipart
8+
39
from django.conf import settings
410
from django.test import TestCase
511

612
from ietf.utils.management.commands import pyflakes
7-
from ietf.utils.mail import send_mail_text, outbox
13+
from ietf.utils.mail import send_mail_text, send_mail_mime, outbox
814

915
class PyFlakesTestCase(TestCase):
1016

@@ -18,16 +24,37 @@ class TestSMTPServer(TestCase):
1824

1925
def test_address_rejected(self):
2026

21-
def send_mail(to):
22-
send_mail_text(None, to=to, frm=None, subject="Test for rejection", txt="dummy body")
27+
def send_simple_mail(to):
28+
send_mail_text(None, to=to, frm=None, subject="Test for rejection", txt="dummy body")
2329

2430
len_before = len(outbox)
25-
send_mail('good@example.com,poison@example.com')
31+
send_simple_mail('good@example.com,poison@example.com')
2632
self.assertEqual(len(outbox),len_before+2)
2733
self.assertTrue('Some recipients were refused' in outbox[-1]['Subject'])
2834

2935
len_before = len(outbox)
30-
send_mail('poison@example.com')
36+
send_simple_mail('poison@example.com')
3137
self.assertEqual(len(outbox),len_before+2)
3238
self.assertTrue('error while sending email' in outbox[-1]['Subject'])
3339

40+
def test_rejecting_complex_mail(self):
41+
42+
def send_complex_mail(to):
43+
msg = MIMEMultipart()
44+
textpart= MIMEText(dedent(u"""\
45+
Sometimes people send mail with things like “smart quotes” in them.
46+
Sometimes they have attachments with pictures.
47+
"""),_charset='utf-8')
48+
msg.attach(textpart)
49+
img = MIMEImage('\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x02\x88IDATx\xda\xa5\x93\xcbO\x13Q\x14\xc6\xbf\xb9\xd32}L\x9fZ\x06\x10Q\x90\x10\x85b\x89\xfe\x01\x06BXK"\xd4hB\xdc\xa0\x06q\xe1c% H1l\xd0\x8dbT6\x1a5\x91\x12#K\x891\xf2\x07\x98\xc8[L\x1ay\xa8@\xdb\xd0\xd2\xe9\x83N;\xbdc\x1f\x11\x03\x04\x17zW\'_\xce\xf9\xdd\xef\x9c\x9c\xc3\xe0?\x1f\xb3S\xf8\xfe\xba\xc2Be\xa9]m\xd6\x9e\xe6x\xde\x9e\xd1\xa4HdF\x0e\xc5G\x89\x8a{X\xec\xfc\x1a\xdc\x1307X\xd4$T\nC\xc6\xfc|\x13\x8d\xa6\x00\xe5O\x16\xd1\xb3\x10}\xbe\x90w\xce\xdbZyeed\x17\xc03(4\x15\x9d(s\x13\xca!\x10\xa6\xb0\x1a\xb6\x9b\x0b\x84\x95\xb4F@\x89\x84\xe5O\x0b\xcdG\xaf\xae\x8dl\x01V\x9f\x1d\xb4q\x16\xde\xa33[\x8d\xe3\x93)\xdc\x7f\x9b\xc4\xf3\x1b\x1c,|\xaex#\n\xb4\x0cH\xb8\xd6\xa8F\xad\x83El# \xc6\x83\xb1\xf2\xa2\x0bK\xfe,`y\xd0\xe6*<V\xda\x99\x92\x15\xb8\xdc\x14\xef>\x03\xaes\x0c\xea\xaas.\xc6g\x14t\xbcR\xd0P\x03t;\tX\x15\x83\xd5\xf9\xc5\xbe\x926_W6\xe3\xe7ca\xc2Z,82\xb1\xeb\r\x8b\xb1)\x82\xde\xa6\x14\xea\xec4\x0b\xf88K\xd0\xe5fQ_M\xd1s&\x95k\xe9\x87w\xf2\xc0eoM\x16\xe0\x1b*\xdc4XM\x9aL\xfca\x8e\xc5\xbd1\x0e//\xc6`\xd5\xe7Z\x08\xa6[8\xffT\x87\xeb\r\x12\xea\xabr\x80p \x14\xcfo]\xd5f\x01k\x8fl\x9bF3\xaf\xf9=\xb0X\x82\x81.O\xd96\xc4\x9d\x9a\xb8\x11\x89\x17\xb4\xf9s\x80\xe5\x01\xc3\xc4\xfe}FG\\\x064\xaa\xbf/\x0eM3\x92i\x13\xe1\x908Yr3\x9ck\xe1[\xbf\xd6%X\xf4\x9d\xef=z$(\xc1\xa9\xc3Q\xf0\x1c\xddV(\xa7\x18Ly9L\xafq8{\\D0\x14\xbd{\xe4V\xac3\x0bX\xe8\xd7\xdb\xb4,\xf5\x18\xb4j\xe3\xf8\xa2\x1e/\xa6\xac`\x18\x06\x02\x9f\x84\x8a\xa4\x07\x16c\xb1\xbe\xc9\xa2\xf6P\x04-\x8e\x00\x12\xc9\x84(&\xd9\xf2\x8a\x8e\x88\x7fk[\xbet\xe75\x0bzf\x98cI\xd6\xe6\xfc\xba\x06\xd3~\x1d\x12\xe9\x9fK\xcd\x12N\x16\xc4\xa0UQH)\x8a\x95\x08\x9c\xf6^\xc9\xbdk\x95\xe7o\xab\x9c&\xb5\xf2\x84W3\xa6\x9dG\x92\x19_$\xa9\x84\xd6%r\xc9\xde\x97\x1c\xde\xf3\x98\x96\xee\xb0\x16\x99\xd2v\x15\x94\xc6<\xc2Te\xb4\x04Ufe\x85\x8c2\x84<(\xeb\x91\xf7>\xa6\x7fy\xbf\x00\x96T\xff\x11\xf7\xd8R\xb9\x00\x00\x00\x00IEND\xaeB`\x82')
50+
51+
msg.attach(img)
52+
send_mail_mime(request=None, to=to, frm=settings.DEFAULT_FROM_EMAIL, subject=u'это сложно', msg=msg, cc=None, extra=None)
53+
54+
len_before = len(outbox)
55+
send_complex_mail('good@example.com')
56+
self.assertEqual(len(outbox),len_before+1)
57+
58+
len_before = len(outbox)
59+
send_complex_mail('good@example.com,poison@example.com')
60+
self.assertEqual(len(outbox),len_before+2)

0 commit comments

Comments
 (0)