<?php

/**
 * This file is part of the htmlMimeMail5 package (http://www.phpguru.org/)
 *
 * htmlMimeMail5 is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * htmlMimeMail5 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with htmlMimeMail5; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * � Copyright 2005 Richard Heyes
 */
require_once(dirname(__FILE__) . '/mimePart.php');

class htmlMimeMail5 {

	/**
	 * The html part of the message
	 * @var string
	 */
	private $html;

	/**
	 * The text part of the message(only used in TEXT only messages)
	 * @var string
	 */
	private $text;


	/**
	 * The calendar part of the message
	 * @var string
	 */
	private $calendar;
	private $calendarMethod;
	
	/**
	 * The main body of the message after building
	 * @var string
	 */
	private $output;

	/**
	 * An array of embedded images   /objects
	 * @var array
	 */
	private $html_images;

	/**
	 * An array of recognised image types for the findHtmlImages() method
	 * @var array
	 */
	private $image_types;

	/**
	 * Parameters that affect the build process
	 * @var array
	 */
	private $build_params;

	/**
	 * Array of attachments
	 * @var array
	 */
	private $attachments;

	/**
	 * The main message headers
	 * @var array
	 */
	private $headers;

	/**
	 * Whether the message has been built or not
	 * @var boolean
	 */
	private $is_built;

	/**
	 * The return path address. If not set the From:
	 * address is used instead
	 * @var string
	 */
	private $return_path;

	/**
	 * Array of information needed for smtp sending
	 * @var array
	 */
	private $smtp_params;

	private $smtp_conn;
	
	/**
	 * Sendmail path. Do not include -f
	 * @var $sendmail_path
	 */
	private $sendmail_path;
	private $default_method = null;
	private $dsn;
	public $errors;
	
