Skip to content

Commit fb740ff

Browse files
committed
Added saving of outgoing emails as Message instances, with accompanying test.
- Legacy-Id: 17348
1 parent 3227e42 commit fb740ff

File tree

2 files changed

+94
-15
lines changed

2 files changed

+94
-15
lines changed

ietf/message/tests.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright The IETF Trust 2013-2019, All Rights Reserved
1+
# Copyright The IETF Trust 2013-2020, All Rights Reserved
22
# -*- coding: utf-8 -*-
33

44

@@ -8,13 +8,14 @@
88

99
from django.urls import reverse as urlreverse
1010

11-
from ietf.utils.test_utils import TestCase
12-
from ietf.utils.mail import outbox
11+
import debug # pyflakes:ignore
1312

14-
from ietf.message.models import Message, SendQueue
15-
from ietf.person.models import Person
1613
from ietf.group.factories import GroupFactory
14+
from ietf.message.models import Message, SendQueue
1715
from ietf.message.utils import send_scheduled_message_from_send_queue
16+
from ietf.person.models import Person
17+
from ietf.utils.mail import outbox, send_mail_text, send_mail_message, get_payload
18+
from ietf.utils.test_utils import TestCase
1819

1920
class MessageTests(TestCase):
2021
def test_message_view(self):
@@ -25,7 +26,7 @@ def test_message_view(self):
2526
to="test@example.com",
2627
frm="nomcomchair@example.com",
2728
body="Hello World!",
28-
content_type="",
29+
content_type="text/plain",
2930
)
3031
msg.related_groups.add(nomcom)
3132

@@ -36,6 +37,41 @@ def test_message_view(self):
3637
self.assertContains(r, msg.frm)
3738
self.assertContains(r, "Hello World!")
3839

40+
def test_capture_send_mail_text(self):
41+
def cmp(e1, e2):
42+
e1keys = set(e1.keys())
43+
e2keys = set(e2.keys())
44+
self.assertEqual(e1keys, e2keys)
45+
self.longMessage = True
46+
for k in e1keys:
47+
if k in ['Date', ]:
48+
continue
49+
self.assertEqual(e1.get_all(k), e2.get_all(k), "Header field: %s" % k)
50+
self.longMessage = False
51+
self.assertEqual(get_payload(e1), get_payload(e2))
52+
53+
#
54+
self.assertEqual(Message.objects.count(), 0)
55+
to = "<iesg-secretary@ietf.org>"
56+
subj = "Dummy subject"
57+
cc="cc.a@example.com, cc.b@example.com"
58+
body = "Dummy message text"
59+
bcc="bcc@example.com"
60+
msg1 = send_mail_text(None, to, None, subj, body, cc=cc, bcc=bcc)
61+
self.assertEqual(Message.objects.count(), 1)
62+
message = Message.objects.last()
63+
self.assertEqual(message.by.name, '(System)')
64+
self.assertEqual(message.to, to)
65+
self.assertEqual(message.subject, subj)
66+
self.assertEqual(message.body, body)
67+
self.assertEqual(message.cc, cc)
68+
self.assertEqual(message.bcc, bcc)
69+
self.assertEqual(message.content_type, 'text/plain')
70+
# Check round-trip msg --> message --> msg
71+
msg2 = send_mail_message(None, message)
72+
cmp(msg1, msg2)
73+
cmp(msg1, outbox[-1])
74+
3975

4076
class SendScheduledAnnouncementsTests(TestCase):
4177
def test_send_plain_announcement(self):
@@ -47,7 +83,7 @@ def test_send_plain_announcement(self):
4783
cc="cc.a@example.com, cc.b@example.com",
4884
bcc="bcc@example.com",
4985
body="Hello World!",
50-
content_type="",
86+
content_type="text/plain",
5187
)
5288

