From 3c42279598dfdd4832b1411f4a94355f2025db4b Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Mon, 6 Jun 2016 09:44:50 +0300 Subject: Merge branch 'feature/email-attachments' into develop --- system/libraries/Email.php | 194 +++++++++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 77 deletions(-) (limited to 'system/libraries/Email.php') diff --git a/system/libraries/Email.php b/system/libraries/Email.php index 6ff3efad9..f8772c656 100644 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -149,13 +149,6 @@ class CI_Email { */ public $charset = 'UTF-8'; - /** - * Multipart message - * - * @var string 'mixed' (in the body) or 'related' (separate) - */ - public $multipart = 'mixed'; // "mixed" (in the body) or "related" (separate) - /** * Alternative message (for HTML messages only) * @@ -260,20 +253,6 @@ class CI_Email { */ protected $_finalbody = ''; - /** - * multipart/alternative boundary - * - * @var string - */ - protected $_alt_boundary = ''; - - /** - * Attachment boundary - * - * @var string - */ - protected $_atc_boundary = ''; - /** * Final headers to send * @@ -743,7 +722,8 @@ class CI_Email { 'name' => array($file, $newname), 'disposition' => empty($disposition) ? 'attachment' : $disposition, // Can also be 'inline' Not sure if it matters 'type' => $mime, - 'content' => chunk_split(base64_encode($file_content)) + 'content' => chunk_split(base64_encode($file_content)), + 'multipart' => 'mixed' ); return $this; @@ -761,15 +741,11 @@ class CI_Email { */ public function attachment_cid($filename) { - if ($this->multipart !== 'related') - { - $this->multipart = 'related'; // Thunderbird need this for inline images - } - for ($i = 0, $c = count($this->_attachments); $i < $c; $i++) { if ($this->_attachments[$i]['name'][0] === $filename) { + $this->_attachments[$i]['multipart'] = 'related'; $this->_attachments[$i]['cid'] = uniqid(basename($this->_attachments[$i]['name'][0]).'@'); return $this->_attachments[$i]['cid']; } @@ -913,19 +889,6 @@ class CI_Email { // -------------------------------------------------------------------- - /** - * Set Message Boundary - * - * @return void - */ - protected function _set_boundaries() - { - $this->_alt_boundary = 'B_ALT_'.uniqid(''); // multipart/alternative - $this->_atc_boundary = 'B_ATC_'.uniqid(''); // attachment boundary - } - - // -------------------------------------------------------------------- - /** * Get the Message ID * @@ -993,9 +956,9 @@ class CI_Email { { if ($this->mailtype === 'html') { - return (count($this->_attachments) === 0) ? 'html' : 'html-attach'; + return empty($this->_attachments) ? 'html' : 'html-attach'; } - elseif ($this->mailtype === 'text' && count($this->_attachments) > 0) + elseif ($this->mailtype === 'text' && ! empty($this->_attachments)) { return 'plain-attach'; } @@ -1301,7 +1264,6 @@ class CI_Email { $this->_body = $this->word_wrap($this->_body); } - $this->_set_boundaries(); $this->_write_headers(); $hdr = ($this->_get_protocol() === 'mail') ? $this->newline : ''; @@ -1309,7 +1271,7 @@ class CI_Email { switch ($this->_get_content_type()) { - case 'plain' : + case 'plain': $hdr .= 'Content-Type: text/plain; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: '.$this->_get_encoding(); @@ -1326,7 +1288,7 @@ class CI_Email { return; - case 'html' : + case 'html': if ($this->send_multipart === FALSE) { @@ -1335,14 +1297,16 @@ class CI_Email { } else { - $hdr .= 'Content-Type: multipart/alternative; boundary="'.$this->_alt_boundary.'"'; + $boundary = uniqid('B_ALT_'); + $hdr .= 'Content-Type: multipart/alternative; boundary="'.$boundary.'"'; $body .= $this->_get_mime_message().$this->newline.$this->newline - .'--'.$this->_alt_boundary.$this->newline + .'--'.$boundary.$this->newline .'Content-Type: text/plain; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline - .$this->_get_alt_message().$this->newline.$this->newline.'--'.$this->_alt_boundary.$this->newline + .$this->_get_alt_message().$this->newline.$this->newline + .'--'.$boundary.$this->newline .'Content-Type: text/html; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline; @@ -1361,14 +1325,15 @@ class CI_Email { if ($this->send_multipart !== FALSE) { - $this->_finalbody .= '--'.$this->_alt_boundary.'--'; + $this->_finalbody .= '--'.$boundary.'--'; } return; - case 'plain-attach' : + case 'plain-attach': - $hdr .= 'Content-Type: multipart/'.$this->multipart.'; boundary="'.$this->_atc_boundary.'"'; + $boundary = uniqid('B_ATC_'); + $hdr .= 'Content-Type: multipart/mixed; boundary="'.$boundary.'"'; if ($this->_get_protocol() === 'mail') { @@ -1377,59 +1342,83 @@ class CI_Email { $body .= $this->_get_mime_message().$this->newline .$this->newline - .'--'.$this->_atc_boundary.$this->newline + .'--'.$boundary.$this->newline .'Content-Type: text/plain; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline .$this->newline .$this->_body.$this->newline.$this->newline; - break; - case 'html-attach' : + $this->_append_attachments($body, $boundary); - $hdr .= 'Content-Type: multipart/'.$this->multipart.'; boundary="'.$this->_atc_boundary.'"'; + break; + case 'html-attach': + + $alt_boundary = uniqid('B_ALT_'); + $last_boundary = NULL; + + if ($this->_attachments_have_multipart('mixed')) + { + $atc_boundary = uniqid('B_ATC_'); + $hdr .= 'Content-Type: multipart/mixed; boundary="'.$atc_boundary.'"'; + $last_boundary = $atc_boundary; + } + + if ($this->_attachments_have_multipart('related')) + { + $rel_boundary = uniqid('B_REL_'); + $rel_boundary_header = 'Content-Type: multipart/related; boundary="'.$rel_boundary.'"'; + + if (isset($last_boundary)) + { + $body .= '--'.$last_boundary.$this->newline.$rel_boundary_header; + } + else + { + $hdr .= $rel_boundary_header; + } + + $last_boundary = $rel_boundary; + } if ($this->_get_protocol() === 'mail') { $this->_header_str .= $hdr; } + strlen($body) && $body .= $this->newline.$this->newline; $body .= $this->_get_mime_message().$this->newline.$this->newline - .'--'.$this->_atc_boundary.$this->newline + .'--'.$last_boundary.$this->newline - .'Content-Type: multipart/alternative; boundary="'.$this->_alt_boundary.'"'.$this->newline.$this->newline - .'--'.$this->_alt_boundary.$this->newline + .'Content-Type: multipart/alternative; boundary="'.$alt_boundary.'"'.$this->newline.$this->newline + .'--'.$alt_boundary.$this->newline .'Content-Type: text/plain; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline - .$this->_get_alt_message().$this->newline.$this->newline.'--'.$this->_alt_boundary.$this->newline + .$this->_get_alt_message().$this->newline.$this->newline + .'--'.$alt_boundary.$this->newline .'Content-Type: text/html; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline .$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline - .'--'.$this->_alt_boundary.'--'.$this->newline.$this->newline; + .'--'.$alt_boundary.'--'.$this->newline.$this->newline; - break; - } - - $attachment = array(); - for ($i = 0, $c = count($this->_attachments), $z = 0; $i < $c; $i++) - { - $filename = $this->_attachments[$i]['name'][0]; - $basename = ($this->_attachments[$i]['name'][1] === NULL) - ? basename($filename) : $this->_attachments[$i]['name'][1]; + if ( ! empty($rel_boundary)) + { + $body .= $this->newline.$this->newline; + $this->_append_attachments($body, $rel_boundary, 'related'); + } - $attachment[$z++] = '--'.$this->_atc_boundary.$this->newline - .'Content-type: '.$this->_attachments[$i]['type'].'; ' - .'name="'.$basename.'"'.$this->newline - .'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline - .'Content-Transfer-Encoding: base64'.$this->newline - .(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline); + // multipart/mixed attachments + if ( ! empty($atc_boundary)) + { + $body .= $this->newline.$this->newline; + $this->_append_attachments($body, $atc_boundary, 'mixed'); + } - $attachment[$z++] = $this->_attachments[$i]['content']; + break; } - $body .= implode($this->newline, $attachment).$this->newline.'--'.$this->_atc_boundary.'--'; $this->_finalbody = ($this->_get_protocol() === 'mail') ? $body : $hdr.$this->newline.$this->newline.$body; @@ -1439,6 +1428,57 @@ class CI_Email { // -------------------------------------------------------------------- + protected function _attachments_have_multipart($type) + { + foreach ($this->_attachments as &$attachment) + { + if ($attachment['multipart'] === $type) + { + return TRUE; + } + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Prepares attachment string + * + * @param string $body Message body to append to + * @param string $boundary Multipart boundary + * @param string $multipart When provided, only attachments of this type will be processed + * @return string + */ + protected function _append_attachments(&$body, $boundary, $multipart = null) + { + for ($i = 0, $c = count($this->_attachments); $i < $c; $i++) + { + if (isset($multipart) && $this->_attachments[$i]['multipart'] !== $multipart) + { + continue; + } + + $name = isset($this->_attachments[$i]['name'][1]) + ? $this->_attachments[$i]['name'][1] + : basename($this->_attachments[$i]['name'][0]); + + $body .= '--'.$boundary.$this->newline + .'Content-Type: '.$this->_attachments[$i]['type'].'; name="'.$name.'"'.$this->newline + .'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline + .'Content-Transfer-Encoding: base64'.$this->newline + .(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline.$this->newline) + .$this->_attachments[$i]['content'].$this->newline; + } + + // $name won't be set if no attachments were appended, + // and therefore a boundary wouldn't be necessary + empty($name) OR $body .= '--'.$boundary.'--'; + } + + // -------------------------------------------------------------------- + /** * Prep Quoted Printable * -- cgit v1.2.3-24-g4f1b