Streamline handling of mime parts
authorTim Düsterhus <duesterhus@woltlab.com>
Mon, 30 May 2016 16:11:48 +0000 (18:11 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Mon, 30 May 2016 16:41:10 +0000 (18:41 +0200)
wcfsetup/install/files/lib/system/email/Email.class.php
wcfsetup/install/files/lib/system/email/mime/AbstractMimePart.class.php
wcfsetup/install/files/lib/system/email/mime/AbstractMultipartMimePart.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/email/mime/MultipartAlternativeMimePart.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/email/mime/MultipartMixedMimePart.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/email/mime/RecipientAwareTextMimePart.class.php
wcfsetup/install/files/lib/system/email/transport/DebugEmailTransport.class.php
wcfsetup/install/files/lib/system/email/transport/SmtpEmailTransport.class.php

index 4bc1dedf3d8d6420b75ea9e494108056473cdd47..492fe40354eadb89b5ce2758bc6ca4a83dacb094 100644 (file)
@@ -5,9 +5,7 @@ use wcf\system\background\job\EmailDeliveryBackgroundJob;
 use wcf\system\background\BackgroundQueueHandler;
 use wcf\system\email\mime\AbstractMimePart;
 use wcf\system\email\mime\IRecipientAwareMimePart;
-use wcf\system\email\mime\TextMimePart;
 use wcf\system\event\EventHandler;
-use wcf\system\exception\SystemException;
 use wcf\util\DateUtil;
 use wcf\util\StringUtil;
 
@@ -78,28 +76,10 @@ class Email {
        protected $extraHeaders = [];
        
        /**
-        * Text parts of this email
-        * @var array
-        */
-       protected $text = [];
-       
-       /**
-        * Attachments of this email
-        * @var array
+        * The body of this Email.
+        * @var AbstractMimePart
         */
-       protected $attachments = [];
-       
-       /**
-        * Boundary between the 'Text' parts of this email
-        * @var string
-        */
-       private $textBoundary;
-       
-       /**
-        * Boundary between the mime parts of this email
-        * @var string
-        */
-       private $mimeBoundary;
+       protected $content = null;
        
        /**
         * Mail host for use in the Message-Id
@@ -107,14 +87,6 @@ class Email {
         */
        private static $host = null;
        
-       /**
-        * Generates boundaries for the mime parts.
-        */
-       public function __construct() {
-               $this->textBoundary = "WoltLab_Community_Framework=_".StringUtil::getRandomID();
-               $this->mimeBoundary = "WoltLab_Community_Framework=_".StringUtil::getRandomID();
-       }
-       
        /**
         * Returns the mail host for use in the Message-Id.
         * 
@@ -338,7 +310,10 @@ class Email {
                                throw new \DomainException("The given type '".$type."' is invalid. Must be one of 'to', 'cc', 'bcc'.");
                }
                
-               $this->recipients[$recipient->getAddress()] = [$type, $recipient];
+               $this->recipients[$recipient->getAddress()] = [
+                       'type' => $type,
+                       'mailbox' => $recipient
+               ];
        }
        
        /**
@@ -376,60 +351,12 @@ class Email {
        }
        
        /**
-        * Adds a mime part to this email. Should be either \wcf\system\email\mime\TextMimePart
-        * or \wcf\system\email\mime\AttachmentMimePart.
-        * The given priority determines the ordering within the Email. A higher priority
-        * mime part will be further down the email (see RFC 2046, 5.1.4).
-        * 
-        * @param       AbstractMimePart        $part
-        * @param       integer                 $priority
-        * @throws      \InvalidArgumentException
-        * @throws      \DomainException
-        */
-       public function addMimePart(AbstractMimePart $part, $priority = 1000) {
-               foreach ($part->getAdditionalHeaders() as $header) {
-                       $header[0] = mb_strtolower($header[0]);
-                       if ($header[0] == 'content-type' || $header[0] == 'content-transfer-encoding') {
-                               throw new \InvalidArgumentException("The header '".$header[0]."' may not be set. Use the proper methods.");
-                       }
-                       
-                       if (!StringUtil::startsWith($header[0], 'x-') && !StringUtil::startsWith($header[0], 'content-')) {
-                               throw new \DomainException("The header '".$header[0]."' may not be set. You may only set headers starting with 'X-' or 'Content-'.");
-                       }
-               }
-               
-               switch ($part->getContentTransferEncoding()) {
-                       case 'base64':
-                       case 'quoted-printable':
-                       break;
-                       default:
-                               throw new \DomainException("The Content-Transfer-Encoding '".$part->getContentTransferEncoding()."' may not be set. You may only use 'quoted-printable' or 'base64'.");
-               }
-               
-               if ($part instanceof TextMimePart) {
-                       $this->text[] = [$priority, $part];
-               }
-               else {
-                       $this->attachments[] = [$priority, $part];
-               }
-       }
-       
-       /**
-        * Returns the text mime parts of this email.
-        * 
-        * @return      array
-        */
-       public function getText() {
-               return $this->text;
-       }
-       
-       /**
-        * Returns the attachments (i.e. the mime parts that are not a TextMimePart) of this email.
+        * Sets the body of this email.
         * 
-        * @return      array
+        * @param       AbstractMimePart        $body
         */
-       public function getAttachments() {
-               return $this->attachments;
+       public function setBody(AbstractMimePart $body) {
+               $this->body = $body;
        }
        
        /**
@@ -445,8 +372,8 @@ class Email {
                $to = [];
                $cc = [];
                foreach ($this->getRecipients() as $recipient) {
-                       if ($recipient[0] == 'to') $to[] = $recipient[1];
-                       else if ($recipient[0] == 'cc') $cc[] = $recipient[1];
+                       if ($recipient['type'] == 'to') $to[] = $recipient['mailbox'];
+                       else if ($recipient['type'] == 'cc') $cc[] = $recipient['mailbox'];
                }
                $headers[] = ['from', (string) $this->getSender()];
                if ($this->getReplyTo()->getAddress() !== $this->getSender()->getAddress()) {
@@ -480,22 +407,14 @@ class Email {
                }
                $headers[] = ['mime-version', '1.0'];
                
-               if (!$this->text) {
-                       throw new \LogicException("Cannot generate message headers, you must specify at least one 'Text' part.");
-               }
-               if ($this->attachments) {
-                       $headers[] = ['content-type', "multipart/mixed;\r\n   boundary=\"".$this->mimeBoundary."\""];
+               if (!$this->body) {
+                       throw new \LogicException("Cannot generate message headers, you must set a body.");
                }
-               else {
-                       if (count($this->text) > 1) {
-                               $headers[] = ['content-type', "multipart/alternative;\r\n   boundary=\"".$this->textBoundary."\""];
-                       }
-                       else {
-                               $headers[] = ['content-type', $this->text[0][1]->getContentType()];
-                               $headers[] = ['content-transfer-encoding', $this->text[0][1]->getContentTransferEncoding()];
-                               $headers = array_merge($headers, $this->text[0][1]->getAdditionalHeaders());
-                       }
+               $headers[] = ['content-type', $this->body->getContentType()];
+               if ($this->body->getContentTransferEncoding()) {
+                       $headers[] = ['content-transfer-encoding', $this->body->getContentTransferEncoding()];
                }
+               $headers = array_merge($headers, $this->body->getAdditionalHeaders());
                
                return array_merge($headers, $this->extraHeaders);
        }
@@ -518,80 +437,18 @@ class Email {
         * @return      string
         */
        public function getBodyString() {
-               $text = "";
-               $body = "";
-               
-               if (count($this->text) > 1 || $this->attachments) {
-                       $body .= StringUtil::wordwrap("This is a MIME encoded email. As you are seeing this your user agent does not support these.");
-                       $body .= "\r\n\r\n";
-               }
-               
-               usort($this->text, function ($a, $b) {
-                       return $a[0] - $b[0];
-               });
-               foreach ($this->text as $part) {
-                       if (count($this->text) > 1) {
-                               $text .= "--".$this->textBoundary."\r\n";
-                       }
-                       if (count($this->text) > 1 || $this->attachments) {
-                               $text .= "content-type: ".$part[1]->getContentType()."\r\n";
-                               $text .= "content-transfer-encoding: ".$part[1]->getContentTransferEncoding()."\r\n";
-                               if ($part[1]->getAdditionalHeaders()) {
-                                       $text .= implode("\r\n", array_map(function ($item) {
-                                               return implode(': ', $item);
-                                       }, $part[1]->getAdditionalHeaders()))."\r\n";
-                               }
-                               $text .= "\r\n";
-                       }
-                       switch ($part[1]->getContentTransferEncoding()) {
-                               case 'quoted-printable':
-                                       $text .= quoted_printable_encode($part[1]->getContent());
-                               break;
-                               case 'base64':
-                                       $text .= chunk_split(base64_encode($part[1]->getContent()));
-                               break;
-                       }
-                       $text .= "\r\n";
-               }
-               if (count($this->text) > 1) {
-                       $text .= "--".$this->textBoundary."--\r\n";
-               }
-               
-               if ($this->attachments) {
-                       $body .= "--".$this->mimeBoundary."\r\n";
-                       if (count($this->text) > 1) {
-                               $body .= "Content-Type: multipart/alternative;\r\n   boundary=\"".$this->textBoundary."\"\r\n";
-                               $body .= "\r\n";
-                       }
-                       $body .= $text;
-                       
-                       foreach ($this->attachments as $part) {
-                               $body .= "\r\n--".$this->mimeBoundary."\r\n";
-                               $body .= "content-type: ".$part[1]->getContentType()."\r\n";
-                               $body .= "content-transfer-encoding: ".$part[1]->getContentTransferEncoding()."\r\n";
-                               if ($part[1]->getAdditionalHeaders()) {
-                                       $body .= implode("\r\n", array_map(function ($item) {
-                                               return implode(': ', $item);
-                                       }, $part[1]->getAdditionalHeaders()))."\r\n";
-                               }
-                               $body .= "\r\n";
-                               switch ($part[1]->getContentTransferEncoding()) {
-                                       case 'quoted-printable':
-                                               $body .= quoted_printable_encode($part[1]->getContent());
-                                       break;
-                                       case 'base64':
-                                               $body .= chunk_split(base64_encode($part[1]->getContent()));
-                                       break;
-                               }
-                               $body .= "\r\n";
-                       }
-                       $body .= "--".$this->mimeBoundary."--\r\n";
-               }
-               else {
-                       $body .= $text;
+               switch ($this->body->getContentTransferEncoding()) {
+                       case 'quoted-printable':
+                               return quoted_printable_encode($this->body->getContent());
+                       break;
+                       case 'base64':
+                               return chunk_split(base64_encode($this->body->getContent()));
+                       break;
+                       case '':
+                               return $this->body->getContent();
                }
                
-               return $body;
+               throw new \LogicException('Unreachable');
        }
        
        /**
@@ -608,21 +465,23 @@ class Email {
                foreach ($this->recipients as $recipient) {
                        $mail = clone $this;
                        
-                       if ($recipient[1] instanceof UserMailbox) {
-                               $mail->addHeader('X-Community-Framework-Recipient', $recipient[1]->getUser()->username);
+                       if ($recipient['mailbox'] instanceof UserMailbox) {
+                               $mail->addHeader('X-Community-Framework-Recipient', $recipient['mailbox']->getUser()->username);
                        }
                        
-                       foreach (array_merge($mail->getText(), $mail->getAttachments()) as $mimePart) {
-                               if ($mimePart[1] instanceof IRecipientAwareMimePart) $mimePart[1]->setRecipient($recipient[1]);
-                       }
+                       if ($this->body instanceof IRecipientAwareMimePart) $this->body->setRecipient($recipient['mailbox']);
                        
-                       $data = ['mail' => $mail, 'recipient' => $recipient, 'skip' => false];
+                       $data = [
+                               'mail' => $mail,
+                               'recipient' => $recipient,
+                               'skip' => false
+                       ];
                        EventHandler::getInstance()->fireAction($this, 'getJobs', $data);
                        
                        // an event decided that this email should be skipped
                        if ($data['skip']) continue;
                        
-                       $jobs[] = new EmailDeliveryBackgroundJob($mail, $recipient[1]);
+                       $jobs[] = new EmailDeliveryBackgroundJob($mail, $recipient['mailbox']);
                }
                
                return $jobs;
@@ -641,12 +500,19 @@ class Email {
                BackgroundQueueHandler::getInstance()->forceCheck();
        }
        
+       /**
+        * @see Email::getEmail()
+        */
+       public function __toString() {
+               return $this->getEmail();
+       }
+       
        /**
         * Returns the email RFC 2822 representation of this email.
         * 
         * @return      string
         */
-       public function __toString() {
+       public function getEmail() {
                return $this->getHeaderString()."\r\n\r\n".$this->getBodyString();
        }
 }
index 4ff62f000786af4d27ca439b01b3d1c657fc3a8e..330fb8203b962530c8abee8131955e15aae8a889 100644 (file)
@@ -16,6 +16,8 @@ abstract class AbstractMimePart {
        /**
         * Returns the Content-Type header value.
         * 
+        * This method must be idempotent.
+        * 
         * @return      string
         */
        abstract public function getContentType();
@@ -24,17 +26,21 @@ abstract class AbstractMimePart {
         * Returns the transfer encoding to use. Must either be
         * 'quoted-printable' or 'base64'.
         * 
+        * This method must be idempotent.
+        * 
         * @return      string          either 'quoted-printable' or 'base64'
         */
        abstract public function getContentTransferEncoding();
        
        /**
-        * Extra headers as an array of [ name, value] tuple for this mime part.
+        * Extra headers as an array of [ name, value ] tuple for this mime part.
         * As per RFC 2046 they may only start with X-* or Content-*. Content-Type
         * and Content-Transfer-Encoding are blacklisted.
         * 
         * Returns an empty array by default.
-        *
+        * 
+        * This method must be idempotent.
+        * 
         * @return      array
         */
        public function getAdditionalHeaders() {
diff --git a/wcfsetup/install/files/lib/system/email/mime/AbstractMultipartMimePart.class.php b/wcfsetup/install/files/lib/system/email/mime/AbstractMultipartMimePart.class.php
new file mode 100644 (file)
index 0000000..9616f9b
--- /dev/null
@@ -0,0 +1,161 @@
+<?php
+namespace wcf\system\email\mime;
+use wcf\system\email\Mailbox;
+use wcf\util\StringUtil;
+
+/**
+ * Represents a multipart/* mime container.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.email.mime
+ * @category   Community Framework
+ * @since      2.2
+ */
+abstract class AbstractMultipartMimePart extends AbstractMimePart implements IRecipientAwareMimePart {
+       /**
+        * The boundary between the distinct parts.
+        * @var string
+        */
+       protected $boundary;
+       
+       /**
+        * The parts.
+        * @var \SplObjectStorage
+        */
+       protected $parts;
+       
+       /**
+        * Sets the multipart boundary.
+        */
+       public function __construct() {
+               $this->boundary = "WoltLab_Community_Framework=_".StringUtil::getRandomID();
+               $this->parts = new \SplObjectStorage();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getContentTransferEncoding() {
+               return '';
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function setRecipient(Mailbox $mailbox = null) {
+               foreach ($this->parts as $part) {
+                       if ($part instanceof IRecipientAwareMimePart) {
+                               $part->setRecipient($mailbox);
+                       }
+               }
+       }
+       
+       /**
+        * Concatenates the given mime parts.
+        * 
+        * @param       \Traversable    $parts
+        */
+       protected function getConcatenatedParts($parts) {
+               $content = "";
+               foreach ($parts as $part) {
+                       $content .= "--".$this->boundary."\r\n";
+                       $content .= "content-type: ".$part->getContentType()."\r\n";
+                       if ($part->getContentTransferEncoding()) {
+                               $content .= "content-transfer-encoding: ".$part->getContentTransferEncoding()."\r\n";
+                       }
+                       
+                       if ($part->getAdditionalHeaders()) {
+                               $content .= implode("\r\n", array_map(function ($item) {
+                                       return implode(': ', $item);
+                               }, $part->getAdditionalHeaders()))."\r\n";
+                       }
+                       $content .= "\r\n";
+                       switch ($part->getContentTransferEncoding()) {
+                               case 'quoted-printable':
+                                       $content .= quoted_printable_encode($part->getContent());
+                               break;
+                               case 'base64':
+                                       $content .= chunk_split(base64_encode($part->getContent()));
+                               break;
+                               case '':
+                                       $content .= $part->getContent();
+                               break;
+                               default:
+                                       throw new \LogicException('Unreachable');
+                       }
+                       $content .= "\r\n";
+               }
+               $content .= "--".$this->boundary."--\r\n";
+               
+               return $content;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getContent() {
+               $content = "";
+               $content .= StringUtil::wordwrap("This is a MIME encoded email. As you are seeing this your user agent does not support these.");
+               $content .= "\r\n\r\n";
+               
+               $content .= $this->getConcatenatedParts($this->parts);
+               
+               return $content;
+       }
+       
+       /**
+        * Adds a mime part to this email. Should be either \wcf\system\email\mime\TextMimePart
+        * or \wcf\system\email\mime\AttachmentMimePart.
+        * 
+        * @param       AbstractMimePart        $part
+        * @param       mixed                   $data   Additional data, to be defined by child classes
+        * @throws      \InvalidArgumentException
+        * @throws      \DomainException
+        */
+       public function addMimePart(AbstractMimePart $part, $data = null) {
+               foreach ($part->getAdditionalHeaders() as $header) {
+                       $header[0] = mb_strtolower($header[0]);
+                       if ($header[0] == 'content-type' || $header[0] == 'content-transfer-encoding') {
+                               throw new \InvalidArgumentException("The header '".$header[0]."' may not be set. Use the proper methods.");
+                       }
+                       
+                       if (!StringUtil::startsWith($header[0], 'x-') && !StringUtil::startsWith($header[0], 'content-')) {
+                               throw new \DomainException("The header '".$header[0]."' may not be set. You may only set headers starting with 'X-' or 'Content-'.");
+                       }
+               }
+               
+               switch ($part->getContentTransferEncoding()) {
+                       case 'base64':
+                       case 'quoted-printable':
+                       case '':
+                       break;
+                       default:
+                               throw new \DomainException("The Content-Transfer-Encoding '".$part->getContentTransferEncoding()."' may not be set. You may only use 'quoted-printable' or 'base64' or ''.");
+               }
+               
+               $this->parts[$part] = $data;
+       }
+       
+       /**
+        * Removes a mime part from this multipart part.
+        * 
+        * @param       AbstractMimePart        $part
+        */
+       public function removeMimePart(AbstractMimePart $part) {
+               $this->parts->detach($part);
+       }
+       
+       /**
+        * Returns the stored mime parts of this multipart part.
+        * Note: The returned \SplObjectStorage is a clone of the internal one.
+        * Modifications will not reflect on this object.
+        *
+        * @return      \SplObjectStorage
+        */
+       public function getMimeParts() {
+               return clone $this->parts;
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/email/mime/MultipartAlternativeMimePart.class.php b/wcfsetup/install/files/lib/system/email/mime/MultipartAlternativeMimePart.class.php
new file mode 100644 (file)
index 0000000..8680d93
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+namespace wcf\system\email\mime;
+use wcf\util\StringUtil;
+
+/**
+ * Represents a multipart/alternative mime container.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.email.mime
+ * @category   Community Framework
+ * @since      2.2
+ */
+class MultipartAlternativeMimePart extends AbstractMultipartMimePart {
+       /**
+        * @inheritDoc
+        */
+       public function getContentType() {
+               return "multipart/alternative;\r\n   boundary=\"".$this->boundary."\"";
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       protected function getConcatenatedParts($parts) {
+               $sortedParts = new \SplPriorityQueue();
+               
+               $parts->rewind();
+               while ($parts->valid()) {
+                       $sortedParts->insert($parts->current(), PHP_INT_MAX - $parts->getInfo());
+                       $parts->next();
+               }
+               
+               return parent::getConcatenatedParts($sortedParts);
+       }
+       
+       /**
+        * Adds a mime part to this multipart container.
+        * 
+        * The given priority determines the ordering within the Email. A higher priority
+        * mime part will be further down the email (see RFC 2046, 5.1.4).
+        * 
+        * @param       AbstractMimePart        $part
+        * @param       integer                 $data   The priority.
+        * @throws      \InvalidArgumentException
+        * @throws      \DomainException
+        */
+       public function addMimePart(AbstractMimePart $part, $data = 1000) {
+               return parent::addMimePart($part, $data);
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/email/mime/MultipartMixedMimePart.class.php b/wcfsetup/install/files/lib/system/email/mime/MultipartMixedMimePart.class.php
new file mode 100644 (file)
index 0000000..67c94e1
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+namespace wcf\system\email\mime;
+use wcf\util\StringUtil;
+
+/**
+ * Represents a multipart/mixed mime container.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.email.mime
+ * @category   Community Framework
+ * @since      2.2
+ */
+class MultipartMixedMimePart extends AbstractMultipartMimePart {
+       /**
+        * @inheritDoc
+        */
+       public function getContentType() {
+               return "multipart/mixed;\r\n   boundary=\"".$this->boundary."\"";
+       }
+       
+       /**
+        * Adds a mime part to this multipart container.
+        * 
+        * The given $data is ignored.
+        * 
+        * @param       AbstractMimePart        $part
+        * @param       mixed                   $data   Ignored.
+        * @throws      \InvalidArgumentException
+        * @throws      \DomainException
+        */
+       public function addMimePart(AbstractMimePart $part, $data = null) {
+               return parent::addMimePart($part, $data);
+       }
+}
index d0b831394cd5d2b69d90156005936a0150b4ffcf..31076ce1716e866d585b6ff9bd4d6031b6667919 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace wcf\system\email\mime;
 use wcf\system\email\Mailbox;
+use wcf\system\template\EmailTemplateEngine;
 use wcf\system\WCF;
 
 /**
@@ -66,9 +67,9 @@ class RecipientAwareTextMimePart extends TextMimePart implements IRecipientAware
                $language = WCF::getLanguage();
                
                try {
-                       WCF::setLanguage($this->mailbox->getLanguage()->languageID);
+                       if ($this->mailbox) WCF::setLanguage($this->mailbox->getLanguage()->languageID);
                        
-                       return WCF::getTPL()->fetch($this->template, $this->application, [
+                       return EmailTemplateEngine::getInstance()->fetch($this->template, $this->application, [
                                'content' => $this->content,
                                'mimeType' => $this->mimeType,
                                'mailbox' => $this->mailbox
index c46342a2ba1ee35541e7c18793a0bae98820eb4e..fd19a4b980667cac101e50d74e540590cf35a3ac 100644 (file)
@@ -44,7 +44,7 @@ class DebugEmailTransport implements EmailTransport {
        public function deliver(Email $email, Mailbox $envelopeTo) {
                $this->mbox->write("From ".$email->getSender()->getAddress()." ".DateUtil::getDateTimeByTimestamp(TIME_NOW)->format('D M d H:i:s Y')."\r\n");
                $this->mbox->write("Delivered-To: ".$envelopeTo->getAddress()."\r\n");
-               $this->mbox->write($email);
+               $this->mbox->write($email->getEmail());
                $this->mbox->write("\r\n");
        }
 }
index fcdb9efe17373e1d1b0c86704909f5a4bdf723d3..87dff5a03579b10d8c6b20c2f87a65d96c192712 100644 (file)
@@ -359,7 +359,7 @@ class SmtpEmailTransport implements EmailTransport {
                        if (StringUtil::startsWith($item, '.')) return '.'.$item;
                        
                        return $item;
-               }, explode("\r\n", $email))));
+               }, explode("\r\n", $email->getEmail()))));
                $this->write(".");
                $this->read([250]);
        }