	/**
	 * Constructor function.
	 */
	public function __construct($senderDomain = "", $skipOwnServer = false) {
		/**
		 * Initialise some variables.
		 */
		$this->attachments = array();
		$this->html_images = array();
		$this->headers = array();
		$this->is_built = false;
		$this->text = '';
		$this->sendmail_path = '/usr/lib/sendmail -ti';

		/**
		 * If you want the auto load functionality
		 * to find other image/file types, add the
		 * extension and content type here.
		 */
		$this->image_types = array('gif' => 'image/gif',
			'jpg' => 'image/jpeg',
			'jpeg' => 'image/jpeg',
			'jpe' => 'image/jpeg',
			'bmp' => 'image/bmp',
			'png' => 'image/png',
			'tif' => 'image/tiff',
			'tiff' => 'image/tiff',
			'swf' => 'application/x-shockwave-flash');

		/**
		 * Set these up
		 */
		$this->build_params['html_encoding'] = new QPrintEncoding();
		$this->build_params['text_encoding'] = new SevenBitEncoding();
		$this->build_params['calendar_encoding'] = new SevenBitEncoding();
		$this->build_params['html_charset'] = 'ISO-8859-1';
		$this->build_params['calendar_charset'] = 'ISO-8859-1';
		$this->build_params['text_charset'] = 'ISO-8859-1';
		$this->build_params['head_charset'] = 'ISO-8859-1';
		$this->build_params['text_wrap'] = 998;

		/**
		 * Defaults for smtp sending
		 */
		if (!empty($_SERVER['HTTP_HOST'])) {
			$helo = $_SERVER['HTTP_HOST'];
		} elseif (!empty($_SERVER['SERVER_NAME'])) {
			$helo = $_SERVER['SERVER_NAME'];
		} else {
			$helo = 'localhost';
		}

		$this->smtp_params['host'] = 'localhost';
		$this->smtp_params['port'] = 25;
		$this->smtp_params['helo'] = $helo;
		$this->smtp_params['auth'] = false;
		$this->smtp_params['user'] = '';
		$this->smtp_params['pass'] = '';

		/**
		 * Make sure the MIME version header is first.
		 */
		$this->headers['MIME-Version'] = '1.0';
		$this->headers['X-Mailer'] = 'htmlMimeMail5 <http://www.phpguru.org/>';

		try {
			$MailServerSet = false;
			$MailServer = LoginData::get("MailServerUserPass");
			if ($MailServer != null AND $MailServer->A("server") != "" AND !$skipOwnServer) {
				$this->default_method = "smtp";

				$MailServerSet = true;
				
				$host = $MailServer->A("server");
				$url = parse_url($host);
				if(isset($url["port"])){
					$this->smtp_params['port'] = $url["port"];
					$host = str_replace(":".$url["port"], "", $host);
				}
				$this->smtp_params['host'] = $host;
				
				#if(isset($xml->Mail->options->helo)) $this->smtp_params['helo'] = $xml->Mail->options->helo["value"]."";
				$this->smtp_params['helo'] = "localhost";
				$this->smtp_params['auth'] = $MailServer->A("benutzername") != "";
				$this->smtp_params['user'] = $MailServer->A("benutzername");
				$this->smtp_params['pass'] = $MailServer->A("passwort");
			}
				
			$MailServer = null;
			for($i = 2; $i <= 5; $i++){
				if($skipOwnServer)
					break;
				
				if($MailServer == null AND $i > 2)
					break;

				$MailServer = LoginData::get("MailServer{$i}UserPass");
				if($MailServer == null OR $MailServer->A("server") == "" OR $MailServer->A("optionen") == "" OR strtolower($MailServer->A("optionen")) != strtolower($senderDomain)) 
					continue;
				
				$MailServerSet = true;
				
				$host = $MailServer->A("server");
				$url = parse_url($host);
				if(isset($url["port"])){
					$this->smtp_params['port'] = $url["port"];
					$host = str_replace(":".$url["port"], "", $host);
				}
				$this->smtp_params['host'] = $host;
				
				#$this->smtp_params['host'] = $MailServer->A("server");
				
				$this->smtp_params['auth'] = $MailServer->A("benutzername") != "";
				$this->smtp_params['user'] = $MailServer->A("benutzername");
				$this->smtp_params['pass'] = $MailServer->A("passwort");

			}
		} catch (Exception $e) {
			$MailServer = null;
		}
			
		$CH = Util::getCloudHost();
		if(!$MailServerSet AND $CH != null AND isset($CH->forceOwnSMTPServer) AND $CH->forceOwnSMTPServer === true AND !$skipOwnServer)
			throw new Exception("Bitte tragen Sie einen eigenen Mailserver ein, um den zuverlässigen Mailversand in Ihrem Namen zu gewährleisten. Melden Sie sich dazu als Admin-Benutzer am System an und tragen Sie den Mailserver im Cloud-Reiter ein.");
		
		/*
		  $optionsFile = (dirname(__FILE__)."/../../specifics"."/phynxOptions.xml");

		  if(file_exists($optionsFile)){
		  $optionsFileContent = file_get_contents($optionsFile);
		  $xml = new SimpleXMLElement($optionsFileContent);

		  if(isset($xml->Mail)){
		  switch($xml->Mail->method["value"]){
		  case "smtp":
		  $this->default_method = $xml->Mail->method["value"];

		  if(isset($xml->Mail->options->host)) $this->smtp_params['host'] = $xml->Mail->options->host["value"]."";
		  if(isset($xml->Mail->options->port)) $this->smtp_params['port'] = $xml->Mail->options->port["value"] * 1;
		  if(isset($xml->Mail->options->helo)) $this->smtp_params['helo'] = $xml->Mail->options->helo["value"]."";
		  if(isset($xml->Mail->options->auth)) $this->smtp_params['auth'] = $xml->Mail->options->auth["value"]."" == "true";
		  if(isset($xml->Mail->options->user)) $this->smtp_params['user'] = $xml->Mail->options->user["value"]."";
		  if(isset($xml->Mail->options->pass)) $this->smtp_params['pass'] = $xml->Mail->options->pass["value"]."";
		  break;
		  }

		  if(isset($xml->Mail->BCC) AND $xml->Mail->BCC["value"]."" != "") $this->setBcc($xml->Mail->BCC["value"]."");

		  if(isset($xml->Mail->XMailer)) $this->headers['X-Mailer'] = $xml->Mail->XMailer["value"]."";
		  }
		  } */
	}

	/**
	 * Accessor to set the CRLF style
	 * 
	 * @param string $crlf CRLF style to use.
	 *                     Use \r\n for SMTP, and \n
	 *                     for normal.
	 */
	public function setCRLF($crlf = "\n") {
		if (!defined('CRLF')) {
			define('CRLF', $crlf);
		}

		if (!defined('MAIL_MIMEPART_CRLF')) {
			define('MAIL_MIMEPART_CRLF', $crlf);
		}
	}
	
	public function setDSN($Success, $Delay, $Failure){
		$this->dsn = array($Success, $Delay, $Failure);
	}

