/home/arranoyd/magicraft/wp-content/plugins/postman-smtp/Postman/Postman-Mail/PostmanMessage.php
<?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;
		}
	}
}