From 8de6b218b96bb6784b104f7995445462515672af Mon Sep 17 00:00:00 2001 From: medmunds Date: Sat, 12 Jan 2013 10:26:42 -0800 Subject: [PATCH] Handle bcc as Mandrill bcc_address, rather than additional to address --- README.rst | 15 ++++++++----- djrill/mail/backends/djrill.py | 16 ++++++++++--- djrill/tests/test_mandrill_send.py | 36 ++++++++++++++++++++++-------- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index 99c5c6a..700cf80 100644 --- a/README.rst +++ b/README.rst @@ -138,11 +138,16 @@ Djrill supports most of the functionality of Django's `EmailMessage`_ and "image/\*", or "application/pdf" (since that is all Mandrill allows). Any other attachment types will raise a ``djrill.NotSupportedByMandrillError`` exception when you attempt to send the message. -* Djrill treats all cc and bcc recipients as if they were additional "to" - addresses. (Mandrill does not distinguish cc, and only allows a single bcc -- - which Djrill doesn't use. *Caution:* depending on the ``preserve_recipients`` - setting, this could result in exposing bcc addresses to all recipients. It's - probably best to just avoid bcc.) +* Djrill treats all "cc" recipients as if they were additional "to" addresses. + (Mandrill does not distinguish "cc" from "to".) Note that you will also need + to set ``preserve_recipients`` True if you want each recipient to see the + other recipients listed in the email headers. +* Mandrill does not permit more than one "bcc" address. Djrill raises + ``djrill.NotSupportedByMandrillError`` if you attempt to send a message with + multiple bcc's. (Mandrill's bcc option seems intended primarily for logging. + To send a single message to multiple recipients without exposing their + email addresses to each other, simply include them all in the "to" list and + leave ``preserve_recipients`` set to False.) * All email addresses (from, to, cc) can be simple ("email@example.com") or can include a display name ("Real Name "). * The ``from_email`` must be in one of the approved sending domains in your diff --git a/djrill/mail/backends/djrill.py b/djrill/mail/backends/djrill.py index b6436de..c14807d 100644 --- a/djrill/mail/backends/djrill.py +++ b/djrill/mail/backends/djrill.py @@ -105,10 +105,11 @@ class DjrillBackend(BaseEmailBackend): sender = sanitize_address(message.from_email, message.encoding) from_name, from_email = parseaddr(sender) - recipients = [parseaddr(sanitize_address(addr, message.encoding)) - for addr in message.recipients()] + recipients = message.to + message.cc # message.recipients() w/o bcc + parsed_rcpts = [parseaddr(sanitize_address(addr, message.encoding)) + for addr in recipients] to_list = [{"email": to_email, "name": to_name} - for (to_name, to_email) in recipients] + for (to_name, to_email) in parsed_rcpts] msg_dict = { "text": message.body, @@ -119,6 +120,15 @@ class DjrillBackend(BaseEmailBackend): if from_name: msg_dict["from_name"] = from_name + if len(message.bcc) == 1: + bcc = message.bcc[0] + _, bcc_addr = parseaddr(sanitize_address(bcc, message.encoding)) + msg_dict['bcc_address'] = bcc_addr + elif len(message.bcc) > 1: + raise NotSupportedByMandrillError( + "Too many bcc addresses (%d) - Mandrill only allows one" + % len(message.bcc)) + if message.extra_headers: for k in message.extra_headers.keys(): if k != "Reply-To" and not k.startswith("X-"): diff --git a/djrill/tests/test_mandrill_send.py b/djrill/tests/test_mandrill_send.py index 2a86147..165dd53 100644 --- a/djrill/tests/test_mandrill_send.py +++ b/djrill/tests/test_mandrill_send.py @@ -35,22 +35,32 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase): (Test both sender and recipient addresses) """ - mail.send_mail('Subject', 'Message', 'From Name ', - ['Recipient #1 ', 'to2@example.com']) + msg = mail.EmailMessage('Subject', 'Message', + 'From Name ', + ['Recipient #1 ', 'to2@example.com'], + cc=['Carbon Copy ', 'cc2@example.com'], + bcc=['Blind Copy ']) + msg.send() data = self.get_api_call_data() self.assertEqual(data['message']['from_name'], "From Name") self.assertEqual(data['message']['from_email'], "from@example.com") - self.assertEqual(len(data['message']['to']), 2) + self.assertEqual(len(data['message']['to']), 4) self.assertEqual(data['message']['to'][0]['name'], "Recipient #1") self.assertEqual(data['message']['to'][0]['email'], "to1@example.com") self.assertEqual(data['message']['to'][1]['name'], "") self.assertEqual(data['message']['to'][1]['email'], "to2@example.com") + self.assertEqual(data['message']['to'][2]['name'], "Carbon Copy") + self.assertEqual(data['message']['to'][2]['email'], "cc1@example.com") + self.assertEqual(data['message']['to'][3]['name'], "") + self.assertEqual(data['message']['to'][3]['email'], "cc2@example.com") + # Mandrill only supports email, not name, for bcc: + self.assertEqual(data['message']['bcc_address'], "bcc@example.com") def test_email_message(self): email = mail.EmailMessage('Subject', 'Body goes here', 'from@example.com', ['to1@example.com', 'Also To '], - bcc=['bcc1@example.com', 'Also BCC '], + bcc=['bcc@example.com'], cc=['cc1@example.com', 'Also CC '], headers={'Reply-To': 'another@example.com', 'X-MyHeader': 'my value'}) @@ -62,16 +72,15 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase): self.assertEqual(data['message']['from_email'], "from@example.com") self.assertEqual(data['message']['headers'], { 'Reply-To': 'another@example.com', 'X-MyHeader': 'my value' }) - # Mandrill doesn't have a notion of cc, and only allows a single bcc. - # Djrill just treats cc and bcc as though they were "to" addresses, + # Mandrill doesn't have a notion of cc. + # Djrill just treats cc as additional "to" addresses, # which may or may not be what you want. - self.assertEqual(len(data['message']['to']), 6) + self.assertEqual(len(data['message']['to']), 4) self.assertEqual(data['message']['to'][0]['email'], "to1@example.com") self.assertEqual(data['message']['to'][1]['email'], "to2@example.com") self.assertEqual(data['message']['to'][2]['email'], "cc1@example.com") self.assertEqual(data['message']['to'][3]['email'], "cc2@example.com") - self.assertEqual(data['message']['to'][4]['email'], "bcc1@example.com") - self.assertEqual(data['message']['to'][5]['email'], "bcc2@example.com") + self.assertEqual(data['message']['bcc_address'], "bcc@example.com") def test_html_message(self): text_content = 'This is an important message.' @@ -178,6 +187,14 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase): mimetype="application/vnd.ms-powerpoint") msg.send() + def test_bcc_errors(self): + # Mandrill only allows a single bcc address + with self.assertRaises(NotSupportedByMandrillError): + msg = mail.EmailMessage('Subject', 'Body', + 'from@example.com', ['to@example.com'], + bcc=['bcc1@example.com>', 'bcc2@example.com']) + msg.send() + def test_mandrill_api_failure(self): self.mock_post.return_value = self.MockResponse(status_code=400) with self.assertRaises(MandrillAPIError): @@ -295,6 +312,7 @@ class DjrillMandrillFeatureTests(DjrillBackendMockAPITestCase): self.assert_mandrill_called("/messages/send.json") data = self.get_api_call_data() self.assertFalse('from_name' in data['message']) + self.assertFalse('bcc_address' in data['message']) self.assertFalse('track_opens' in data['message']) self.assertFalse('track_clicks' in data['message']) self.assertFalse('auto_text' in data['message'])