	/**
	 * Accessor to set the SMTP parameters
	 * 
	 * @param string $host Hostname
	 * @param string $port Port
	 * @param string $helo HELO string to use
	 * @param bool   $auth User authentication or not
	 * @param string $user Username
	 * @param string $pass Password
	 */
	public function setSMTPParams($host = null, $port = null, $helo = null, $auth = null, $user = null, $pass = null) {
		if (!is_null($host))
			$this->smtp_params['host'] = $host;
		if (!is_null($port))
			$this->smtp_params['port'] = $port;
		if (!is_null($helo))
			$this->smtp_params['helo'] = $helo;
		if (!is_null($auth))
			$this->smtp_params['auth'] = $auth;
		if (!is_null($user))
			$this->smtp_params['user'] = $user;
		if (!is_null($pass))
			$this->smtp_params['pass'] = $pass;
	}

	public function getSMTPParams(){
		return $this->smtp_params;
	}

	public function getSMTPLog(){
		if($this->smtp_conn === null)
			return "";
		
		return trim($this->smtp_conn->getLog());
	}
	
	/**
	 * Sets sendmail path and options (optionally) (when directly piping to sendmail)
	 * 
	 * @param string $path Path and options for sendmail command
	 */
	public function setSendmailPath($path) {
		$this->sendmail_path = $path;
	}

	/**
	 * Accessor function to set the text encoding
	 * 
	 * @param object $encoding Text encoding to use
	 */
	public function setTextEncoding(iEncoding $encoding) {
		$this->build_params['text_encoding'] = $encoding;
	}

	/**
	 * Accessor function to set the HTML encoding
	 * 
	 * @param object $encoding HTML encoding to use
	 */
	public function setHTMLEncoding(iEncoding $encoding) {
		$this->build_params['html_encoding'] = $encoding;
	}

	/**
	 * Accessor function to set the text charset
	 * 
	 * @param string $charset Character set to use
	 */
	public function setTextCharset($charset = 'ISO-8859-1') {
		$this->build_params['text_charset'] = $charset;
	}

	/**
	 * Accessor function to set the HTML charset
	 * 
	 * @param string $charset Character set to use
	 */
	public function setHTMLCharset($charset = 'ISO-8859-1') {
		$this->build_params['html_charset'] = $charset;
	}

	/**
	 * Accessor function to set the calendar charset
	 * 
	 * @param string $charset Character set to use
	 */
	public function setCalendarCharset($charset = 'ISO-8859-1') {
		$this->build_params['calendar_charset'] = $charset;
	}

	/**
	 * Accessor function to set the header encoding charset
	 * 
	 * @param string $charset Character set to use
	 */
	public function setHeadCharset($charset = 'ISO-8859-1') {
		$this->build_params['head_charset'] = $charset;
	}

	/**
	 * Accessor function to set the text wrap count
	 * 
	 * @param integer $count Point at which to wrap text
	 */
	public function setTextWrap($count = 998) {
		$this->build_params['text_wrap'] = $count;
	}

	/**
	 * Accessor to set a header
	 * 
	 * @param string $name  Name of header
	 * @param string $value Value of header
	 */
	public function setHeader($name, $value) {
		$this->headers[$name] = $value;
	}

	/**
	 * Accessor to add a Subject: header
	 * 
	 * @param string $subject Subject to set
	 */
	public function setSubject($subject) {
		$this->headers['Subject'] = $subject;
	}

	/**
	 * Accessor to add a From: header
	 * 
	 * @param string $from From address
	 */
	public function setFrom($from) {
		$this->headers['From'] = $from;
	}

	/**
	 * Accessor to set priority. Priority given should be either
	 * high, normal or low. Can also be specified numerically, 
	 * being 1, 3 or 5 (respectively).
	 * 
	 * @param mixed $priority The priority to use.
	 */
	public function setPriority($priority = 'normal') {
		switch (strtolower($priority)) {
			case 'high':
			case '1':
				$this->headers['X-Priority'] = '1';
				$this->headers['X-MSMail-Priority'] = 'High';
				break;

			case 'normal':
			case '3':
				$this->headers['X-Priority'] = '3';
				$this->headers['X-MSMail-Priority'] = 'Normal';
				break;

			case 'low':
			case '5':
				$this->headers['X-Priority'] = '5';
				$this->headers['X-MSMail-Priority'] = 'Low';
				break;
		}
	}

