From b8fa7ce1349e48047539a83f574c3ff87849cafa Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 13 May 2014 17:47:11 -0400 Subject: [PATCH] Fix issues where emails would have multipart/mixed when they should not. When sending multi-part emails with no attachments we shouldn't include the outer multipart/mixed header as it confuses Outlook and causes it to show the email as having attachments even though there are none. A bunch of tests need to be adjusted as the empty multipart/mixed container has been removed. Fixes #3474 --- lib/Cake/Network/Email/CakeEmail.php | 13 +++-- .../Test/Case/Network/Email/CakeEmailTest.php | 50 +++++++++++++++++-- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/lib/Cake/Network/Email/CakeEmail.php b/lib/Cake/Network/Email/CakeEmail.php index 8f640ecc8..8951bd1b3 100644 --- a/lib/Cake/Network/Email/CakeEmail.php +++ b/lib/Cake/Network/Email/CakeEmail.php @@ -756,8 +756,10 @@ class CakeEmail { } $headers['MIME-Version'] = '1.0'; - if (!empty($this->_attachments) || $this->_emailFormat === 'both') { + if (!empty($this->_attachments)) { $headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"'; + } elseif ($this->_emailFormat === 'both') { + $headers['Content-Type'] = 'multipart/alternative; boundary="' . $this->_boundary . '"'; } elseif ($this->_emailFormat === 'text') { $headers['Content-Type'] = 'text/plain; charset=' . $this->_getContentTypeCharset(); } elseif ($this->_emailFormat === 'html') { @@ -1521,6 +1523,7 @@ class CakeEmail { $hasInlineAttachments = count($contentIds) > 0; $hasAttachments = !empty($this->_attachments); $hasMultipleTypes = count($rendered) > 1; + $multiPart = ($hasAttachments || $hasMultipleTypes); $boundary = $relBoundary = $textBoundary = $this->_boundary; @@ -1531,7 +1534,7 @@ class CakeEmail { $relBoundary = $textBoundary = 'rel-' . $boundary; } - if ($hasMultipleTypes) { + if ($hasMultipleTypes && $hasAttachments) { $msg[] = '--' . $relBoundary; $msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $boundary . '"'; $msg[] = ''; @@ -1539,7 +1542,7 @@ class CakeEmail { } if (isset($rendered['text'])) { - if ($textBoundary !== $boundary || $hasAttachments) { + if ($multiPart) { $msg[] = '--' . $textBoundary; $msg[] = 'Content-Type: text/plain; charset=' . $this->_getContentTypeCharset(); $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding(); @@ -1552,7 +1555,7 @@ class CakeEmail { } if (isset($rendered['html'])) { - if ($textBoundary !== $boundary || $hasAttachments) { + if ($multiPart) { $msg[] = '--' . $textBoundary; $msg[] = 'Content-Type: text/html; charset=' . $this->_getContentTypeCharset(); $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding(); @@ -1564,7 +1567,7 @@ class CakeEmail { $msg[] = ''; } - if ($hasMultipleTypes) { + if ($textBoundary !== $boundary) { $msg[] = '--' . $textBoundary . '--'; $msg[] = ''; } diff --git a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php index e5865fe13..8e42acc75 100644 --- a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php +++ b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php @@ -1301,6 +1301,52 @@ class CakeEmailTest extends CakeTestCase { $this->assertNotContains('This email was sent using the CakePHP Framework', $result['message']); } +/** + * testSendRender both method + * + * @return void + */ + public function testSendRenderBoth() { + $this->CakeEmail->reset(); + $this->CakeEmail->transport('debug'); + + $this->CakeEmail->from('cake@cakephp.org'); + $this->CakeEmail->to(array('you@cakephp.org' => 'You')); + $this->CakeEmail->subject('My title'); + $this->CakeEmail->config(array('empty')); + $this->CakeEmail->template('default', 'default'); + $this->CakeEmail->emailFormat('both'); + $result = $this->CakeEmail->send(); + + $this->assertContains('Message-ID: ', $result['headers']); + $this->assertContains('To: ', $result['headers']); + + $boundary = $this->CakeEmail->getBoundary(); + $this->assertContains('Content-Type: multipart/alternative; boundary="' . $boundary . '"', $result['headers']); + + $expected = "--$boundary\r\n" . + "Content-Type: text/plain; charset=UTF-8\r\n" . + "Content-Transfer-Encoding: 8bit\r\n" . + "\r\n" . + "\r\n" . + "\r\n" . + "This email was sent using the CakePHP Framework, http://cakephp.org." . + "\r\n" . + "\r\n" . + "--$boundary\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Content-Transfer-Encoding: 8bit\r\n" . + "\r\n" . + "assertStringStartsWith($expected, $result['message']); + + $expected = "\r\n" . + "\r\n" . + "\r\n" . + "--$boundary--\r\n"; + $this->assertStringEndsWith($expected, $result['message']); + } + /** * testSendRender method for ISO-2022-JP * @@ -1542,8 +1588,6 @@ class CakeEmailTest extends CakeTestCase { $this->assertFalse(empty($boundary)); $this->assertContains('--' . $boundary, $message); $this->assertContains('--' . $boundary . '--', $message); - $this->assertContains('--alt-' . $boundary, $message); - $this->assertContains('--alt-' . $boundary . '--', $message); $this->CakeEmail->attachments(array('fake.php' => __FILE__)); $this->CakeEmail->send(); @@ -2005,7 +2049,7 @@ class CakeEmailTest extends CakeTestCase { } protected function _checkContentTransferEncoding($message, $charset) { - $boundary = '--alt-' . $this->CakeEmail->getBoundary(); + $boundary = '--' . $this->CakeEmail->getBoundary(); $result['text'] = false; $result['html'] = false; $length = count($message);