5389
q = SendQueue.objects.create(

ietf/utils/mail.py

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,10 @@ def encode_message(txt):
192192
assert isinstance(txt, six.text_type)
193193
return MIMEText(txt.encode('utf-8'), 'plain', 'UTF-8')
194194

195-
def send_mail_text(request, to, frm, subject, txt, cc=None, extra=None, toUser=False, bcc=None, copy=True):
195+
def send_mail_text(request, to, frm, subject, txt, cc=None, extra=None, toUser=False, bcc=None, copy=True, save=True):
196196
"""Send plain text message."""
197197
msg = encode_message(txt)
198-
return send_mail_mime(request, to, frm, subject, msg, cc, extra, toUser, bcc, copy=copy)
198+
return send_mail_mime(request, to, frm, subject, msg, cc, extra, toUser, bcc, copy=copy, save=save)
199199

200200
def on_behalf_of(frm):
201201
if isinstance(frm, tuple):
@@ -320,6 +320,9 @@ def condition_message(to, frm, subject, msg, cc, extra):
320320
msg[k] = ", ".join(v)
321321
except Exception:
322322
raise
323+
if not msg.get('Message-ID', None):
324+
msg['Message-ID'] = make_msgid()
325+
323326

324327
def show_that_mail_was_sent(request,leadline,msg,bcc):
325328
if request and request.user:
@@ -334,11 +337,29 @@ def show_that_mail_was_sent(request,leadline,msg,bcc):
334337
info += "Bcc: %s\n" % bcc
335338
messages.info(request,info,extra_tags='preformatted',fail_silently=True)
336339

337-
def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=False, bcc=None, copy=True):
340+
def save_as_message(request, msg, bcc):
341+
by = ((request and request.user and not request.user.is_anonymous and request.user.person)
342+
or ietf.person.models.Person.objects.get(name="(System)"))
343+
headers, body = force_text(str(msg)).split('\n\n', 1)
344+
kwargs = {'by': by, 'body': body, 'content_type': msg.get_content_type(), 'bcc': bcc or '' }
345+
for (arg, field) in [
346+
('cc', 'Cc'),
347+
('frm', 'From'),
348+
('msgid', 'Message-ID'),
349+
('reply_to', 'Reply-To'),
350+
('subject', 'Subject'),
351+
('to', 'To'),
352+
]:
353+
kwargs[arg] = msg.get(field, '')
354+
m = ietf.message.models.Message.objects.create(**kwargs)
355+
log("Saved outgoing email from '%s' to %s id %s subject '%s as Message[%s]'" % (m.frm, m.to, m.msgid, m.subject, m.pk))
356+
return m
357+
358+
def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=False, bcc=None, copy=True, save=True):
338359
"""Send MIME message with content already filled in."""
339360

340361
condition_message(to, frm, subject, msg, cc, extra)
341-
362+
342363
# start debug server with python -m smtpd -n -c DebuggingServer localhost:2025
343364
# then put USING_DEBUG_EMAIL_SERVER=True and EMAIL_HOST='localhost'
344365
# and EMAIL_PORT=2025 in settings_local.py
@@ -352,16 +373,24 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=F
352373
if settings.SERVER_MODE == 'development':
353374
show_that_mail_was_sent(request,'In production, email would have been sent',msg,bcc)
354375

376+
# Maybe save in the database as a Message object
377+
if save:
378+
message = save_as_message(request, msg, bcc)
379+
else:
380+
message = None
381+
355382
if test_mode or debugging or production:
356383
try:
357384
send_smtp(msg, bcc)
385+
if save:
386+
message.sent = datetime.datetime.now()
387+
message.save()
388+
show_that_mail_was_sent(request,'Email was sent',msg,bcc)
358389
except smtplib.SMTPException as e:
359390
log_smtp_exception(e)
360391
build_warning_message(request, e)
361392
send_error_email(e)
362393

363-
show_that_mail_was_sent(request,'Email was sent',msg,bcc)
364-
365394
elif settings.SERVER_MODE == 'test':
366395
if toUser:
367396
copy_email(msg, to, toUser=True, originalBcc=bcc)
@@ -448,15 +477,29 @@ def send_mail_preformatted(request, preformatted, extra={}, override={}):
448477
def send_mail_message(request, message, extra={}):
449478
"""Send a Message object."""
450479
# note that this doesn't handle MIME messages at the moment
480+
assertion('isinstance(message.to, six.string_types) and isinstance(message.cc, six.string_types) and isinstance(message.bcc, six.string_types)')
451481

452482
e = extra.copy()
453483
if message.reply_to:
454484
e['Reply-To'] = message.get('reply_to')
455485
if message.msgid:
456486
e['Message-ID'] = [ message.msgid, ]
457487

458-
return send_mail_text(request, message.to, message.frm, message.subject,
459-
message.body, cc=message.cc, bcc=message.bcc, extra=e)
488+
content_type = message.content_type or 'text/plain'
489+
if 'multipart' in content_type:
490+
body = ("MIME-Version: 1.0\r\nContent-Type: %s\r\n\r\n" % content_type) + message.body
491+
msg = message_from_string(force_str(body))
492+
else:
493+
msg = encode_message(message.body)
494+
495+
msg = send_mail_mime(request, message.to, message.frm, message.subject,
496+
msg, cc=message.cc, bcc=message.bcc, extra=e, save=False)
497+
498+
# msg = send_mail_text(request, message.to, message.frm, message.subject,
499+
# message.body, cc=message.cc, bcc=message.bcc, extra=e, save=False)
500+
message.sent = datetime.datetime.now()
501+
message.save()
502+
return msg
460503

461504
def exception_components(e):
462505
# See if it's a non-smtplib exception that we faked

0 commit comments

Comments
 (0)