	/**
	 * Accessor to set the return path
	 * 
	 * @param string $return_path Return path to use
	 */
	public function setReturnPath($return_path) {
		$this->return_path = $return_path;
	}

	/**
	 * Accessor to set the replay to header
	 * 
	 * @param string $reply_to email address to use
	 */
	public function setReplyTo($reply_to) {
		$this->headers["Reply-To"] = $reply_to;
	}

	/**
	 * Accessor to add a Cc: header
	 * 
	 * @param string $cc Carbon Copy address
	 */
	public function setCc($cc) {
		$this->headers['Cc'] = $cc;
	}

	/**
	 * Accessor to add a Bcc: header
	 * 
	 * @param string $bcc Blind Carbon Copy address
	 */
	public function setBcc($bcc) {
		$this->headers['Bcc'] = $bcc;
	}

	/**
	 * Adds plain text. Use this function
	 * when NOT sending html email
	 * 
	 * @param string $text Plain text of email
	 */
	public function setText($text) {
		$this->text = $text;
	}

	/**
	 * Adds calendar information as its own
	 * message part
	 * 
	 * @param string $ics ICS content of email
	 */
	public function setCalendar($ics, $method){
		$this->calendar = $ics;
		$this->calendarMethod = $method;
	}
	
	/**
	 * Adds HTML to the emails, with an associated text part.
	 * If third part is given, images in the email will be loaded
	 * from this directory.
	 * 
	 * @param string $html       HTML part of email
	 * @param string $images_dir Images directory
	 */
	function setHTML($html, $images_dir = null) {
		$this->html = $html;

		if (!empty($images_dir)) {
			$this->findHtmlImages($images_dir);
		}
	}

	/**
	 * Function for extracting images from
	 * html source. This function will look
	 * through the html code supplied by setHTML()
	 * and find any file that ends in one of the
	 * extensions defined in $obj->image_types.
	 * If the file exists it will read it in and
	 * embed it, (not an attachment).
	 * 
	 * @param string $images_dir Images directory to look in
	 */
	private function findHtmlImages($images_dir) {
		// Build the list of image extensions
		$extensions = array_keys($this->image_types);

		preg_match_all('/(?:"|\')([^"\']+\.(' . implode('|', $extensions) . '))(?:"|\')/Ui', $this->html, $matches);

		foreach ($matches[1] as $m) {
			if (file_exists($images_dir . $m)) {
				$html_images[] = $m;
				$this->html = str_replace($m, basename($m), $this->html);
			}
		}

		/**
		 * Go thru found images
		 */
		if (!empty($html_images)) {

			// If duplicate images are embedded, they may show up as attachments, so remove them.
			$html_images = array_unique($html_images);
			sort($html_images);

			foreach ($html_images as $img) {
				if ($image = file_get_contents($images_dir . $img)) {
					$ext = preg_replace('#^.*\.(\w{3,4})$#e', 'strtolower("$1")', $img);
					$content_type = $this->image_types[$ext];
					$this->addEmbeddedImage(new stringEmbeddedImage($image, basename($img), $content_type));
				}
			}
		}
	}

	/**
	 * Adds an image to the list of embedded
	 * images.
	 * 
	 * @param string $object Embedded image object
	 */
	public function addEmbeddedImage($embeddedImage) {
		$embeddedImage->cid = md5(uniqid(time()));

		$this->html_images[] = $embeddedImage;
	}

	/**
	 * Adds a file to the list of attachments.
	 * 
	 * @param string $attachment Attachment object
	 */
	public function addAttachment($attachment) {
		$this->attachments[] = $attachment;
	}

	/**
	 * Adds a text subpart to a mime_part object
	 * 
	 * @param  object $obj 
	 * @return object      Mime part object
	 */
	private function addTextPart(&$message) {
		$params['content_type'] = 'text/plain';
		$params['encoding'] = $this->build_params['text_encoding']->getType();
		$params['charset'] = $this->build_params['text_charset'];

		if (!empty($message)) {
			$message->addSubpart($this->text, $params);
		} else {
			$message = new Mail_mimePart($this->text, $params);
		}
	}

	/**
	 * Adds a calendar subpart to a mime_part object
	 * 
	 * @param  object $obj 
	 * @return object      Mime part object
	 */
	private function addCalendarPart(&$message) {
		$params['content_type'] = 'text/calendar';
		$params['encoding'] = $this->build_params['calendar_encoding']->getType();
		$params['charset'] = $this->build_params['calendar_charset'];
		$params['method'] = $this->calendarMethod;

		if (!empty($message)) {
			$message->addSubpart($this->calendar, $params);
		} else {
			$message = new Mail_mimePart($this->calendar, $params);
		}
	}

