Index: t3lib/mail/class.tx_t3lib_mail_hooks.php =================================================================== --- t3lib/mail/class.tx_t3lib_mail_hooks.php (revision 9973) +++ t3lib/mail/class.tx_t3lib_mail_hooks.php (working copy) @@ -45,6 +45,9 @@ /** @var $messageHeaders Swift_Mime_HeaderSet */ protected $messageHeaders; + /** @var string */ + protected $boundary = ''; + /** * @param array $parameters Array with keys: 'to', 'subject', 'messageBody', 'additionalHeaders', 'additionalParameters' * @param bool $fakeSending If set fake sending a mail @@ -61,7 +64,8 @@ $this->mailerObject = t3lib_div::makeInstance('t3lib_mail_Mailer'); // create message object - $this->messageObject = Swift_Message::newInstance($parameters['subject'], $parameters['messageBody']); + $this->messageObject = Swift_Message::newInstance(); + $this->messageObject->setSubject($parameters['subject']); $this->messageObject->setTo($parameters['to']); // handle additional headers $headers = t3lib_div::trimExplode(LF, $parameters['additionalHeaders'], TRUE); @@ -88,6 +92,8 @@ $fromName = 'TYPO3 Installation'; } $this->messageObject->setFrom(array($fromAddress => $fromName)); + // handle message body + $this->setBody($parameters['messageBody']); // send mail $result = $this->mailerObject->send($this->messageObject); @@ -118,6 +124,84 @@ } /** + * Sets body of mail message. Handles multi-part and single part messages. Encoded body parts are decoded prior to adding + * them to the message object. + * + * @param string $body Raw body, may be multi-part + * @return void + */ + protected function setBody($body) { + if ($this->boundary) { + // handle multi-part + $bodyParts = preg_split('/--' . preg_quote($this->boundary) . '(--)?/m', $body, NULL, PREG_SPLIT_NO_EMPTY); + foreach ($bodyParts as $bodyPart) { + // skip empty parts + if (trim($bodyPart) == '') { + continue; + } + // keep leading white space when exploding the text + $lines = explode(LF, $bodyPart); + // set defaults for this part + $encoding = ''; + $charset = 'utf-8'; + $contentType = 'text/plain'; + // skip intro messages + if (trim($lines[0]) == 'This is a multi-part message in MIME format.') { + continue; + } + // first line is empty leftover from splitting + array_shift($lines); + while (count($lines) > 0) { + $line = array_shift($lines); + if (preg_match('/^content-type:(.*);( charset=(.*))?$/i', $line, $matches)) { + $contentType = trim($matches[1]); + if ($matches[2]) { + $charset = trim($matches[3]); + } + } else if (preg_match('/^content-transfer-encoding:(.*)$/i', $line, $matches)) { + $encoding = trim($matches[1]); + } else if (strlen(trim($line)) == 0) { + // empty line before actual content of this part + break; + } + } + // use rest of part as body, but reverse encoding first + $bodyPart = $this->decode(implode(LF, $lines), $encoding); + $this->messageObject->addPart($bodyPart, $contentType, $charset); + } + } else { + // Handle single body + // The headers have already been set, so use header information + $contentType = $this->messageObject->getContentType(); + $charset = $this->messageObject->getCharset(); + $encoding = $this->messageObject->getEncoder(); + // reverse encoding and set body + $rawBody = $this->decode($body, $encoding); + $this->messageObject->setBody($rawBody, $contentType, $charset); + } + } + + /** + * Reverts encoding of body text + * + * @param string $text Body text to be decoded + * @param string $encoding Encoding type to be reverted + * @return string Decoded message body + */ + protected function decode($text, $encoding) { + $result = $text; + switch ($encoding) { + case 'quoted-printable': + $result = quoted_printable_decode($text); + break; + case 'base64': + $result = base64_decode($text); + break; + } + return $result; + } + + /** * Handles setting and replacing of mail headers * * @param $headerName Name of header @@ -125,6 +209,12 @@ * @return void */ protected function setHeader($headerName, $headerValue) { + // check for boundary in headers + if (preg_match('/^boundary="(.*)"$/', $headerName, $matches) > 0) { + $this->boundary = $matches[1]; + return; + } + // process other, real headers if ($this->messageHeaders->has($headerName)) { $header = $this->messageHeaders->get($headerName); $headerType = $header->getFieldType(); @@ -133,7 +223,7 @@ $header->setValue($headerValue); break; case Swift_Mime_Header::TYPE_PARAMETERIZED: - $header->setValue($headerValue); + $header->setValue(rtrim($headerValue, ';')); break; case Swift_Mime_Header::TYPE_MAILBOX: // mailbox headers look like: @@ -218,7 +308,7 @@ // parameterized headers case 'Content-Type': case 'Content-Disposition': - $this->messageHeaders->addParameterizedHeader($headerName, $headerValue); + $this->messageHeaders->addParameterizedHeader($headerName, rtrim($headerValue, ';')); break; // text headers default: