summaryrefslogtreecommitdiff
path: root/Postman/Postman-Mail/PostmanMessage.php
diff options
context:
space:
mode:
Diffstat (limited to 'Postman/Postman-Mail/PostmanMessage.php')
-rw-r--r--Postman/Postman-Mail/PostmanMessage.php564
1 files changed, 564 insertions, 0 deletions
diff --git a/Postman/Postman-Mail/PostmanMessage.php b/Postman/Postman-Mail/PostmanMessage.php
new file mode 100644
index 0000000..bd852c4
--- /dev/null
+++ b/Postman/Postman-Mail/PostmanMessage.php
@@ -0,0 +1,564 @@
+<?php
+if (! class_exists ( "PostmanMessage" )) {
+
+ require_once 'PostmanEmailAddress.php';
+
+ /**
+ * This class knows how to interface with Wordpress
+ * including loading/saving to the database.
+ *
+ * The various Transports available:
+ * http://framework.zend.com/manual/current/en/modules/zend.mail.smtp.options.html
+ *
+ * @author jasonhendriks
+ *
+ */
+ class PostmanMessage {
+ const EOL = "\r\n";
+
+ // logger for all concrete classes - populate with setLogger($logger)
+ protected $logger;
+
+ // set by the caller
+ private $from;
+ private $replyTo;
+ private $toRecipients;
+ private $ccRecipients;
+ private $bccRecipients;
+ private $subject;
+ private $body;
+ private $bodyTextPart;
+ private $bodyHtmlPart;
+ private $headers;
+ private $attachments;
+ private $date;
+ private $messageId;
+
+ // determined by the send() method
+ private $isTextHtml;
+ private $contentType;
+ private $charset;
+
+ //
+ private $boundary;
+
+ /**
+ * No-argument constructor
+ */
+ function __construct() {
+ $this->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;
+ }
+ }
+}