	/**
	 * Adds a html subpart to a mime_part object
	 * 
	 * @param object $obj
	 * @return object     Mime part object
	 */
	private function addHtmlPart(&$message) {
		$params['content_type'] = 'text/html';
		$params['encoding'] = $this->build_params['html_encoding']->getType();
		$params['charset'] = $this->build_params['html_charset'];

		if (!empty($message)) {
			$message->addSubpart($this->html, $params);
		} else {
			$message = new Mail_mimePart($this->html, $params);
		}
	}

	/**
	 * Starts a message with a mixed part
	 * 
	 * @return object Mime part object
	 */
	private function addMixedPart(&$message) {
		$params['content_type'] = 'multipart/mixed';

		$message = new Mail_mimePart('', $params);
	}

	/**
	 * Adds an alternative part to a mime_part object
	 * 
	 * @param  object $obj
	 * @return object      Mime part object
	 */
	private function addAlternativePart(&$message) {
		$params['content_type'] = 'multipart/alternative';

		if (!empty($message)) {
			return $message->addSubpart('', $params);
		} else {
			$message = new Mail_mimePart('', $params);
		}
	}

	/**
	 * Adds a html subpart to a mime_part object
	 * 
	 * @param  object $obj
	 * @return object      Mime part object
	 */
	private function addRelatedPart(&$message) {
		$params['content_type'] = 'multipart/related';

		if (!empty($message)) {
			return $message->addSubpart('', $params);
		} else {
			$message = new Mail_mimePart('', $params);
		}
	}

	/**
	 * Adds all html images to a mime_part object
	 * 
	 * @param  object $obj Message object
	 */
	private function addHtmlImageParts(&$message) {
		foreach ($this->html_images as $value) {
			$params['content_type'] = $value->contentType;
			$params['encoding'] = $value->encoding->getType();
			$params['disposition'] = 'inline';
			$params['dfilename'] = $value->name;
			$params['cid'] = $value->cid;

			$message->addSubpart($value->data, $params);
		}
	}

	/**
	 * Adds all attachments to a mime_part object
	 * 
	 * @param object $obj Message object
	 */
	private function addAttachmentParts(&$message) {
		foreach ($this->attachments as $value) {
			$params['content_type'] = $value->contentType;
			$params['encoding'] = $value->encoding->getType();
			$params['disposition'] = 'attachment';
			$params['dfilename'] = $value->name;

			$message->addSubpart($value->data, $params);
		}
	}
	
