* @license PHP License * @package Wombat * @subpackage MailMime */ WBClass::load('WBMail_Mime'); /** * Wombat Mail Mime Storage String * * Read and save mail from and to string. Usefull process incomming mail or * to transform mail object to sendable e-mail * * @version 0.2.0 * @package Wombat * @subpackage MailMime */ class WBMail_Mime_Storage_String extends WBMail_Mime_Storage { /** * second constructor */ protected function init() { } /** * Convert e-mail object to mime message * * Use current mail object and convert it to mime-mail using * PEAR package Mail_mime * * @param array $header * @return string $body */ public function saveToString(&$header = array()) { if (!is_array( $header)) { $header = array(); } $header['To'] = $this->mail->getRcpt()->get(); $header['From'] = $this->mail->getFrom()->get(); $header['Subject'] = WBMail_Mime::utf82base64( $this->mail->GetSubject() ); $header['Date'] = gmdate('r', $this->mail->getDate()); WBClass::load('Mail', 'Mail_mime'); $mime = new Mail_mime("\n"); // fetch html body to replace content-ids $html = $this->mail->getHtmlBody(); // add files $atts = $this->mail->getAttachments(); foreach ($atts as $att) { /** @var $att WBMail_Mime_Part */ $cid = $att->getCid(); if (empty($cid)) { $mime->addAttachment( $att->getData() , $att->getMime() , $att->getName() , false ); continue; } $html = str_replace('cid:' . $cid, $att->getName(), $html); $mime->addHTMLImage( $att->getData() , $att->getMime() , $att->getName() , false ); } // add textual body $mime->setTXTBody($this->mail->getPlainBody()); if (!empty($html)) { $mime->setHTMLBody($html); } // build mime body and headers $params = array( 'head_charset' => 'utf-8' , 'text_charset' => 'utf-8' , 'text_encoding' => 'base64' , 'html_charset' => 'utf-8' , 'html_encoding' => 'base64' ); $body = $mime->get($params); $header = $mime->headers($header); return $body; } /** * This message should be called if a mail MESSAGE part was encountered * * @param object $part the mail part to be considered as mail text content * @return void */ private function handleMailText($part) { $bodyDecoded = $part->body; $primary = strtolower($part->ctype_primary); $secondary = strtolower($part->ctype_secondary); // force UTF-8 if ('text' == $primary && 'utf-8' != strtolower($part->ctype_parameters['charset'])) { $charset = $part->ctype_parameters['charset']; $bodyDecoded = mb_convert_encoding($part->body, 'UTF-8', $charset); } if ('text' == $primary && 'plain' == $secondary) { $this->mail->setPlainBody($bodyDecoded); return; } if ('text' == $primary && 'html' == $secondary) { $this->mail->setHtmlBody($bodyDecoded); return; } $ex = array( 'msg' => 'Cannot handle message part of type: "'.$primary . '/' . $secondary.'"', 'code' => 1, 'class' => __CLASS__ ); throw WBClass::create('WBException_Type', $ex); } /** * Parse mime string * * Use PEAR::Mail_mimeDecode to parse mail string * * @param string $str */ public function loadFromString($str) { $params = array( 'include_bodies' => true , 'decode_headers' => true , 'decode_bodies' => true ); WBClass::load('Mail_mimeDecode'); $decoder = new Mail_mimeDecode($str); $mime = $decoder->decode($params); $this->clear(); $charset = null; // guess charset from subject encoding $head = explode("\n", $str, 100); foreach ($head as $h){ if (preg_match('/^Subject:\s+=\?(.+)\?/', $h, $matches)) { $charset = $matches[1]; break; } } $this->extractHeaders($mime->headers, $charset); $this->parseParts($mime); } /** * Extract e-mail header * * Figure out: * - subject * - date * - from - address * - to - address * * @param array $headers * @param string $charset */ protected function extractHeaders($headers, $charset = null) { // convert headers to UTF-8 if ($charset) { foreach (array('subject', 'from', 'to') as $hdr) { $headers[$hdr] = mb_convert_encoding($headers[$hdr], 'UTF-8', $charset); } } $this->mail->setSubject($headers['subject']); $timestamp = strtotime($headers['date']); $this->mail->setDate(gmdate( 'Y-m-d H:i:s', $timestamp)); $this->mail->setFrom($this->createPartAddress( $headers['from'])); $this->mail->setRcpt($this->createPartAddress( $headers['to'])); } /** * Create address parts * * Instantiate address objects from mail address string * * @param string $string * @return WBMail_Mime_Part_Address */ protected function createPartAddress( $string ) { $add = WBClass::create('WBMail_Mime_Part_Address'); if (!preg_match('/(.+) \<(.+)\>/', $string, $match)) { // set plain e-mail address $string = trim($string, '<> '); $add->setEmailAddress($string); return $add; } // set e-mail address as well as name $add->setEmailAddress($match[2]); $name = trim($match[1], '"'); $name = explode(' ', $name, 2); if (count($name) == 2) { $add->setForename($name[0]); array_shift($name); } $add->setSurname($name[0]); return $add; } /** * Recursively parses the mail party * * @param object $part to parse * @param int $level level of nesting */ private function parseParts($part, $level = 0) { $primary = strtolower($part->ctype_primary); $secondary = strtolower($part->ctype_secondary); if ('multipart' == $primary && is_array($part->parts)) { foreach ($part->parts as $i => $part) { $this->parseParts($part, $level+1); } return; } $disposition = null; if (isset($part->disposition)) { $disposition = $part->disposition; } $name = null; if (isset($part->ctype_parameters) && isset($part->ctype_parameters['name'])) { $name = $part->ctype_parameters['name']; } $isAttachment = false; $isAttachment |= strlen($name) > 0; $isAttachment |= isset($part->headers) && isset($part->headers['content-id']); $isAttachment |= in_array($disposition, array('attachment', 'inline')); if ($isAttachment) { $this->mail->addAttachment($this->createPartAttachment($part)); return; } $this->handleMailText($part); } /** * create attachment * * Validate body part. Create and populate attachment object * * @param stdClass $part * @return WBMail_Mime_Part_Attachment|null */ protected function createPartAttachment($part) { $att = WBClass::create( 'WBMail_Mime_Part_Attachment' ); $att->setId(); $att->setData($part->body); $att->setMime($part->ctype_primary . '/' . $part->ctype_secondary); $att->setName($part->ctype_parameters['name']); if (!isset($part->headers['content-id']) || empty($part->headers['content-id'])) { return $att; } $cid = trim($part->headers['content-id'], "<> \n\r"); if (empty($cid)) { return $att; } $att->setCid( $cid ); return $att; } }