logger = new PostmanLogger ( get_class ( $this ) ); $this->headers = array (); $this->toRecipients = array (); $this->ccRecipients = array (); $this->bccRecipients = array (); } /** * * @return boolean */ public function isBodyPartsEmpty() { return empty ( $this->bodyTextPart ) && empty ( $this->bodyHtmlPart ); } /** * * @param PostmanModuleTransport $transport */ public function validate(PostmanModuleTransport $transport) { if ($transport->isEmailValidationSupported ()) { $this->internalValidate (); } } /** * Create body parts based on content type * MyMail creates its own body parts */ public function createBodyParts() { // modify the content-type to include the boundary if (false !== stripos ( $this->contentType, 'multipart' ) && ! empty ( $this->boundary )) { // Lines in email are terminated by CRLF ("\r\n") according to RFC2821 $this->contentType = sprintf ( "%s;\r\n\t boundary=\"%s\"", $this->contentType, $this->getBoundary () ); } // $body = $this->getBody (); $contentType = $this->getContentType (); // add the message content as either text or html if (empty ( $contentType ) || substr ( $contentType, 0, 10 ) === 'text/plain') { $this->logger->debug ( 'Creating text body part' ); $this->setBodyTextPart ( $body ); } else if (substr ( $contentType, 0, 9 ) === 'text/html') { $this->logger->debug ( 'Creating html body part' ); $this->setBodyHtmlPart ( $body ); } else if (substr ( $contentType, 0, 21 ) === 'multipart/alternative') { $this->logger->debug ( 'Adding body as multipart/alternative' ); $arr = explode ( PHP_EOL, $body ); $textBody = ''; $htmlBody = ''; $mode = ''; foreach ( $arr as $s ) { $this->logger->trace ( 'mode: ' . $mode . ' bodyline: ' . $s ); if (substr ( $s, 0, 25 ) === "Content-Type: text/plain;") { $mode = 'foundText'; } else if (substr ( $s, 0, 24 ) === "Content-Type: text/html;") { $mode = 'foundHtml'; } else if ($mode == 'textReading') { $textBody .= $s; } else if ($mode == 'htmlReading') { $htmlBody .= $s; } else if ($mode == 'foundText') { $trim = trim ( $s ); if (empty ( $trim )) { $mode = 'textReading'; } } else if ($mode == 'foundHtml') { $trim = trim ( $s ); if (empty ( $trim )) { $mode = 'htmlReading'; } } } $this->setBodyHtmlPart ( $htmlBody ); $this->setBodyTextPart ( $textBody ); } else { $this->logger->error ( 'Unknown content-type: ' . $contentType ); $this->setBodyTextPart ( $body ); } } /** * Apply the WordPress filters to the email */ public function applyFilters() { if ($this->logger->isDebug ()) { $this->logger->debug ( 'Applying WordPress filters' ); } /** * Filter the email address to send from. * * @since 2.2.0 * * @param string $from_email * Email address to send from. */ $filteredEmail = apply_filters ( 'wp_mail_from', $this->getFromAddress ()->getEmail () ); if ($this->logger->isTrace ()) { $this->logger->trace ( 'wp_mail_from: ' . $filteredEmail ); } if ($this->getFromAddress ()->getEmail () !== $filteredEmail) { $this->logger->debug ( sprintf ( 'Filtering From email address: before=%s after=%s', $this->getFromAddress ()->getEmail (), $filteredEmail ) ); $this->getFromAddress ()->setEmail ( $filteredEmail ); } /** * Filter the name to associate with the "from" email address. * * @since 2.3.0 * * @param string $from_name * Name associated with the "from" email address. */ $filteredName = apply_filters ( 'wp_mail_from_name', $this->getFromAddress ()->getName () ); if ($this->logger->isTrace ()) { $this->logger->trace ( 'wp_mail_from_name: ' . $filteredName ); } if ($this->getFromAddress ()->getName () !== $filteredName) { $this->logger->debug ( sprintf ( 'Filtering From email name: before=%s after=%s', $this->getFromAddress ()->getName (), $filteredName ) ); $this->getFromAddress ()->setName ( $filteredName ); } /** * Filter the default wp_mail() charset. * * @since 2.3.0 * * @param string $charset * Default email charset. */ $filteredCharset = apply_filters ( 'wp_mail_charset', $this->getCharset () ); if ($this->logger->isTrace ()) { $this->logger->trace ( 'wp_mail_charset: ' . $filteredCharset ); } if ($this->getCharset () !== $filteredCharset) { $this->logger->debug ( sprintf ( 'Filtering Charset: before=%s after=%s', $this->getCharset (), $filteredCharset ) ); $this->setCharset ( $filteredCharset ); } /** * Filter the wp_mail() content type. * * @since 2.3.0 * * @param string $content_type * Default wp_mail() content type. */ $filteredContentType = apply_filters ( 'wp_mail_content_type', $this->getContentType () ); if ($this->logger->isTrace ()) { $this->logger->trace ( sprintf ( 'wp_mail_content_type: "%s"', $filteredContentType ) ); } if ($this->getContentType () != $filteredContentType) { $this->logger->debug ( sprintf ( 'Filtering Content-Type: before=%s after=%s', $this->getContentType (), $filteredContentType ) ); $this->setContentType ( $filteredContentType ); } // Postman has it's own 'user override' filter $options = PostmanOptions::getInstance (); $forcedEmailAddress = $options->getMessageSenderEmail (); if ($options->isSenderEmailOverridePrevented () && $this->getFromAddress ()->getEmail () !== $forcedEmailAddress) { $this->logger->debug ( sprintf ( 'Forced From email address: before=%s after=%s', $this->getFromAddress ()->getEmail (), $forcedEmailAddress ) ); $this->getFromAddress ()->setEmail ( $forcedEmailAddress ); } $forcedEmailName = $options->getMessageSenderName (); if ($options->isSenderNameOverridePrevented () && $this->getFromAddress ()->getName () !== $forcedEmailName) { $this->logger->debug ( sprintf ( 'Forced From email name: before=%s after=%s', $this->getFromAddress ()->getName (), $forcedEmailName ) ); $this->getFromAddress ()->setName ( $forcedEmailName ); } } /** * Check all email headers for errors * Throw an exception if an error is found */ private function internalValidate() { // check the reply-to address for errors if (isset ( $this->replyTo )) { $this->getReplyTo ()->validate ( 'Reply-To' ); } // check the from address for errors $this->getFromAddress ()->validate ( 'From' ); // validate the To recipients foreach ( ( array ) $this->getToRecipients () as $toRecipient ) { $toRecipient->validate ( 'To' ); } // validate the Cc recipients foreach ( ( array ) $this->getCcRecipients () as $ccRecipient ) { $ccRecipient->validate ( 'Cc' ); } // validate the Bcc recipients foreach ( ( array ) $this->getBccRecipients () as $bccRecipient ) { $bccRecipient->validate ( 'Bcc' ); } } /** * * @return PostmanEmailAddress */ public function getFromAddress() { return $this->from; } /** * Get the charset, checking first the WordPress bloginfo, then the header, then the wp_mail_charset filter. * * @return string */ public function getCharset() { return $this->charset; } /** * Set the charset * * @param unknown $charset */ public function setCharset($charset) { $this->charset = $charset; } /** * Get the content type, checking first the header, then the wp_mail_content_type filter * * @return string */ public function getContentType() { return $this->contentType; } public function setContentType($contentType) { $this->contentType = $contentType; } /** * * @param unknown $recipients * Array or comma-separated list of email addresses to send message. * @throws Exception */ public function addTo($to) { $this->addRecipients ( $this->toRecipients, $to ); } /** * * @param unknown $recipients * Array or comma-separated list of email addresses to send message. * @throws Exception */ public function addCc($cc) { $this->addRecipients ( $this->ccRecipients, $cc ); } /** * * @param unknown $recipients * Array or comma-separated list of email addresses to send message. * @throws Exception */ public function addBcc($bcc) { $this->addRecipients ( $this->bccRecipients, $bcc ); } /** * * @param unknown $recipients * Array or comma-separated list of email addresses to send message. * @throws Exception */ private function addRecipients(&$recipientList, $recipients) { if (! empty ( $recipients )) { $recipients = PostmanEmailAddress::convertToArray ( $recipients ); foreach ( $recipients as $recipient ) { if (! empty ( $recipient )) { $this->logger->debug ( sprintf ( 'User added recipient: "%s"', $recipient ) ); array_push ( $recipientList, new PostmanEmailAddress ( $recipient ) ); } } } } /** * For the string version, each header line (beginning with From:, Cc:, etc.) is delimited with a newline ("\r\n") */ public function addHeaders($headers) { if (! is_array ( $headers )) { // WordPress may send a string where "each header line (beginning with From:, Cc:, etc.) is delimited with a newline ("\r\n") (advanced)" // this converts that string to an array $headers = explode ( "\n", str_replace ( "\r\n", "\n", $headers ) ); // $headers = explode ( PHP_EOL, $headers ); } // otherwise WordPress sends an array foreach ( $headers as $header ) { if (! empty ( $header )) { // boundary may be in a header line, but it's not a header // eg. boundary="----=_NextPart_DC7E1BB5... if (strpos ( $header, ':' ) === false) { if (false !== stripos ( $header, 'boundary=' )) { $parts = preg_split ( '/boundary=/i', trim ( $header ) ); $this->boundary = trim ( str_replace ( array ( "'", '"' ), '', $parts [1] ) ); $this->logger->debug ( sprintf ( 'Processing special boundary header \'%s\'', $this->getBoundary () ) ); } else { $this->logger->debug ( sprintf ( 'Ignoring broken header \'%s\'', $header ) ); } continue; } list ( $name, $content ) = explode ( ':', trim ( $header ), 2 ); $this->processHeader ( $name, $content ); } } } /** * Add the headers that were processed in processHeaders() * Zend requires that several headers are specially handled. * * @param unknown $name * @param unknown $value * @param Postman_Zend_Mail $mail */ private function processHeader($name, $content) { $name = trim ( $name ); $content = trim ( $content ); switch (strtolower ( $name )) { case 'content-type' : $this->logProcessHeader ( 'Content-Type', $name, $content ); if (strpos ( $content, ';' ) !== false) { list ( $type, $charset ) = explode ( ';', $content ); $this->setContentType ( trim ( $type ) ); if (false !== stripos ( $charset, 'charset=' )) { $charset = trim ( str_replace ( array ( 'charset=', '"' ), '', $charset ) ); } elseif (false !== stripos ( $charset, 'boundary=' )) { $this->boundary = trim ( str_replace ( array ( 'BOUNDARY=', 'boundary=', '"' ), '', $charset ) ); $charset = ''; } if (! empty ( $charset )) { $this->setCharset ( $charset ); } } else { $this->setContentType ( trim ( $content ) ); } break; case 'to' : $this->logProcessHeader ( 'To', $name, $content ); $this->addTo ( $content ); break; case 'cc' : $this->logProcessHeader ( 'Cc', $name, $content ); $this->addCc ( $content ); break; case 'bcc' : $this->logProcessHeader ( 'Bcc', $name, $content ); $this->addBcc ( $content ); break; case 'from' : $this->logProcessHeader ( 'From', $name, $content ); $this->setFrom ( $content ); break; case 'subject' : $this->logProcessHeader ( 'Subject', $name, $content ); $this->setSubject ( $content ); break; case 'reply-to' : $this->logProcessHeader ( 'Reply-To', $name, $content ); $this->setReplyTo ( $content ); break; case 'sender' : $this->logProcessHeader ( 'Sender', $name, $content ); $this->logger->warn ( sprintf ( 'Ignoring Sender header \'%s\'', $content ) ); break; case 'return-path' : $this->logProcessHeader ( 'Return-Path', $name, $content ); $this->logger->warn ( sprintf ( 'Ignoring Return-Path header \'%s\'', $content ) ); break; case 'date' : $this->logProcessHeader ( 'Date', $name, $content ); $this->setDate ( $content ); break; case 'message-id' : $this->logProcessHeader ( 'Message-Id', $name, $content ); $this->setMessageId ( $content ); break; default : // Add it to our grand headers array $this->logProcessHeader ( 'other', $name, $content ); array_push ( $this->headers, array ( 'name' => $name, 'content' => $content ) ); break; } } /** * * @param unknown $desc * @param unknown $name * @param unknown $content */ private function logProcessHeader($desc, $name, $content) { $this->logger->debug ( 'Processing ' . $desc . ' Header - ' . $name . ': ' . $content ); } /** * Add attachments to the message * * @param Postman_Zend_Mail $mail */ public function addAttachmentsToMail(Postman_Zend_Mail $mail) { $attachments = $this->attachments; if (! is_array ( $attachments )) { // WordPress may a single filename or a newline-delimited string list of multiple filenames $attArray = explode ( PHP_EOL, $attachments ); } else { $attArray = $attachments; } // otherwise WordPress sends an array foreach ( $attArray as $file ) { if (! empty ( $file )) { $this->logger->debug ( "Adding attachment: " . $file ); $at = new Postman_Zend_Mime_Part ( file_get_contents ( $file ) ); // $at->type = 'image/gif'; $at->disposition = Postman_Zend_Mime::DISPOSITION_ATTACHMENT; $at->encoding = Postman_Zend_Mime::ENCODING_BASE64; $at->filename = basename ( $file ); $mail->addAttachment ( $at ); } } } function setBody($body) { $this->body = $body; } function setBodyTextPart($bodyTextPart) { $this->bodyTextPart = $bodyTextPart; } function setBodyHtmlPart($bodyHtmlPart) { $this->bodyHtmlPart = $bodyHtmlPart; } function setSubject($subject) { $this->subject = $subject; } function setAttachments($attachments) { $this->attachments = $attachments; } function setFrom($email, $name = null) { if (! empty ( $email )) { $this->from = new PostmanEmailAddress ( $email, $name ); } } function setReplyTo($replyTo) { if (! empty ( $replyTo )) { $this->replyTo = new PostmanEmailAddress ( $replyTo ); } } function setMessageId($messageId) { $this->messageId = $messageId; } function setDate($date) { $this->date = $date; } // return the headers public function getHeaders() { return $this->headers; } public function getBoundary() { return $this->boundary; } public function getToRecipients() { return $this->toRecipients; } public function getCcRecipients() { return $this->ccRecipients; } public function getBccRecipients() { return $this->bccRecipients; } public function getReplyTo() { return $this->replyTo; } public function getDate() { return $this->date; } public function getMessageId() { return $this->messageId; } public function getSubject() { return $this->subject; } public function getBody() { return $this->body; } public function getBodyTextPart() { return $this->bodyTextPart; } public function getBodyHtmlPart() { return $this->bodyHtmlPart; } public function getAttachments() { return $this->attachments; } } }