	/**
	 * Builds the multipart message.
	 */
	private function build() {
		if (!empty($this->html_images)) {
			foreach ($this->html_images as $value) {
				$quoted = preg_quote($value->name);
				$cid = preg_quote($value->cid);

				$this->html = preg_replace("#src=\"$quoted\"|src='$quoted'#", "src=\"cid:$cid\"", $this->html);
				$this->html = preg_replace("#background=\"$quoted\"|background='$quoted'#", "background=\"cid:$cid\"", $this->html);
			}
		}
		
		$message = null;
		$attachments = !empty($this->attachments);
		$html_images = !empty($this->html_images);
		$html = !empty($this->html);
		$calendar = !empty($this->calendar);
		$text = !$html;

		switch (true) {
			case $calendar AND $text:
				$this->addMixedPart($message);
				$alt = $this->addAlternativePart($message);
				
				$this->addTextPart($alt);
				$this->addCalendarPart($alt);
				
				$this->addAttachmentParts($message);
				break;
			
			case $text:
				$message = null;
				if ($attachments) {
					$this->addMixedPart($message);
				}

				$this->addTextPart($message);

				// Attachments
				$this->addAttachmentParts($message);
				break;
				
			case $html AND !$attachments AND !$html_images:
				$this->addAlternativePart($message);

				$this->addTextPart($message);
				$this->addHtmlPart($message);
				break;

			case $html AND !$attachments AND $html_images:
				$this->addRelatedPart($message);
				$alt = $this->addAlternativePart($message);

				$this->addTextPart($alt);
				$this->addHtmlPart($alt);

				// HTML images
				$this->addHtmlImageParts($message);
				break;

			case $html AND $attachments AND !$html_images:
				$this->addMixedPart($message);
				$alt = $this->addAlternativePart($message);

				$this->addTextPart($alt);
				$this->addHtmlPart($alt);

				// Attachments
				$this->addAttachmentParts($message);
				break;

			case $html AND $attachments AND $html_images:
				$this->addMixedPart($message);
				$rel = $this->addRelatedPart($message);
				$alt = $this->addAlternativePart($rel);

				$this->addTextPart($alt);
				$this->addHtmlPart($alt);

				// HTML images
				$this->addHtmlImageParts($rel);

				// Attachments
				$this->addAttachmentParts($message);
				break;
		}

		if (isset($message)) {
			$output = $message->encode();
			$this->output = $output['body'];
			$this->headers = array_merge($this->headers, $output['headers']);

			// Figure out hostname
			if (!empty($_SERVER['HTTP_HOST'])) {
				$hostname = $_SERVER['HTTP_HOST'];
			} else if (!empty($_SERVER['SERVER_NAME'])) {
				$hostname = $_SERVER['SERVER_NAME'];
			} else if (!empty($_ENV['HOSTNAME'])) {
				$hostname = $_ENV['HOSTNAME'];
			} else {
				$hostname = 'localhost';
			}

			$message_id = sprintf('<%s.%s@%s>', base_convert(time(), 10, 36), base_convert(rand(), 10, 36), $hostname);
			$this->headers['Message-ID'] = $message_id;

			$this->is_built = true;
			
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Function to encode a header if necessary
	 * according to RFC2047
	 * 
	 * @param  string $input   Value to encode
	 * @param  string $charset Character set to use
	 * @return string          Encoded value
	 */
	private function encodeHeader($input, $charset = 'ISO-8859-1') {
		preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $input, $matches);
		foreach ($matches[1] as $value) {
			if(!defined("PHP_VERSION_ID") OR PHP_VERSION_ID < 50500)
				$replacement = preg_replace('/([\x80-\xFF])/e', '"=" . strtoupper(dechex(ord("\1")))', $value);
			else
				$replacement = preg_replace_callback(
					'/([\x80-\xFF])/',
					function ($m) {
						return "=".strtoupper(dechex(ord($m[1])));
					},
					$value);
					
			$input = str_replace($value, '=?' . $charset . '?Q?' . $replacement . '?=', $input);
		}

		return $input;
	}

	/**
	 * Sends the mail.
	 *
	 * @param  array  $recipients Array of receipients to send the mail to
	 * @param  string $type       How to send the mail ('mail' or 'sendmail' or 'smtp')
	 * @return mixed
	 */
	public function send($recipients, $type = 'mail', $forceRebuild = false) {

		if ($this->default_method != null) {
			$type = $this->default_method;
		}

		if (!defined('CRLF')) {
			$this->setCRLF(($type == 'mail' OR $type == 'sendmail') ? "\n" : "\r\n");
		}

		if (!$this->is_built OR $forceRebuild) {
			$this->build();
		}

		switch ($type) {
			case 'mail':
				$subject = '';
				if (!empty($this->headers['Subject'])) {
					$subject = $this->encodeHeader($this->headers['Subject'], $this->build_params['head_charset']);
					unset($this->headers['Subject']);
				}

				// Get flat representation of headers
				foreach ($this->headers as $name => $value) {
					$headers[] = $name . ': ' . $this->encodeHeader($value, $this->build_params['head_charset']);
				}

				$to = $this->encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);

				if (!empty($this->return_path)) {
					$result = mail($to, $subject, $this->output, implode(CRLF, $headers), '-f' . $this->return_path);
				} else {
					$result = mail($to, $subject, $this->output, implode(CRLF, $headers));
				}

				// Reset the subject in case mail is resent
				if ($subject !== '') {
					$this->headers['Subject'] = $subject;
				}


				// Return
				return $result;
				break;

			case 'sendmail':
				// Get flat representation of headers
				foreach ($this->headers as $name => $value) {
					$headers[] = $name . ': ' . $this->encodeHeader($value, $this->build_params['head_charset']);
				}

				// Encode To:
				$headers[] = 'To: ' . $this->encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);

				// Get return path arg for sendmail command if necessary
				$returnPath = '';
				if (!empty($this->return_path)) {
					$returnPath = '-f' . $this->return_path;
				}

				$pipe = popen($this->sendmail_path . " " . $returnPath, 'w');
				$bytes = fputs($pipe, implode(CRLF, $headers) . CRLF . CRLF . $this->output);
				$r = pclose($pipe);

				return $r;
				break;

			case 'smtp':
				require_once(dirname(__FILE__) . '/hmmsmtp.php');
				require_once(dirname(__FILE__) . '/RFC822.php');
				if($this->smtp_conn == null){
					$this->smtp_conn = new hmmsmtp();
				
					$this->smtp_conn->connect($this->smtp_params);
					if($this->dsn !== null)
						$this->smtp_conn->dsn($this->dsn[0], $this->dsn[1], $this->dsn[2]);

				}
				$smtp = $this->smtp_conn;
				// Parse recipients argument for internet addresses
				foreach ($recipients as $recipient) {
					$Mail_RFC822 = new Mail_RFC822();
					$addresses = $Mail_RFC822->parseAddressList($recipient, $this->smtp_params['helo'], null, false);
					foreach ($addresses as $address) {
						$smtp_recipients[] = sprintf('%s@%s', $address->mailbox, $address->host);
					}
				}
				unset($addresses); // These are reused
				unset($address);   // These are reused
				
				$this->setHeader('Date', date('D, d M Y H:i:s O')); //add date header!
				// Get flat representation of headers, parsing
				// Cc and Bcc as we go
				foreach ($this->headers as $name => $value) {
					if ($name == 'Cc' OR $name == 'Bcc') {
						$Mail_RFC822 = new Mail_RFC822();
						$addresses = $Mail_RFC822->parseAddressList($value, $this->smtp_params['helo'], null, false);
						foreach ($addresses as $address) {
							$smtp_recipients[] = sprintf('%s@%s', $address->mailbox, $address->host);
						}
					}
					if ($name == 'Bcc') {
						continue;
					}
					$headers[] = $name . ': ' . $this->encodeHeader($value, $this->build_params['head_charset']);
				}
				// Add To header based on $recipients argument
				$headers[] = 'To: ' . $this->encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);
				
