Skip to content

Commit

Permalink
First pass at making Email\Email use Multipart classes.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
markstory committed Feb 4, 2013
1 parent 31109a6 commit d7197f1
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 27 deletions.
36 changes: 20 additions & 16 deletions lib/Cake/Network/Email/Email.php
Expand Up @@ -18,6 +18,7 @@
use Cake\Core\Configure;
use Cake\Error;
use Cake\Log\Log;
use Cake\Network\Http\FormData\Part;
use Cake\Utility\Hash;
use Cake\Utility\String;
use Cake\Utility\Validation;
Expand Down Expand Up @@ -1162,7 +1163,7 @@ protected function _applyConfig($config) {
}

/**
* Reset all EmailComponent internal variables to be able to send out a new email.
* Reset all the internal variables to be able to send out a new email.
*
* @return Cake\Network\Email\Email $this
*/
Expand Down Expand Up @@ -1352,18 +1353,21 @@ protected function _attachFiles($boundary = null) {
continue;
}
$data = $this->_readFile($fileInfo['file']);

$msg[] = '--' . $boundary;
$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
$msg[] = 'Content-Transfer-Encoding: base64';
if (
$hasDisposition = (
!isset($fileInfo['contentDisposition']) ||
$fileInfo['contentDisposition']
) {
$msg[] = 'Content-Disposition: attachment; filename="' . $filename . '"';
);
$part = new Part(false, $data, false);

if ($hasDisposition) {
$part->disposition('attachment');
$part->filename($filename);
}
$msg[] = '';
$msg[] = $data;
$part->transferEncoding('base64');
$part->type($fileInfo['mimetype']);

$msg[] = '--' . $boundary;
$msg[] = (string)$part;
$msg[] = '';
}
return $msg;
Expand Down Expand Up @@ -1402,12 +1406,12 @@ protected function _attachInlineFiles($boundary = null) {
$data = $this->_readFile($fileInfo['file']);

$msg[] = '--' . $boundary;
$msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
$msg[] = 'Content-Transfer-Encoding: base64';
$msg[] = 'Content-ID: <' . $fileInfo['contentId'] . '>';
$msg[] = 'Content-Disposition: inline; filename="' . $filename . '"';
$msg[] = '';
$msg[] = $data;
$part = new Part(false, $data, 'inline');
$part->type($fileInfo['mimetype']);
$part->transferEncoding('base64');
$part->contentId($fileInfo['contentId']);
$part->filename($filename);
$msg[] = (string)$part;
$msg[] = '';
}
return $msg;
Expand Down
80 changes: 76 additions & 4 deletions lib/Cake/Network/Http/FormData/Part.php
Expand Up @@ -57,6 +57,20 @@ class Part {
*/
protected $_filename;

/**
* The encoding used in this part.
*
* @var string
*/
protected $_transferEncoding;

/**
* The contentId for the part
*
* @var string
*/
protected $_contentId;

/**
* Constructor
*
Expand All @@ -71,9 +85,41 @@ public function __construct($name, $value, $disposition = 'form-data') {
$this->_disposition = $disposition;
}

/**
* Get/set the disposition type
*
* By passing in `false` you can disable the disposition
* header from being added.
*
* @param null|string $disposition Use null to get/string to set.
* @return mixed
*/
public function disposition($disposition = null) {
if ($disposition === null) {
return $this->_disposition;
}
$this->_disposition = $disposition;
}

/**
* Get/set the contentId for a part.
*
* @param null|string $id The content id.
* @return mixed.
*/
public function contentId($id = null) {
if ($id === null) {
return $this->_contentId = $id;
}
$this->_contentId = $id;
}

/**
* Get/set the filename.
*
* Setting the filname to `false` will exclude it from the
* generated output.
*
* @param null|string $filename Use null to get/string to set.
* @return mixed
*/
Expand All @@ -97,6 +143,21 @@ public function type($type) {
$this->_type = $type;
}

/**
* Set the transfer-encoding for multipart.
*
* Useful when content bodies are in encodings like base64.
*
* @param null|string $type The type of encoding the value has.
* @return mixed
*/
public function transferEncoding($type) {
if ($type === null) {
return $this->_transferEncoding;
}
$this->_transferEncoding = $type;
}

/**
* Convert the part into a string.
*
Expand All @@ -106,14 +167,25 @@ public function type($type) {
*/
public function __toString() {
$out = '';
$out .= sprintf('Content-Disposition: %s; name="%s"', $this->_disposition, $this->_name);
if ($this->_filename) {
$out .= '; filename="' . $this->_filename . '"';
if ($this->_disposition) {
$out .= 'Content-Disposition: ' . $this->_disposition;
if ($this->_name) {
$out .= '; name="' . $this->_name . '"';
}
if ($this->_filename) {
$out .= '; filename="' . $this->_filename . '"';
}
$out .= "\r\n";
}
$out .= "\r\n";
if ($this->_type) {
$out .= 'Content-Type: ' . $this->_type . "\r\n";
}
if ($this->_transferEncoding) {
$out .= 'Content-Transfer-Encoding: ' . $this->_transferEncoding . "\r\n";
}
if ($this->_contentId) {
$out .= 'Content-ID: <' . $this->_contentId . ">\r\n";
}
$out .= "\r\n";
$out .= (string)$this->_value;
return $out;
Expand Down
30 changes: 23 additions & 7 deletions lib/Cake/Test/TestCase/Network/Email/EmailTest.php
Expand Up @@ -848,9 +848,10 @@ public function testSendNoTemplateWithAttachments() {
"\r\n" .
"\r\n" .
"--$boundary\r\n" .
"Content-Disposition: attachment; filename=\"basics.php\"\r\n" .
"Content-Type: application/octet-stream\r\n" .
"Content-Transfer-Encoding: base64\r\n" .
"Content-Disposition: attachment; filename=\"basics.php\"\r\n\r\n";
"\r\n";
$this->assertContains($expected, $result['message']);
}

Expand Down Expand Up @@ -892,9 +893,10 @@ public function testSendNoTemplateWithAttachmentsAsBoth() {
"--alt-{$boundary}--\r\n" .
"\r\n" .
"--$boundary\r\n" .
"Content-Disposition: attachment; filename=\"VERSION.txt\"\r\n" .
"Content-Type: application/octet-stream\r\n" .
"Content-Transfer-Encoding: base64\r\n" .
"Content-Disposition: attachment; filename=\"VERSION.txt\"\r\n\r\n";
"\r\n";
$this->assertContains($expected, $result['message']);
}

Expand Down Expand Up @@ -944,10 +946,11 @@ public function testSendWithInlineAttachments() {
"--alt-{$boundary}--\r\n" .
"\r\n" .
"--rel-$boundary\r\n" .
"Content-Disposition: inline; filename=\"cake.png\"\r\n" .
"Content-Type: application/octet-stream\r\n" .
"Content-Transfer-Encoding: base64\r\n" .
"Content-ID: <abc123>\r\n" .
"Content-Disposition: inline; filename=\"cake.png\"\r\n\r\n";
"\r\n";
$this->assertContains($expected, $result['message']);
$this->assertContains('--rel-' . $boundary . '--', $result['message']);
$this->assertContains('--' . $boundary . '--', $result['message']);
Expand Down Expand Up @@ -1280,19 +1283,32 @@ public function testSendAttachment() {
$this->CakeEmail->config(array());
$this->CakeEmail->attachments(array(CAKE . 'basics.php'));
$result = $this->CakeEmail->send('body');
$this->assertContains("Content-Type: application/octet-stream\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"basics.php\"", $result['message']);
$expected = "Content-Disposition: attachment; filename=\"basics.php\"\r\n" .
"Content-Type: application/octet-stream\r\n" .
"Content-Transfer-Encoding: base64\r\n";
$this->assertContains($expected, $result['message']);

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

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

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

/**
Expand Down

0 comments on commit d7197f1

Please sign in to comment.