Skip to content

Commit d7197f1

Browse files
committed
First pass at making Email\Email use Multipart classes.
There is still work to be done around a more generic multipart builder, but I think that might require a separate Mime building sub-package.
1 parent 31109a6 commit d7197f1

File tree

3 files changed

+119
-27
lines changed

3 files changed

+119
-27
lines changed

lib/Cake/Network/Email/Email.php

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Cake\Core\Configure;
1919
use Cake\Error;
2020
use Cake\Log\Log;
21+
use Cake\Network\Http\FormData\Part;
2122
use Cake\Utility\Hash;
2223
use Cake\Utility\String;
2324
use Cake\Utility\Validation;
@@ -1162,7 +1163,7 @@ protected function _applyConfig($config) {
11621163
}
11631164

11641165
/**
1165-
* Reset all EmailComponent internal variables to be able to send out a new email.
1166+
* Reset all the internal variables to be able to send out a new email.
11661167
*
11671168
* @return Cake\Network\Email\Email $this
11681169
*/
@@ -1352,18 +1353,21 @@ protected function _attachFiles($boundary = null) {
13521353
continue;
13531354
}
13541355
$data = $this->_readFile($fileInfo['file']);
1355-
1356-
$msg[] = '--' . $boundary;
1357-
$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
1358-
$msg[] = 'Content-Transfer-Encoding: base64';
1359-
if (
1356+
$hasDisposition = (
13601357
!isset($fileInfo['contentDisposition']) ||
13611358
$fileInfo['contentDisposition']
1362-
) {
1363-
$msg[] = 'Content-Disposition: attachment; filename="' . $filename . '"';
1359+
);
1360+
$part = new Part(false, $data, false);
1361+
1362+
if ($hasDisposition) {
1363+
$part->disposition('attachment');
1364+
$part->filename($filename);
13641365
}
1365-
$msg[] = '';
1366-
$msg[] = $data;
1366+
$part->transferEncoding('base64');
1367+
$part->type($fileInfo['mimetype']);
1368+
1369+
$msg[] = '--' . $boundary;
1370+
$msg[] = (string)$part;
13671371
$msg[] = '';
13681372
}
13691373
return $msg;
@@ -1402,12 +1406,12 @@ protected function _attachInlineFiles($boundary = null) {
14021406
$data = $this->_readFile($fileInfo['file']);
14031407

14041408
$msg[] = '--' . $boundary;
1405-
$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
1406-
$msg[] = 'Content-Transfer-Encoding: base64';
1407-
$msg[] = 'Content-ID: <' . $fileInfo['contentId'] . '>';
1408-
$msg[] = 'Content-Disposition: inline; filename="' . $filename . '"';
1409-
$msg[] = '';
1410-
$msg[] = $data;
1409+
$part = new Part(false, $data, 'inline');
1410+
$part->type($fileInfo['mimetype']);
1411+
$part->transferEncoding('base64');
1412+
$part->contentId($fileInfo['contentId']);
1413+
$part->filename($filename);
1414+
$msg[] = (string)$part;
14111415
$msg[] = '';
14121416
}
14131417
return $msg;