				// Add headers to send_params
				$send_params['headers'] = $headers;
				$send_params['recipients'] = array_values(array_unique($smtp_recipients));
				$send_params['body'] = $this->output;

				// Setup return path
				if (isset($this->return_path)) {
					$send_params['from'] = $this->return_path;
				} elseif (!empty($this->headers['From'])) {
					$Mail_RFC822 = new Mail_RFC822();
					$from = $Mail_RFC822->parseAddressList($this->headers['From']);
					$send_params['from'] = sprintf('%s@%s', $from[0]->mailbox, $from[0]->host);
				} else {
					$send_params['from'] = 'postmaster@' . $this->smtp_params['helo'];
				}
				
				// Send it
				if (!$smtp->send($send_params)) {
					$this->errors = $smtp->getErrors();
					return false;
				}
				return true;
				break;
		}
	}

	/**
	 * Use this method to return the email
	 * in message/rfc822 format. Useful for
	 * adding an email to another email as
	 * an attachment. there's a commented
	 * out example in example.php.
	 * 
	 * @param array  $recipients Array of recipients 
	 * @param string $type       Method to be used to send the mail.
	 *                           Used to determine the line ending type.
	 */
	public function getRFC822($recipients, $type = 'mail', $useDate = null) {
		// Make up the date header as according to RFC822
		$this->setHeader('Date', date('D, d M Y H:i:s O', $useDate == null ? time() : $useDate));

		if (!defined('CRLF')) {
			$this->setCRLF($type == 'mail' ? "\n" : "\r\n");
		}

		if (!$this->is_built) {
			$this->build();
		}

		// Return path ?
		if (isset($this->return_path)) {
			$headers[] = 'Return-Path: ' . $this->return_path;
		}

		// Get flat representation of headers
		foreach ($this->headers as $name => $value) {
			$headers[] = $name . ': ' . $value;
		}
		$headers[] = 'To: ' . implode(', ', $recipients);

		return implode(CRLF, $headers) . CRLF . CRLF . $this->output;
	}

}

// End of class.

/**
 * Attachment classes
 */
class attachment {

	/**
	 * Data of attachment
	 * @var string
	 */
	public $data;

	/**
	 * Name of attachment (filename)
	 * @var string
	 */
	public $name;

	/**
	 * Content type of attachment
	 * @var string
	 */
	public $contentType;

	/**
	 * Encoding type of attachment
	 * @var object
	 */
	public $encoding;

	/**
	 * Constructor
	 * 
	 * @param string $data        File data
	 * @param string $name        Name of attachment (filename)
	 * @param string $contentType Content type of attachment
	 * @param object $encoding    Encoding type to use
	 */
	public function __construct($data, $name, $contentType, iEncoding $encoding) {
		$this->data = $data;
		$this->name = $name;
		$this->contentType = $contentType;
		$this->encoding = $encoding;
	}

}

/**
 * File based attachment class
 */
class fileAttachment extends attachment {

	/**
	 * Constructor
	 * 
	 * @param string $filename    Name of file
	 * @param string $contentType Content type of file
	 * @param string $encoding    What encoding to use
	 */
	public function __construct($filename, $contentType = 'application/octet-stream', $encoding = null, $name = '') {
		$encoding = is_null($encoding) ? new Base64Encoding() : $encoding;

		if ($name == "")
			$name = basename($filename);

		parent::__construct(file_get_contents($filename), $name, $contentType, $encoding);
	}

}

/**
 * Attachment class to handle attachments which are contained
 * in a variable.
 */
class stringAttachment extends attachment {

	/**
	 * Constructor
	 * 
	 * @param string $data        File data
	 * @param string $name        Name of attachment (filename)
	 * @param string $contentType Content type of file
	 * @param string $encoding    What encoding to use
	 */
	public function __construct($data, $name = '', $contentType = 'application/octet-stream', $encoding = null) {
		$encoding = is_null($encoding) ? new Base64Encoding() : $encoding;

		parent::__construct($data, $name, $contentType, $encoding);
	}

}

/**
 * File based embedded image class
 */
class fileEmbeddedImage extends fileAttachment {
	
}

/**
 * String based embedded image class
 */
class stringEmbeddedImage extends stringAttachment {
	
}

/**
 * 
 */

/**
 * Encoding interface
 */
interface iEncoding {

	public function encode($input);

	public function getType();
}

/**
 * Base64 Encoding class
 */
class Base64Encoding implements iEncoding {
	/*
	 * Function to encode data using
	 * base64 encoding.
	 * 
	 * @param string $input Data to encode
	 */

	public function encode($input) {
		return rtrim(chunk_split(base64_encode($input), 76, defined('MAIL_MIME_PART_CRLF') ? MAIL_MIME_PART_CRLF : "\r\n"));
	}

	/**
	 * Returns type
	 */
	public function getType() {
		return 'base64';
	}

}

/**
 * Quoted Printable Encoding class
 */
class QPrintEncoding implements iEncoding {
	/*
	 * Function to encode data using
	 * quoted-printable encoding.
	 * 
	 * @param string $input Data to encode
	 */

	public function encode($input) {
		// Replace non printables
		$input = preg_replace('/([^\x20\x21-\x3C\x3E-\x7E\x0A\x0D])/e', 'sprintf("=%02X", ord("\1"))', $input);
		$inputLen = strlen($input);
		$outLines = array();
		$output = '';

		$lines = preg_split('/\r?\n/', $input);

		// Walk through each line
		for ($i = 0; $i < count($lines); $i++) {
			// Is line too long ?
			if (strlen($lines[$i]) > $lineMax) {
				$outLines[] = substr($lines[$i], 0, $lineMax - 1) . "="; // \r\n Gets added when lines are imploded
				$lines[$i] = substr($lines[$i], $lineMax - 1);
				$i--; // Ensure this line gets redone as we just changed it
			} else {
				$outLines[] = $lines[$i];
			}
		}

		// Convert trailing whitespace		
		$output = preg_replace('/(\x20+)$/me', 'str_replace(" ", "=20", "\1")', $outLines);

		return implode("\r\n", $output);
	}

	/**
	 * Returns type
	 */
	public function getType() {
		return 'quoted-printable';
	}

}

/**
 * 7Bit Encoding class
 */
class SevenBitEncoding implements iEncoding {
	/*
	 * Function to "encode" data using
	 * 7bit encoding.
	 * 
	 * @param string $input Data to encode
	 */

	public function encode($input) {
		return $input;
	}

	/**
	 * Returns type
	 */
	public function getType() {
		return '7bit';
	}

}

/**
 * 8Bit Encoding class
 */
class EightBitEncoding implements iEncoding {
	/*
	 * Function to "encode" data using
	 * 8bit encoding.
	 * 
	 * @param string $input Data to encode
	 */

	public function encode($input) {
		return $input;
	}

	/**
	 * Returns type
	 */
	public function getType() {
		return '8bit';
	}

}

?>