lib/Cake/Network/Http/FormData/Part.php

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ class Part {
5757
*/
5858
protected $_filename;
5959

60+
/**
61+
* The encoding used in this part.
62+
*
63+
* @var string
64+
*/
65+
protected $_transferEncoding;
66+
67+
/**
68+
* The contentId for the part
69+
*
70+
* @var string
71+
*/
72+
protected $_contentId;
73+
6074
/**
6175
* Constructor
6276
*
@@ -71,9 +85,41 @@ public function __construct($name, $value, $disposition = 'form-data') {
7185
$this->_disposition = $disposition;
7286
}
7387

88+
/**
89+
* Get/set the disposition type
90+
*
91+
* By passing in `false` you can disable the disposition
92+
* header from being added.
93+
*
94+
* @param null|string $disposition Use null to get/string to set.
95+
* @return mixed
96+
*/
97+
public function disposition($disposition = null) {
98+
if ($disposition === null) {
99+
return $this->_disposition;
100+
}
101+
$this->_disposition = $disposition;
102+
}
103+
104+
/**
105+
* Get/set the contentId for a part.
106+
*
107+
* @param null|string $id The content id.
108+
* @return mixed.
109+
*/
110+
public function contentId($id = null) {
111+
if ($id === null) {
112+
return $this->_contentId = $id;
113+
}
114+
$this->_contentId = $id;
115+
}
116+
74117
/**
75118
* Get/set the filename.
76119
*
120+
* Setting the filname to `false` will exclude it from the
121+
* generated output.
122+
*
77123
* @param null|string $filename Use null to get/string to set.
78124
* @return mixed
79125
*/
@@ -97,6 +143,21 @@ public function type($type) {
97143
$this->_type = $type;
98144
}
99145

146+
/**
147+
* Set the transfer-encoding for multipart.
148+
*
149+
* Useful when content bodies are in encodings like base64.
150+
*
151+
* @param null|string $type The type of encoding the value has.
152+
* @return mixed
153+
*/
154+
public function transferEncoding($type) {
155+
if ($type === null) {
156+
return $this->_transferEncoding;
157+
}
158+
$this->_transferEncoding = $type;
159+
}
160+
100161
/**
101162
* Convert the part into a string.
102163
*
@@ -106,14 +167,25 @@ public function type($type) {
106167
*/
107168
public function __toString() {
108169
$out = '';
109-
$out .= sprintf('Content-Disposition: %s; name="%s"', $this->_disposition, $this->_name);
110-
if ($this->_filename) {
111-
$out .= '; filename="' . $this->_filename . '"';
170+
if ($this->_disposition) {
171+
$out .= 'Content-Disposition: ' . $this->_disposition;
172+
if ($this->_name) {
173+
$out .= '; name="' . $this->_name . '"';
174+
}
175+
if ($this->_filename) {
176+
$out .= '; filename="' . $this->_filename . '"';
177+
}
178+
$out .= "\r\n";
112179
}
113-
$out .= "\r\n";
114180
if ($this->_type) {
115181
$out .= 'Content-Type: ' . $this->_type . "\r\n";
116182
}
183+
if ($this->_transferEncoding) {
184+
$out .= 'Content-Transfer-Encoding: ' . $this->_transferEncoding . "\r\n";
185+
}
186+
if ($this->_contentId) {
187+
$out .= 'Content-ID: <' . $this->_contentId . ">\r\n";
188+
}
117189
$out .= "\r\n";
118190
$out .= (string)$this->_value;
119191
return $out;

lib/Cake/Test/TestCase/Network/Email/EmailTest.php

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -848,9 +848,10 @@ public function testSendNoTemplateWithAttachments() {
848848
"\r\n" .
849849
"\r\n" .
850850
"--$boundary\r\n" .
851+
"Content-Disposition: attachment; filename=\"basics.php\"\r\n" .
851852
"Content-Type: application/octet-stream\r\n" .
852853
"Content-Transfer-Encoding: base64\r\n" .
853-
"Content-Disposition: attachment; filename=\"basics.php\"\r\n\r\n";
854+
"\r\n";
854855
$this->assertContains($expected, $result['message']);
855856
}
856857

@@ -892,9 +893,10 @@ public function testSendNoTemplateWithAttachmentsAsBoth() {
892893
"--alt-{$boundary}--\r\n" .
893894
"\r\n" .
894895
"--$boundary\r\n" .
896+
"Content-Disposition: attachment; filename=\"VERSION.txt\"\r\n" .
895897
"Content-Type: application/octet-stream\r\n" .
896898
"Content-Transfer-Encoding: base64\r\n" .
897-
"Content-Disposition: attachment; filename=\"VERSION.txt\"\r\n\r\n";
899+
"\r\n";
898900
$this->assertContains($expected, $result['message']);
899901
}
900902

@@ -944,10 +946,11 @@ public function testSendWithInlineAttachments() {
944946
"--alt-{$boundary}--\r\n" .
945947
"\r\n" .
946948
"--rel-$boundary\r\n" .
949+
"Content-Disposition: inline; filename=\"cake.png\"\r\n" .
947950
"Content-Type: application/octet-stream\r\n" .
948951
"Content-Transfer-Encoding: base64\r\n" .
949952
"Content-ID: <abc123>\r\n" .
950-
"Content-Disposition: inline; filename=\"cake.png\"\r\n\r\n";
953+
"\r\n";
951954
$this->assertContains($expected, $result['message']);
952955
$this->assertContains('--rel-' . $boundary . '--', $result['message']);
953956
$this->assertContains('--' . $boundary . '--', $result['message']);
@@ -1280,19 +1283,32 @@ public function testSendAttachment() {
12801283
$this->CakeEmail->config(array());
12811284
$this->CakeEmail->attachments(array(CAKE . 'basics.php'));
12821285
$result = $this->CakeEmail->send('body');
1283-
$this->assertContains("Content-Type: application/octet-stream\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"basics.php\"", $result['message']);
1286+
$expected = "Content-Disposition: attachment; filename=\"basics.php\"\r\n" .
1287+
"Content-Type: application/octet-stream\r\n" .
1288+
"Content-Transfer-Encoding: base64\r\n";
1289+
$this->assertContains($expected, $result['message']);
12841290

12851291
$this->CakeEmail->attachments(array('my.file.txt' => CAKE . 'basics.php'));
12861292
$result = $this->CakeEmail->send('body');
1287-
$this->assertContains("Content-Type: application/octet-stream\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"my.file.txt\"", $result['message']);
1293+
$expected = "Content-Disposition: attachment; filename=\"my.file.txt\"\r\n" .
1294+
"Content-Type: application/octet-stream\r\n" .
1295+
"Content-Transfer-Encoding: base64\r\n";
1296+
$this->assertContains($expected, $result['message']);
12881297

12891298
$this->CakeEmail->attachments(array('file.txt' => array('file' => CAKE . 'basics.php', 'mimetype' => 'text/plain')));
12901299
$result = $this->CakeEmail->send('body');
1291-
$this->assertContains("Content-Type: text/plain\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"file.txt\"", $result['message']);
1300+
$expected = "Content-Disposition: attachment; filename=\"file.txt\"\r\n" .
1301+
"Content-Type: text/plain\r\n" .
1302+
"Content-Transfer-Encoding: base64\r\n";
1303+
$this->assertContains($expected, $result['message']);
12921304

12931305
$this->CakeEmail->attachments(array('file2.txt' => array('file' => CAKE . 'basics.php', 'mimetype' => 'text/plain', 'contentId' => 'a1b1c1')));
12941306
$result = $this->CakeEmail->send('body');
1295-
$this->assertContains("Content-Type: text/plain\r\nContent-Transfer-Encoding: base64\r\nContent-ID: <a1b1c1>\r\nContent-Disposition: inline; filename=\"file2.txt\"", $result['message']);
1307+
$expected = "Content-Disposition: inline; filename=\"file2.txt\"\r\n" .
1308+
"Content-Type: text/plain\r\n" .
1309+
"Content-Transfer-Encoding: base64\r\n" .
1310+
"Content-ID: <a1b1c1>\r\n";
1311+
$this->assertContains($expected, $result['message']);
12961312
}
12971313

12981314
/**

0 commit comments

Comments
 (0)