corrected a few docblock comments to accurately reflect return values
removed some unneeded whitespace
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index e9a5344..ac96882 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -1,1953 +1,1955 @@
-<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

-/**

- * CodeIgniter

- *

- * An open source application development framework for PHP 4.3.2 or newer

- *

- * @package		CodeIgniter

- * @author		ExpressionEngine Dev Team

- * @copyright	Copyright (c) 2008, EllisLab, Inc.

- * @license		http://codeigniter.com/user_guide/license.html

- * @link		http://codeigniter.com

- * @since		Version 1.0

- * @filesource

- */

-

-// ------------------------------------------------------------------------

-

-/**

- * CodeIgniter Email Class

- *

- * Permits email to be sent using Mail, Sendmail, or SMTP.

- *

- * @package		CodeIgniter

- * @subpackage	Libraries

- * @category	Libraries

- * @author		ExpressionEngine Dev Team

- * @link		http://codeigniter.com/user_guide/libraries/email.html

- */

-class CI_Email {

-

-	var	$useragent		= "CodeIgniter";

-	var	$mailpath		= "/usr/sbin/sendmail";	// Sendmail path

-	var	$protocol		= "mail";	// mail/sendmail/smtp

-	var	$smtp_host		= "";		// SMTP Server.  Example: mail.earthlink.net

-	var	$smtp_user		= "";		// SMTP Username

-	var	$smtp_pass		= "";		// SMTP Password

-	var	$smtp_port		= "25";		// SMTP Port

-	var	$smtp_timeout	= 5;		// SMTP Timeout in seconds

-	var	$wordwrap		= TRUE;		// TRUE/FALSE  Turns word-wrap on/off

-	var	$wrapchars		= "76";		// Number of characters to wrap at.

-	var	$mailtype		= "text";	// text/html  Defines email formatting

-	var	$charset		= "utf-8";	// Default char set: iso-8859-1 or us-ascii

-	var	$multipart		= "mixed";	// "mixed" (in the body) or "related" (separate)

-	var $alt_message	= '';		// Alternative message for HTML emails

-	var	$validate		= FALSE;	// TRUE/FALSE.  Enables email validation

-	var	$priority		= "3";		// Default priority (1 - 5)

-	var	$newline		= "\n";		// Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)

-	var $crlf			= "\n";		// The RFC 2045 compliant CRLF for quoted-printable is "\r\n".  Apparently some servers,

-									// even on the receiving end think they need to muck with CRLFs, so using "\n", while

-									// distasteful, is the only thing that seems to work for all environments.

-	var $send_multipart	= TRUE;		// TRUE/FALSE - Yahoo does not like multipart alternative, so this is an override.  Set to FALSE for Yahoo.	

-	var	$bcc_batch_mode	= FALSE;	// TRUE/FALSE  Turns on/off Bcc batch feature

-	var	$bcc_batch_size	= 200;		// If bcc_batch_mode = TRUE, sets max number of Bccs in each batch

-	var $_safe_mode		= FALSE;

-	var	$_subject		= "";

-	var	$_body			= "";

-	var	$_finalbody		= "";

-	var	$_alt_boundary	= "";

-	var	$_atc_boundary	= "";

-	var	$_header_str	= "";

-	var	$_smtp_connect	= "";

-	var	$_encoding		= "8bit";

-	var $_IP			= FALSE;

-	var	$_smtp_auth		= FALSE;

-	var $_replyto_flag	= FALSE;

-	var	$_debug_msg		= array();

-	var	$_recipients	= array();

-	var	$_cc_array		= array();

-	var	$_bcc_array		= array();

-	var	$_headers		= array();

-	var	$_attach_name	= array();

-	var	$_attach_type	= array();

-	var	$_attach_disp	= array();

-	var	$_protocols		= array('mail', 'sendmail', 'smtp');

-	var	$_base_charsets	= array('us-ascii', 'iso-2022-');	// 7-bit charsets (excluding language suffix)

-	var	$_bit_depths	= array('7bit', '8bit');

-	var	$_priorities	= array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');

-

-

-	/**

-	 * Constructor - Sets Email Preferences

-	 *

-	 * The constructor can be passed an array of config values

-	 */

-	function CI_Email($config = array())

-	{	

-		if (count($config) > 0)

-		{

-			$this->initialize($config);

-		}	

-		else

-		{

-			$this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;	

-			$this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;

-		}

-		

-		log_message('debug', "Email Class Initialized");

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Initialize preferences

-	 *

-	 * @access	public

-	 * @param	array

-	 * @return	void

-	 */

-	function initialize($config = array())

-	{

-		$this->clear();

-		foreach ($config as $key => $val)

-		{

-			if (isset($this->$key))

-			{

-				$method = 'set_'.$key;

-

-				if (method_exists($this, $method))

-				{

-					$this->$method($val);

-				}

-				else

-				{

-					$this->$key = $val;

-				}	

-			}

-		}

-		

-		$this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;	

-		$this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Initialize the Email Data

-	 *

-	 * @access	public

-	 * @return	void

-	 */	

-	function clear($clear_attachments = FALSE)

-	{

-		$this->_subject		= "";

-		$this->_body		= "";

-		$this->_finalbody	= "";

-		$this->_header_str	= "";

-		$this->_replyto_flag = FALSE;

-		$this->_recipients	= array();

-		$this->_headers		= array();

-		$this->_debug_msg	= array();

-

-		$this->_set_header('User-Agent', $this->useragent);

-		$this->_set_header('Date', $this->_set_date());

-

-		if ($clear_attachments !== FALSE)

-		{

-			$this->_attach_name = array();

-			$this->_attach_type = array();

-			$this->_attach_disp = array();

-		}   

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set FROM

-	 *

-	 * @access	public

-	 * @param	string

-	 * @param	string

-	 * @return	void

-	 */	

-	function from($from, $name = '')

-	{

-		if (preg_match( '/\<(.*)\>/', $from, $match))

-		{

-			$from = $match['1'];

-		}

-

-		if ($this->validate)

-		{

-			$this->validate_email($this->_str_to_array($from));

-		}

-	

-		if ($name != '' && strncmp($name, '"', 1) != 0)

-		{

-			$name = '"'.$name.'"';

-		}

-	

-		$this->_set_header('From', $name.' <'.$from.'>');

-		$this->_set_header('Return-Path', '<'.$from.'>');

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Reply-to

-	 *

-	 * @access	public

-	 * @param	string

-	 * @param	string

-	 * @return	void

-	 */	

-	function reply_to($replyto, $name = '')

-	{

-		if (preg_match( '/\<(.*)\>/', $replyto, $match))

-		{

-			$replyto = $match['1'];

-		}

-

-		if ($this->validate)

-		{

-			$this->validate_email($this->_str_to_array($replyto));	

-		}

-

-		if ($name == '')

-		{

-			$name = $replyto;

-		}

-

-		if (strncmp($name, '"', 1) != 0)

-		{

-			$name = '"'.$name.'"';

-		}

-

-		$this->_set_header('Reply-To', $name.' <'.$replyto.'>');

-		$this->_replyto_flag = TRUE;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Recipients

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function to($to)

-	{

-		$to = $this->_str_to_array($to);

-		$to = $this->clean_email($to);

-	

-		if ($this->validate)

-		{

-			$this->validate_email($to);

-		}

-	

-		if ($this->_get_protocol() != 'mail')

-		{

-			$this->_set_header('To', implode(", ", $to));

-		}

-

-		switch ($this->_get_protocol())

-		{

-			case 'smtp'		: $this->_recipients = $to;

-			break;

-			case 'sendmail'	: $this->_recipients = implode(", ", $to);

-			break;

-			case 'mail'		: $this->_recipients = implode(", ", $to);

-			break;

-		}	

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set CC

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function cc($cc)

-	{	

-		$cc = $this->_str_to_array($cc);

-		$cc = $this->clean_email($cc);

-

-		if ($this->validate)

-		{

-			$this->validate_email($cc);

-		}

-

-		$this->_set_header('Cc', implode(", ", $cc));

-

-		if ($this->_get_protocol() == "smtp")

-		{

-			$this->_cc_array = $cc;

-		}

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set BCC

-	 *

-	 * @access	public

-	 * @param	string

-	 * @param	string

-	 * @return	void

-	 */	

-	function bcc($bcc, $limit = '')

-	{

-		if ($limit != '' && is_numeric($limit))

-		{

-			$this->bcc_batch_mode = TRUE;

-			$this->bcc_batch_size = $limit;

-		}

-

-		$bcc = $this->_str_to_array($bcc);

-		$bcc = $this->clean_email($bcc);

-

-		if ($this->validate)

-		{

-			$this->validate_email($bcc);

-		}

-

-		if (($this->_get_protocol() == "smtp") OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))

-		{

-			$this->_bcc_array = $bcc;

-		}

-		else

-		{

-			$this->_set_header('Bcc', implode(", ", $bcc));

-		}

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Email Subject

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function subject($subject)

-	{

-		if (strpos($subject, "\r") !== FALSE OR strpos($subject, "\n") !== FALSE)

-		{

-			$subject = str_replace(array("\r\n", "\r", "\n"), '', $subject);			

-		}

-

-		if (strpos($subject, "\t"))

-		{

-			$subject = str_replace("\t", ' ', $subject);

-		}

-

-		$this->_set_header('Subject', trim($subject));

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Body

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function message($body)

-	{

-		$this->_body = stripslashes(rtrim(str_replace("\r", "", $body)));	

-	}	

- 	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Assign file attachments

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	string

-	 */

-	function attach($filename, $disposition = 'attachment')

-	{	

-		$this->_attach_name[] = $filename;

-		$this->_attach_type[] = $this->_mime_types(next(explode('.', basename($filename))));

-		$this->_attach_disp[] = $disposition; // Can also be 'inline'  Not sure if it matters

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Add a Header Item

-	 *

-	 * @access	private

-	 * @param	string

-	 * @param	string

-	 * @return	void

-	 */	

-	function _set_header($header, $value)

-	{

-		$this->_headers[$header] = $value;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Convert a String to an Array

-	 *

-	 * @access	private

-	 * @param	string

-	 * @return	array

-	 */	

-	function _str_to_array($email)

-	{

-		if ( ! is_array($email))

-		{

-			if (strpos($email, ',') !== FALSE)

-			{

-				$email = preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY);

-			}

-			else

-			{

-				$email = trim($email);

-				settype($email, "array");

-			}

-		}

-		return $email;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Multipart Value

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function set_alt_message($str = '')

-	{

-		$this->alt_message = ($str == '') ? '' : $str;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Mailtype

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function set_mailtype($type = 'text')

-	{

-		$this->mailtype = ($type == 'html') ? 'html' : 'text';

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Wordwrap

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function set_wordwrap($wordwrap = TRUE)

-	{

-		$this->wordwrap = ($wordwrap === FALSE) ? FALSE : TRUE;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Protocol

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function set_protocol($protocol = 'mail')

-	{

-		$this->protocol = ( ! in_array($protocol, $this->_protocols, TRUE)) ? 'mail' : strtolower($protocol);

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Priority

-	 *

-	 * @access	public

-	 * @param	integer

-	 * @return	void

-	 */	

-	function set_priority($n = 3)

-	{

-		if ( ! is_numeric($n))

-		{

-			$this->priority = 3;

-			return;

-		}

-	

-		if ($n < 1 OR $n > 5)

-		{

-			$this->priority = 3;

-			return;

-		}

-	

-		$this->priority = $n;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Newline Character

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function set_newline($newline = "\n")

-	{

-		if ($newline != "\n" AND $newline != "\r\n" AND $newline != "\r")

-		{

-			$this->newline	= "\n";	

-			return;

-		}

-	

-		$this->newline	= $newline;	

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set CRLF

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function set_crlf($crlf = "\n")

-	{

-		if ($crlf != "\n" AND $crlf != "\r\n" AND $crlf != "\r")

-		{

-			$this->crlf	= "\n";	

-			return;

-		}

-	

-		$this->crlf	= $crlf;	

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Message Boundary

-	 *

-	 * @access	private

-	 * @return	void

-	 */	

-	function _set_boundaries()

-	{

-		$this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative

-		$this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Get the Message ID

-	 *

-	 * @access	private

-	 * @return	string

-	 */	

-	function _get_message_id()

-	{

-		$from = $this->_headers['Return-Path'];

-		$from = str_replace(">", "", $from);

-		$from = str_replace("<", "", $from);

-	

-		return  "<".uniqid('').strstr($from, '@').">";	

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Get Mail Protocol

-	 *

-	 * @access	private

-	 * @param	bool

-	 * @return	string

-	 */	

-	function _get_protocol($return = TRUE)

-	{

-		$this->protocol = strtolower($this->protocol);

-		$this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol;

-

-		if ($return == TRUE)

-		{

-			return $this->protocol;

-		}

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Get Mail Encoding

-	 *

-	 * @access	private

-	 * @param	bool

-	 * @return	string

-	 */	

-	function _get_encoding($return = TRUE)

-	{

-		$this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding;

-

-		foreach ($this->_base_charsets as $charset)

-		{

-			if (strncmp($charset, $this->charset, strlen($charset)) == 0)

-			{

-				$this->_encoding = '7bit';

-			}

-		}

-	

-		if ($return == TRUE)

-		{

-			return $this->_encoding;	

-		}

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Get content type (text/html/attachment)

-	 *

-	 * @access	private

-	 * @return	string

-	 */	

-	function _get_content_type()

-	{	

-		if	($this->mailtype == 'html' &&  count($this->_attach_name) == 0)

-		{

-			return 'html';

-		}

-		elseif	($this->mailtype == 'html' &&  count($this->_attach_name)  > 0)

-		{

-			return 'html-attach';

-		}

-		elseif	($this->mailtype == 'text' &&  count($this->_attach_name)  > 0)

-		{

-			return 'plain-attach';

-		}

-		else

-		{

-			return 'plain';

-		}

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set RFC 822 Date

-	 *

-	 * @access	private

-	 * @return	string

-	 */	

-	function _set_date()

-	{

-		$timezone = date("Z");

-		$operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+';

-		$timezone = abs($timezone);

-		$timezone = floor($timezone/3600) * 100 + ($timezone % 3600 ) / 60;

-

-		return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone);

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Mime message

-	 *

-	 * @access	private

-	 * @return	string

-	 */	

-	function _get_mime_message()

-	{

-		return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Validate Email Address

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	bool

-	 */	

-	function validate_email($email)

-	{	

-		if ( ! is_array($email))

-		{

-			$this->_set_error_message('email_must_be_array');

-			return FALSE;

-		}

-

-		foreach ($email as $val)

-		{

-			if ( ! $this->valid_email($val))

-			{

-				$this->_set_error_message('email_invalid_address', $val);

-				return FALSE;

-			}

-		}

-	}	

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Email Validation

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	bool

-	 */	

-	function valid_email($address)

-	{

-		return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address)) ? FALSE : TRUE;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Clean Extended Email Address: Joe Smith <joe@smith.com>

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	string

-	 */	

-	function clean_email($email)

-	{

-		if ( ! is_array($email))

-		{

-			if (preg_match('/\<(.*)\>/', $email, $match))

-			{

-		   		return $match['1'];

-			}

-		   	else

-			{

-		   		return $email;

-			}

-		}

-	

-		$clean_email = array();

-

-		foreach ($email as $addy)

-		{

-			if (preg_match( '/\<(.*)\>/', $addy, $match))

-			{

-		   		$clean_email[] = $match['1'];

-			}

-		   	else

-			{

-		   		$clean_email[] = $addy;	

-			}

-		}

-

-		return $clean_email;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Build alternative plain text message

-	 *

-	 * This function provides the raw message for use

-	 * in plain-text headers of HTML-formatted emails.

-	 * If the user hasn't specified his own alternative message

-	 * it creates one by stripping the HTML

-	 *

-	 * @access	private

-	 * @return	string

-	 */	

-	function _get_alt_message()

-	{

-		if ($this->alt_message != "")

-		{

-			return $this->word_wrap($this->alt_message, '76');

-		}

-

-		if (preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match))

-		{

-			$body = $match['1'];

-		}

-		else

-		{

-			$body = $this->_body;

-		}

-

-		$body = trim(strip_tags($body));

-		$body = preg_replace( '#<!--(.*)--\>#', "", $body);

-		$body = str_replace("\t", "", $body);

-

-		for ($i = 20; $i >= 3; $i--)

-		{

-			$n = "";

-	

-			for ($x = 1; $x <= $i; $x ++)

-			{

-				 $n .= "\n";

-			}

-

-			$body = str_replace($n, "\n\n", $body);	

-		}

-

-		return $this->word_wrap($body, '76');

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Word Wrap

-	 *

-	 * @access	public

-	 * @param	string

-	 * @param	integer

-	 * @return	string

-	 */	

-	function word_wrap($str, $charlim = '')

-	{

-		// Se the character limit

-		if ($charlim == '')

-		{

-			$charlim = ($this->wrapchars == "") ? "76" : $this->wrapchars;

-		}

-

-		// Reduce multiple spaces

-		$str = preg_replace("| +|", " ", $str);

-

-		// Standardize newlines

-		if (strpos($str, "\r") !== FALSE)

-		{

-			$str = str_replace(array("\r\n", "\r"), "\n", $str);			

-		}

-

-		// If the current word is surrounded by {unwrap} tags we'll 

-		// strip the entire chunk and replace it with a marker.

-		$unwrap = array();

-		if (preg_match_all("|(\{unwrap\}.+?\{/unwrap\})|s", $str, $matches))

-		{

-			for ($i = 0; $i < count($matches['0']); $i++)

-			{

-				$unwrap[] = $matches['1'][$i];

-				$str = str_replace($matches['1'][$i], "{{unwrapped".$i."}}", $str);

-			}

-		}

-

-		// Use PHP's native function to do the initial wordwrap.  

-		// We set the cut flag to FALSE so that any individual words that are 

-		// too long get left alone.  In the next step we'll deal with them.

-		$str = wordwrap($str, $charlim, "\n", FALSE);

-

-		// Split the string into individual lines of text and cycle through them

-		$output = "";

-		foreach (explode("\n", $str) as $line) 

-		{

-			// Is the line within the allowed character count?

-			// If so we'll join it to the output and continue

-			if (strlen($line) <= $charlim)

-			{

-				$output .= $line.$this->newline;	

-				continue;

-			}

-

-			$temp = '';

-			while((strlen($line)) > $charlim) 

-			{

-				// If the over-length word is a URL we won't wrap it

-				if (preg_match("!\[url.+\]|://|wwww.!", $line))

-				{

-					break;

-				}

-

-				// Trim the word down

-				$temp .= substr($line, 0, $charlim-1);

-				$line = substr($line, $charlim-1);

-			}

-	

-			// If $temp contains data it means we had to split up an over-length 

-			// word into smaller chunks so we'll add it back to our current line

-			if ($temp != '')

-			{

-				$output .= $temp.$this->newline.$line;

-			}

-			else

-			{

-				$output .= $line;

-			}

-

-			$output .= $this->newline;

-		}

-

-		// Put our markers back

-		if (count($unwrap) > 0)

-		{	

-			foreach ($unwrap as $key => $val)

-			{

-				$output = str_replace("{{unwrapped".$key."}}", $val, $output);

-			}

-		}

-

-		return $output;	

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Build final headers

-	 *

-	 * @access	private

-	 * @param	string

-	 * @return	string

-	 */	

-	function _build_headers()

-	{

-		$this->_set_header('X-Sender', $this->clean_email($this->_headers['From']));

-		$this->_set_header('X-Mailer', $this->useragent);

-		$this->_set_header('X-Priority', $this->_priorities[$this->priority - 1]);

-		$this->_set_header('Message-ID', $this->_get_message_id());

-		$this->_set_header('Mime-Version', '1.0');

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Write Headers as a string

-	 *

-	 * @access	private

-	 * @return	void

-	 */

-	function _write_headers()

-	{

-		if ($this->protocol == 'mail')

-		{

-			$this->_subject = $this->_headers['Subject'];

-			unset($this->_headers['Subject']);

-		}	

-

-		reset($this->_headers);

-		$this->_header_str = "";

-

-		foreach($this->_headers as $key => $val)

-		{

-			$val = trim($val);

-

-			if ($val != "")

-			{

-				$this->_header_str .= $key.": ".$val.$this->newline;

-			}

-		}

-

-		if ($this->_get_protocol() == 'mail')

-		{

-			$this->_header_str = substr($this->_header_str, 0, -1);

-		}

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Build Final Body and attachments

-	 *

-	 * @access	private

-	 * @return	void

-	 */	

-	function _build_message()

-	{

-		if ($this->wordwrap === TRUE  AND  $this->mailtype != 'html')

-		{

-			$this->_body = $this->word_wrap($this->_body);

-		}

-	

-		$this->_set_boundaries();

-		$this->_write_headers();

-

-		$hdr = ($this->_get_protocol() == 'mail') ? $this->newline : '';

-	

-		switch ($this->_get_content_type())

-		{

-			case 'plain' :

-	

-				$hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;

-				$hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding();

-

-				if ($this->_get_protocol() == 'mail')

-				{

-					$this->_header_str .= $hdr;

-					$this->_finalbody = $this->_body;

-	

-					return;

-				}

-

-				$hdr .= $this->newline . $this->newline . $this->_body;

-

-				$this->_finalbody = $hdr;

-				return;

-	

-			break;

-			case 'html' :

-	

-				if ($this->send_multipart === FALSE)

-				{

-					$hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;

-					$hdr .= "Content-Transfer-Encoding: quoted-printable";

-				}

-				else

-				{	

-					$hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline;

-					$hdr .= $this->_get_mime_message() . $this->newline . $this->newline;

-					$hdr .= "--" . $this->_alt_boundary . $this->newline;

-	

-					$hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;

-					$hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;

-					$hdr .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;

-

-					$hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;

-					$hdr .= "Content-Transfer-Encoding: quoted-printable";

-				}

-

-				$this->_body = $this->_prep_quoted_printable($this->_body);

-

-				if ($this->_get_protocol() == 'mail')

-				{

-					$this->_header_str .= $hdr;

-					$this->_finalbody = $this->_body . $this->newline . $this->newline;

-	

-					if ($this->send_multipart !== FALSE)

-					{

-						$this->_finalbody .= "--" . $this->_alt_boundary . "--";

-					}

-	

-					return;

-				}

-

-				$hdr .= $this->newline . $this->newline;

-				$hdr .= $this->_body . $this->newline . $this->newline;

-

-				if ($this->send_multipart !== FALSE)

-				{

-					$hdr .= "--" . $this->_alt_boundary . "--";

-				}

-

-				$this->_finalbody = $hdr;

-				return;

-

-			break;

-			case 'plain-attach' :

-	

-				$hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline;

-				$hdr .= $this->_get_mime_message() . $this->newline . $this->newline;

-				$hdr .= "--" . $this->_atc_boundary . $this->newline;

-	

-				$hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;

-				$hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding();

-

-				if ($this->_get_protocol() == 'mail')

-				{

-					$this->_header_str .= $hdr;

-	

-					$body  = $this->_body . $this->newline . $this->newline;

-				}

-

-				$hdr .= $this->newline . $this->newline;

-				$hdr .= $this->_body . $this->newline . $this->newline;

-

-			break;

-			case 'html-attach' :

-	

-				$hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline;

-				$hdr .= $this->_get_mime_message() . $this->newline . $this->newline;

-				$hdr .= "--" . $this->_atc_boundary . $this->newline;

-	

-				$hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline .$this->newline;

-				$hdr .= "--" . $this->_alt_boundary . $this->newline;

-

-				$hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;

-				$hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;

-				$hdr .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;

-	

-				$hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;

-				$hdr .= "Content-Transfer-Encoding: quoted-printable";

-

-				$this->_body = $this->_prep_quoted_printable($this->_body);

-

-				if ($this->_get_protocol() == 'mail')

-				{

-					$this->_header_str .= $hdr;	

-	

-					$body  = $this->_body . $this->newline . $this->newline;

-					$body .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;

-				}

-

-				$hdr .= $this->newline . $this->newline;

-				$hdr .= $this->_body . $this->newline . $this->newline;

-				$hdr .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;

-

-			break;

-		}

-

-		$attachment = array();

-

-		$z = 0;

-

-		for ($i=0; $i < count($this->_attach_name); $i++)

-		{

-			$filename = $this->_attach_name[$i];

-			$basename = basename($filename);

-			$ctype = $this->_attach_type[$i];

-

-			if ( ! file_exists($filename))

-			{

-				$this->_set_error_message('email_attachment_missing', $filename);

-				return FALSE;

-			}	

-

-			$h  = "--".$this->_atc_boundary.$this->newline;

-			$h .= "Content-type: ".$ctype."; ";

-			$h .= "name=\"".$basename."\"".$this->newline;

-			$h .= "Content-Disposition: ".$this->_attach_disp[$i].";".$this->newline;

-			$h .= "Content-Transfer-Encoding: base64".$this->newline;

-

-			$attachment[$z++] = $h;

-			$file = filesize($filename) +1;

-	

-			if ( ! $fp = fopen($filename, FOPEN_READ))

-			{

-				$this->_set_error_message('email_attachment_unreadable', $filename);

-				return FALSE;

-			}

-	

-			$attachment[$z++] = chunk_split(base64_encode(fread($fp, $file)));

-			fclose($fp);

-		}

-

-		if ($this->_get_protocol() == 'mail')

-		{

-			$this->_finalbody = $body . implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";	

-	

-			return;

-		}

-

-		$this->_finalbody = $hdr.implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";	

-

-		return;	

-	}

-  	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Prep Quoted Printable

-	 *

-	 * Prepares string for Quoted-Printable Content-Transfer-Encoding

-	 * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt

-	 *

-	 * @access	private

-	 * @param	string

-	 * @param	integer

-	 * @return	string

-	 */

-	function _prep_quoted_printable($str, $charlim = '')

-	{

-		// Set the character limit

-		// Don't allow over 76, as that will make servers and MUAs barf

-		// all over quoted-printable data

-		if ($charlim == '' OR $charlim > '76')

-		{

-			$charlim = '76';

-		}

-

-		// Reduce multiple spaces

-		$str = preg_replace("| +|", " ", $str);

-

-		// kill nulls

-		$str = preg_replace('/\x00+/', '', $str);

-		

-		// Standardize newlines

-		if (strpos($str, "\r") !== FALSE)

-		{

-			$str = str_replace(array("\r\n", "\r"), "\n", $str);

-		}

-

-		// We are intentionally wrapping so mail servers will encode characters

-		// properly and MUAs will behave, so {unwrap} must go!

-		$str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);

-

-		// Break into an array of lines

-		$lines = explode("\n", $str);

-

-		$escape = '=';

-		$output = '';

-

-		foreach ($lines as $line)

-		{

-			$length = strlen($line);

-			$temp = '';

-

-			// Loop through each character in the line to add soft-wrap

-			// characters at the end of a line " =\r\n" and add the newly

-			// processed line(s) to the output (see comment on $crlf class property)

-			for ($i = 0; $i < $length; $i++)

-			{

-				// Grab the next character

-				$char = substr($line, $i, 1);

-				$ascii = ord($char);

-

-				// Convert spaces and tabs but only if it's the end of the line

-				if ($i == ($length - 1))

-				{

-					$char = ($ascii == '32' OR $ascii == '9') ? $escape.sprintf('%02s', dechex($ascii)) : $char;

-				}

-

-				// encode = signs

-				if ($ascii == '61')

-				{

-					$char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));  // =3D

-				}

-

-				// If we're at the character limit, add the line to the output,

-				// reset our temp variable, and keep on chuggin'

-				if ((strlen($temp) + strlen($char)) >= $charlim)

-				{

-					$output .= $temp.$escape.$this->crlf;

-					$temp = '';

-				}

-

-				// Add the character to our temporary line

-				$temp .= $char;

-			}

-

-			// Add our completed line to the output

-			$output .= $temp.$this->crlf;

-		}

-

-		// get rid of extra CRLF tacked onto the end

-		$output = substr($output, 0, strlen($this->crlf) * -1);

-

-		return $output;

-	}

-

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Send Email

-	 *

-	 * @access	public

-	 * @return	bool

-	 */	

-	function send()

-	{	

-		if ($this->_replyto_flag == FALSE)

-		{

-			$this->reply_to($this->_headers['From']);

-		}

-	

-		if (( ! isset($this->_recipients) AND ! isset($this->_headers['To']))  AND

-			( ! isset($this->_bcc_array) AND ! isset($this->_headers['Bcc'])) AND

-			( ! isset($this->_headers['Cc'])))

-		{

-			$this->_set_error_message('email_no_recipients');

-			return FALSE;

-		}

-

-		$this->_build_headers();

-

-		if ($this->bcc_batch_mode  AND  count($this->_bcc_array) > 0)

-		{

-			if (count($this->_bcc_array) > $this->bcc_batch_size)

-				return $this->batch_bcc_send();

-		}

-

-		$this->_build_message();

-

-		if ( ! $this->_spool_email())

-		{

-			return FALSE;

-		}

-		else

-		{

-			return TRUE;

-		}

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Batch Bcc Send.  Sends groups of BCCs in batches

-	 *

-	 * @access	public

-	 * @return	bool

-	 */	

-	function batch_bcc_send()

-	{

-		$float = $this->bcc_batch_size -1;

-

-		$set = "";

-

-		$chunk = array();

-

-		for ($i = 0; $i < count($this->_bcc_array); $i++)

-		{

-			if (isset($this->_bcc_array[$i]))

-			{

-				$set .= ", ".$this->_bcc_array[$i];

-			}

-

-			if ($i == $float)

-			{	

-				$chunk[] = substr($set, 1);

-				$float = $float + $this->bcc_batch_size;

-				$set = "";

-			}

-	

-			if ($i == count($this->_bcc_array)-1)

-			{

-				$chunk[] = substr($set, 1);

-			}

-		}

-

-		for ($i = 0; $i < count($chunk); $i++)

-		{

-			unset($this->_headers['Bcc']);

-			unset($bcc);

-

-			$bcc = $this->_str_to_array($chunk[$i]);

-			$bcc = $this->clean_email($bcc);

-	

-			if ($this->protocol != 'smtp')

-			{

-				$this->_set_header('Bcc', implode(", ", $bcc));

-			}

-			else

-			{

-				$this->_bcc_array = $bcc;

-			}

-	

-			$this->_build_message();

-			$this->_spool_email();

-		}

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Unwrap special elements

-	 *

-	 * @access	private

-	 * @return	void

-	 */	

-	function _unwrap_specials()

-	{

-		$this->_finalbody = preg_replace_callback("/\{unwrap\}(.*?)\{\/unwrap\}/si", array($this, '_remove_nl_callback'), $this->_finalbody);

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Strip line-breaks via callback

-	 *

-	 * @access	private

-	 * @return	string

-	 */	

-	function _remove_nl_callback($matches)

-	{

-		if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE)

-		{

-			$matches[1] = str_replace(array("\r\n", "\r", "\n"), '', $matches[1]);

-		}

-		

-		return $matches[1];

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Spool mail to the mail server

-	 *

-	 * @access	private

-	 * @return	bool

-	 */	

-	function _spool_email()

-	{

-		$this->_unwrap_specials();

-

-		switch ($this->_get_protocol())

-		{

-			case 'mail'	:

-	

-					if ( ! $this->_send_with_mail())

-					{

-						$this->_set_error_message('email_send_failure_phpmail');	

-						return FALSE;

-					}

-			break;

-			case 'sendmail'	:

-		

-					if ( ! $this->_send_with_sendmail())

-					{

-						$this->_set_error_message('email_send_failure_sendmail');	

-						return FALSE;

-					}

-			break;

-			case 'smtp'	:

-		

-					if ( ! $this->_send_with_smtp())

-					{

-						$this->_set_error_message('email_send_failure_smtp');	

-						return FALSE;

-					}

-			break;

-

-		}

-

-		$this->_set_error_message('email_sent', $this->_get_protocol());

-		return TRUE;

-	}	

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Send using mail()

-	 *

-	 * @access	private

-	 * @return	bool

-	 */	

-	function _send_with_mail()

-	{	

-		if ($this->_safe_mode == TRUE)

-		{

-			if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str))

-			{

-				return FALSE;

-			}

-			else

-			{

-				return TRUE;

-			}

-		}

-		else

-		{

-			// most documentation of sendmail using the "-f" flag lacks a space after it, however

-			// we've encountered servers that seem to require it to be in place.

-			if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f ".$this->clean_email($this->_headers['From'])))

-			{

-				return FALSE;

-			}

-			else

-			{

-				return TRUE;

-			}

-		}

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Send using Sendmail

-	 *

-	 * @access	private

-	 * @return	bool

-	 */	

-	function _send_with_sendmail()

-	{

-		$fp = @popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w');

-

-		if ( ! is_resource($fp))

-		{		

-			$this->_set_error_message('email_no_socket');

-			return FALSE;

-		}

-

-		fputs($fp, $this->_header_str);

-		fputs($fp, $this->_finalbody);

-		pclose($fp) >> 8 & 0xFF;

-

-		return TRUE;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Send using SMTP

-	 *

-	 * @access	private

-	 * @return	bool

-	 */	

-	function _send_with_smtp()

-	{	

-		if ($this->smtp_host == '')

-		{	

-			$this->_set_error_message('email_no_hostname');

-			return FALSE;

-		}

-

-		$this->_smtp_connect();

-		$this->_smtp_authenticate();

-

-		$this->_send_command('from', $this->clean_email($this->_headers['From']));

-

-		foreach($this->_recipients as $val)

-		{

-			$this->_send_command('to', $val);

-		}

-	

-		if (count($this->_cc_array) > 0)

-		{

-			foreach($this->_cc_array as $val)

-			{

-				if ($val != "")

-				{

-					$this->_send_command('to', $val);

-				}

-			}

-		}

-

-		if (count($this->_bcc_array) > 0)

-		{

-			foreach($this->_bcc_array as $val)

-			{

-				if ($val != "")

-				{

-					$this->_send_command('to', $val);

-				}

-			}

-		}

-

-		$this->_send_command('data');

-

-		// perform dot transformation on any lines that begin with a dot

-		$this->_send_data($this->_header_str . preg_replace('/^\./m', '..$1', $this->_finalbody));

-

-		$this->_send_data('.');

-

-		$reply = $this->_get_smtp_data();

-

-		$this->_set_error_message($reply);	

-

-		if (strncmp($reply, '250', 3) != 0)

-		{

-			$this->_set_error_message('email_smtp_error', $reply);	

-			return FALSE;

-		}

-

-		$this->_send_command('quit');

-		return TRUE;

-	}	

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * SMTP Connect

-	 *

-	 * @access	private

-	 * @param	string

-	 * @return	string

-	 */	

-	function _smtp_connect()

-	{

-		$this->_smtp_connect = fsockopen($this->smtp_host,

-										$this->smtp_port,

-										$errno,

-										$errstr,

-										$this->smtp_timeout);

-

-		if( ! is_resource($this->_smtp_connect))

-		{		

-			$this->_set_error_message('email_smtp_error', $errno." ".$errstr);

-			return FALSE;

-		}

-

-		$this->_set_error_message($this->_get_smtp_data());

-		return $this->_send_command('hello');

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Send SMTP command

-	 *

-	 * @access	private

-	 * @param	string

-	 * @param	string

-	 * @return	string

-	 */	

-	function _send_command($cmd, $data = '')

-	{

-		switch ($cmd)

-		{

-			case 'hello' :

-

-					if ($this->_smtp_auth OR $this->_get_encoding() == '8bit')

-						$this->_send_data('EHLO '.$this->_get_hostname());

-					else

-						$this->_send_data('HELO '.$this->_get_hostname());

-

-						$resp = 250;

-			break;

-			case 'from' :

-	

-						$this->_send_data('MAIL FROM:<'.$data.'>');

-

-						$resp = 250;

-			break;

-			case 'to'	:

-	

-						$this->_send_data('RCPT TO:<'.$data.'>');

-

-						$resp = 250;	

-			break;

-			case 'data'	:

-	

-						$this->_send_data('DATA');

-

-						$resp = 354;	

-			break;

-			case 'quit'	:

-

-						$this->_send_data('QUIT');

-

-						$resp = 221;

-			break;

-		}

-

-		$reply = $this->_get_smtp_data();	

-

-		$this->_debug_msg[] = "<pre>".$cmd.": ".$reply."</pre>";

-

-		if (substr($reply, 0, 3) != $resp)

-		{

-			$this->_set_error_message('email_smtp_error', $reply);

-			return FALSE;

-		}

-	

-		if ($cmd == 'quit')

-		{

-			fclose($this->_smtp_connect);

-		}

-	

-		return TRUE;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 *  SMTP Authenticate

-	 *

-	 * @access	private

-	 * @return	bool

-	 */	

-	function _smtp_authenticate()

-	{	

-		if ( ! $this->_smtp_auth)

-		{

-			return TRUE;

-		}

-	

-		if ($this->smtp_user == ""  AND  $this->smtp_pass == "")

-		{

-			$this->_set_error_message('email_no_smtp_unpw');

-			return FALSE;

-		}

-

-		$this->_send_data('AUTH LOGIN');

-

-		$reply = $this->_get_smtp_data();	

-

-		if (strncmp($reply, '334', 3) != 0)

-		{

-			$this->_set_error_message('email_failed_smtp_login', $reply);	

-			return FALSE;

-		}

-

-		$this->_send_data(base64_encode($this->smtp_user));

-

-		$reply = $this->_get_smtp_data();	

-

-		if (strncmp($reply, '334', 3) != 0)

-		{

-			$this->_set_error_message('email_smtp_auth_un', $reply);	

-			return FALSE;

-		}

-

-		$this->_send_data(base64_encode($this->smtp_pass));

-

-		$reply = $this->_get_smtp_data();	

-

-		if (strncmp($reply, '235', 3) != 0)

-		{

-			$this->_set_error_message('email_smtp_auth_pw', $reply);	

-			return FALSE;

-		}

-	

-		return TRUE;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Send SMTP data

-	 *

-	 * @access	private

-	 * @return	bool

-	 */	

-	function _send_data($data)

-	{

-		if ( ! fwrite($this->_smtp_connect, $data . $this->newline))

-		{

-			$this->_set_error_message('email_smtp_data_failure', $data);	

-			return FALSE;

-		}

-		else

-		{

-			return TRUE;

-		}

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Get SMTP data

-	 *

-	 * @access	private

-	 * @return	string

-	 */	

-	function _get_smtp_data()

-	{

-		$data = "";

-

-		while ($str = fgets($this->_smtp_connect, 512))

-		{

-			$data .= $str;

-	

-			if (substr($str, 3, 1) == " ")

-			{

-				break;

-			}

-		}

-

-		return $data;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Get Hostname

-	 *

-	 * @access	private

-	 * @return	string

-	 */

-	function _get_hostname()

-	{	

-		return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';	

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Get IP

-	 *

-	 * @access	private

-	 * @return	string

-	 */

-	function _get_ip()

-	{

-		if ($this->_IP !== FALSE)

-		{

-			return $this->_IP;

-		}

-	

-		$cip = (isset($_SERVER['HTTP_CLIENT_IP']) AND $_SERVER['HTTP_CLIENT_IP'] != "") ? $_SERVER['HTTP_CLIENT_IP'] : FALSE;

-		$rip = (isset($_SERVER['REMOTE_ADDR']) AND $_SERVER['REMOTE_ADDR'] != "") ? $_SERVER['REMOTE_ADDR'] : FALSE;

-		$fip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND $_SERVER['HTTP_X_FORWARDED_FOR'] != "") ? $_SERVER['HTTP_X_FORWARDED_FOR'] : FALSE;

-	

-		if ($cip && $rip) 	$this->_IP = $cip;	

-		elseif ($rip)		$this->_IP = $rip;

-		elseif ($cip)		$this->_IP = $cip;

-		elseif ($fip)		$this->_IP = $fip;

-

-		if (strstr($this->_IP, ','))

-		{

-			$x = explode(',', $this->_IP);

-			$this->_IP = end($x);

-		}

-

-		if ( ! preg_match( "/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $this->_IP))

-		{

-			$this->_IP = '0.0.0.0';

-		}

-

-		unset($cip);

-		unset($rip);

-		unset($fip);

-

-		return $this->_IP;

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Get Debug Message

-	 *

-	 * @access	public

-	 * @return	string

-	 */	

-	function print_debugger()

-	{

-		$msg = '';

-

-		if (count($this->_debug_msg) > 0)

-		{

-			foreach ($this->_debug_msg as $val)

-			{

-				$msg .= $val;

-			}

-		}

-

-		$msg .= "<pre>".$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>';	

-		return $msg;

-	}	

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set Message

-	 *

-	 * @access	private

-	 * @param	string

-	 * @return	string

-	 */	

-	function _set_error_message($msg, $val = '')

-	{

-		$CI =& get_instance();

-		$CI->lang->load('email');

-	

-		if (FALSE === ($line = $CI->lang->line($msg)))

-		{	

-			$this->_debug_msg[] = str_replace('%s', $val, $msg)."<br />";

-		}	

-		else

-		{

-			$this->_debug_msg[] = str_replace('%s', $val, $line)."<br />";

-		}	

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Mime Types

-	 *

-	 * @access	private

-	 * @param	string

-	 * @return	string

-	 */

-	function _mime_types($ext = "")

-	{

-		$mimes = array(	'hqx'	=>	'application/mac-binhex40',

-						'cpt'	=>	'application/mac-compactpro',

-						'doc'	=>	'application/msword',

-						'bin'	=>	'application/macbinary',

-						'dms'	=>	'application/octet-stream',

-						'lha'	=>	'application/octet-stream',

-						'lzh'	=>	'application/octet-stream',

-						'exe'	=>	'application/octet-stream',

-						'class'	=>	'application/octet-stream',

-						'psd'	=>	'application/octet-stream',

-						'so'	=>	'application/octet-stream',

-						'sea'	=>	'application/octet-stream',

-						'dll'	=>	'application/octet-stream',

-						'oda'	=>	'application/oda',

-						'pdf'	=>	'application/pdf',

-						'ai'	=>	'application/postscript',

-						'eps'	=>	'application/postscript',

-						'ps'	=>	'application/postscript',

-						'smi'	=>	'application/smil',

-						'smil'	=>	'application/smil',

-						'mif'	=>	'application/vnd.mif',

-						'xls'	=>	'application/vnd.ms-excel',

-						'ppt'	=>	'application/vnd.ms-powerpoint',

-						'wbxml'	=>	'application/vnd.wap.wbxml',

-						'wmlc'	=>	'application/vnd.wap.wmlc',

-						'dcr'	=>	'application/x-director',

-						'dir'	=>	'application/x-director',

-						'dxr'	=>	'application/x-director',

-						'dvi'	=>	'application/x-dvi',

-						'gtar'	=>	'application/x-gtar',

-						'php'	=>	'application/x-httpd-php',

-						'php4'	=>	'application/x-httpd-php',

-						'php3'	=>	'application/x-httpd-php',

-						'phtml'	=>	'application/x-httpd-php',

-						'phps'	=>	'application/x-httpd-php-source',

-						'js'	=>	'application/x-javascript',

-						'swf'	=>	'application/x-shockwave-flash',

-						'sit'	=>	'application/x-stuffit',

-						'tar'	=>	'application/x-tar',

-						'tgz'	=>	'application/x-tar',

-						'xhtml'	=>	'application/xhtml+xml',

-						'xht'	=>	'application/xhtml+xml',

-						'zip'	=>	'application/zip',

-						'mid'	=>	'audio/midi',

-						'midi'	=>	'audio/midi',

-						'mpga'	=>	'audio/mpeg',

-						'mp2'	=>	'audio/mpeg',

-						'mp3'	=>	'audio/mpeg',

-						'aif'	=>	'audio/x-aiff',

-						'aiff'	=>	'audio/x-aiff',

-						'aifc'	=>	'audio/x-aiff',

-						'ram'	=>	'audio/x-pn-realaudio',

-						'rm'	=>	'audio/x-pn-realaudio',

-						'rpm'	=>	'audio/x-pn-realaudio-plugin',

-						'ra'	=>	'audio/x-realaudio',

-						'rv'	=>	'video/vnd.rn-realvideo',

-						'wav'	=>	'audio/x-wav',

-						'bmp'	=>	'image/bmp',

-						'gif'	=>	'image/gif',

-						'jpeg'	=>	'image/jpeg',

-						'jpg'	=>	'image/jpeg',

-						'jpe'	=>	'image/jpeg',

-						'png'	=>	'image/png',

-						'tiff'	=>	'image/tiff',

-						'tif'	=>	'image/tiff',

-						'css'	=>	'text/css',

-						'html'	=>	'text/html',

-						'htm'	=>	'text/html',

-						'shtml'	=>	'text/html',

-						'txt'	=>	'text/plain',

-						'text'	=>	'text/plain',

-						'log'	=>	'text/plain',

-						'rtx'	=>	'text/richtext',

-						'rtf'	=>	'text/rtf',

-						'xml'	=>	'text/xml',

-						'xsl'	=>	'text/xml',

-						'mpeg'	=>	'video/mpeg',

-						'mpg'	=>	'video/mpeg',

-						'mpe'	=>	'video/mpeg',

-						'qt'	=>	'video/quicktime',

-						'mov'	=>	'video/quicktime',

-						'avi'	=>	'video/x-msvideo',

-						'movie'	=>	'video/x-sgi-movie',

-						'doc'	=>	'application/msword',

-						'word'	=>	'application/msword',

-						'xl'	=>	'application/excel',

-						'eml'	=>	'message/rfc822'

-					);

-

-		return ( ! isset($mimes[strtolower($ext)])) ? "application/x-unknown-content-type" : $mimes[strtolower($ext)];

-	}

-

-}

-// END CI_Email class

-

-/* End of file Email.php */

+<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2008, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CodeIgniter Email Class
+ *
+ * Permits email to be sent using Mail, Sendmail, or SMTP.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Libraries
+ * @author		ExpressionEngine Dev Team
+ * @link		http://codeigniter.com/user_guide/libraries/email.html
+ */
+class CI_Email {
+
+	var	$useragent		= "CodeIgniter";
+	var	$mailpath		= "/usr/sbin/sendmail";	// Sendmail path
+	var	$protocol		= "mail";	// mail/sendmail/smtp
+	var	$smtp_host		= "";		// SMTP Server.  Example: mail.earthlink.net
+	var	$smtp_user		= "";		// SMTP Username
+	var	$smtp_pass		= "";		// SMTP Password
+	var	$smtp_port		= "25";		// SMTP Port
+	var	$smtp_timeout	= 5;		// SMTP Timeout in seconds
+	var	$wordwrap		= TRUE;		// TRUE/FALSE  Turns word-wrap on/off
+	var	$wrapchars		= "76";		// Number of characters to wrap at.
+	var	$mailtype		= "text";	// text/html  Defines email formatting
+	var	$charset		= "utf-8";	// Default char set: iso-8859-1 or us-ascii
+	var	$multipart		= "mixed";	// "mixed" (in the body) or "related" (separate)
+	var $alt_message	= '';		// Alternative message for HTML emails
+	var	$validate		= FALSE;	// TRUE/FALSE.  Enables email validation
+	var	$priority		= "3";		// Default priority (1 - 5)
+	var	$newline		= "\n";		// Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
+	var $crlf			= "\n";		// The RFC 2045 compliant CRLF for quoted-printable is "\r\n".  Apparently some servers,
+									// even on the receiving end think they need to muck with CRLFs, so using "\n", while
+									// distasteful, is the only thing that seems to work for all environments.
+	var $send_multipart	= TRUE;		// TRUE/FALSE - Yahoo does not like multipart alternative, so this is an override.  Set to FALSE for Yahoo.
+	var	$bcc_batch_mode	= FALSE;	// TRUE/FALSE  Turns on/off Bcc batch feature
+	var	$bcc_batch_size	= 200;		// If bcc_batch_mode = TRUE, sets max number of Bccs in each batch
+	var $_safe_mode		= FALSE;
+	var	$_subject		= "";
+	var	$_body			= "";
+	var	$_finalbody		= "";
+	var	$_alt_boundary	= "";
+	var	$_atc_boundary	= "";
+	var	$_header_str	= "";
+	var	$_smtp_connect	= "";
+	var	$_encoding		= "8bit";
+	var $_IP			= FALSE;
+	var	$_smtp_auth		= FALSE;
+	var $_replyto_flag	= FALSE;
+	var	$_debug_msg		= array();
+	var	$_recipients	= array();
+	var	$_cc_array		= array();
+	var	$_bcc_array		= array();
+	var	$_headers		= array();
+	var	$_attach_name	= array();
+	var	$_attach_type	= array();
+	var	$_attach_disp	= array();
+	var	$_protocols		= array('mail', 'sendmail', 'smtp');
+	var	$_base_charsets	= array('us-ascii', 'iso-2022-');	// 7-bit charsets (excluding language suffix)
+	var	$_bit_depths	= array('7bit', '8bit');
+	var	$_priorities	= array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
+
+
+	/**
+	 * Constructor - Sets Email Preferences
+	 *
+	 * The constructor can be passed an array of config values
+	 */
+	function CI_Email($config = array())
+	{
+		if (count($config) > 0)
+		{
+			$this->initialize($config);
+		}
+		else
+		{
+			$this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
+			$this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
+		}
+
+		log_message('debug', "Email Class Initialized");
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initialize preferences
+	 *
+	 * @access	public
+	 * @param	array
+	 * @return	void
+	 */
+	function initialize($config = array())
+	{
+		$this->clear();
+		foreach ($config as $key => $val)
+		{
+			if (isset($this->$key))
+			{
+				$method = 'set_'.$key;
+
+				if (method_exists($this, $method))
+				{
+					$this->$method($val);
+				}
+				else
+				{
+					$this->$key = $val;
+				}
+			}
+		}
+
+		$this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
+		$this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initialize the Email Data
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function clear($clear_attachments = FALSE)
+	{
+		$this->_subject		= "";
+		$this->_body		= "";
+		$this->_finalbody	= "";
+		$this->_header_str	= "";
+		$this->_replyto_flag = FALSE;
+		$this->_recipients	= array();
+		$this->_headers		= array();
+		$this->_debug_msg	= array();
+
+		$this->_set_header('User-Agent', $this->useragent);
+		$this->_set_header('Date', $this->_set_date());
+
+		if ($clear_attachments !== FALSE)
+		{
+			$this->_attach_name = array();
+			$this->_attach_type = array();
+			$this->_attach_disp = array();
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set FROM
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	string
+	 * @return	void
+	 */
+	function from($from, $name = '')
+	{
+		if (preg_match( '/\<(.*)\>/', $from, $match))
+		{
+			$from = $match['1'];
+		}
+
+		if ($this->validate)
+		{
+			$this->validate_email($this->_str_to_array($from));
+		}
+
+		if ($name != '' && strncmp($name, '"', 1) != 0)
+		{
+			$name = '"'.$name.'"';
+		}
+
+		$this->_set_header('From', $name.' <'.$from.'>');
+		$this->_set_header('Return-Path', '<'.$from.'>');
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Reply-to
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	string
+	 * @return	void
+	 */
+	function reply_to($replyto, $name = '')
+	{
+		if (preg_match( '/\<(.*)\>/', $replyto, $match))
+		{
+			$replyto = $match['1'];
+		}
+
+		if ($this->validate)
+		{
+			$this->validate_email($this->_str_to_array($replyto));
+		}
+
+		if ($name == '')
+		{
+			$name = $replyto;
+		}
+
+		if (strncmp($name, '"', 1) != 0)
+		{
+			$name = '"'.$name.'"';
+		}
+
+		$this->_set_header('Reply-To', $name.' <'.$replyto.'>');
+		$this->_replyto_flag = TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Recipients
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function to($to)
+	{
+		$to = $this->_str_to_array($to);
+		$to = $this->clean_email($to);
+
+		if ($this->validate)
+		{
+			$this->validate_email($to);
+		}
+
+		if ($this->_get_protocol() != 'mail')
+		{
+			$this->_set_header('To', implode(", ", $to));
+		}
+
+		switch ($this->_get_protocol())
+		{
+			case 'smtp'		: $this->_recipients = $to;
+			break;
+			case 'sendmail'	: $this->_recipients = implode(", ", $to);
+			break;
+			case 'mail'		: $this->_recipients = implode(", ", $to);
+			break;
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set CC
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function cc($cc)
+	{
+		$cc = $this->_str_to_array($cc);
+		$cc = $this->clean_email($cc);
+
+		if ($this->validate)
+		{
+			$this->validate_email($cc);
+		}
+
+		$this->_set_header('Cc', implode(", ", $cc));
+
+		if ($this->_get_protocol() == "smtp")
+		{
+			$this->_cc_array = $cc;
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set BCC
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	string
+	 * @return	void
+	 */
+	function bcc($bcc, $limit = '')
+	{
+		if ($limit != '' && is_numeric($limit))
+		{
+			$this->bcc_batch_mode = TRUE;
+			$this->bcc_batch_size = $limit;
+		}
+
+		$bcc = $this->_str_to_array($bcc);
+		$bcc = $this->clean_email($bcc);
+
+		if ($this->validate)
+		{
+			$this->validate_email($bcc);
+		}
+
+		if (($this->_get_protocol() == "smtp") OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))
+		{
+			$this->_bcc_array = $bcc;
+		}
+		else
+		{
+			$this->_set_header('Bcc', implode(", ", $bcc));
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Email Subject
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function subject($subject)
+	{
+		if (strpos($subject, "\r") !== FALSE OR strpos($subject, "\n") !== FALSE)
+		{
+			$subject = str_replace(array("\r\n", "\r", "\n"), '', $subject);
+		}
+
+		if (strpos($subject, "\t"))
+		{
+			$subject = str_replace("\t", ' ', $subject);
+		}
+
+		$this->_set_header('Subject', trim($subject));
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Body
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function message($body)
+	{
+		$this->_body = stripslashes(rtrim(str_replace("\r", "", $body)));
+	}
+ 
+	// --------------------------------------------------------------------
+
+	/**
+	 * Assign file attachments
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function attach($filename, $disposition = 'attachment')
+	{
+		$this->_attach_name[] = $filename;
+		$this->_attach_type[] = $this->_mime_types(next(explode('.', basename($filename))));
+		$this->_attach_disp[] = $disposition; // Can also be 'inline'  Not sure if it matters
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Add a Header Item
+	 *
+	 * @access	private
+	 * @param	string
+	 * @param	string
+	 * @return	void
+	 */
+	function _set_header($header, $value)
+	{
+		$this->_headers[$header] = $value;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Convert a String to an Array
+	 *
+	 * @access	private
+	 * @param	string
+	 * @return	array
+	 */
+	function _str_to_array($email)
+	{
+		if ( ! is_array($email))
+		{
+			if (strpos($email, ',') !== FALSE)
+			{
+				$email = preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY);
+			}
+			else
+			{
+				$email = trim($email);
+				settype($email, "array");
+			}
+		}
+		return $email;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Multipart Value
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function set_alt_message($str = '')
+	{
+		$this->alt_message = ($str == '') ? '' : $str;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Mailtype
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function set_mailtype($type = 'text')
+	{
+		$this->mailtype = ($type == 'html') ? 'html' : 'text';
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Wordwrap
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function set_wordwrap($wordwrap = TRUE)
+	{
+		$this->wordwrap = ($wordwrap === FALSE) ? FALSE : TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Protocol
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function set_protocol($protocol = 'mail')
+	{
+		$this->protocol = ( ! in_array($protocol, $this->_protocols, TRUE)) ? 'mail' : strtolower($protocol);
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Priority
+	 *
+	 * @access	public
+	 * @param	integer
+	 * @return	void
+	 */
+	function set_priority($n = 3)
+	{
+		if ( ! is_numeric($n))
+		{
+			$this->priority = 3;
+			return;
+		}
+
+		if ($n < 1 OR $n > 5)
+		{
+			$this->priority = 3;
+			return;
+		}
+
+		$this->priority = $n;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Newline Character
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function set_newline($newline = "\n")
+	{
+		if ($newline != "\n" AND $newline != "\r\n" AND $newline != "\r")
+		{
+			$this->newline	= "\n";
+			return;
+		}
+
+		$this->newline	= $newline;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set CRLF
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function set_crlf($crlf = "\n")
+	{
+		if ($crlf != "\n" AND $crlf != "\r\n" AND $crlf != "\r")
+		{
+			$this->crlf	= "\n";
+			return;
+		}
+
+		$this->crlf	= $crlf;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Message Boundary
+	 *
+	 * @access	private
+	 * @return	void
+	 */
+	function _set_boundaries()
+	{
+		$this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative
+		$this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get the Message ID
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _get_message_id()
+	{
+		$from = $this->_headers['Return-Path'];
+		$from = str_replace(">", "", $from);
+		$from = str_replace("<", "", $from);
+
+		return  "<".uniqid('').strstr($from, '@').">";
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Mail Protocol
+	 *
+	 * @access	private
+	 * @param	bool
+	 * @return	string
+	 */
+	function _get_protocol($return = TRUE)
+	{
+		$this->protocol = strtolower($this->protocol);
+		$this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol;
+
+		if ($return == TRUE)
+		{
+			return $this->protocol;
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Mail Encoding
+	 *
+	 * @access	private
+	 * @param	bool
+	 * @return	string
+	 */
+	function _get_encoding($return = TRUE)
+	{
+		$this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding;
+
+		foreach ($this->_base_charsets as $charset)
+		{
+			if (strncmp($charset, $this->charset, strlen($charset)) == 0)
+			{
+				$this->_encoding = '7bit';
+			}
+		}
+
+		if ($return == TRUE)
+		{
+			return $this->_encoding;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get content type (text/html/attachment)
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _get_content_type()
+	{
+		if	($this->mailtype == 'html' &&  count($this->_attach_name) == 0)
+		{
+			return 'html';
+		}
+		elseif	($this->mailtype == 'html' &&  count($this->_attach_name)  > 0)
+		{
+			return 'html-attach';
+		}
+		elseif	($this->mailtype == 'text' &&  count($this->_attach_name)  > 0)
+		{
+			return 'plain-attach';
+		}
+		else
+		{
+			return 'plain';
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set RFC 822 Date
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _set_date()
+	{
+		$timezone = date("Z");
+		$operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+';
+		$timezone = abs($timezone);
+		$timezone = floor($timezone/3600) * 100 + ($timezone % 3600 ) / 60;
+
+		return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone);
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Mime message
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _get_mime_message()
+	{
+		return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Validate Email Address
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	bool
+	 */
+	function validate_email($email)
+	{
+		if ( ! is_array($email))
+		{
+			$this->_set_error_message('email_must_be_array');
+			return FALSE;
+		}
+
+		foreach ($email as $val)
+		{
+			if ( ! $this->valid_email($val))
+			{
+				$this->_set_error_message('email_invalid_address', $val);
+				return FALSE;
+			}
+		}
+
+		return TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Email Validation
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	bool
+	 */
+	function valid_email($address)
+	{
+		return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address)) ? FALSE : TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clean Extended Email Address: Joe Smith <joe@smith.com>
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	string
+	 */
+	function clean_email($email)
+	{
+		if ( ! is_array($email))
+		{
+			if (preg_match('/\<(.*)\>/', $email, $match))
+			{
+		   		return $match['1'];
+			}
+		   	else
+			{
+		   		return $email;
+			}
+		}
+
+		$clean_email = array();
+
+		foreach ($email as $addy)
+		{
+			if (preg_match( '/\<(.*)\>/', $addy, $match))
+			{
+		   		$clean_email[] = $match['1'];
+			}
+		   	else
+			{
+		   		$clean_email[] = $addy;
+			}
+		}
+
+		return $clean_email;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Build alternative plain text message
+	 *
+	 * This function provides the raw message for use
+	 * in plain-text headers of HTML-formatted emails.
+	 * If the user hasn't specified his own alternative message
+	 * it creates one by stripping the HTML
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _get_alt_message()
+	{
+		if ($this->alt_message != "")
+		{
+			return $this->word_wrap($this->alt_message, '76');
+		}
+
+		if (preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match))
+		{
+			$body = $match['1'];
+		}
+		else
+		{
+			$body = $this->_body;
+		}
+
+		$body = trim(strip_tags($body));
+		$body = preg_replace( '#<!--(.*)--\>#', "", $body);
+		$body = str_replace("\t", "", $body);
+
+		for ($i = 20; $i >= 3; $i--)
+		{
+			$n = "";
+
+			for ($x = 1; $x <= $i; $x ++)
+			{
+				 $n .= "\n";
+			}
+
+			$body = str_replace($n, "\n\n", $body);
+		}
+
+		return $this->word_wrap($body, '76');
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Word Wrap
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	integer
+	 * @return	string
+	 */
+	function word_wrap($str, $charlim = '')
+	{
+		// Se the character limit
+		if ($charlim == '')
+		{
+			$charlim = ($this->wrapchars == "") ? "76" : $this->wrapchars;
+		}
+
+		// Reduce multiple spaces
+		$str = preg_replace("| +|", " ", $str);
+
+		// Standardize newlines
+		if (strpos($str, "\r") !== FALSE)
+		{
+			$str = str_replace(array("\r\n", "\r"), "\n", $str);
+		}
+
+		// If the current word is surrounded by {unwrap} tags we'll
+		// strip the entire chunk and replace it with a marker.
+		$unwrap = array();
+		if (preg_match_all("|(\{unwrap\}.+?\{/unwrap\})|s", $str, $matches))
+		{
+			for ($i = 0; $i < count($matches['0']); $i++)
+			{
+				$unwrap[] = $matches['1'][$i];
+				$str = str_replace($matches['1'][$i], "{{unwrapped".$i."}}", $str);
+			}
+		}
+
+		// Use PHP's native function to do the initial wordwrap.
+		// We set the cut flag to FALSE so that any individual words that are
+		// too long get left alone.  In the next step we'll deal with them.
+		$str = wordwrap($str, $charlim, "\n", FALSE);
+
+		// Split the string into individual lines of text and cycle through them
+		$output = "";
+		foreach (explode("\n", $str) as $line)
+		{
+			// Is the line within the allowed character count?
+			// If so we'll join it to the output and continue
+			if (strlen($line) <= $charlim)
+			{
+				$output .= $line.$this->newline;
+				continue;
+			}
+
+			$temp = '';
+			while((strlen($line)) > $charlim)
+			{
+				// If the over-length word is a URL we won't wrap it
+				if (preg_match("!\[url.+\]|://|wwww.!", $line))
+				{
+					break;
+				}
+
+				// Trim the word down
+				$temp .= substr($line, 0, $charlim-1);
+				$line = substr($line, $charlim-1);
+			}
+
+			// If $temp contains data it means we had to split up an over-length
+			// word into smaller chunks so we'll add it back to our current line
+			if ($temp != '')
+			{
+				$output .= $temp.$this->newline.$line;
+			}
+			else
+			{
+				$output .= $line;
+			}
+
+			$output .= $this->newline;
+		}
+
+		// Put our markers back
+		if (count($unwrap) > 0)
+		{
+			foreach ($unwrap as $key => $val)
+			{
+				$output = str_replace("{{unwrapped".$key."}}", $val, $output);
+			}
+		}
+
+		return $output;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Build final headers
+	 *
+	 * @access	private
+	 * @param	string
+	 * @return	string
+	 */
+	function _build_headers()
+	{
+		$this->_set_header('X-Sender', $this->clean_email($this->_headers['From']));
+		$this->_set_header('X-Mailer', $this->useragent);
+		$this->_set_header('X-Priority', $this->_priorities[$this->priority - 1]);
+		$this->_set_header('Message-ID', $this->_get_message_id());
+		$this->_set_header('Mime-Version', '1.0');
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Write Headers as a string
+	 *
+	 * @access	private
+	 * @return	void
+	 */
+	function _write_headers()
+	{
+		if ($this->protocol == 'mail')
+		{
+			$this->_subject = $this->_headers['Subject'];
+			unset($this->_headers['Subject']);
+		}
+
+		reset($this->_headers);
+		$this->_header_str = "";
+
+		foreach($this->_headers as $key => $val)
+		{
+			$val = trim($val);
+
+			if ($val != "")
+			{
+				$this->_header_str .= $key.": ".$val.$this->newline;
+			}
+		}
+
+		if ($this->_get_protocol() == 'mail')
+		{
+			$this->_header_str = substr($this->_header_str, 0, -1);
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Build Final Body and attachments
+	 *
+	 * @access	private
+	 * @return	void
+	 */
+	function _build_message()
+	{
+		if ($this->wordwrap === TRUE  AND  $this->mailtype != 'html')
+		{
+			$this->_body = $this->word_wrap($this->_body);
+		}
+
+		$this->_set_boundaries();
+		$this->_write_headers();
+
+		$hdr = ($this->_get_protocol() == 'mail') ? $this->newline : '';
+
+		switch ($this->_get_content_type())
+		{
+			case 'plain' :
+
+				$hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
+				$hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding();
+
+				if ($this->_get_protocol() == 'mail')
+				{
+					$this->_header_str .= $hdr;
+					$this->_finalbody = $this->_body;
+
+					return;
+				}
+
+				$hdr .= $this->newline . $this->newline . $this->_body;
+
+				$this->_finalbody = $hdr;
+				return;
+
+			break;
+			case 'html' :
+
+				if ($this->send_multipart === FALSE)
+				{
+					$hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
+					$hdr .= "Content-Transfer-Encoding: quoted-printable";
+				}
+				else
+				{
+					$hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline;
+					$hdr .= $this->_get_mime_message() . $this->newline . $this->newline;
+					$hdr .= "--" . $this->_alt_boundary . $this->newline;
+
+					$hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
+					$hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
+					$hdr .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
+
+					$hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
+					$hdr .= "Content-Transfer-Encoding: quoted-printable";
+				}
+
+				$this->_body = $this->_prep_quoted_printable($this->_body);
+
+				if ($this->_get_protocol() == 'mail')
+				{
+					$this->_header_str .= $hdr;
+					$this->_finalbody = $this->_body . $this->newline . $this->newline;
+
+					if ($this->send_multipart !== FALSE)
+					{
+						$this->_finalbody .= "--" . $this->_alt_boundary . "--";
+					}
+
+					return;
+				}
+
+				$hdr .= $this->newline . $this->newline;
+				$hdr .= $this->_body . $this->newline . $this->newline;
+
+				if ($this->send_multipart !== FALSE)
+				{
+					$hdr .= "--" . $this->_alt_boundary . "--";
+				}
+
+				$this->_finalbody = $hdr;
+				return;
+
+			break;
+			case 'plain-attach' :
+
+				$hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline;
+				$hdr .= $this->_get_mime_message() . $this->newline . $this->newline;
+				$hdr .= "--" . $this->_atc_boundary . $this->newline;
+
+				$hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
+				$hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding();
+
+				if ($this->_get_protocol() == 'mail')
+				{
+					$this->_header_str .= $hdr;
+
+					$body  = $this->_body . $this->newline . $this->newline;
+				}
+
+				$hdr .= $this->newline . $this->newline;
+				$hdr .= $this->_body . $this->newline . $this->newline;
+
+			break;
+			case 'html-attach' :
+
+				$hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline;
+				$hdr .= $this->_get_mime_message() . $this->newline . $this->newline;
+				$hdr .= "--" . $this->_atc_boundary . $this->newline;
+
+				$hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline .$this->newline;
+				$hdr .= "--" . $this->_alt_boundary . $this->newline;
+
+				$hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
+				$hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
+				$hdr .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
+
+				$hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
+				$hdr .= "Content-Transfer-Encoding: quoted-printable";
+
+				$this->_body = $this->_prep_quoted_printable($this->_body);
+
+				if ($this->_get_protocol() == 'mail')
+				{
+					$this->_header_str .= $hdr;
+
+					$body  = $this->_body . $this->newline . $this->newline;
+					$body .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;
+				}
+
+				$hdr .= $this->newline . $this->newline;
+				$hdr .= $this->_body . $this->newline . $this->newline;
+				$hdr .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;
+
+			break;
+		}
+
+		$attachment = array();
+
+		$z = 0;
+
+		for ($i=0; $i < count($this->_attach_name); $i++)
+		{
+			$filename = $this->_attach_name[$i];
+			$basename = basename($filename);
+			$ctype = $this->_attach_type[$i];
+
+			if ( ! file_exists($filename))
+			{
+				$this->_set_error_message('email_attachment_missing', $filename);
+				return FALSE;
+			}
+
+			$h  = "--".$this->_atc_boundary.$this->newline;
+			$h .= "Content-type: ".$ctype."; ";
+			$h .= "name=\"".$basename."\"".$this->newline;
+			$h .= "Content-Disposition: ".$this->_attach_disp[$i].";".$this->newline;
+			$h .= "Content-Transfer-Encoding: base64".$this->newline;
+
+			$attachment[$z++] = $h;
+			$file = filesize($filename) +1;
+
+			if ( ! $fp = fopen($filename, FOPEN_READ))
+			{
+				$this->_set_error_message('email_attachment_unreadable', $filename);
+				return FALSE;
+			}
+
+			$attachment[$z++] = chunk_split(base64_encode(fread($fp, $file)));
+			fclose($fp);
+		}
+
+		if ($this->_get_protocol() == 'mail')
+		{
+			$this->_finalbody = $body . implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";
+
+			return;
+		}
+
+		$this->_finalbody = $hdr.implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";
+
+		return;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Prep Quoted Printable
+	 *
+	 * Prepares string for Quoted-Printable Content-Transfer-Encoding
+	 * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
+	 *
+	 * @access	private
+	 * @param	string
+	 * @param	integer
+	 * @return	string
+	 */
+	function _prep_quoted_printable($str, $charlim = '')
+	{
+		// Set the character limit
+		// Don't allow over 76, as that will make servers and MUAs barf
+		// all over quoted-printable data
+		if ($charlim == '' OR $charlim > '76')
+		{
+			$charlim = '76';
+		}
+
+		// Reduce multiple spaces
+		$str = preg_replace("| +|", " ", $str);
+
+		// kill nulls
+		$str = preg_replace('/\x00+/', '', $str);
+
+		// Standardize newlines
+		if (strpos($str, "\r") !== FALSE)
+		{
+			$str = str_replace(array("\r\n", "\r"), "\n", $str);
+		}
+
+		// We are intentionally wrapping so mail servers will encode characters
+		// properly and MUAs will behave, so {unwrap} must go!
+		$str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
+
+		// Break into an array of lines
+		$lines = explode("\n", $str);
+
+		$escape = '=';
+		$output = '';
+
+		foreach ($lines as $line)
+		{
+			$length = strlen($line);
+			$temp = '';
+
+			// Loop through each character in the line to add soft-wrap
+			// characters at the end of a line " =\r\n" and add the newly
+			// processed line(s) to the output (see comment on $crlf class property)
+			for ($i = 0; $i < $length; $i++)
+			{
+				// Grab the next character
+				$char = substr($line, $i, 1);
+				$ascii = ord($char);
+
+				// Convert spaces and tabs but only if it's the end of the line
+				if ($i == ($length - 1))
+				{
+					$char = ($ascii == '32' OR $ascii == '9') ? $escape.sprintf('%02s', dechex($ascii)) : $char;
+				}
+
+				// encode = signs
+				if ($ascii == '61')
+				{
+					$char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));  // =3D
+				}
+
+				// If we're at the character limit, add the line to the output,
+				// reset our temp variable, and keep on chuggin'
+				if ((strlen($temp) + strlen($char)) >= $charlim)
+				{
+					$output .= $temp.$escape.$this->crlf;
+					$temp = '';
+				}
+
+				// Add the character to our temporary line
+				$temp .= $char;
+			}
+
+			// Add our completed line to the output
+			$output .= $temp.$this->crlf;
+		}
+
+		// get rid of extra CRLF tacked onto the end
+		$output = substr($output, 0, strlen($this->crlf) * -1);
+
+		return $output;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Send Email
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function send()
+	{
+		if ($this->_replyto_flag == FALSE)
+		{
+			$this->reply_to($this->_headers['From']);
+		}
+
+		if (( ! isset($this->_recipients) AND ! isset($this->_headers['To']))  AND
+			( ! isset($this->_bcc_array) AND ! isset($this->_headers['Bcc'])) AND
+			( ! isset($this->_headers['Cc'])))
+		{
+			$this->_set_error_message('email_no_recipients');
+			return FALSE;
+		}
+
+		$this->_build_headers();
+
+		if ($this->bcc_batch_mode  AND  count($this->_bcc_array) > 0)
+		{
+			if (count($this->_bcc_array) > $this->bcc_batch_size)
+				return $this->batch_bcc_send();
+		}
+
+		$this->_build_message();
+
+		if ( ! $this->_spool_email())
+		{
+			return FALSE;
+		}
+		else
+		{
+			return TRUE;
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Batch Bcc Send.  Sends groups of BCCs in batches
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function batch_bcc_send()
+	{
+		$float = $this->bcc_batch_size -1;
+
+		$set = "";
+
+		$chunk = array();
+
+		for ($i = 0; $i < count($this->_bcc_array); $i++)
+		{
+			if (isset($this->_bcc_array[$i]))
+			{
+				$set .= ", ".$this->_bcc_array[$i];
+			}
+
+			if ($i == $float)
+			{
+				$chunk[] = substr($set, 1);
+				$float = $float + $this->bcc_batch_size;
+				$set = "";
+			}
+
+			if ($i == count($this->_bcc_array)-1)
+			{
+				$chunk[] = substr($set, 1);
+			}
+		}
+
+		for ($i = 0; $i < count($chunk); $i++)
+		{
+			unset($this->_headers['Bcc']);
+			unset($bcc);
+
+			$bcc = $this->_str_to_array($chunk[$i]);
+			$bcc = $this->clean_email($bcc);
+
+			if ($this->protocol != 'smtp')
+			{
+				$this->_set_header('Bcc', implode(", ", $bcc));
+			}
+			else
+			{
+				$this->_bcc_array = $bcc;
+			}
+
+			$this->_build_message();
+			$this->_spool_email();
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Unwrap special elements
+	 *
+	 * @access	private
+	 * @return	void
+	 */
+	function _unwrap_specials()
+	{
+		$this->_finalbody = preg_replace_callback("/\{unwrap\}(.*?)\{\/unwrap\}/si", array($this, '_remove_nl_callback'), $this->_finalbody);
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Strip line-breaks via callback
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _remove_nl_callback($matches)
+	{
+		if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE)
+		{
+			$matches[1] = str_replace(array("\r\n", "\r", "\n"), '', $matches[1]);
+		}
+
+		return $matches[1];
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Spool mail to the mail server
+	 *
+	 * @access	private
+	 * @return	bool
+	 */
+	function _spool_email()
+	{
+		$this->_unwrap_specials();
+
+		switch ($this->_get_protocol())
+		{
+			case 'mail'	:
+
+					if ( ! $this->_send_with_mail())
+					{
+						$this->_set_error_message('email_send_failure_phpmail');
+						return FALSE;
+					}
+			break;
+			case 'sendmail'	:
+
+					if ( ! $this->_send_with_sendmail())
+					{
+						$this->_set_error_message('email_send_failure_sendmail');
+						return FALSE;
+					}
+			break;
+			case 'smtp'	:
+
+					if ( ! $this->_send_with_smtp())
+					{
+						$this->_set_error_message('email_send_failure_smtp');
+						return FALSE;
+					}
+			break;
+
+		}
+
+		$this->_set_error_message('email_sent', $this->_get_protocol());
+		return TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Send using mail()
+	 *
+	 * @access	private
+	 * @return	bool
+	 */
+	function _send_with_mail()
+	{
+		if ($this->_safe_mode == TRUE)
+		{
+			if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str))
+			{
+				return FALSE;
+			}
+			else
+			{
+				return TRUE;
+			}
+		}
+		else
+		{
+			// most documentation of sendmail using the "-f" flag lacks a space after it, however
+			// we've encountered servers that seem to require it to be in place.
+			if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f ".$this->clean_email($this->_headers['From'])))
+			{
+				return FALSE;
+			}
+			else
+			{
+				return TRUE;
+			}
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Send using Sendmail
+	 *
+	 * @access	private
+	 * @return	bool
+	 */
+	function _send_with_sendmail()
+	{
+		$fp = @popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w');
+
+		if ( ! is_resource($fp))
+		{
+			$this->_set_error_message('email_no_socket');
+			return FALSE;
+		}
+
+		fputs($fp, $this->_header_str);
+		fputs($fp, $this->_finalbody);
+		pclose($fp) >> 8 & 0xFF;
+
+		return TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Send using SMTP
+	 *
+	 * @access	private
+	 * @return	bool
+	 */
+	function _send_with_smtp()
+	{
+		if ($this->smtp_host == '')
+		{
+			$this->_set_error_message('email_no_hostname');
+			return FALSE;
+		}
+
+		$this->_smtp_connect();
+		$this->_smtp_authenticate();
+
+		$this->_send_command('from', $this->clean_email($this->_headers['From']));
+
+		foreach($this->_recipients as $val)
+		{
+			$this->_send_command('to', $val);
+		}
+
+		if (count($this->_cc_array) > 0)
+		{
+			foreach($this->_cc_array as $val)
+			{
+				if ($val != "")
+				{
+					$this->_send_command('to', $val);
+				}
+			}
+		}
+
+		if (count($this->_bcc_array) > 0)
+		{
+			foreach($this->_bcc_array as $val)
+			{
+				if ($val != "")
+				{
+					$this->_send_command('to', $val);
+				}
+			}
+		}
+
+		$this->_send_command('data');
+
+		// perform dot transformation on any lines that begin with a dot
+		$this->_send_data($this->_header_str . preg_replace('/^\./m', '..$1', $this->_finalbody));
+
+		$this->_send_data('.');
+
+		$reply = $this->_get_smtp_data();
+
+		$this->_set_error_message($reply);
+
+		if (strncmp($reply, '250', 3) != 0)
+		{
+			$this->_set_error_message('email_smtp_error', $reply);
+			return FALSE;
+		}
+
+		$this->_send_command('quit');
+		return TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * SMTP Connect
+	 *
+	 * @access	private
+	 * @param	string
+	 * @return	string
+	 */
+	function _smtp_connect()
+	{
+		$this->_smtp_connect = fsockopen($this->smtp_host,
+										$this->smtp_port,
+										$errno,
+										$errstr,
+										$this->smtp_timeout);
+
+		if( ! is_resource($this->_smtp_connect))
+		{
+			$this->_set_error_message('email_smtp_error', $errno." ".$errstr);
+			return FALSE;
+		}
+
+		$this->_set_error_message($this->_get_smtp_data());
+		return $this->_send_command('hello');
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Send SMTP command
+	 *
+	 * @access	private
+	 * @param	string
+	 * @param	string
+	 * @return	string
+	 */
+	function _send_command($cmd, $data = '')
+	{
+		switch ($cmd)
+		{
+			case 'hello' :
+
+					if ($this->_smtp_auth OR $this->_get_encoding() == '8bit')
+						$this->_send_data('EHLO '.$this->_get_hostname());
+					else
+						$this->_send_data('HELO '.$this->_get_hostname());
+
+						$resp = 250;
+			break;
+			case 'from' :
+
+						$this->_send_data('MAIL FROM:<'.$data.'>');
+
+						$resp = 250;
+			break;
+			case 'to'	:
+
+						$this->_send_data('RCPT TO:<'.$data.'>');
+
+						$resp = 250;
+			break;
+			case 'data'	:
+
+						$this->_send_data('DATA');
+
+						$resp = 354;
+			break;
+			case 'quit'	:
+
+						$this->_send_data('QUIT');
+
+						$resp = 221;
+			break;
+		}
+
+		$reply = $this->_get_smtp_data();
+
+		$this->_debug_msg[] = "<pre>".$cmd.": ".$reply."</pre>";
+
+		if (substr($reply, 0, 3) != $resp)
+		{
+			$this->_set_error_message('email_smtp_error', $reply);
+			return FALSE;
+		}
+
+		if ($cmd == 'quit')
+		{
+			fclose($this->_smtp_connect);
+		}
+
+		return TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 *  SMTP Authenticate
+	 *
+	 * @access	private
+	 * @return	bool
+	 */
+	function _smtp_authenticate()
+	{
+		if ( ! $this->_smtp_auth)
+		{
+			return TRUE;
+		}
+
+		if ($this->smtp_user == ""  AND  $this->smtp_pass == "")
+		{
+			$this->_set_error_message('email_no_smtp_unpw');
+			return FALSE;
+		}
+
+		$this->_send_data('AUTH LOGIN');
+
+		$reply = $this->_get_smtp_data();
+
+		if (strncmp($reply, '334', 3) != 0)
+		{
+			$this->_set_error_message('email_failed_smtp_login', $reply);
+			return FALSE;
+		}
+
+		$this->_send_data(base64_encode($this->smtp_user));
+
+		$reply = $this->_get_smtp_data();
+
+		if (strncmp($reply, '334', 3) != 0)
+		{
+			$this->_set_error_message('email_smtp_auth_un', $reply);
+			return FALSE;
+		}
+
+		$this->_send_data(base64_encode($this->smtp_pass));
+
+		$reply = $this->_get_smtp_data();
+
+		if (strncmp($reply, '235', 3) != 0)
+		{
+			$this->_set_error_message('email_smtp_auth_pw', $reply);
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Send SMTP data
+	 *
+	 * @access	private
+	 * @return	bool
+	 */
+	function _send_data($data)
+	{
+		if ( ! fwrite($this->_smtp_connect, $data . $this->newline))
+		{
+			$this->_set_error_message('email_smtp_data_failure', $data);
+			return FALSE;
+		}
+		else
+		{
+			return TRUE;
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get SMTP data
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _get_smtp_data()
+	{
+		$data = "";
+
+		while ($str = fgets($this->_smtp_connect, 512))
+		{
+			$data .= $str;
+
+			if (substr($str, 3, 1) == " ")
+			{
+				break;
+			}
+		}
+
+		return $data;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Hostname
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _get_hostname()
+	{
+		return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get IP
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _get_ip()
+	{
+		if ($this->_IP !== FALSE)
+		{
+			return $this->_IP;
+		}
+
+		$cip = (isset($_SERVER['HTTP_CLIENT_IP']) AND $_SERVER['HTTP_CLIENT_IP'] != "") ? $_SERVER['HTTP_CLIENT_IP'] : FALSE;
+		$rip = (isset($_SERVER['REMOTE_ADDR']) AND $_SERVER['REMOTE_ADDR'] != "") ? $_SERVER['REMOTE_ADDR'] : FALSE;
+		$fip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND $_SERVER['HTTP_X_FORWARDED_FOR'] != "") ? $_SERVER['HTTP_X_FORWARDED_FOR'] : FALSE;
+
+		if ($cip && $rip) 	$this->_IP = $cip;
+		elseif ($rip)		$this->_IP = $rip;
+		elseif ($cip)		$this->_IP = $cip;
+		elseif ($fip)		$this->_IP = $fip;
+
+		if (strstr($this->_IP, ','))
+		{
+			$x = explode(',', $this->_IP);
+			$this->_IP = end($x);
+		}
+
+		if ( ! preg_match( "/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $this->_IP))
+		{
+			$this->_IP = '0.0.0.0';
+		}
+
+		unset($cip);
+		unset($rip);
+		unset($fip);
+
+		return $this->_IP;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get Debug Message
+	 *
+	 * @access	public
+	 * @return	string
+	 */
+	function print_debugger()
+	{
+		$msg = '';
+
+		if (count($this->_debug_msg) > 0)
+		{
+			foreach ($this->_debug_msg as $val)
+			{
+				$msg .= $val;
+			}
+		}
+
+		$msg .= "<pre>".$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>';
+		return $msg;
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set Message
+	 *
+	 * @access	private
+	 * @param	string
+	 * @return	string
+	 */
+	function _set_error_message($msg, $val = '')
+	{
+		$CI =& get_instance();
+		$CI->lang->load('email');
+
+		if (FALSE === ($line = $CI->lang->line($msg)))
+		{
+			$this->_debug_msg[] = str_replace('%s', $val, $msg)."<br />";
+		}
+		else
+		{
+			$this->_debug_msg[] = str_replace('%s', $val, $line)."<br />";
+		}
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Mime Types
+	 *
+	 * @access	private
+	 * @param	string
+	 * @return	string
+	 */
+	function _mime_types($ext = "")
+	{
+		$mimes = array(	'hqx'	=>	'application/mac-binhex40',
+						'cpt'	=>	'application/mac-compactpro',
+						'doc'	=>	'application/msword',
+						'bin'	=>	'application/macbinary',
+						'dms'	=>	'application/octet-stream',
+						'lha'	=>	'application/octet-stream',
+						'lzh'	=>	'application/octet-stream',
+						'exe'	=>	'application/octet-stream',
+						'class'	=>	'application/octet-stream',
+						'psd'	=>	'application/octet-stream',
+						'so'	=>	'application/octet-stream',
+						'sea'	=>	'application/octet-stream',
+						'dll'	=>	'application/octet-stream',
+						'oda'	=>	'application/oda',
+						'pdf'	=>	'application/pdf',
+						'ai'	=>	'application/postscript',
+						'eps'	=>	'application/postscript',
+						'ps'	=>	'application/postscript',
+						'smi'	=>	'application/smil',
+						'smil'	=>	'application/smil',
+						'mif'	=>	'application/vnd.mif',
+						'xls'	=>	'application/vnd.ms-excel',
+						'ppt'	=>	'application/vnd.ms-powerpoint',
+						'wbxml'	=>	'application/vnd.wap.wbxml',
+						'wmlc'	=>	'application/vnd.wap.wmlc',
+						'dcr'	=>	'application/x-director',
+						'dir'	=>	'application/x-director',
+						'dxr'	=>	'application/x-director',
+						'dvi'	=>	'application/x-dvi',
+						'gtar'	=>	'application/x-gtar',
+						'php'	=>	'application/x-httpd-php',
+						'php4'	=>	'application/x-httpd-php',
+						'php3'	=>	'application/x-httpd-php',
+						'phtml'	=>	'application/x-httpd-php',
+						'phps'	=>	'application/x-httpd-php-source',
+						'js'	=>	'application/x-javascript',
+						'swf'	=>	'application/x-shockwave-flash',
+						'sit'	=>	'application/x-stuffit',
+						'tar'	=>	'application/x-tar',
+						'tgz'	=>	'application/x-tar',
+						'xhtml'	=>	'application/xhtml+xml',
+						'xht'	=>	'application/xhtml+xml',
+						'zip'	=>	'application/zip',
+						'mid'	=>	'audio/midi',
+						'midi'	=>	'audio/midi',
+						'mpga'	=>	'audio/mpeg',
+						'mp2'	=>	'audio/mpeg',
+						'mp3'	=>	'audio/mpeg',
+						'aif'	=>	'audio/x-aiff',
+						'aiff'	=>	'audio/x-aiff',
+						'aifc'	=>	'audio/x-aiff',
+						'ram'	=>	'audio/x-pn-realaudio',
+						'rm'	=>	'audio/x-pn-realaudio',
+						'rpm'	=>	'audio/x-pn-realaudio-plugin',
+						'ra'	=>	'audio/x-realaudio',
+						'rv'	=>	'video/vnd.rn-realvideo',
+						'wav'	=>	'audio/x-wav',
+						'bmp'	=>	'image/bmp',
+						'gif'	=>	'image/gif',
+						'jpeg'	=>	'image/jpeg',
+						'jpg'	=>	'image/jpeg',
+						'jpe'	=>	'image/jpeg',
+						'png'	=>	'image/png',
+						'tiff'	=>	'image/tiff',
+						'tif'	=>	'image/tiff',
+						'css'	=>	'text/css',
+						'html'	=>	'text/html',
+						'htm'	=>	'text/html',
+						'shtml'	=>	'text/html',
+						'txt'	=>	'text/plain',
+						'text'	=>	'text/plain',
+						'log'	=>	'text/plain',
+						'rtx'	=>	'text/richtext',
+						'rtf'	=>	'text/rtf',
+						'xml'	=>	'text/xml',
+						'xsl'	=>	'text/xml',
+						'mpeg'	=>	'video/mpeg',
+						'mpg'	=>	'video/mpeg',
+						'mpe'	=>	'video/mpeg',
+						'qt'	=>	'video/quicktime',
+						'mov'	=>	'video/quicktime',
+						'avi'	=>	'video/x-msvideo',
+						'movie'	=>	'video/x-sgi-movie',
+						'doc'	=>	'application/msword',
+						'word'	=>	'application/msword',
+						'xl'	=>	'application/excel',
+						'eml'	=>	'message/rfc822'
+					);
+
+		return ( ! isset($mimes[strtolower($ext)])) ? "application/x-unknown-content-type" : $mimes[strtolower($ext)];
+	}
+
+}
+// END CI_Email class
+
+/* End of file Email.php */
 /* Location: ./system/libraries/Email.php */
\ No newline at end of file
diff --git a/system/libraries/Ftp.php b/system/libraries/Ftp.php
index 0e3dd5e..ec84eb0 100644
--- a/system/libraries/Ftp.php
+++ b/system/libraries/Ftp.php
@@ -1,618 +1,618 @@
-<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

-/**

- * CodeIgniter

- *

- * An open source application development framework for PHP 4.3.2 or newer

- *

- * @package		CodeIgniter

- * @author		ExpressionEngine Dev Team

- * @copyright	Copyright (c) 2008, EllisLab, Inc.

- * @license		http://codeigniter.com/user_guide/license.html

- * @link		http://codeigniter.com

- * @since		Version 1.0

- * @filesource

- */

-

-// ------------------------------------------------------------------------

-

-/**

- * FTP Class

- *

- * @package		CodeIgniter

- * @subpackage	Libraries

- * @category	Libraries

- * @author		ExpressionEngine Dev Team

- * @link		http://codeigniter.com/user_guide/libraries/ftp.html

- */ 

-class CI_FTP {

-

-	var $hostname	= '';

-	var $username	= '';

-	var $password	= '';

-	var $port		= 21;

-	var $passive	= TRUE;

-	var $debug		= FALSE;

-	var $conn_id	= FALSE;

-

-

-	/**

-	 * Constructor - Sets Preferences

-	 *

-	 * The constructor can be passed an array of config values

-	 */	

-	function CI_FTP($config = array())

-	{		

-		if (count($config) > 0)

-		{

-			$this->initialize($config);

-		}	

-

-		log_message('debug', "FTP Class Initialized");

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Initialize preferences

-	 *

-	 * @access	public

-	 * @param	array

-	 * @return	void

-	 */	

-	function initialize($config = array())

-	{

-		foreach ($config as $key => $val)

-		{

-			if (isset($this->$key))

-			{

-				$this->$key = $val;

-			}

-		}

-		

-		// Prep the hostname

-		$this->hostname = preg_replace('|.+?://|', '', $this->hostname);

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * FTP Connect

-	 *

-	 * @access	public

-	 * @param	array	 the connection values

-	 * @return	bool

-	 */	

-	function connect($config = array())

-	{		

-		if (count($config) > 0)

-		{

-			$this->initialize($config);

-		}	

-	

-		if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port)))

-		{

-			if ($this->debug == TRUE)

-			{

-				$this->_error('ftp_unable_to_connect');

-			}		

-			return FALSE;

-		}

-		

-		if ( ! $this->_login())

-		{

-			if ($this->debug == TRUE)

-			{

-				$this->_error('ftp_unable_to_login');

-			}		

-			return FALSE;

-		}

-		

-		// Set passive mode if needed

-		if ($this->passive == TRUE)

-		{

-			ftp_pasv($this->conn_id, TRUE);

-		}

-		

-		return TRUE;

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * FTP Login

-	 *

-	 * @access	private

-	 * @return	bool

-	 */	

-	function _login()

-	{

-		return @ftp_login($this->conn_id, $this->username, $this->password);

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Validates the connection ID

-	 *

-	 * @access	private

-	 * @return	bool

-	 */	

-	function _is_conn()

-	{

-		if ( ! is_resource($this->conn_id))

-		{

-			if ($this->debug == TRUE)

-			{

-				$this->_error('ftp_no_connection');

-			}		

-			return FALSE;

-		}

-		return TRUE;

-	}

-

-	// --------------------------------------------------------------------

-

-

-	/**

-	 * Change direcotry

-	 *

-	 * The second parameter lets us momentarily turn off debugging so that

-	 * this function can be used to test for the existance of a folder

-	 * without throwing an error.  There's no FTP equivalent to is_dir()

-	 * so we do it by trying to change to a particular directory.  

-	 * Internally, this paramter is only used by the "mirror" function below.

-	 *

-	 * @access	public

-	 * @param	string

-	 * @param	bool

-	 * @return	bool

-	 */	

-	function changedir($path = '', $supress_debug = FALSE)

-	{

-		if ($path == '' OR ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-		

-		$result = @ftp_chdir($this->conn_id, $path);

-		

-		if ($result === FALSE)

-		{

-			if ($this->debug == TRUE AND $supress_debug == FALSE)

-			{

-				$this->_error('ftp_unable_to_changedir');

-			}		

-			return FALSE;		

-		}

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Create a directory

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	bool

-	 */	

-	function mkdir($path = '', $permissions = NULL)

-	{

-		if ($path == '' OR ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-	

-		$result = @ftp_mkdir($this->conn_id, $path);

-		

-		if ($result === FALSE)

-		{

-			if ($this->debug == TRUE)

-			{

-				$this->_error('ftp_unable_to_makdir');

-			}		

-			return FALSE;		

-		}

-

-		// Set file permissions if needed

-		if ( ! is_null($permissions))

-		{

-			$this->chmod($path, (int)$permissions);

-		}

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Upload a file to the server

-	 *

-	 * @access	public

-	 * @param	string

-	 * @param	string

-	 * @param	string

-	 * @return	bool

-	 */	

-	function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL)

-	{

-		if ( ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-

-		if ( ! file_exists($locpath))

-		{

-			$this->_error('ftp_no_source_file');

-			return FALSE;

-		}

-	

-		// Set the mode if not specified

-		if ($mode == 'auto')

-		{

-			// Get the file extension so we can set the upload type

-			$ext = $this->_getext($locpath);

-			$mode = $this->_settype($ext);

-		}

-		

-		$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;

-		

-		$result = @ftp_put($this->conn_id, $rempath, $locpath, $mode);

-		

-		if ($result === FALSE)

-		{

-			if ($this->debug == TRUE)

-			{

-				$this->_error('ftp_unable_to_upload');

-			}		

-			return FALSE;		

-		}

-		

-		// Set file permissions if needed

-		if ( ! is_null($permissions))

-		{

-			$this->chmod($rempath, (int)$permissions);

-		}

-		

-		return TRUE;

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Rename (or move) a file

-	 *

-	 * @access	public

-	 * @param	string

-	 * @param	string

-	 * @param	bool

-	 * @return	bool

-	 */	

-	function rename($old_file, $new_file, $move = FALSE)

-	{

-		if ( ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-

-		$result = @ftp_rename($this->conn_id, $old_file, $new_file);

-		

-		if ($result === FALSE)

-		{

-			if ($this->debug == TRUE)

-			{

-				$msg = ($move == FALSE) ? 'ftp_unable_to_rename' : 'ftp_unable_to_move';

-				

-				$this->_error($msg);

-			}		

-			return FALSE;		

-		}

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Move a file

-	 *

-	 * @access	public

-	 * @param	string

-	 * @param	string

-	 * @return	bool

-	 */	

-	function move($old_file, $new_file)

-	{

-		return $this->rename($old_file, $new_file, TRUE);

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Rename (or move) a file

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	bool

-	 */	

-	function delete_file($filepath)

-	{

-		if ( ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-

-		$result = @ftp_delete($this->conn_id, $filepath);

-		

-		if ($result === FALSE)

-		{

-			if ($this->debug == TRUE)

-			{				

-				$this->_error('ftp_unable_to_delete');

-			}		

-			return FALSE;		

-		}

-		

-		return TRUE;

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Delete a folder and recursively delete everything (including sub-folders)

-	 * containted within it.

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	bool

-	 */	

-	function delete_dir($filepath)

-	{

-		if ( ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-

-		// Add a trailing slash to the file path if needed

-		$filepath = preg_replace("/(.+?)\/*$/", "\\1/",  $filepath);

-		

-		$list = $this->list_files($filepath);

-		

-		if ($list !== FALSE AND count($list) > 0)

-		{

-			foreach ($list as $item)

-			{			

-				// If we can't delete the item it's probaly a folder so

-				// we'll recursively call delete_dir()

-				if ( ! @ftp_delete($this->conn_id, $item))

-				{

-					$this->delete_dir($item);

-				}

-			}

-		}

-	

-		$result = @ftp_rmdir($this->conn_id, $filepath);

-		

-		if ($result === FALSE)

-		{

-			if ($this->debug == TRUE)

-			{				

-				$this->_error('ftp_unable_to_delete');

-			}		

-			return FALSE;		

-		}

-		

-		return TRUE;

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Set file permissions

-	 *

-	 * @access	public

-	 * @param	string 	the file path

-	 * @param	string	the permissions

-	 * @return	bool

-	 */		

-	function chmod($path, $perm)

-	{

-		if ( ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-

-		// Permissions can only be set when running PHP 5

-		if ( ! function_exists('ftp_chmod'))

-		{

-			if ($this->debug == TRUE)

-			{

-				$this->_error('ftp_unable_to_chmod');

-			}		

-			return FALSE;		

-		}

-	

-		$result = @ftp_chmod($this->conn_id, $perm, $path);

-		

-		if ($result === FALSE)

-		{

-			if ($this->debug == TRUE)

-			{

-				$this->_error('ftp_unable_to_chmod');

-			}		

-			return FALSE;		

-		}

-		

-		return TRUE;

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * FTP List files in the specified directory

-	 *

-	 * @access	public

-	 * @return	array

-	 */	

-	function list_files($path = '.')

-	{

-		if ( ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-

-		return ftp_nlist($this->conn_id, $path);

-	}

-

-	// ------------------------------------------------------------------------

-	

-	/**

-	 * Read a directory and recreate it remotely

-	 *

-	 * This function recursively reads a folder and everything it contains (including

-	 * sub-folders) and creates a mirror via FTP based on it.  Whatever the directory structure

-	 * of the original file path will be recreated on the server.

-	 *

-	 * @access	public

-	 * @param	string	path to source with trailing slash

-	 * @param	string	path to destination - include the base folder with trailing slash

-	 * @return	bool

-	 */	

-	function mirror($locpath, $rempath)

-	{

-		if ( ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-

-		// Open the local file path

-		if ($fp = @opendir($locpath))

-		{

-			// Attempt to open the remote file path.

-			if ( ! $this->changedir($rempath, TRUE))

-			{

-				// If it doesn't exist we'll attempt to create the direcotory

-				if ( ! $this->mkdir($rempath) OR ! $this->changedir($rempath))

-				{

-					return FALSE;

-				}

-			}

-		

-			// Recursively read the local directory

-			while (FALSE !== ($file = readdir($fp)))

-			{

-				if (@is_dir($locpath.$file) && substr($file, 0, 1) != '.')

-				{					

-					$this->mirror($locpath.$file."/", $rempath.$file."/");

-				}

-				elseif (substr($file, 0, 1) != ".")

-				{

-					// Get the file extension so we can se the upload type

-					$ext = $this->_getext($file);

-					$mode = $this->_settype($ext);

-					

-					$this->upload($locpath.$file, $rempath.$file, $mode);

-				}

-			}

-			return TRUE;

-		}

-		

-		return FALSE;

-	}

-

-

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Extract the file extension

-	 *

-	 * @access	private

-	 * @param	string

-	 * @return	string

-	 */	

-	function _getext($filename)

-	{

-		if (FALSE === strpos($filename, '.'))

-		{

-			return 'txt';

-		}

-	

-		$x = explode('.', $filename);

-		return end($x);

-	}	

-

-

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Set the upload type

-	 *

-	 * @access	private

-	 * @param	string

-	 * @return	string

-	 */	

-	function _settype($ext)

-	{

-		$text_types = array(

-							'txt',

-							'text',

-							'php',

-							'phps',

-							'php4',

-							'js',

-							'css',

-							'htm',

-							'html',

-							'phtml',

-							'shtml',

-							'log',

-							'xml'

-							);

-	

-	

-		return (in_array($ext, $text_types)) ? 'ascii' : 'binary';

-	}

-

-	// ------------------------------------------------------------------------

-	

-	/**

-	 * Close the connection

-	 *

-	 * @access	public

-	 * @param	string	path to source

-	 * @param	string	path to destination

-	 * @return	bool

-	 */	

-	function close()

-	{

-		if ( ! $this->_is_conn())

-		{

-			return FALSE;

-		}

-

-		@ftp_close($this->conn_id);

-	}

-

-	// ------------------------------------------------------------------------

-	

-	/**

-	 * Display error message

-	 *

-	 * @access	private

-	 * @param	string

-	 * @return	bool

-	 */	

-	function _error($line)

-	{

-		$CI =& get_instance();

-		$CI->lang->load('ftp');

-		show_error($CI->lang->line($line));		

-	}

-

-

-}

-// END FTP Class

-

-/* End of file Ftp.php */

+<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2008, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * FTP Class
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Libraries
+ * @author		ExpressionEngine Dev Team
+ * @link		http://codeigniter.com/user_guide/libraries/ftp.html
+ */
+class CI_FTP {
+
+	var $hostname	= '';
+	var $username	= '';
+	var $password	= '';
+	var $port		= 21;
+	var $passive	= TRUE;
+	var $debug		= FALSE;
+	var $conn_id	= FALSE;
+
+
+	/**
+	 * Constructor - Sets Preferences
+	 *
+	 * The constructor can be passed an array of config values
+	 */
+	function CI_FTP($config = array())
+	{
+		if (count($config) > 0)
+		{
+			$this->initialize($config);
+		}
+
+		log_message('debug', "FTP Class Initialized");
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initialize preferences
+	 *
+	 * @access	public
+	 * @param	array
+	 * @return	void
+	 */
+	function initialize($config = array())
+	{
+		foreach ($config as $key => $val)
+		{
+			if (isset($this->$key))
+			{
+				$this->$key = $val;
+			}
+		}
+
+		// Prep the hostname
+		$this->hostname = preg_replace('|.+?://|', '', $this->hostname);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * FTP Connect
+	 *
+	 * @access	public
+	 * @param	array	 the connection values
+	 * @return	bool
+	 */
+	function connect($config = array())
+	{
+		if (count($config) > 0)
+		{
+			$this->initialize($config);
+		}
+
+		if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port)))
+		{
+			if ($this->debug == TRUE)
+			{
+				$this->_error('ftp_unable_to_connect');
+			}
+			return FALSE;
+		}
+
+		if ( ! $this->_login())
+		{
+			if ($this->debug == TRUE)
+			{
+				$this->_error('ftp_unable_to_login');
+			}
+			return FALSE;
+		}
+
+		// Set passive mode if needed
+		if ($this->passive == TRUE)
+		{
+			ftp_pasv($this->conn_id, TRUE);
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * FTP Login
+	 *
+	 * @access	private
+	 * @return	bool
+	 */
+	function _login()
+	{
+		return @ftp_login($this->conn_id, $this->username, $this->password);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Validates the connection ID
+	 *
+	 * @access	private
+	 * @return	bool
+	 */
+	function _is_conn()
+	{
+		if ( ! is_resource($this->conn_id))
+		{
+			if ($this->debug == TRUE)
+			{
+				$this->_error('ftp_no_connection');
+			}
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+
+	/**
+	 * Change direcotry
+	 *
+	 * The second parameter lets us momentarily turn off debugging so that
+	 * this function can be used to test for the existance of a folder
+	 * without throwing an error.  There's no FTP equivalent to is_dir()
+	 * so we do it by trying to change to a particular directory.
+	 * Internally, this paramter is only used by the "mirror" function below.
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	bool
+	 * @return	bool
+	 */
+	function changedir($path = '', $supress_debug = FALSE)
+	{
+		if ($path == '' OR ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		$result = @ftp_chdir($this->conn_id, $path);
+
+		if ($result === FALSE)
+		{
+			if ($this->debug == TRUE AND $supress_debug == FALSE)
+			{
+				$this->_error('ftp_unable_to_changedir');
+			}
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Create a directory
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	bool
+	 */
+	function mkdir($path = '', $permissions = NULL)
+	{
+		if ($path == '' OR ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		$result = @ftp_mkdir($this->conn_id, $path);
+
+		if ($result === FALSE)
+		{
+			if ($this->debug == TRUE)
+			{
+				$this->_error('ftp_unable_to_makdir');
+			}
+			return FALSE;
+		}
+
+		// Set file permissions if needed
+		if ( ! is_null($permissions))
+		{
+			$this->chmod($path, (int)$permissions);
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Upload a file to the server
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	string
+	 * @param	string
+	 * @return	bool
+	 */
+	function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL)
+	{
+		if ( ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		if ( ! file_exists($locpath))
+		{
+			$this->_error('ftp_no_source_file');
+			return FALSE;
+		}
+
+		// Set the mode if not specified
+		if ($mode == 'auto')
+		{
+			// Get the file extension so we can set the upload type
+			$ext = $this->_getext($locpath);
+			$mode = $this->_settype($ext);
+		}
+
+		$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
+
+		$result = @ftp_put($this->conn_id, $rempath, $locpath, $mode);
+
+		if ($result === FALSE)
+		{
+			if ($this->debug == TRUE)
+			{
+				$this->_error('ftp_unable_to_upload');
+			}
+			return FALSE;
+		}
+
+		// Set file permissions if needed
+		if ( ! is_null($permissions))
+		{
+			$this->chmod($rempath, (int)$permissions);
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Rename (or move) a file
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	string
+	 * @param	bool
+	 * @return	bool
+	 */
+	function rename($old_file, $new_file, $move = FALSE)
+	{
+		if ( ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		$result = @ftp_rename($this->conn_id, $old_file, $new_file);
+
+		if ($result === FALSE)
+		{
+			if ($this->debug == TRUE)
+			{
+				$msg = ($move == FALSE) ? 'ftp_unable_to_rename' : 'ftp_unable_to_move';
+
+				$this->_error($msg);
+			}
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Move a file
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	string
+	 * @return	bool
+	 */
+	function move($old_file, $new_file)
+	{
+		return $this->rename($old_file, $new_file, TRUE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Rename (or move) a file
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	bool
+	 */
+	function delete_file($filepath)
+	{
+		if ( ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		$result = @ftp_delete($this->conn_id, $filepath);
+
+		if ($result === FALSE)
+		{
+			if ($this->debug == TRUE)
+			{
+				$this->_error('ftp_unable_to_delete');
+			}
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete a folder and recursively delete everything (including sub-folders)
+	 * containted within it.
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	bool
+	 */
+	function delete_dir($filepath)
+	{
+		if ( ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		// Add a trailing slash to the file path if needed
+		$filepath = preg_replace("/(.+?)\/*$/", "\\1/",  $filepath);
+
+		$list = $this->list_files($filepath);
+
+		if ($list !== FALSE AND count($list) > 0)
+		{
+			foreach ($list as $item)
+			{
+				// If we can't delete the item it's probaly a folder so
+				// we'll recursively call delete_dir()
+				if ( ! @ftp_delete($this->conn_id, $item))
+				{
+					$this->delete_dir($item);
+				}
+			}
+		}
+
+		$result = @ftp_rmdir($this->conn_id, $filepath);
+
+		if ($result === FALSE)
+		{
+			if ($this->debug == TRUE)
+			{
+				$this->_error('ftp_unable_to_delete');
+			}
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set file permissions
+	 *
+	 * @access	public
+	 * @param	string 	the file path
+	 * @param	string	the permissions
+	 * @return	bool
+	 */
+	function chmod($path, $perm)
+	{
+		if ( ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		// Permissions can only be set when running PHP 5
+		if ( ! function_exists('ftp_chmod'))
+		{
+			if ($this->debug == TRUE)
+			{
+				$this->_error('ftp_unable_to_chmod');
+			}
+			return FALSE;
+		}
+
+		$result = @ftp_chmod($this->conn_id, $perm, $path);
+
+		if ($result === FALSE)
+		{
+			if ($this->debug == TRUE)
+			{
+				$this->_error('ftp_unable_to_chmod');
+			}
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * FTP List files in the specified directory
+	 *
+	 * @access	public
+	 * @return	array
+	 */
+	function list_files($path = '.')
+	{
+		if ( ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		return ftp_nlist($this->conn_id, $path);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Read a directory and recreate it remotely
+	 *
+	 * This function recursively reads a folder and everything it contains (including
+	 * sub-folders) and creates a mirror via FTP based on it.  Whatever the directory structure
+	 * of the original file path will be recreated on the server.
+	 *
+	 * @access	public
+	 * @param	string	path to source with trailing slash
+	 * @param	string	path to destination - include the base folder with trailing slash
+	 * @return	bool
+	 */
+	function mirror($locpath, $rempath)
+	{
+		if ( ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		// Open the local file path
+		if ($fp = @opendir($locpath))
+		{
+			// Attempt to open the remote file path.
+			if ( ! $this->changedir($rempath, TRUE))
+			{
+				// If it doesn't exist we'll attempt to create the direcotory
+				if ( ! $this->mkdir($rempath) OR ! $this->changedir($rempath))
+				{
+					return FALSE;
+				}
+			}
+
+			// Recursively read the local directory
+			while (FALSE !== ($file = readdir($fp)))
+			{
+				if (@is_dir($locpath.$file) && substr($file, 0, 1) != '.')
+				{
+					$this->mirror($locpath.$file."/", $rempath.$file."/");
+				}
+				elseif (substr($file, 0, 1) != ".")
+				{
+					// Get the file extension so we can se the upload type
+					$ext = $this->_getext($file);
+					$mode = $this->_settype($ext);
+
+					$this->upload($locpath.$file, $rempath.$file, $mode);
+				}
+			}
+			return TRUE;
+		}
+
+		return FALSE;
+	}
+
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Extract the file extension
+	 *
+	 * @access	private
+	 * @param	string
+	 * @return	string
+	 */
+	function _getext($filename)
+	{
+		if (FALSE === strpos($filename, '.'))
+		{
+			return 'txt';
+		}
+
+		$x = explode('.', $filename);
+		return end($x);
+	}
+
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set the upload type
+	 *
+	 * @access	private
+	 * @param	string
+	 * @return	string
+	 */
+	function _settype($ext)
+	{
+		$text_types = array(
+							'txt',
+							'text',
+							'php',
+							'phps',
+							'php4',
+							'js',
+							'css',
+							'htm',
+							'html',
+							'phtml',
+							'shtml',
+							'log',
+							'xml'
+							);
+
+
+		return (in_array($ext, $text_types)) ? 'ascii' : 'binary';
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Close the connection
+	 *
+	 * @access	public
+	 * @param	string	path to source
+	 * @param	string	path to destination
+	 * @return	bool
+	 */
+	function close()
+	{
+		if ( ! $this->_is_conn())
+		{
+			return FALSE;
+		}
+
+		@ftp_close($this->conn_id);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Display error message
+	 *
+	 * @access	private
+	 * @param	string
+	 * @return	bool
+	 */
+	function _error($line)
+	{
+		$CI =& get_instance();
+		$CI->lang->load('ftp');
+		show_error($CI->lang->line($line));
+	}
+
+
+}
+// END FTP Class
+
+/* End of file Ftp.php */
 /* Location: ./system/libraries/Ftp.php */
\ No newline at end of file
diff --git a/system/libraries/Hooks.php b/system/libraries/Hooks.php
index 34dd98f..46f3ac4 100644
--- a/system/libraries/Hooks.php
+++ b/system/libraries/Hooks.php
@@ -1,226 +1,226 @@
-<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

-/**

- * CodeIgniter

- *

- * An open source application development framework for PHP 4.3.2 or newer

- *

- * @package		CodeIgniter

- * @author		ExpressionEngine Dev Team

- * @copyright	Copyright (c) 2008, EllisLab, Inc.

- * @license		http://codeigniter.com/user_guide/license.html

- * @link		http://codeigniter.com

- * @since		Version 1.0

- * @filesource

- */

-

-// ------------------------------------------------------------------------

-

-/**

- * CodeIgniter Hooks Class

- *

- * Provides a mechanism to extend the base system without hacking.  Most of

- * this class is borrowed from Paul's Extension class in ExpressionEngine.

- *

- * @package		CodeIgniter

- * @subpackage	Libraries

- * @category	Libraries

- * @author		ExpressionEngine Dev Team

- * @link		http://codeigniter.com/user_guide/libraries/encryption.html

- */

-class CI_Hooks {

-	

-	var $enabled 		= FALSE;

-	var $hooks   		= array();

-	var $in_progress	= FALSE;

-	

-	/**

-	 * Constructor

-	 *

-	 */

-	function CI_Hooks()

-	{

-		$this->_initialize();	

-		log_message('debug', "Hooks Class Initialized");

-	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Initialize the Hooks Preferences

-	 *

-	 * @access	private

-	 * @return	void

-	 */  	

-  	function _initialize()

-  	{

-		$CFG =& load_class('Config');

-		

-		// If hooks are not enabled in the config file

-		// there is nothing else to do

-		

-		if ($CFG->item('enable_hooks') == FALSE)

-		{

-			return;

-		}

-		

-		// Grab the "hooks" definition file.

-		// If there are no hooks, we're done.

-		

-		@include(APPPATH.'config/hooks'.EXT);

-		

-		if ( ! isset($hook) OR ! is_array($hook))

-		{

-			return;

-		}

-

-		$this->hooks =& $hook;

-		$this->enabled = TRUE;

-  	}

-  	

-	// --------------------------------------------------------------------

-

-	/**

-	 * Call Hook

-	 *

-	 * Calls a particular hook

-	 *

-	 * @access	private

-	 * @param	string	the hook name

-	 * @return	mixed

-	 */

-	function _call_hook($which = '')

-	{

-		if ( ! $this->enabled OR ! isset($this->hooks[$which]))

-		{

-			return FALSE;

-		}

-	

-		if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0]))

-		{

-			foreach ($this->hooks[$which] as $val)

-			{

-				$this->_run_hook($val);

-			}

-		}

-		else

-		{

-			$this->_run_hook($this->hooks[$which]);

-		}

-		

-		return TRUE;

-	}

-

-	// --------------------------------------------------------------------

-

-	/**

-	 * Run Hook

-	 *

-	 * Runs a particular hook

-	 *

-	 * @access	private

-	 * @param	array	the hook details

-	 * @return	bool

-	 */

-	function _run_hook($data)

-	{

-		if ( ! is_array($data))

-		{

-			return FALSE;

-		}

-		

-		// -----------------------------------

-		// Safety - Prevents run-away loops

-		// -----------------------------------

-	

-		// If the script being called happens to have the same

-		// hook call within it a loop can happen

-		

-		if ($this->in_progress == TRUE)

-		{

-			return;

-		}

-

-		// -----------------------------------

-		// Set file path

-		// -----------------------------------

-		

-		if ( ! isset($data['filepath']) OR ! isset($data['filename']))

-		{

-			return FALSE;

-		}

-		

-		$filepath = APPPATH.$data['filepath'].'/'.$data['filename'];

-	

-		if ( ! file_exists($filepath))

-		{

-			return FALSE;

-		}

-		

-		// -----------------------------------

-		// Set class/function name

-		// -----------------------------------

-		

-		$class		= FALSE;

-		$function	= FALSE;

-		$params		= '';

-		

-		if (isset($data['class']) AND $data['class'] != '')

-		{

-			$class = $data['class'];

-		}

-

-		if (isset($data['function']))

-		{

-			$function = $data['function'];

-		}

-

-		if (isset($data['params']))

-		{

-			$params = $data['params'];

-		}

-		

-		if ($class === FALSE AND $function === FALSE)

-		{

-			return FALSE;

-		}

-		

-		// -----------------------------------

-		// Set the in_progress flag

-		// -----------------------------------

-

-		$this->in_progress = TRUE;

-		

-		// -----------------------------------

-		// Call the requested class and/or function

-		// -----------------------------------

-		

-		if ($class !== FALSE)

-		{

-			if ( ! class_exists($class))

-			{

-				require($filepath);

-			}

-		

-			$HOOK = new $class;

-			$HOOK->$function($params);

-		}

-		else

-		{

-			if ( ! function_exists($function))

-			{

-				require($filepath);

-			}

-		

-			$function($params);

-		}

-	

-		$this->in_progress = FALSE;

-		return TRUE;

-	}

-

-}

-

-// END CI_Hooks class

-

-/* End of file Hooks.php */

+<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2008, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CodeIgniter Hooks Class
+ *
+ * Provides a mechanism to extend the base system without hacking.  Most of
+ * this class is borrowed from Paul's Extension class in ExpressionEngine.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Libraries
+ * @author		ExpressionEngine Dev Team
+ * @link		http://codeigniter.com/user_guide/libraries/encryption.html
+ */
+class CI_Hooks {
+
+	var $enabled 		= FALSE;
+	var $hooks   		= array();
+	var $in_progress	= FALSE;
+
+	/**
+	 * Constructor
+	 *
+	 */
+	function CI_Hooks()
+	{
+		$this->_initialize();
+		log_message('debug', "Hooks Class Initialized");
+	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initialize the Hooks Preferences
+	 *
+	 * @access	private
+	 * @return	void
+	 */  
+  	function _initialize()
+  	{
+		$CFG =& load_class('Config');
+
+		// If hooks are not enabled in the config file
+		// there is nothing else to do
+
+		if ($CFG->item('enable_hooks') == FALSE)
+		{
+			return;
+		}
+
+		// Grab the "hooks" definition file.
+		// If there are no hooks, we're done.
+
+		@include(APPPATH.'config/hooks'.EXT);
+
+		if ( ! isset($hook) OR ! is_array($hook))
+		{
+			return;
+		}
+
+		$this->hooks =& $hook;
+		$this->enabled = TRUE;
+  	}
+  
+	// --------------------------------------------------------------------
+
+	/**
+	 * Call Hook
+	 *
+	 * Calls a particular hook
+	 *
+	 * @access	private
+	 * @param	string	the hook name
+	 * @return	mixed
+	 */
+	function _call_hook($which = '')
+	{
+		if ( ! $this->enabled OR ! isset($this->hooks[$which]))
+		{
+			return FALSE;
+		}
+
+		if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0]))
+		{
+			foreach ($this->hooks[$which] as $val)
+			{
+				$this->_run_hook($val);
+			}
+		}
+		else
+		{
+			$this->_run_hook($this->hooks[$which]);
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Run Hook
+	 *
+	 * Runs a particular hook
+	 *
+	 * @access	private
+	 * @param	array	the hook details
+	 * @return	bool
+	 */
+	function _run_hook($data)
+	{
+		if ( ! is_array($data))
+		{
+			return FALSE;
+		}
+
+		// -----------------------------------
+		// Safety - Prevents run-away loops
+		// -----------------------------------
+
+		// If the script being called happens to have the same
+		// hook call within it a loop can happen
+
+		if ($this->in_progress == TRUE)
+		{
+			return;
+		}
+
+		// -----------------------------------
+		// Set file path
+		// -----------------------------------
+
+		if ( ! isset($data['filepath']) OR ! isset($data['filename']))
+		{
+			return FALSE;
+		}
+
+		$filepath = APPPATH.$data['filepath'].'/'.$data['filename'];
+
+		if ( ! file_exists($filepath))
+		{
+			return FALSE;
+		}
+
+		// -----------------------------------
+		// Set class/function name
+		// -----------------------------------
+
+		$class		= FALSE;
+		$function	= FALSE;
+		$params		= '';
+
+		if (isset($data['class']) AND $data['class'] != '')
+		{
+			$class = $data['class'];
+		}
+
+		if (isset($data['function']))
+		{
+			$function = $data['function'];
+		}
+
+		if (isset($data['params']))
+		{
+			$params = $data['params'];
+		}
+
+		if ($class === FALSE AND $function === FALSE)
+		{
+			return FALSE;
+		}
+
+		// -----------------------------------
+		// Set the in_progress flag
+		// -----------------------------------
+
+		$this->in_progress = TRUE;
+
+		// -----------------------------------
+		// Call the requested class and/or function
+		// -----------------------------------
+
+		if ($class !== FALSE)
+		{
+			if ( ! class_exists($class))
+			{
+				require($filepath);
+			}
+
+			$HOOK = new $class;
+			$HOOK->$function($params);
+		}
+		else
+		{
+			if ( ! function_exists($function))
+			{
+				require($filepath);
+			}
+
+			$function($params);
+		}
+
+		$this->in_progress = FALSE;
+		return TRUE;
+	}
+
+}
+
+// END CI_Hooks class
+
+/* End of file Hooks.php */
 /* Location: ./system/libraries/Hooks.php */
\ No newline at end of file
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index 67b7309..16201ee 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -1,1546 +1,1548 @@
-<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

-/**

- * CodeIgniter

- *

- * An open source application development framework for PHP 4.3.2 or newer

- *

- * @package		CodeIgniter

- * @author		ExpressionEngine Dev Team

- * @copyright	Copyright (c) 2008, EllisLab, Inc.

- * @license		http://codeigniter.com/user_guide/license.html

- * @link		http://codeigniter.com

- * @since		Version 1.0

- * @filesource

- */

-

-// ------------------------------------------------------------------------

-

-/**

- * Image Manipulation class

- *

- * @package		CodeIgniter

- * @subpackage	Libraries

- * @category	Image_lib

- * @author		ExpressionEngine Dev Team

- * @link		http://codeigniter.com/user_guide/libraries/image_lib.html

- */

-class CI_Image_lib {

-	

-	var $image_library		= 'gd2';  	// Can be:  imagemagick, netpbm, gd, gd2

-	var $library_path		= '';

-	var $dynamic_output		= FALSE;	// Whether to send to browser or write to disk

-	var $source_image		= '';	

-	var $new_image			= '';

-	var $width				= '';

-	var $height				= '';

-	var $quality			= '90';

-	var $create_thumb		= FALSE;

-	var $thumb_marker		= '_thumb';

-	var $maintain_ratio		= TRUE;  	// Whether to maintain aspect ratio when resizing or use hard values

-	var $master_dim			= 'auto';	// auto, height, or width.  Determines what to use as the master dimension

-	var $rotation_angle		= '';

-	var $x_axis				= '';

-	var	$y_axis				= '';

-	

-	// Watermark Vars

-	var $wm_text			= '';			// Watermark text if graphic is not used

-	var $wm_type			= 'text';		// Type of watermarking.  Options:  text/overlay

-	var $wm_x_transp		= 4;

-	var $wm_y_transp		= 4;

-	var $wm_overlay_path	= '';			// Watermark image path

-	var $wm_font_path		= '';			// TT font

-	var $wm_font_size		= 17;			// Font size (different versions of GD will either use points or pixels)

-	var $wm_vrt_alignment	= 'B';			// Vertical alignment:   T M B

-	var $wm_hor_alignment	= 'C';			// Horizontal alignment: L R C

-	var $wm_padding			= 0;			// Padding around text

-	var $wm_hor_offset		= 0;			// Lets you push text to the right

-	var $wm_vrt_offset		= 0;			 // Lets you push  text down

-	var $wm_font_color		= '#ffffff';	// Text color

-	var $wm_shadow_color	= '';			// Dropshadow color

-	var $wm_shadow_distance	= 2;			// Dropshadow distance

-	var $wm_opacity			= 50; 			// Image opacity: 1 - 100  Only works with image

-	

-	// Private Vars

-	var $source_folder		= '';

-	var $dest_folder		= '';

-	var $mime_type			= '';

-	var $orig_width			= '';

-	var $orig_height		= '';

-	var $image_type			= '';

-	var $size_str			= '';

-	var $full_src_path		= '';

-	var $full_dst_path		= '';

-	var $create_fnc			= 'imagecreatetruecolor';

-	var $copy_fnc			= 'imagecopyresampled';

-	var $error_msg			= array();

-	var $wm_use_drop_shadow	= FALSE;

-	var $wm_use_truetype	= FALSE;		

-	

-	/**

-	 * Constructor

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function CI_Image_lib($props = array())

-	{

-		if (count($props) > 0)

-		{

-			$this->initialize($props);

-		}

-		

-		log_message('debug', "Image Lib Class Initialized");

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Initialize image properties

-	 *

-	 * Resets values in case this class is used in a loop

-	 *

-	 * @access	public

-	 * @return	void

-	 */	

-	function clear()

-	{

-		$props = array('source_folder', 'dest_folder', 'source_image', 'full_src_path', 'full_dst_path', 'new_image', 'image_type', 'size_str', 'quality', 'orig_width', 'orig_height', 'rotation_angle', 'x_axis', 'y_axis', 'create_fnc', 'copy_fnc', 'wm_overlay_path', 'wm_use_truetype', 'dynamic_output', 'wm_font_size', 'wm_text', 'wm_vrt_alignment', 'wm_hor_alignment', 'wm_padding', 'wm_hor_offset', 'wm_vrt_offset', 'wm_font_color', 'wm_use_drop_shadow', 'wm_shadow_color', 'wm_shadow_distance', 'wm_opacity');

-	

-		foreach ($props as $val)

-		{

-			$this->$val = '';

-		}

-

-		// special consideration for master_dim

-		$this->master_dim = 'auto';

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * initialize image preferences

-	 *

-	 * @access	public

-	 * @param	array

-	 * @return	void

-	 */	

-	function initialize($props = array())

-	{

-		/*

-		 * Convert array elements into class variables

-		 */

-		if (count($props) > 0)

-		{

-			foreach ($props as $key => $val)

-			{

-				$this->$key = $val;

-			}

-		}

-

-		/*

-		 * Is there a source image?

-		 *

-		 * If not, there's no reason to continue

-		 *

-		 */

-		if ($this->source_image == '')

-		{

-			$this->set_error('imglib_source_image_required');

-			return FALSE;	   	

-		}

-		

-		/*

-		 * Is getimagesize() Available?

-		 *

-		 * We use it to determine the image properties (width/height).

-		 * Note:  We need to figure out how to determine image

-		 * properties using ImageMagick and NetPBM

-		 *

-		 */		

-		if ( ! function_exists('getimagesize'))

-		{

-			$this->set_error('imglib_gd_required_for_props');

-			return FALSE;		

-		}

-		

-		$this->image_library = strtolower($this->image_library);

-		

-		/*

-		 * Set the full server path

-		 *

-		 * The source image may or may not contain a path.

-		 * Either way, we'll try use realpath to generate the

-		 * full server path in order to more reliably read it.

-		 *

-		 */	

-		if (function_exists('realpath') AND @realpath($this->source_image) !== FALSE)

-		{

-			$full_source_path = str_replace("\\", "/", realpath($this->source_image));

-		}

-		else

-		{

-			$full_source_path = $this->source_image;

-		}

-		

-		$x = explode('/', $full_source_path);

-		$this->source_image = end($x);

-		$this->source_folder = str_replace($this->source_image, '', $full_source_path);

-								

-		// Set the Image Properties

-		if ( ! $this->get_image_properties($this->source_folder.$this->source_image))

-		{

-			return FALSE;	   	

-		}				

-

-		/*

-		 * Assign the "new" image name/path

-		 *

-		 * If the user has set a "new_image" name it means

-		 * we are making a copy of the source image. If not

-		 * it means we are altering the original.  We'll

-		 * set the destination filename and path accordingly.

-		 *

-		 */			

-		if ($this->new_image == '')

-		{

-			$this->dest_image = $this->source_image;

-			$this->dest_folder = $this->source_folder;

-		}

-		else

-		{

-			if (strpos($this->new_image, '/') === FALSE)

-			{

-				$this->dest_folder = $this->source_folder;

-				$this->dest_image = $this->new_image;

-			}

-			else

-			{

-				if (function_exists('realpath') AND @realpath($this->new_image) !== FALSE)

-				{

-					$full_dest_path = str_replace("\\", "/", realpath($this->new_image));

-				}

-				else

-				{

-					$full_dest_path = $this->new_image;

-				}

-				

-				// Is there a file name?

-				if ( ! preg_match("#\.(jpg|jpeg|gif|png)$#i", $full_dest_path))

-				{

-					$this->dest_folder = $full_dest_path.'/';

-					$this->dest_image = $this->source_image;

-				}

-				else

-				{

-					$x = explode('/', $full_dest_path);

-					$this->dest_image = end($x);

-					$this->dest_folder = str_replace($this->dest_image, '', $full_dest_path);

-				}

-			}

-		}

-

-		/*

-		 * Compile the finalized filenames/paths

-		 *

-		 * We'll create two master strings containing the

-		 * full server path to the source image and the

-		 * full server path to the destination image.

-		 * We'll also split the destination image name

-		 * so we can insert the thumbnail marker if needed.

-		 *

-		 */	

-		if ($this->create_thumb === FALSE OR $this->thumb_marker == '')

-		{

-			$this->thumb_marker = '';

-		}

-

-		$xp	= $this->explode_name($this->dest_image);

-	

-		$filename = $xp['name'];

-		$file_ext = $xp['ext'];

-				

-		$this->full_src_path = $this->source_folder.$this->source_image;		

-		$this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext;

-

-		/*

-		 * Should we maintain image proportions?

-		 *

-		 * When creating thumbs or copies, the target width/height

-		 * might not be in correct proportion with the source

-		 * image's width/height.  We'll recalculate it here.

-		 *

-		 */	

-		if ($this->maintain_ratio === TRUE && ($this->width != '' AND $this->height != ''))

-		{

-			$this->image_reproportion();

-		}

-

-		/*

-		 * Was a width and height specified?

-		 *

-		 * If the destination width/height was

-		 * not submitted we will use the values

-		 * from the actual file

-		 *

-		 */	

-		if ($this->width == '')

-			$this->width = $this->orig_width;

-	

-		if ($this->height == '')

-			$this->height = $this->orig_height;

-	

-		// Set the quality

-		$this->quality = trim(str_replace("%", "", $this->quality));

-		

-		if ($this->quality == '' OR $this->quality == 0 OR ! is_numeric($this->quality))

-			$this->quality = 90;

-	

-		// Set the x/y coordinates

-		$this->x_axis = ($this->x_axis == '' OR ! is_numeric($this->x_axis)) ? 0 : $this->x_axis;

-		$this->y_axis = ($this->y_axis == '' OR ! is_numeric($this->y_axis)) ? 0 : $this->y_axis;

-	

-		// Watermark-related Stuff...

-		if ($this->wm_font_color != '')

-		{

-			if (strlen($this->wm_font_color) == 6)

-			{

-				$this->wm_font_color = '#'.$this->wm_font_color;

-			}

-		}

-		

-		if ($this->wm_shadow_color != '')

-		{

-			if (strlen($this->wm_shadow_color) == 6)

-			{

-				$this->wm_shadow_color = '#'.$this->wm_shadow_color;

-			}

-		}

-	

-		if ($this->wm_overlay_path != '')

-		{

-			$this->wm_overlay_path = str_replace("\\", "/", realpath($this->wm_overlay_path));

-		}

-	

-		if ($this->wm_shadow_color != '')

-		{

-			$this->wm_use_drop_shadow = TRUE;

-		}

-

-		if ($this->wm_font_path != '')

-		{

-			$this->wm_use_truetype = TRUE;

-		}

-

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Image Resize

-	 *

-	 * This is a wrapper function that chooses the proper

-	 * resize function based on the protocol specified

-	 *

-	 * @access	public

-	 * @return	bool

-	 */	

-	function resize()

-	{

-		$protocol = 'image_process_'.$this->image_library;

-		

-		if (eregi("gd2$", $protocol))

-		{

-			$protocol = 'image_process_gd';

-		}

-		

-		return $this->$protocol('resize');

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Image Crop

-	 *

-	 * This is a wrapper function that chooses the proper

-	 * cropping function based on the protocol specified

-	 *

-	 * @access	public

-	 * @return	bool

-	 */	

-	function crop()

-	{

-		$protocol = 'image_process_'.$this->image_library;

-		

-		if (eregi("gd2$", $protocol))

-		{

-			$protocol = 'image_process_gd';

-		}

-		

-		return $this->$protocol('crop');

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Image Rotate

-	 *

-	 * This is a wrapper function that chooses the proper

-	 * rotation function based on the protocol specified

-	 *

-	 * @access	public

-	 * @return	bool

-	 */	

-	function rotate()

-	{

-		// Allowed rotation values		

-		$degs = array(90, 180, 270, 'vrt', 'hor');	

-	

-		if ($this->rotation_angle == '' OR ! in_array($this->rotation_angle, $degs, TRUE))

-		{

-			$this->set_error('imglib_rotation_angle_required');

-			return FALSE;	   	

-		}

-	

-		// Reassign the width and height

-		if ($this->rotation_angle == 90 OR $this->rotation_angle == 270)

-		{

-			$this->width	= $this->orig_height;

-			$this->height	= $this->orig_width;

-		}

-		else

-		{

-			$this->width	= $this->orig_width;

-			$this->height	= $this->orig_height;

-		}

-	

-

-		// Choose resizing function

-		if ($this->image_library == 'imagemagick' OR $this->image_library == 'netpbm')

-		{

-			$protocol = 'image_process_'.$this->image_library;

-		

-			return $this->$protocol('rotate');

-		}

-		

-		if ($this->rotation_angle == 'hor' OR $this->rotation_angle == 'vrt')

-		{

-			return $this->image_mirror_gd();

-		}

-		else

-		{		

-			return $this->image_rotate_gd();

-		}

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Image Process Using GD/GD2

-	 *

-	 * This function will resize or crop

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	bool

-	 */		

-	function image_process_gd($action = 'resize')

-	{	

-		$v2_override = FALSE;

-

-		// If the target width/height match the source, AND if the new file name is not equal to the old file name

-		// we'll simply make a copy of the original with the new name... assuming dynamic rendering is off.

-		if ($this->dynamic_output === FALSE)

-		{

-			if ($this->orig_width == $this->width AND $this->orig_height == $this->height)			

-			{

- 				if ($this->source_image != $this->new_image)

- 				{

-					if (@copy($this->full_src_path, $this->full_dst_path))

-					{

-						@chmod($this->full_dst_path, DIR_WRITE_MODE);

-					}

-				}

-				

-				return TRUE;

-			}

-		}

-		

-		// Let's set up our values based on the action

-		if ($action == 'crop')

-		{

-			//  Reassign the source width/height if cropping

-			$this->orig_width  = $this->width;

-			$this->orig_height = $this->height;	

-				

-			// GD 2.0 has a cropping bug so we'll test for it

-			if ($this->gd_version() !== FALSE)

-			{

-				$gd_version = str_replace('0', '', $this->gd_version());			

-				$v2_override = ($gd_version == 2) ? TRUE : FALSE;

-			}

-		}

-		else

-		{			

-			// If resizing the x/y axis must be zero

-			$this->x_axis = 0;

-			$this->y_axis = 0;

-		}

-		

-		//  Create the image handle

-		if ( ! ($src_img = $this->image_create_gd()))

-		{		

-			return FALSE;

-		}

-

- 		//  Create The Image

-		//

-		//  old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater"

-		//  it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment

-		//  below should that ever prove inaccurate.

-		//

-		//  if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor') AND $v2_override == FALSE)

- 		if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor'))		

-		{

-			$create	= 'imagecreatetruecolor';

-			$copy	= 'imagecopyresampled';

-		}

-		else

-		{

-			$create	= 'imagecreate';	

-			$copy	= 'imagecopyresized';

-		}

-			

-		$dst_img = $create($this->width, $this->height);

-		$copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height);

-

-		//  Show the image	

-		if ($this->dynamic_output == TRUE)

-		{

-			$this->image_display_gd($dst_img);

-		}

-		else

-		{

-			// Or save it

-			if ( ! $this->image_save_gd($dst_img))

-			{

-				return FALSE;

-			}

-		}

-

-		//  Kill the file handles

-		imagedestroy($dst_img);

-		imagedestroy($src_img);

-		

-		// Set the file to 777

-		@chmod($this->full_dst_path, DIR_WRITE_MODE);

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Image Process Using ImageMagick

-	 *

-	 * This function will resize, crop or rotate

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	bool

-	 */		

-	function image_process_imagemagick($action = 'resize')

-	{

-		//  Do we have a vaild library path?

-		if ($this->library_path == '')

-		{

-			$this->set_error('imglib_libpath_invalid');

-			return FALSE;

-		}

-				

-		if ( ! eregi("convert$", $this->library_path))

-		{

-			if ( ! eregi("/$", $this->library_path)) $this->library_path .= "/";

-		

-			$this->library_path .= 'convert';

-		}

-		

-		// Execute the command

-		$cmd = $this->library_path." -quality ".$this->quality;

-	

-		if ($action == 'crop')

-		{

-			$cmd .= " -crop ".$this->width."x".$this->height."+".$this->x_axis."+".$this->y_axis." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";

-		}

-		elseif ($action == 'rotate')

-		{

-			switch ($this->rotation_angle)

-			{

-				case 'hor' 	: $angle = '-flop';

-					break;

-				case 'vrt' 	: $angle = '-flip';

-					break;

-				default		: $angle = '-rotate '.$this->rotation_angle;

-					break;

-			}			

-		

-			$cmd .= " ".$angle." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";

-		}

-		else  // Resize

-		{

-			$cmd .= " -resize ".$this->width."x".$this->height." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";

-		}

-	

-		$retval = 1;

-	

-		@exec($cmd, $output, $retval);

-

-		//	Did it work?	

-		if ($retval > 0)

-		{

-			$this->set_error('imglib_image_process_failed');

-			return FALSE;

-		}

-		

-		// Set the file to 777

-		@chmod($this->full_dst_path, DIR_WRITE_MODE);

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Image Process Using NetPBM

-	 *

-	 * This function will resize, crop or rotate

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	bool

-	 */		

-	function image_process_netpbm($action = 'resize')

-	{

-		if ($this->library_path == '')

-		{

-			$this->set_error('imglib_libpath_invalid');

-			return FALSE;

-		}

-			

-		//  Build the resizing command

-		switch ($this->image_type)

-		{

-			case 1 :

-						$cmd_in		= 'giftopnm';

-						$cmd_out	= 'ppmtogif';

-				break;

-			case 2 :

-						$cmd_in		= 'jpegtopnm';

-						$cmd_out	= 'ppmtojpeg';			

-				break;

-			case 3 :

-						$cmd_in		= 'pngtopnm';

-						$cmd_out	= 'ppmtopng';

-				break;

-		}

-		

-		if ($action == 'crop')

-		{

-			$cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height;

-		}

-		elseif ($action == 'rotate')

-		{

-			switch ($this->rotation_angle)

-			{

-				case 90		:	$angle = 'r270';

-					break;

-				case 180	:	$angle = 'r180';

-					break;

-				case 270 	:	$angle = 'r90';

-					break;

-				case 'vrt'	:	$angle = 'tb';

-					break;

-				case 'hor'	:	$angle = 'lr';

-					break;

-			}

-		

-			$cmd_inner = 'pnmflip -'.$angle.' ';

-		}

-		else // Resize

-		{

-			$cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height;

-		}

-						

-		$cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';

-		

-		$retval = 1;

-		

-		@exec($cmd, $output, $retval);

-		

-		//  Did it work?

-		if ($retval > 0)

-		{

-			$this->set_error('imglib_image_process_failed');

-			return FALSE;

-		}

-		

-		// With NetPBM we have to create a temporary image.

-		// If you try manipulating the original it fails so

-		// we have to rename the temp file.

-		copy ($this->dest_folder.'netpbm.tmp', $this->full_dst_path);

-		unlink ($this->dest_folder.'netpbm.tmp');

-		@chmod($this->full_dst_path, DIR_WRITE_MODE);

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Image Rotate Using GD

-	 *

-	 * @access	public

-	 * @return	bool

-	 */		

-	function image_rotate_gd()

-	{	

-		// Is Image Rotation Supported?

-		// this function is only supported as of PHP 4.3

-		if ( ! function_exists('imagerotate'))

-		{

-			$this->set_error('imglib_rotate_unsupported');

-			return FALSE;

-		}

-		

-		//  Create the image handle

-		if ( ! ($src_img = $this->image_create_gd()))

-		{		

-			return FALSE;

-		}

-

-		// Set the background color		

-		// This won't work with transparent PNG files so we are

-		// going to have to figure out how to determine the color

-		// of the alpha channel in a future release.

-	

-		$white	= imagecolorallocate($src_img, 255, 255, 255);

-

-		//  Rotate it!

-		$dst_img = imagerotate($src_img, $this->rotation_angle, $white);

-	

-		//  Save the Image

-		if ($this->dynamic_output == TRUE)

-		{

-			$this->image_display_gd($dst_img);

-		}

-		else

-		{

-			// Or save it

-			if ( ! $this->image_save_gd($dst_img))

-			{

-				return FALSE;

-			}

-		}

-

-		//  Kill the file handles

-		imagedestroy($dst_img);

-		imagedestroy($src_img);

-		

-		// Set the file to 777

-		

-		@chmod($this->full_dst_path, DIR_WRITE_MODE);

-		

-		return true;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Create Mirror Image using GD

-	 *

-	 * This function will flip horizontal or vertical

-	 *

-	 * @access	public

-	 * @return	bool

-	 */			

-	function image_mirror_gd()

-	{		

-		if ( ! $src_img = $this->image_create_gd())

-		{

-			return FALSE;

-		}

-		

-		$width  = $this->orig_width;

-		$height = $this->orig_height;

-	

-		if ($this->rotation_angle == 'hor')

-		{

-			for ($i = 0; $i < $height; $i++)

-			{

-				$left  = 0;

-				$right = $width-1;

-	

-				while ($left < $right)

-				{

-					$cl = imagecolorat($src_img, $left, $i);

-					$cr = imagecolorat($src_img, $right, $i);

-					

-					imagesetpixel($src_img, $left, $i, $cr);

-					imagesetpixel($src_img, $right, $i, $cl);

-					

-					$left++;

-					$right--;

-				}

-			}

-		}

-		else

-		{

-			for ($i = 0; $i < $width; $i++)

-			{

-				$top = 0;

-				$bot = $height-1;

-	

-				while ($top < $bot)

-				{

-					$ct = imagecolorat($src_img, $i, $top);

-					$cb = imagecolorat($src_img, $i, $bot);

-					

-					imagesetpixel($src_img, $i, $top, $cb);

-					imagesetpixel($src_img, $i, $bot, $ct);

-					

-					$top++;

-					$bot--;

-				}

-			}		

-		}		

-

-		//  Show the image

-		if ($this->dynamic_output == TRUE)

-		{

-			$this->image_display_gd($src_img);

-		}

-		else

-		{

-			// Or save it

-			if ( ! $this->image_save_gd($src_img))

-			{

-				return FALSE;

-			}

-		}

-		

-		//  Kill the file handles

-		imagedestroy($src_img);

-		

-		// Set the file to 777

-		@chmod($this->full_dst_path, DIR_WRITE_MODE);

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Image Watermark

-	 *

-	 * This is a wrapper function that chooses the type

-	 * of watermarking based on the specified preference.

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	bool

-	 */	

-	function watermark()

-	{

-		if ($this->wm_type == 'overlay')

-		{

-			return $this->overlay_watermark();

-		}

-		else

-		{

-			return $this->text_watermark();

-		}

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Watermark - Graphic Version

-	 *

-	 * @access	public

-	 * @return	bool

-	 */			

-	function overlay_watermark()

-	{

-		if ( ! function_exists('imagecolortransparent'))

-		{

-			$this->set_error('imglib_gd_required');

-			return FALSE;		

-		}

-	

-		//  Fetch source image properties

-		$this->get_image_properties();

-

-		//  Fetch watermark image properties

-		$props 			= $this->get_image_properties($this->wm_overlay_path, TRUE);	

-		$wm_img_type	= $props['image_type'];

-		$wm_width		= $props['width'];

-		$wm_height		= $props['height'];

-	

-		//  Create two image resources	

-		$wm_img  = $this->image_create_gd($this->wm_overlay_path, $wm_img_type);

-		$src_img = $this->image_create_gd($this->full_src_path);

-		

-		// Reverse the offset if necessary		

-		// When the image is positioned at the bottom

-		// we don't want the vertical offset to push it

-		// further down.  We want the reverse, so we'll

-		// invert the offset.  Same with the horizontal

-		// offset when the image is at the right

-		

-		$this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));

-		$this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1));

-	

-		if ($this->wm_vrt_alignment == 'B')

-			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;

-	

-		if ($this->wm_hor_alignment == 'R')

-			$this->wm_hor_offset = $this->wm_hor_offset * -1;

-

-		//  Set the base x and y axis values

-		$x_axis = $this->wm_hor_offset + $this->wm_padding;

-		$y_axis = $this->wm_vrt_offset + $this->wm_padding;

-

-		//  Set the vertical position

-		switch ($this->wm_vrt_alignment)

-		{

-			case 'T':

-				break;

-			case 'M':	$y_axis += ($this->orig_height / 2) - ($wm_height / 2);

-				break;

-			case 'B':	$y_axis += $this->orig_height - $wm_height;

-				break;

-		}

-

-		//  Set the horizontal position

-		switch ($this->wm_hor_alignment)

-		{

-			case 'L':

-				break;	

-			case 'C':	$x_axis += ($this->orig_width / 2) - ($wm_width / 2);

-				break;

-			case 'R':	$x_axis += $this->orig_width - $wm_width;

-				break;

-		}

-	

-		//  Build the finalized image			

-		if ($wm_img_type == 3 AND function_exists('imagealphablending'))

-		{

-			@imagealphablending($src_img, TRUE);

-		} 		

-

-		// Set RGB values for text and shadow

-		$rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp);

-		$alpha = ($rgba & 0x7F000000) >> 24;

-		

-		// make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency

-		if ($alpha > 0)

-		{

-			// copy the image directly, the image's alpha transparency being the sole determinant of blending

-			imagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height);

-		}

-		else

-		{

-			// set our RGB value from above to be transparent and merge the images with the specified opacity

-			imagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp));

-			imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity);			

-		}

-				

-		//  Output the image

-		if ($this->dynamic_output == TRUE)

-		{

-			$this->image_display_gd($src_img);

-		}

-		else

-		{

-			if ( ! $this->image_save_gd($src_img))

-			{

-				return FALSE;

-			}

-		}

-		

-		imagedestroy($src_img);

-		imagedestroy($wm_img);

-				

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Watermark - Text Version

-	 *

-	 * @access	public

-	 * @return	bool

-	 */			

-	function text_watermark()

-	{

-		if ( ! ($src_img = $this->image_create_gd()))

-		{		

-			return FALSE;

-		}

-				

-		if ($this->wm_use_truetype == TRUE AND ! file_exists($this->wm_font_path))

-		{

-			$this->set_error('imglib_missing_font');

-			return FALSE;

-		}

-		

-		//  Fetch source image properties		

-		$this->get_image_properties();				

-		

-		// Set RGB values for text and shadow		

-		$this->wm_font_color	= str_replace('#', '', $this->wm_font_color);

-		$this->wm_shadow_color	= str_replace('#', '', $this->wm_shadow_color);

-		

-		$R1 = hexdec(substr($this->wm_font_color, 0, 2));

-		$G1 = hexdec(substr($this->wm_font_color, 2, 2));

-		$B1 = hexdec(substr($this->wm_font_color, 4, 2));

-	

-		$R2 = hexdec(substr($this->wm_shadow_color, 0, 2));

-		$G2 = hexdec(substr($this->wm_shadow_color, 2, 2));

-		$B2 = hexdec(substr($this->wm_shadow_color, 4, 2));

-		

-		$txt_color	= imagecolorclosest($src_img, $R1, $G1, $B1);

-		$drp_color	= imagecolorclosest($src_img, $R2, $G2, $B2);

-

-		// Reverse the vertical offset

-		// When the image is positioned at the bottom

-		// we don't want the vertical offset to push it

-		// further down.  We want the reverse, so we'll

-		// invert the offset.  Note: The horizontal

-		// offset flips itself automatically

-	

-		if ($this->wm_vrt_alignment == 'B')

-			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;

-			

-		if ($this->wm_hor_alignment == 'R')

-			$this->wm_hor_offset = $this->wm_hor_offset * -1;

-

-		// Set font width and height

-		// These are calculated differently depending on

-		// whether we are using the true type font or not

-		if ($this->wm_use_truetype == TRUE)

-		{

-			if ($this->wm_font_size == '')

-				$this->wm_font_size = '17';

-		

-			$fontwidth  = $this->wm_font_size-($this->wm_font_size/4);

-			$fontheight = $this->wm_font_size;

-			$this->wm_vrt_offset += $this->wm_font_size;

-		}

-		else

-		{

-			$fontwidth  = imagefontwidth($this->wm_font_size);

-			$fontheight = imagefontheight($this->wm_font_size);

-		}

-

-		// Set base X and Y axis values

-		$x_axis = $this->wm_hor_offset + $this->wm_padding;

-		$y_axis = $this->wm_vrt_offset + $this->wm_padding;

-

-		// Set verticle alignment

-		if ($this->wm_use_drop_shadow == FALSE)

-			$this->wm_shadow_distance = 0;

-			

-		$this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));

-		$this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1));

-	

-		switch ($this->wm_vrt_alignment)

-		{

-			case	 "T" :

-				break;

-			case "M":	$y_axis += ($this->orig_height/2)+($fontheight/2);

-				break;

-			case "B":	$y_axis += ($this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight/2));

-				break;

-		}

-	

-		$x_shad = $x_axis + $this->wm_shadow_distance;

-		$y_shad = $y_axis + $this->wm_shadow_distance;

-		

-		// Set horizontal alignment

-		switch ($this->wm_hor_alignment)

-		{

-			case "L":

-				break;

-			case "R":

-						if ($this->wm_use_drop_shadow)

-							$x_shad += ($this->orig_width - $fontwidth*strlen($this->wm_text));

-							$x_axis += ($this->orig_width - $fontwidth*strlen($this->wm_text));

-				break;

-			case "C":

-						if ($this->wm_use_drop_shadow)

-							$x_shad += floor(($this->orig_width - $fontwidth*strlen($this->wm_text))/2);

-							$x_axis += floor(($this->orig_width  -$fontwidth*strlen($this->wm_text))/2);

-				break;

-		}

-		

-		//  Add the text to the source image

-		if ($this->wm_use_truetype)

-		{	

-			if ($this->wm_use_drop_shadow)

-				imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text);

-				imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text);

-		}

-		else

-		{

-			if ($this->wm_use_drop_shadow)

-				imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);

-				imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);

-		}

-	

-		//  Output the final image

-		if ($this->dynamic_output == TRUE)

-		{

-			$this->image_display_gd($src_img);

-		}

-		else

-		{

-			$this->image_save_gd($src_img);

-		}

-		

-		imagedestroy($src_img);

-	

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Create Image - GD

-	 *

-	 * This simply creates an image resource handle

-	 * based on the type of image being processed

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	resource

-	 */			

-	function image_create_gd($path = '', $image_type = '')

-	{

-		if ($path == '')

-			$path = $this->full_src_path;

-			

-		if ($image_type == '')

-			$image_type = $this->image_type;

-	

-		

-		switch ($image_type)

-		{

-			case	 1 :

-						if ( ! function_exists('imagecreatefromgif'))

-						{

-							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));

-							return FALSE;

-						}

-					

-						return imagecreatefromgif($path);

-				break;

-			case 2 :

-						if ( ! function_exists('imagecreatefromjpeg'))

-						{

-							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));

-							return FALSE;

-						}

-					

-						return imagecreatefromjpeg($path);

-				break;

-			case 3 :

-						if ( ! function_exists('imagecreatefrompng'))

-						{

-							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));				

-							return FALSE;

-						}

-					

-						return imagecreatefrompng($path);

-				break;			

-		

-		}

-		

-		$this->set_error(array('imglib_unsupported_imagecreate'));

-		return FALSE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Write image file to disk - GD

-	 *

-	 * Takes an image resource as input and writes the file

-	 * to the specified destination

-	 *

-	 * @access	public

-	 * @param	resource

-	 * @return	bool

-	 */			

-	function image_save_gd($resource)

-	{	

-		switch ($this->image_type)

-		{

-			case 1 :

-						if ( ! function_exists('imagegif'))

-						{

-							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));

-							return FALSE;		

-						}

-						

-						@imagegif($resource, $this->full_dst_path);

-				break;

-			case 2	:

-						if ( ! function_exists('imagejpeg'))

-						{

-							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));

-							return FALSE;		

-						}

-						

-						if (phpversion() == '4.4.1')

-						{

-							@touch($this->full_dst_path); // PHP 4.4.1 bug #35060 - workaround

-						}

-						

-						@imagejpeg($resource, $this->full_dst_path, $this->quality);

-				break;

-			case 3	:

-						if ( ! function_exists('imagepng'))

-						{

-							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));

-							return FALSE;		

-						}

-					

-						@imagepng($resource, $this->full_dst_path);

-				break;

-			default		:

-							$this->set_error(array('imglib_unsupported_imagecreate'));

-							return FALSE;		

-				break;		

-		}

-	

-		return TRUE;

-	}	

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Dynamically outputs an image

-	 *

-	 * @access	public

-	 * @param	resource

-	 * @return	void

-	 */			

-	function image_display_gd($resource)

-	{		

-		header("Content-Disposition: filename={$this->source_image};");

-		header("Content-Type: {$this->mime_type}");

-		header('Content-Transfer-Encoding: binary');

-		header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');

-	

-		switch ($this->image_type)

-		{

-			case 1 		:	imagegif($resource);

-				break;

-			case 2		:	imagejpeg($resource, '', $this->quality);

-				break;

-			case 3		:	imagepng($resource);

-				break;

-			default		:	echo 'Unable to display the image';

-				break;		

-		}			

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Re-proportion Image Width/Height

-	 *

-	 * When creating thumbs, the desired width/height

-	 * can end up warping the image due to an incorrect

-	 * ratio between the full-sized image and the thumb.

-	 *

-	 * This function lets us re-proportion the width/height

-	 * if users choose to maintain the aspect ratio when resizing.

-	 *

-	 * @access	public

-	 * @return	void

-	 */			

-	function image_reproportion()

-	{

-		if ( ! is_numeric($this->width) OR ! is_numeric($this->height) OR $this->width == 0 OR $this->height == 0)

-			return;

-		

-		if ( ! is_numeric($this->orig_width) OR ! is_numeric($this->orig_height) OR $this->orig_width == 0 OR $this->orig_height == 0)

-			return;

-		

-		$new_width	= ceil($this->orig_width*$this->height/$this->orig_height);		

-		$new_height	= ceil($this->width*$this->orig_height/$this->orig_width);

-		

-		$ratio = (($this->orig_height/$this->orig_width) - ($this->height/$this->width));

-	

-		if ($this->master_dim != 'width' AND $this->master_dim != 'height')

-		{

-			$this->master_dim = ($ratio < 0) ? 'width' : 'height';

-		}

-		

-		if (($this->width != $new_width) AND ($this->height != $new_height))

-		{

-			if ($this->master_dim == 'height')

-			{

-				$this->width = $new_width;

-			}

-			else

-			{

-				$this->height = $new_height;

-			}

-		}

-	}	

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Get image properties

-	 *

-	 * A helper function that gets info about the file

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	mixed

-	 */			

-	function get_image_properties($path = '', $return = FALSE)

-	{

-		// For now we require GD but we should

-		// find a way to determine this using IM or NetPBM

-		

-		if ($path == '')

-			$path = $this->full_src_path;

-				

-		if ( ! file_exists($path))

-		{

-			$this->set_error('imglib_invalid_path');		

-			return FALSE;				

-		}

-		

-		$vals = @getimagesize($path);

-		

-		$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');

-		

-		$mime = (isset($types[$vals['2']])) ? 'image/'.$types[$vals['2']] : 'image/jpg';

-				

-		if ($return == TRUE)

-		{

-			$v['width']			= $vals['0'];

-			$v['height']		= $vals['1'];

-			$v['image_type']	= $vals['2'];

-			$v['size_str']		= $vals['3'];

-			$v['mime_type']		= $mime;

-			

-			return $v;

-		}

-		

-		$this->orig_width	= $vals['0'];

-		$this->orig_height	= $vals['1'];

-		$this->image_type	= $vals['2'];

-		$this->size_str		= $vals['3'];

-		$this->mime_type	= $mime;

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Size calculator

-	 *

-	 * This function takes a known width x height and

-	 * recalculates it to a new size.  Only one

-	 * new variable needs to be known

-	 *

-	 *	$props = array(

-	 *					'width' 		=> $width,

-	 *					'height' 		=> $height,

-	 *					'new_width'		=> 40,

-	 *					'new_height'	=> ''

-	 *				  );

-	 *

-	 * @access	public

-	 * @param	array

-	 * @return	array

-	 */			

-	function size_calculator($vals)

-	{

-		if ( ! is_array($vals))

-			return;

-			

-		$allowed = array('new_width', 'new_height', 'width', 'height');

-	

-		foreach ($allowed as $item)

-		{

-			if ( ! isset($vals[$item]) OR $vals[$item] == '')

-				$vals[$item] = 0;

-		}

-		

-		if ($vals['width'] == 0 OR $vals['height'] == 0)

-		{

-			return $vals;

-		}

-			

-		if ($vals['new_width'] == 0)

-		{

-			$vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']);

-		}

-		elseif ($vals['new_height'] == 0)

-		{

-			$vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']);

-		}

-	

-		return $vals;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Explode source_image

-	 *

-	 * This is a helper function that extracts the extension

-	 * from the source_image.  This function lets us deal with

-	 * source_images with multiple periods, like:  my.cool.jpg

-	 * It returns an associative array with two elements:

-	 * $array['ext']  = '.jpg';

-	 * $array['name'] = 'my.cool';

-	 *

-	 * @access	public

-	 * @param	array

-	 * @return	array

-	 */	

-	function explode_name($source_image)

-	{

-		$x = explode('.', $source_image);

-		$ret['ext'] = '.'.end($x);

-		

-		$name = '';

-		

-		$ct = count($x)-1;

-		

-		for ($i = 0; $i < $ct; $i++)

-		{

-			$name .= $x[$i];

-			

-			if ($i < ($ct - 1))

-			{

-				$name .= '.';

-			}

-		}

-		

-		$ret['name'] = $name;

-		

-		return $ret;

-	}	

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Is GD Installed?

-	 *

-	 * @access	public

-	 * @return	bool

-	 */	

-	function gd_loaded()

-	{

-		if ( ! extension_loaded('gd'))

-		{

-			if ( ! dl('gd.so'))

-			{

-				return FALSE;

-			}

-		}

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Get GD version

-	 *

-	 * @access	public

-	 * @return	mixed

-	 */	

-	function gd_version()

-	{

-		if (function_exists('gd_info'))

-		{

-			$gd_version = @gd_info();

-			$gd_version = preg_replace("/\D/", "", $gd_version['GD Version']);

-			

-			return $gd_version;

-		}

-		

-		return FALSE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Set error message

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */	

-	function set_error($msg)

-	{

-		$CI =& get_instance();

-		$CI->lang->load('imglib');

-		

-		if (is_array($msg))

-		{

-			foreach ($msg as $val)

-			{

-				

-				$msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);

-				$this->error_msg[] = $msg;

-				log_message('error', $msg);

-			}		

-		}

-		else

-		{

-			$msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);

-			$this->error_msg[] = $msg;

-			log_message('error', $msg);

-		}

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Show error messages

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	string

-	 */	

-	function display_errors($open = '<p>', $close = '</p>')

-	{	

-		$str = '';

-		foreach ($this->error_msg as $val)

-		{

-			$str .= $open.$val.$close;

-		}

-	

-		return $str;

-	}

-

-}

-// END Image_lib Class

-

-/* End of file Image_lib.php */

+<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2008, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * Image Manipulation class
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Image_lib
+ * @author		ExpressionEngine Dev Team
+ * @link		http://codeigniter.com/user_guide/libraries/image_lib.html
+ */
+class CI_Image_lib {
+
+	var $image_library		= 'gd2';  	// Can be:  imagemagick, netpbm, gd, gd2
+	var $library_path		= '';
+	var $dynamic_output		= FALSE;	// Whether to send to browser or write to disk
+	var $source_image		= '';
+	var $new_image			= '';
+	var $width				= '';
+	var $height				= '';
+	var $quality			= '90';
+	var $create_thumb		= FALSE;
+	var $thumb_marker		= '_thumb';
+	var $maintain_ratio		= TRUE;  	// Whether to maintain aspect ratio when resizing or use hard values
+	var $master_dim			= 'auto';	// auto, height, or width.  Determines what to use as the master dimension
+	var $rotation_angle		= '';
+	var $x_axis				= '';
+	var	$y_axis				= '';
+
+	// Watermark Vars
+	var $wm_text			= '';			// Watermark text if graphic is not used
+	var $wm_type			= 'text';		// Type of watermarking.  Options:  text/overlay
+	var $wm_x_transp		= 4;
+	var $wm_y_transp		= 4;
+	var $wm_overlay_path	= '';			// Watermark image path
+	var $wm_font_path		= '';			// TT font
+	var $wm_font_size		= 17;			// Font size (different versions of GD will either use points or pixels)
+	var $wm_vrt_alignment	= 'B';			// Vertical alignment:   T M B
+	var $wm_hor_alignment	= 'C';			// Horizontal alignment: L R C
+	var $wm_padding			= 0;			// Padding around text
+	var $wm_hor_offset		= 0;			// Lets you push text to the right
+	var $wm_vrt_offset		= 0;			 // Lets you push  text down
+	var $wm_font_color		= '#ffffff';	// Text color
+	var $wm_shadow_color	= '';			// Dropshadow color
+	var $wm_shadow_distance	= 2;			// Dropshadow distance
+	var $wm_opacity			= 50; 			// Image opacity: 1 - 100  Only works with image
+
+	// Private Vars
+	var $source_folder		= '';
+	var $dest_folder		= '';
+	var $mime_type			= '';
+	var $orig_width			= '';
+	var $orig_height		= '';
+	var $image_type			= '';
+	var $size_str			= '';
+	var $full_src_path		= '';
+	var $full_dst_path		= '';
+	var $create_fnc			= 'imagecreatetruecolor';
+	var $copy_fnc			= 'imagecopyresampled';
+	var $error_msg			= array();
+	var $wm_use_drop_shadow	= FALSE;
+	var $wm_use_truetype	= FALSE;
+
+	/**
+	 * Constructor
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function CI_Image_lib($props = array())
+	{
+		if (count($props) > 0)
+		{
+			$this->initialize($props);
+		}
+
+		log_message('debug', "Image Lib Class Initialized");
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Initialize image properties
+	 *
+	 * Resets values in case this class is used in a loop
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function clear()
+	{
+		$props = array('source_folder', 'dest_folder', 'source_image', 'full_src_path', 'full_dst_path', 'new_image', 'image_type', 'size_str', 'quality', 'orig_width', 'orig_height', 'rotation_angle', 'x_axis', 'y_axis', 'create_fnc', 'copy_fnc', 'wm_overlay_path', 'wm_use_truetype', 'dynamic_output', 'wm_font_size', 'wm_text', 'wm_vrt_alignment', 'wm_hor_alignment', 'wm_padding', 'wm_hor_offset', 'wm_vrt_offset', 'wm_font_color', 'wm_use_drop_shadow', 'wm_shadow_color', 'wm_shadow_distance', 'wm_opacity');
+
+		foreach ($props as $val)
+		{
+			$this->$val = '';
+		}
+
+		// special consideration for master_dim
+		$this->master_dim = 'auto';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * initialize image preferences
+	 *
+	 * @access	public
+	 * @param	array
+	 * @return	bool
+	 */
+	function initialize($props = array())
+	{
+		/*
+		 * Convert array elements into class variables
+		 */
+		if (count($props) > 0)
+		{
+			foreach ($props as $key => $val)
+			{
+				$this->$key = $val;
+			}
+		}
+
+		/*
+		 * Is there a source image?
+		 *
+		 * If not, there's no reason to continue
+		 *
+		 */
+		if ($this->source_image == '')
+		{
+			$this->set_error('imglib_source_image_required');
+			return FALSE;	   
+		}
+
+		/*
+		 * Is getimagesize() Available?
+		 *
+		 * We use it to determine the image properties (width/height).
+		 * Note:  We need to figure out how to determine image
+		 * properties using ImageMagick and NetPBM
+		 *
+		 */
+		if ( ! function_exists('getimagesize'))
+		{
+			$this->set_error('imglib_gd_required_for_props');
+			return FALSE;
+		}
+
+		$this->image_library = strtolower($this->image_library);
+
+		/*
+		 * Set the full server path
+		 *
+		 * The source image may or may not contain a path.
+		 * Either way, we'll try use realpath to generate the
+		 * full server path in order to more reliably read it.
+		 *
+		 */
+		if (function_exists('realpath') AND @realpath($this->source_image) !== FALSE)
+		{
+			$full_source_path = str_replace("\\", "/", realpath($this->source_image));
+		}
+		else
+		{
+			$full_source_path = $this->source_image;
+		}
+
+		$x = explode('/', $full_source_path);
+		$this->source_image = end($x);
+		$this->source_folder = str_replace($this->source_image, '', $full_source_path);
+
+		// Set the Image Properties
+		if ( ! $this->get_image_properties($this->source_folder.$this->source_image))
+		{
+			return FALSE;	   
+		}
+
+		/*
+		 * Assign the "new" image name/path
+		 *
+		 * If the user has set a "new_image" name it means
+		 * we are making a copy of the source image. If not
+		 * it means we are altering the original.  We'll
+		 * set the destination filename and path accordingly.
+		 *
+		 */
+		if ($this->new_image == '')
+		{
+			$this->dest_image = $this->source_image;
+			$this->dest_folder = $this->source_folder;
+		}
+		else
+		{
+			if (strpos($this->new_image, '/') === FALSE)
+			{
+				$this->dest_folder = $this->source_folder;
+				$this->dest_image = $this->new_image;
+			}
+			else
+			{
+				if (function_exists('realpath') AND @realpath($this->new_image) !== FALSE)
+				{
+					$full_dest_path = str_replace("\\", "/", realpath($this->new_image));
+				}
+				else
+				{
+					$full_dest_path = $this->new_image;
+				}
+
+				// Is there a file name?
+				if ( ! preg_match("#\.(jpg|jpeg|gif|png)$#i", $full_dest_path))
+				{
+					$this->dest_folder = $full_dest_path.'/';
+					$this->dest_image = $this->source_image;
+				}
+				else
+				{
+					$x = explode('/', $full_dest_path);
+					$this->dest_image = end($x);
+					$this->dest_folder = str_replace($this->dest_image, '', $full_dest_path);
+				}
+			}
+		}
+
+		/*
+		 * Compile the finalized filenames/paths
+		 *
+		 * We'll create two master strings containing the
+		 * full server path to the source image and the
+		 * full server path to the destination image.
+		 * We'll also split the destination image name
+		 * so we can insert the thumbnail marker if needed.
+		 *
+		 */
+		if ($this->create_thumb === FALSE OR $this->thumb_marker == '')
+		{
+			$this->thumb_marker = '';
+		}
+
+		$xp	= $this->explode_name($this->dest_image);
+
+		$filename = $xp['name'];
+		$file_ext = $xp['ext'];
+
+		$this->full_src_path = $this->source_folder.$this->source_image;
+		$this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext;
+
+		/*
+		 * Should we maintain image proportions?
+		 *
+		 * When creating thumbs or copies, the target width/height
+		 * might not be in correct proportion with the source
+		 * image's width/height.  We'll recalculate it here.
+		 *
+		 */
+		if ($this->maintain_ratio === TRUE && ($this->width != '' AND $this->height != ''))
+		{
+			$this->image_reproportion();
+		}
+
+		/*
+		 * Was a width and height specified?
+		 *
+		 * If the destination width/height was
+		 * not submitted we will use the values
+		 * from the actual file
+		 *
+		 */
+		if ($this->width == '')
+			$this->width = $this->orig_width;
+
+		if ($this->height == '')
+			$this->height = $this->orig_height;
+
+		// Set the quality
+		$this->quality = trim(str_replace("%", "", $this->quality));
+
+		if ($this->quality == '' OR $this->quality == 0 OR ! is_numeric($this->quality))
+			$this->quality = 90;
+
+		// Set the x/y coordinates
+		$this->x_axis = ($this->x_axis == '' OR ! is_numeric($this->x_axis)) ? 0 : $this->x_axis;
+		$this->y_axis = ($this->y_axis == '' OR ! is_numeric($this->y_axis)) ? 0 : $this->y_axis;
+
+		// Watermark-related Stuff...
+		if ($this->wm_font_color != '')
+		{
+			if (strlen($this->wm_font_color) == 6)
+			{
+				$this->wm_font_color = '#'.$this->wm_font_color;
+			}
+		}
+
+		if ($this->wm_shadow_color != '')
+		{
+			if (strlen($this->wm_shadow_color) == 6)
+			{
+				$this->wm_shadow_color = '#'.$this->wm_shadow_color;
+			}
+		}
+
+		if ($this->wm_overlay_path != '')
+		{
+			$this->wm_overlay_path = str_replace("\\", "/", realpath($this->wm_overlay_path));
+		}
+
+		if ($this->wm_shadow_color != '')
+		{
+			$this->wm_use_drop_shadow = TRUE;
+		}
+
+		if ($this->wm_font_path != '')
+		{
+			$this->wm_use_truetype = TRUE;
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Image Resize
+	 *
+	 * This is a wrapper function that chooses the proper
+	 * resize function based on the protocol specified
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function resize()
+	{
+		$protocol = 'image_process_'.$this->image_library;
+
+		if (eregi("gd2$", $protocol))
+		{
+			$protocol = 'image_process_gd';
+		}
+
+		return $this->$protocol('resize');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Image Crop
+	 *
+	 * This is a wrapper function that chooses the proper
+	 * cropping function based on the protocol specified
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function crop()
+	{
+		$protocol = 'image_process_'.$this->image_library;
+
+		if (eregi("gd2$", $protocol))
+		{
+			$protocol = 'image_process_gd';
+		}
+
+		return $this->$protocol('crop');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Image Rotate
+	 *
+	 * This is a wrapper function that chooses the proper
+	 * rotation function based on the protocol specified
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function rotate()
+	{
+		// Allowed rotation values
+		$degs = array(90, 180, 270, 'vrt', 'hor');
+
+		if ($this->rotation_angle == '' OR ! in_array($this->rotation_angle, $degs, TRUE))
+		{
+			$this->set_error('imglib_rotation_angle_required');
+			return FALSE;	   
+		}
+
+		// Reassign the width and height
+		if ($this->rotation_angle == 90 OR $this->rotation_angle == 270)
+		{
+			$this->width	= $this->orig_height;
+			$this->height	= $this->orig_width;
+		}
+		else
+		{
+			$this->width	= $this->orig_width;
+			$this->height	= $this->orig_height;
+		}
+
+
+		// Choose resizing function
+		if ($this->image_library == 'imagemagick' OR $this->image_library == 'netpbm')
+		{
+			$protocol = 'image_process_'.$this->image_library;
+
+			return $this->$protocol('rotate');
+		}
+
+		if ($this->rotation_angle == 'hor' OR $this->rotation_angle == 'vrt')
+		{
+			return $this->image_mirror_gd();
+		}
+		else
+		{
+			return $this->image_rotate_gd();
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Image Process Using GD/GD2
+	 *
+	 * This function will resize or crop
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	bool
+	 */
+	function image_process_gd($action = 'resize')
+	{
+		$v2_override = FALSE;
+
+		// If the target width/height match the source, AND if the new file name is not equal to the old file name
+		// we'll simply make a copy of the original with the new name... assuming dynamic rendering is off.
+		if ($this->dynamic_output === FALSE)
+		{
+			if ($this->orig_width == $this->width AND $this->orig_height == $this->height)
+			{
+ 				if ($this->source_image != $this->new_image)
+ 				{
+					if (@copy($this->full_src_path, $this->full_dst_path))
+					{
+						@chmod($this->full_dst_path, DIR_WRITE_MODE);
+					}
+				}
+
+				return TRUE;
+			}
+		}
+
+		// Let's set up our values based on the action
+		if ($action == 'crop')
+		{
+			//  Reassign the source width/height if cropping
+			$this->orig_width  = $this->width;
+			$this->orig_height = $this->height;
+
+			// GD 2.0 has a cropping bug so we'll test for it
+			if ($this->gd_version() !== FALSE)
+			{
+				$gd_version = str_replace('0', '', $this->gd_version());
+				$v2_override = ($gd_version == 2) ? TRUE : FALSE;
+			}
+		}
+		else
+		{
+			// If resizing the x/y axis must be zero
+			$this->x_axis = 0;
+			$this->y_axis = 0;
+		}
+
+		//  Create the image handle
+		if ( ! ($src_img = $this->image_create_gd()))
+		{
+			return FALSE;
+		}
+
+ 		//  Create The Image
+		//
+		//  old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater"
+		//  it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment
+		//  below should that ever prove inaccurate.
+		//
+		//  if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor') AND $v2_override == FALSE)
+ 		if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor'))
+		{
+			$create	= 'imagecreatetruecolor';
+			$copy	= 'imagecopyresampled';
+		}
+		else
+		{
+			$create	= 'imagecreate';
+			$copy	= 'imagecopyresized';
+		}
+
+		$dst_img = $create($this->width, $this->height);
+		$copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height);
+
+		//  Show the image
+		if ($this->dynamic_output == TRUE)
+		{
+			$this->image_display_gd($dst_img);
+		}
+		else
+		{
+			// Or save it
+			if ( ! $this->image_save_gd($dst_img))
+			{
+				return FALSE;
+			}
+		}
+
+		//  Kill the file handles
+		imagedestroy($dst_img);
+		imagedestroy($src_img);
+
+		// Set the file to 777
+		@chmod($this->full_dst_path, DIR_WRITE_MODE);
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Image Process Using ImageMagick
+	 *
+	 * This function will resize, crop or rotate
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	bool
+	 */
+	function image_process_imagemagick($action = 'resize')
+	{
+		//  Do we have a vaild library path?
+		if ($this->library_path == '')
+		{
+			$this->set_error('imglib_libpath_invalid');
+			return FALSE;
+		}
+
+		if ( ! eregi("convert$", $this->library_path))
+		{
+			if ( ! eregi("/$", $this->library_path)) $this->library_path .= "/";
+
+			$this->library_path .= 'convert';
+		}
+
+		// Execute the command
+		$cmd = $this->library_path." -quality ".$this->quality;
+
+		if ($action == 'crop')
+		{
+			$cmd .= " -crop ".$this->width."x".$this->height."+".$this->x_axis."+".$this->y_axis." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";
+		}
+		elseif ($action == 'rotate')
+		{
+			switch ($this->rotation_angle)
+			{
+				case 'hor' 	: $angle = '-flop';
+					break;
+				case 'vrt' 	: $angle = '-flip';
+					break;
+				default		: $angle = '-rotate '.$this->rotation_angle;
+					break;
+			}
+
+			$cmd .= " ".$angle." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";
+		}
+		else  // Resize
+		{
+			$cmd .= " -resize ".$this->width."x".$this->height." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";
+		}
+
+		$retval = 1;
+
+		@exec($cmd, $output, $retval);
+
+		//	Did it work?
+		if ($retval > 0)
+		{
+			$this->set_error('imglib_image_process_failed');
+			return FALSE;
+		}
+
+		// Set the file to 777
+		@chmod($this->full_dst_path, DIR_WRITE_MODE);
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Image Process Using NetPBM
+	 *
+	 * This function will resize, crop or rotate
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	bool
+	 */
+	function image_process_netpbm($action = 'resize')
+	{
+		if ($this->library_path == '')
+		{
+			$this->set_error('imglib_libpath_invalid');
+			return FALSE;
+		}
+
+		//  Build the resizing command
+		switch ($this->image_type)
+		{
+			case 1 :
+						$cmd_in		= 'giftopnm';
+						$cmd_out	= 'ppmtogif';
+				break;
+			case 2 :
+						$cmd_in		= 'jpegtopnm';
+						$cmd_out	= 'ppmtojpeg';
+				break;
+			case 3 :
+						$cmd_in		= 'pngtopnm';
+						$cmd_out	= 'ppmtopng';
+				break;
+		}
+
+		if ($action == 'crop')
+		{
+			$cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height;
+		}
+		elseif ($action == 'rotate')
+		{
+			switch ($this->rotation_angle)
+			{
+				case 90		:	$angle = 'r270';
+					break;
+				case 180	:	$angle = 'r180';
+					break;
+				case 270 	:	$angle = 'r90';
+					break;
+				case 'vrt'	:	$angle = 'tb';
+					break;
+				case 'hor'	:	$angle = 'lr';
+					break;
+			}
+
+			$cmd_inner = 'pnmflip -'.$angle.' ';
+		}
+		else // Resize
+		{
+			$cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height;
+		}
+
+		$cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
+
+		$retval = 1;
+
+		@exec($cmd, $output, $retval);
+
+		//  Did it work?
+		if ($retval > 0)
+		{
+			$this->set_error('imglib_image_process_failed');
+			return FALSE;
+		}
+
+		// With NetPBM we have to create a temporary image.
+		// If you try manipulating the original it fails so
+		// we have to rename the temp file.
+		copy ($this->dest_folder.'netpbm.tmp', $this->full_dst_path);
+		unlink ($this->dest_folder.'netpbm.tmp');
+		@chmod($this->full_dst_path, DIR_WRITE_MODE);
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Image Rotate Using GD
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function image_rotate_gd()
+	{
+		// Is Image Rotation Supported?
+		// this function is only supported as of PHP 4.3
+		if ( ! function_exists('imagerotate'))
+		{
+			$this->set_error('imglib_rotate_unsupported');
+			return FALSE;
+		}
+
+		//  Create the image handle
+		if ( ! ($src_img = $this->image_create_gd()))
+		{
+			return FALSE;
+		}
+
+		// Set the background color
+		// This won't work with transparent PNG files so we are
+		// going to have to figure out how to determine the color
+		// of the alpha channel in a future release.
+
+		$white	= imagecolorallocate($src_img, 255, 255, 255);
+
+		//  Rotate it!
+		$dst_img = imagerotate($src_img, $this->rotation_angle, $white);
+
+		//  Save the Image
+		if ($this->dynamic_output == TRUE)
+		{
+			$this->image_display_gd($dst_img);
+		}
+		else
+		{
+			// Or save it
+			if ( ! $this->image_save_gd($dst_img))
+			{
+				return FALSE;
+			}
+		}
+
+		//  Kill the file handles
+		imagedestroy($dst_img);
+		imagedestroy($src_img);
+
+		// Set the file to 777
+
+		@chmod($this->full_dst_path, DIR_WRITE_MODE);
+
+		return true;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Create Mirror Image using GD
+	 *
+	 * This function will flip horizontal or vertical
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function image_mirror_gd()
+	{
+		if ( ! $src_img = $this->image_create_gd())
+		{
+			return FALSE;
+		}
+
+		$width  = $this->orig_width;
+		$height = $this->orig_height;
+
+		if ($this->rotation_angle == 'hor')
+		{
+			for ($i = 0; $i < $height; $i++)
+			{
+				$left  = 0;
+				$right = $width-1;
+
+				while ($left < $right)
+				{
+					$cl = imagecolorat($src_img, $left, $i);
+					$cr = imagecolorat($src_img, $right, $i);
+
+					imagesetpixel($src_img, $left, $i, $cr);
+					imagesetpixel($src_img, $right, $i, $cl);
+
+					$left++;
+					$right--;
+				}
+			}
+		}
+		else
+		{
+			for ($i = 0; $i < $width; $i++)
+			{
+				$top = 0;
+				$bot = $height-1;
+
+				while ($top < $bot)
+				{
+					$ct = imagecolorat($src_img, $i, $top);
+					$cb = imagecolorat($src_img, $i, $bot);
+
+					imagesetpixel($src_img, $i, $top, $cb);
+					imagesetpixel($src_img, $i, $bot, $ct);
+
+					$top++;
+					$bot--;
+				}
+			}
+		}
+
+		//  Show the image
+		if ($this->dynamic_output == TRUE)
+		{
+			$this->image_display_gd($src_img);
+		}
+		else
+		{
+			// Or save it
+			if ( ! $this->image_save_gd($src_img))
+			{
+				return FALSE;
+			}
+		}
+
+		//  Kill the file handles
+		imagedestroy($src_img);
+
+		// Set the file to 777
+		@chmod($this->full_dst_path, DIR_WRITE_MODE);
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Image Watermark
+	 *
+	 * This is a wrapper function that chooses the type
+	 * of watermarking based on the specified preference.
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	bool
+	 */
+	function watermark()
+	{
+		if ($this->wm_type == 'overlay')
+		{
+			return $this->overlay_watermark();
+		}
+		else
+		{
+			return $this->text_watermark();
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Watermark - Graphic Version
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function overlay_watermark()
+	{
+		if ( ! function_exists('imagecolortransparent'))
+		{
+			$this->set_error('imglib_gd_required');
+			return FALSE;
+		}
+
+		//  Fetch source image properties
+		$this->get_image_properties();
+
+		//  Fetch watermark image properties
+		$props 			= $this->get_image_properties($this->wm_overlay_path, TRUE);
+		$wm_img_type	= $props['image_type'];
+		$wm_width		= $props['width'];
+		$wm_height		= $props['height'];
+
+		//  Create two image resources
+		$wm_img  = $this->image_create_gd($this->wm_overlay_path, $wm_img_type);
+		$src_img = $this->image_create_gd($this->full_src_path);
+
+		// Reverse the offset if necessary
+		// When the image is positioned at the bottom
+		// we don't want the vertical offset to push it
+		// further down.  We want the reverse, so we'll
+		// invert the offset.  Same with the horizontal
+		// offset when the image is at the right
+
+		$this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));
+		$this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1));
+
+		if ($this->wm_vrt_alignment == 'B')
+			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
+
+		if ($this->wm_hor_alignment == 'R')
+			$this->wm_hor_offset = $this->wm_hor_offset * -1;
+
+		//  Set the base x and y axis values
+		$x_axis = $this->wm_hor_offset + $this->wm_padding;
+		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
+
+		//  Set the vertical position
+		switch ($this->wm_vrt_alignment)
+		{
+			case 'T':
+				break;
+			case 'M':	$y_axis += ($this->orig_height / 2) - ($wm_height / 2);
+				break;
+			case 'B':	$y_axis += $this->orig_height - $wm_height;
+				break;
+		}
+
+		//  Set the horizontal position
+		switch ($this->wm_hor_alignment)
+		{
+			case 'L':
+				break;
+			case 'C':	$x_axis += ($this->orig_width / 2) - ($wm_width / 2);
+				break;
+			case 'R':	$x_axis += $this->orig_width - $wm_width;
+				break;
+		}
+
+		//  Build the finalized image
+		if ($wm_img_type == 3 AND function_exists('imagealphablending'))
+		{
+			@imagealphablending($src_img, TRUE);
+		} 
+
+		// Set RGB values for text and shadow
+		$rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp);
+		$alpha = ($rgba & 0x7F000000) >> 24;
+
+		// make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency
+		if ($alpha > 0)
+		{
+			// copy the image directly, the image's alpha transparency being the sole determinant of blending
+			imagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height);
+		}
+		else
+		{
+			// set our RGB value from above to be transparent and merge the images with the specified opacity
+			imagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp));
+			imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity);
+		}
+
+		//  Output the image
+		if ($this->dynamic_output == TRUE)
+		{
+			$this->image_display_gd($src_img);
+		}
+		else
+		{
+			if ( ! $this->image_save_gd($src_img))
+			{
+				return FALSE;
+			}
+		}
+
+		imagedestroy($src_img);
+		imagedestroy($wm_img);
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Watermark - Text Version
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function text_watermark()
+	{
+		if ( ! ($src_img = $this->image_create_gd()))
+		{
+			return FALSE;
+		}
+
+		if ($this->wm_use_truetype == TRUE AND ! file_exists($this->wm_font_path))
+		{
+			$this->set_error('imglib_missing_font');
+			return FALSE;
+		}
+
+		//  Fetch source image properties
+		$this->get_image_properties();
+
+		// Set RGB values for text and shadow
+		$this->wm_font_color	= str_replace('#', '', $this->wm_font_color);
+		$this->wm_shadow_color	= str_replace('#', '', $this->wm_shadow_color);
+
+		$R1 = hexdec(substr($this->wm_font_color, 0, 2));
+		$G1 = hexdec(substr($this->wm_font_color, 2, 2));
+		$B1 = hexdec(substr($this->wm_font_color, 4, 2));
+
+		$R2 = hexdec(substr($this->wm_shadow_color, 0, 2));
+		$G2 = hexdec(substr($this->wm_shadow_color, 2, 2));
+		$B2 = hexdec(substr($this->wm_shadow_color, 4, 2));
+
+		$txt_color	= imagecolorclosest($src_img, $R1, $G1, $B1);
+		$drp_color	= imagecolorclosest($src_img, $R2, $G2, $B2);
+
+		// Reverse the vertical offset
+		// When the image is positioned at the bottom
+		// we don't want the vertical offset to push it
+		// further down.  We want the reverse, so we'll
+		// invert the offset.  Note: The horizontal
+		// offset flips itself automatically
+
+		if ($this->wm_vrt_alignment == 'B')
+			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
+
+		if ($this->wm_hor_alignment == 'R')
+			$this->wm_hor_offset = $this->wm_hor_offset * -1;
+
+		// Set font width and height
+		// These are calculated differently depending on
+		// whether we are using the true type font or not
+		if ($this->wm_use_truetype == TRUE)
+		{
+			if ($this->wm_font_size == '')
+				$this->wm_font_size = '17';
+
+			$fontwidth  = $this->wm_font_size-($this->wm_font_size/4);
+			$fontheight = $this->wm_font_size;
+			$this->wm_vrt_offset += $this->wm_font_size;
+		}
+		else
+		{
+			$fontwidth  = imagefontwidth($this->wm_font_size);
+			$fontheight = imagefontheight($this->wm_font_size);
+		}
+
+		// Set base X and Y axis values
+		$x_axis = $this->wm_hor_offset + $this->wm_padding;
+		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
+
+		// Set verticle alignment
+		if ($this->wm_use_drop_shadow == FALSE)
+			$this->wm_shadow_distance = 0;
+
+		$this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));
+		$this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1));
+
+		switch ($this->wm_vrt_alignment)
+		{
+			case	 "T" :
+				break;
+			case "M":	$y_axis += ($this->orig_height/2)+($fontheight/2);
+				break;
+			case "B":	$y_axis += ($this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight/2));
+				break;
+		}
+
+		$x_shad = $x_axis + $this->wm_shadow_distance;
+		$y_shad = $y_axis + $this->wm_shadow_distance;
+
+		// Set horizontal alignment
+		switch ($this->wm_hor_alignment)
+		{
+			case "L":
+				break;
+			case "R":
+						if ($this->wm_use_drop_shadow)
+							$x_shad += ($this->orig_width - $fontwidth*strlen($this->wm_text));
+							$x_axis += ($this->orig_width - $fontwidth*strlen($this->wm_text));
+				break;
+			case "C":
+						if ($this->wm_use_drop_shadow)
+							$x_shad += floor(($this->orig_width - $fontwidth*strlen($this->wm_text))/2);
+							$x_axis += floor(($this->orig_width  -$fontwidth*strlen($this->wm_text))/2);
+				break;
+		}
+
+		//  Add the text to the source image
+		if ($this->wm_use_truetype)
+		{
+			if ($this->wm_use_drop_shadow)
+				imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text);
+				imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text);
+		}
+		else
+		{
+			if ($this->wm_use_drop_shadow)
+				imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);
+				imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);
+		}
+
+		//  Output the final image
+		if ($this->dynamic_output == TRUE)
+		{
+			$this->image_display_gd($src_img);
+		}
+		else
+		{
+			$this->image_save_gd($src_img);
+		}
+
+		imagedestroy($src_img);
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Create Image - GD
+	 *
+	 * This simply creates an image resource handle
+	 * based on the type of image being processed
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	resource
+	 */
+	function image_create_gd($path = '', $image_type = '')
+	{
+		if ($path == '')
+			$path = $this->full_src_path;
+
+		if ($image_type == '')
+			$image_type = $this->image_type;
+
+
+		switch ($image_type)
+		{
+			case	 1 :
+						if ( ! function_exists('imagecreatefromgif'))
+						{
+							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
+							return FALSE;
+						}
+
+						return imagecreatefromgif($path);
+				break;
+			case 2 :
+						if ( ! function_exists('imagecreatefromjpeg'))
+						{
+							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
+							return FALSE;
+						}
+
+						return imagecreatefromjpeg($path);
+				break;
+			case 3 :
+						if ( ! function_exists('imagecreatefrompng'))
+						{
+							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
+							return FALSE;
+						}
+
+						return imagecreatefrompng($path);
+				break;
+
+		}
+
+		$this->set_error(array('imglib_unsupported_imagecreate'));
+		return FALSE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Write image file to disk - GD
+	 *
+	 * Takes an image resource as input and writes the file
+	 * to the specified destination
+	 *
+	 * @access	public
+	 * @param	resource
+	 * @return	bool
+	 */
+	function image_save_gd($resource)
+	{
+		switch ($this->image_type)
+		{
+			case 1 :
+						if ( ! function_exists('imagegif'))
+						{
+							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
+							return FALSE;
+						}
+
+						@imagegif($resource, $this->full_dst_path);
+				break;
+			case 2	:
+						if ( ! function_exists('imagejpeg'))
+						{
+							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
+							return FALSE;
+						}
+
+						if (phpversion() == '4.4.1')
+						{
+							@touch($this->full_dst_path); // PHP 4.4.1 bug #35060 - workaround
+						}
+
+						@imagejpeg($resource, $this->full_dst_path, $this->quality);
+				break;
+			case 3	:
+						if ( ! function_exists('imagepng'))
+						{
+							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
+							return FALSE;
+						}
+
+						@imagepng($resource, $this->full_dst_path);
+				break;
+			default		:
+							$this->set_error(array('imglib_unsupported_imagecreate'));
+							return FALSE;
+				break;
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Dynamically outputs an image
+	 *
+	 * @access	public
+	 * @param	resource
+	 * @return	void
+	 */
+	function image_display_gd($resource)
+	{
+		header("Content-Disposition: filename={$this->source_image};");
+		header("Content-Type: {$this->mime_type}");
+		header('Content-Transfer-Encoding: binary');
+		header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');
+
+		switch ($this->image_type)
+		{
+			case 1 		:	imagegif($resource);
+				break;
+			case 2		:	imagejpeg($resource, '', $this->quality);
+				break;
+			case 3		:	imagepng($resource);
+				break;
+			default		:	echo 'Unable to display the image';
+				break;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Re-proportion Image Width/Height
+	 *
+	 * When creating thumbs, the desired width/height
+	 * can end up warping the image due to an incorrect
+	 * ratio between the full-sized image and the thumb.
+	 *
+	 * This function lets us re-proportion the width/height
+	 * if users choose to maintain the aspect ratio when resizing.
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function image_reproportion()
+	{
+		if ( ! is_numeric($this->width) OR ! is_numeric($this->height) OR $this->width == 0 OR $this->height == 0)
+			return;
+
+		if ( ! is_numeric($this->orig_width) OR ! is_numeric($this->orig_height) OR $this->orig_width == 0 OR $this->orig_height == 0)
+			return;
+
+		$new_width	= ceil($this->orig_width*$this->height/$this->orig_height);
+		$new_height	= ceil($this->width*$this->orig_height/$this->orig_width);
+
+		$ratio = (($this->orig_height/$this->orig_width) - ($this->height/$this->width));
+
+		if ($this->master_dim != 'width' AND $this->master_dim != 'height')
+		{
+			$this->master_dim = ($ratio < 0) ? 'width' : 'height';
+		}
+
+		if (($this->width != $new_width) AND ($this->height != $new_height))
+		{
+			if ($this->master_dim == 'height')
+			{
+				$this->width = $new_width;
+			}
+			else
+			{
+				$this->height = $new_height;
+			}
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get image properties
+	 *
+	 * A helper function that gets info about the file
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	mixed
+	 */
+	function get_image_properties($path = '', $return = FALSE)
+	{
+		// For now we require GD but we should
+		// find a way to determine this using IM or NetPBM
+
+		if ($path == '')
+			$path = $this->full_src_path;
+
+		if ( ! file_exists($path))
+		{
+			$this->set_error('imglib_invalid_path');
+			return FALSE;
+		}
+
+		$vals = @getimagesize($path);
+
+		$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
+
+		$mime = (isset($types[$vals['2']])) ? 'image/'.$types[$vals['2']] : 'image/jpg';
+
+		if ($return == TRUE)
+		{
+			$v['width']			= $vals['0'];
+			$v['height']		= $vals['1'];
+			$v['image_type']	= $vals['2'];
+			$v['size_str']		= $vals['3'];
+			$v['mime_type']		= $mime;
+
+			return $v;
+		}
+
+		$this->orig_width	= $vals['0'];
+		$this->orig_height	= $vals['1'];
+		$this->image_type	= $vals['2'];
+		$this->size_str		= $vals['3'];
+		$this->mime_type	= $mime;
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Size calculator
+	 *
+	 * This function takes a known width x height and
+	 * recalculates it to a new size.  Only one
+	 * new variable needs to be known
+	 *
+	 *	$props = array(
+	 *					'width' 		=> $width,
+	 *					'height' 		=> $height,
+	 *					'new_width'		=> 40,
+	 *					'new_height'	=> ''
+	 *				  );
+	 *
+	 * @access	public
+	 * @param	array
+	 * @return	array
+	 */
+	function size_calculator($vals)
+	{
+		if ( ! is_array($vals))
+		{
+			return;
+		}
+
+		$allowed = array('new_width', 'new_height', 'width', 'height');
+
+		foreach ($allowed as $item)
+		{
+			if ( ! isset($vals[$item]) OR $vals[$item] == '')
+				$vals[$item] = 0;
+		}
+
+		if ($vals['width'] == 0 OR $vals['height'] == 0)
+		{
+			return $vals;
+		}
+
+		if ($vals['new_width'] == 0)
+		{
+			$vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']);
+		}
+		elseif ($vals['new_height'] == 0)
+		{
+			$vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']);
+		}
+
+		return $vals;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Explode source_image
+	 *
+	 * This is a helper function that extracts the extension
+	 * from the source_image.  This function lets us deal with
+	 * source_images with multiple periods, like:  my.cool.jpg
+	 * It returns an associative array with two elements:
+	 * $array['ext']  = '.jpg';
+	 * $array['name'] = 'my.cool';
+	 *
+	 * @access	public
+	 * @param	array
+	 * @return	array
+	 */
+	function explode_name($source_image)
+	{
+		$x = explode('.', $source_image);
+		$ret['ext'] = '.'.end($x);
+
+		$name = '';
+
+		$ct = count($x)-1;
+
+		for ($i = 0; $i < $ct; $i++)
+		{
+			$name .= $x[$i];
+
+			if ($i < ($ct - 1))
+			{
+				$name .= '.';
+			}
+		}
+
+		$ret['name'] = $name;
+
+		return $ret;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Is GD Installed?
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function gd_loaded()
+	{
+		if ( ! extension_loaded('gd'))
+		{
+			if ( ! dl('gd.so'))
+			{
+				return FALSE;
+			}
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get GD version
+	 *
+	 * @access	public
+	 * @return	mixed
+	 */
+	function gd_version()
+	{
+		if (function_exists('gd_info'))
+		{
+			$gd_version = @gd_info();
+			$gd_version = preg_replace("/\D/", "", $gd_version['GD Version']);
+
+			return $gd_version;
+		}
+
+		return FALSE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set error message
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function set_error($msg)
+	{
+		$CI =& get_instance();
+		$CI->lang->load('imglib');
+
+		if (is_array($msg))
+		{
+			foreach ($msg as $val)
+			{
+
+				$msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
+				$this->error_msg[] = $msg;
+				log_message('error', $msg);
+			}
+		}
+		else
+		{
+			$msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
+			$this->error_msg[] = $msg;
+			log_message('error', $msg);
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show error messages
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	string
+	 */
+	function display_errors($open = '<p>', $close = '</p>')
+	{
+		$str = '';
+		foreach ($this->error_msg as $val)
+		{
+			$str .= $open.$val.$close;
+		}
+
+		return $str;
+	}
+
+}
+// END Image_lib Class
+
+/* End of file Image_lib.php */
 /* Location: ./system/libraries/Image_lib.php */
\ No newline at end of file
diff --git a/system/libraries/Language.php b/system/libraries/Language.php
index eb4613d..78f4143 100644
--- a/system/libraries/Language.php
+++ b/system/libraries/Language.php
@@ -1,124 +1,123 @@
-<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

-/**

- * CodeIgniter

- *

- * An open source application development framework for PHP 4.3.2 or newer

- *

- * @package		CodeIgniter

- * @author		ExpressionEngine Dev Team

- * @copyright	Copyright (c) 2008, EllisLab, Inc.

- * @license		http://codeigniter.com/user_guide/license.html

- * @link		http://codeigniter.com

- * @since		Version 1.0

- * @filesource

- */

-

-// ------------------------------------------------------------------------

-

-/**

- * Language Class

- *

- * @package		CodeIgniter

- * @subpackage	Libraries

- * @category	Language

- * @author		ExpressionEngine Dev Team

- * @link		http://codeigniter.com/user_guide/libraries/language.html

- */

-class CI_Language {

-

-	var $language	= array();

-	var $is_loaded	= array();

-

-	/**

-	 * Constructor

-	 *

-	 * @access	public

-	 */	

-	function CI_Language()

-	{

-		log_message('debug', "Language Class Initialized");

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Load a language file

-	 *

-	 * @access	public

-	 * @param	mixed	the name of the language file to be loaded. Can be an array

-	 * @param	string	the language (english, etc.)

-	 * @return	void

-	 */

-	function load($langfile = '', $idiom = '', $return = FALSE)

-	{	

-		$langfile = str_replace(EXT, '', str_replace('_lang.', '', $langfile)).'_lang'.EXT;

-		

-		if (in_array($langfile, $this->is_loaded, TRUE))

-		{

-			return;

-		}

-		

-		if ($idiom == '')

-		{

-			$CI =& get_instance();

-			$deft_lang = $CI->config->item('language');

-			$idiom = ($deft_lang == '') ? 'english' : $deft_lang;

-		}

-	

-		// Determine where the language file is and load it

-		if (file_exists(APPPATH.'language/'.$idiom.'/'.$langfile))

-		{

-			include(APPPATH.'language/'.$idiom.'/'.$langfile);

-		}

-		else

-		{		

-			if (file_exists(BASEPATH.'language/'.$idiom.'/'.$langfile))

-			{

-				include(BASEPATH.'language/'.$idiom.'/'.$langfile);

-			}

-			else

-			{

-				show_error('Unable to load the requested language file: language/'.$langfile);

-			}

-		}

-

-		

-		if ( ! isset($lang))

-		{

-			log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile);

-			return;

-		}

-		

-		if ($return == TRUE)

-		{

-			return $lang;

-		}

-		

-		$this->is_loaded[] = $langfile;

-		$this->language = array_merge($this->language, $lang);

-		unset($lang);

-		

-		log_message('debug', 'Language file loaded: language/'.$idiom.'/'.$langfile);

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Fetch a single line of text from the language array

-	 *

-	 * @access	public

-	 * @param	string	$line 	the language line

-	 * @return	string

-	 */

-	function line($line = '')

-	{

-		$line = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];

-		return $line;

-	}

-

-}

-// END Language Class

-

-/* End of file Language.php */

+<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2008, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * Language Class
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Language
+ * @author		ExpressionEngine Dev Team
+ * @link		http://codeigniter.com/user_guide/libraries/language.html
+ */
+class CI_Language {
+
+	var $language	= array();
+	var $is_loaded	= array();
+
+	/**
+	 * Constructor
+	 *
+	 * @access	public
+	 */
+	function CI_Language()
+	{
+		log_message('debug', "Language Class Initialized");
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Load a language file
+	 *
+	 * @access	public
+	 * @param	mixed	the name of the language file to be loaded. Can be an array
+	 * @param	string	the language (english, etc.)
+	 * @return	mixed
+	 */
+	function load($langfile = '', $idiom = '', $return = FALSE)
+	{
+		$langfile = str_replace(EXT, '', str_replace('_lang.', '', $langfile)).'_lang'.EXT;
+
+		if (in_array($langfile, $this->is_loaded, TRUE))
+		{
+			return;
+		}
+
+		if ($idiom == '')
+		{
+			$CI =& get_instance();
+			$deft_lang = $CI->config->item('language');
+			$idiom = ($deft_lang == '') ? 'english' : $deft_lang;
+		}
+
+		// Determine where the language file is and load it
+		if (file_exists(APPPATH.'language/'.$idiom.'/'.$langfile))
+		{
+			include(APPPATH.'language/'.$idiom.'/'.$langfile);
+		}
+		else
+		{
+			if (file_exists(BASEPATH.'language/'.$idiom.'/'.$langfile))
+			{
+				include(BASEPATH.'language/'.$idiom.'/'.$langfile);
+			}
+			else
+			{
+				show_error('Unable to load the requested language file: language/'.$langfile);
+			}
+		}
+
+		if ( ! isset($lang))
+		{
+			log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile);
+			return;
+		}
+
+		if ($return == TRUE)
+		{
+			return $lang;
+		}
+
+		$this->is_loaded[] = $langfile;
+		$this->language = array_merge($this->language, $lang);
+		unset($lang);
+
+		log_message('debug', 'Language file loaded: language/'.$idiom.'/'.$langfile);
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch a single line of text from the language array
+	 *
+	 * @access	public
+	 * @param	string	$line 	the language line
+	 * @return	string
+	 */
+	function line($line = '')
+	{
+		$line = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
+		return $line;
+	}
+
+}
+// END Language Class
+
+/* End of file Language.php */
 /* Location: ./system/libraries/Language.php */
\ No newline at end of file
diff --git a/system/libraries/Session.php b/system/libraries/Session.php
index ed5f9c3..660ce8f 100644
--- a/system/libraries/Session.php
+++ b/system/libraries/Session.php
@@ -1,758 +1,758 @@
-<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

-/**

- * CodeIgniter

- *

- * An open source application development framework for PHP 4.3.2 or newer

- *

- * @package		CodeIgniter

- * @author		ExpressionEngine Dev Team

- * @copyright	Copyright (c) 2008, EllisLab, Inc.

- * @license		http://codeigniter.com/user_guide/license.html

- * @link		http://codeigniter.com

- * @since		Version 1.0

- * @filesource

- */

-

-// ------------------------------------------------------------------------

-

-/**

- * Session Class

- *

- * @package		CodeIgniter

- * @subpackage	Libraries

- * @category	Sessions

- * @author		ExpressionEngine Dev Team

- * @link		http://codeigniter.com/user_guide/libraries/sessions.html

- */

-class CI_Session {

-

-	var $sess_encrypt_cookie		= FALSE;

-	var $sess_use_database			= FALSE;

-	var $sess_table_name			= '';

-	var $sess_expiration			= 7200;

-	var $sess_match_ip				= FALSE;

-	var $sess_match_useragent		= TRUE;

-	var $sess_cookie_name			= 'ci_session';

-	var $cookie_prefix				= '';

-	var $cookie_path				= '';

-	var $cookie_domain				= '';

-	var $sess_time_to_update		= 300;

-	var $encryption_key				= '';

-	var $flashdata_key 				= 'flash';

-	var $time_reference				= 'time';

-	var $gc_probability				= 5;

-	var $userdata					= array();

-	var $CI;

-	var $now;

-

-	/**

-	 * Session Constructor

-	 *

-	 * The constructor runs the session routines automatically

-	 * whenever the class is instantiated.

-	 */		

-	function CI_Session($params = array())

-	{

-		log_message('debug', "Session Class Initialized");

-

-		// Set the super object to a local variable for use throughout the class

-		$this->CI =& get_instance();

-		

-		// Set all the session preferences, which can either be set 

-		// manually via the $params array above or via the config file

-		foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path', 'cookie_domain', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key)

-		{

-			$this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key);

-		}		

-	

-		// Load the string helper so we can use the strip_slashes() function

-		$this->CI->load->helper('string');

-

-		// Do we need encryption? If so, load the encryption class

-		if ($this->sess_encrypt_cookie == TRUE)

-		{

-			$this->CI->load->library('encrypt');

-		}

-

-		// Are we using a database?  If so, load it

-		if ($this->sess_use_database === TRUE AND $this->sess_table_name != '')

-		{

-			$this->CI->load->database();

-		}

-

-		// Set the "now" time.  Can either be GMT or server time, based on the

-		// config prefs.  We use this to set the "last activity" time

-		$this->now = $this->_get_time();

-

-		// Set the session length. If the session expiration is

-		// set to zero we'll set the expiration two years from now.

-		if ($this->sess_expiration == 0)

-		{

-			$this->sess_expiration = (60*60*24*365*2);

-		}

-		 				

-		// Set the cookie name

-		$this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;

-	

-		// Run the Session routine. If a session doesn't exist we'll 

-		// create a new one.  If it does, we'll update it.

-		if ( ! $this->sess_read())

-		{

-			$this->sess_create();

-		}

-		else

-		{	

-			$this->sess_update();

-		}

-		

-		// Delete 'old' flashdata (from last request)

-	   	$this->_flashdata_sweep();

-		

-		// Mark all new flashdata as old (data will be deleted before next request)

-	   	$this->_flashdata_mark();

-

-		// Delete expired sessions if necessary

-		$this->_sess_gc();

-

-		log_message('debug', "Session routines successfully run");

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Fetch the current session data if it exists

-	 *

-	 * @access	public

-	 * @return	void

-	 */

-	function sess_read()

-	{	

-		// Fetch the cookie

-		$session = $this->CI->input->cookie($this->sess_cookie_name);

-		

-		// No cookie?  Goodbye cruel world!...

-		if ($session === FALSE)

-		{

-			log_message('debug', 'A session cookie was not found.');

-			return FALSE;

-		}

-		

-		// Decrypt the cookie data

-		if ($this->sess_encrypt_cookie == TRUE)

-		{

-			$session = $this->CI->encrypt->decode($session);

-		}

-		else

-		{	

-			// encryption was not used, so we need to check the md5 hash

-			$hash	 = substr($session, strlen($session)-32); // get last 32 chars

-			$session = substr($session, 0, strlen($session)-32);

-

-			// Does the md5 hash match?  This is to prevent manipulation of session data in userspace

-			if ($hash !==  md5($session.$this->encryption_key))

-			{

-				log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.');

-				$this->sess_destroy();

-				return FALSE;

-			}

-		}

-		

-		// Unserialize the session array

-		$session = $this->_unserialize($session);

-		

-		// Is the session data we unserialized an array with the correct format?

-		if ( ! is_array($session) OR ! isset($session['session_id']) OR ! isset($session['ip_address']) OR ! isset($session['user_agent']) OR ! isset($session['last_activity']))

-		{

-			$this->sess_destroy();

-			return FALSE;

-		}

-		

-		// Is the session current?

-		if (($session['last_activity'] + $this->sess_expiration) < $this->now)

-		{

-			$this->sess_destroy();

-			return FALSE;

-		}

-

-		// Does the IP Match?

-		if ($this->sess_match_ip == TRUE AND $session['ip_address'] != $this->CI->input->ip_address())

-		{

-			$this->sess_destroy();

-			return FALSE;

-		}

-		

-		// Does the User Agent Match?

-		if ($this->sess_match_useragent == TRUE AND trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 50)))

-		{

-			$this->sess_destroy();

-			return FALSE;

-		}

-		

-		// Is there a corresponding session in the DB?

-		if ($this->sess_use_database === TRUE)

-		{

-			$this->CI->db->where('session_id', $session['session_id']);

-					

-			if ($this->sess_match_ip == TRUE)

-			{

-				$this->CI->db->where('ip_address', $session['ip_address']);

-			}

-

-			if ($this->sess_match_useragent == TRUE)

-			{

-				$this->CI->db->where('user_agent', $session['user_agent']);

-			}

-			

-			$query = $this->CI->db->get($this->sess_table_name);

-

-			// No result?  Kill it!

-			if ($query->num_rows() == 0)

-			{

-				$this->sess_destroy();

-				return FALSE;

-			}

-

-			// Is there custom data?  If so, add it to the main session array

-			$row = $query->row();

-			if (isset($row->user_data) AND $row->user_data != '')

-			{

-				$custom_data = $this->_unserialize($row->user_data);

-

-				if (is_array($custom_data))

-				{

-					foreach ($custom_data as $key => $val)

-					{

-						$session[$key] = $val;

-					}

-				}

-			}				

-		}

-	

-		// Session is valid!

-		$this->userdata = $session;

-		unset($session);

-		

-		return TRUE;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Write the session data

-	 *

-	 * @access	public

-	 * @return	void

-	 */

-	function sess_write()

-	{

-		// Are we saving custom data to the DB?  If not, all we do is update the cookie

-		if ($this->sess_use_database === FALSE)

-		{

-			$this->_set_cookie();

-			return;

-		}

-

-		// set the custom userdata, the session data we will set in a second

-		$custom_userdata = $this->userdata;

-		$cookie_userdata = array();

-		

-		// Before continuing, we need to determine if there is any custom data to deal with.

-		// Let's determine this by removing the default indexes to see if there's anything left in the array

-		// and set the session data while we're at it

-		foreach (array('session_id','ip_address','user_agent','last_activity') as $val)

-		{

-			unset($custom_userdata[$val]);

-			$cookie_userdata[$val] = $this->userdata[$val];

-		}

-		

-		// Did we find any custom data?  If not, we turn the empty array into a string

-		// since there's no reason to serialize and store an empty array in the DB

-		if (count($custom_userdata) === 0)

-		{

-			$custom_userdata = '';

-		}

-		else

-		{	

-			// Serialize the custom data array so we can store it

-			$custom_userdata = $this->_serialize($custom_userdata);

-		}

-		

-		// Run the update query

-		$this->CI->db->where('session_id', $this->userdata['session_id']);

-		$this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata));

-

-		// Write the cookie.  Notice that we manually pass the cookie data array to the

-		// _set_cookie() function. Normally that function will store $this->userdata, but 

-		// in this case that array contains custom data, which we do not want in the cookie.

-		$this->_set_cookie($cookie_userdata);

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Create a new session

-	 *

-	 * @access	public

-	 * @return	void

-	 */

-	function sess_create()

-	{	

-		$sessid = '';

-		while (strlen($sessid) < 32)

-		{

-			$sessid .= mt_rand(0, mt_getrandmax());

-		}

-		

-		// To make the session ID even more secure we'll combine it with the user's IP

-		$sessid .= $this->CI->input->ip_address();

-	

-		$this->userdata = array(

-							'session_id' 	=> md5(uniqid($sessid, TRUE)),

-							'ip_address' 	=> $this->CI->input->ip_address(),

-							'user_agent' 	=> substr($this->CI->input->user_agent(), 0, 50),

-							'last_activity'	=> $this->now

-							);

-		

-		

-		// Save the data to the DB if needed

-		if ($this->sess_use_database === TRUE)

-		{

-			$this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->userdata));

-		}

-			

-		// Write the cookie

-		$this->_set_cookie();

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Update an existing session

-	 *

-	 * @access	public

-	 * @return	void

-	 */

-	function sess_update()

-	{

-		// We only update the session every five minutes by default

-		if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)

-		{

-			return;

-		}

-	

-		// Save the old session id so we know which record to 

-		// update in the database if we need it

-		$old_sessid = $this->userdata['session_id'];

-		$new_sessid = '';

-		while (strlen($new_sessid) < 32)

-		{

-			$new_sessid .= mt_rand(0, mt_getrandmax());

-		}

-		

-		// To make the session ID even more secure we'll combine it with the user's IP

-		$new_sessid .= $this->CI->input->ip_address();

-		

-		// Turn it into a hash

-		$new_sessid = md5(uniqid($new_sessid, TRUE));

-		

-		// Update the session data in the session data array

-		$this->userdata['session_id'] = $new_sessid;

-		$this->userdata['last_activity'] = $this->now;

-		

-		// _set_cookie() will handle this for us if we aren't using database sessions

-		// by pushing all userdata to the cookie.

-		$cookie_data = NULL;

-		

-		// Update the session ID and last_activity field in the DB if needed

-		if ($this->sess_use_database === TRUE)

-		{

-			// set cookie explicitly to only have our session data

-			$cookie_data = array();

-			foreach (array('session_id','ip_address','user_agent','last_activity') as $val)

-			{

-				$cookie_data[$val] = $this->userdata[$val];

-			}

-			

-			$this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));

-		}

-		

-		// Write the cookie

-		$this->_set_cookie($cookie_data);

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Destroy the current session

-	 *

-	 * @access	public

-	 * @return	void

-	 */

-	function sess_destroy()

-	{	

-		// Kill the session DB row

-		if ($this->sess_use_database === TRUE AND isset($this->userdata['session_id']))

-		{

-			$this->CI->db->where('session_id', $this->userdata['session_id']);

-			$this->CI->db->delete($this->sess_table_name);

-		}

-	

-		// Kill the cookie

-		setcookie(

-					$this->sess_cookie_name,

-					addslashes(serialize(array())),

-					($this->now - 31500000),

-					$this->cookie_path,

-					$this->cookie_domain,

-					0

-				);

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Fetch a specific item from the session array

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	string

-	 */		

-	function userdata($item)

-	{

-		return ( ! isset($this->userdata[$item])) ? FALSE : $this->userdata[$item];

-	}

-

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Fetch all session data

-	 *

-	 * @access	public

-	 * @return	mixed

-	 */	

-	function all_userdata()

-	{

-		return ( ! isset($this->userdata)) ? FALSE : $this->userdata;

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Add or change data in the "userdata" array

-	 *

-	 * @access	public

-	 * @param	mixed

-	 * @param	string

-	 * @return	void

-	 */		

-	function set_userdata($newdata = array(), $newval = '')

-	{

-		if (is_string($newdata))

-		{

-			$newdata = array($newdata => $newval);

-		}

-	

-		if (count($newdata) > 0)

-		{

-			foreach ($newdata as $key => $val)

-			{

-				$this->userdata[$key] = $val;

-			}

-		}

-

-		$this->sess_write();

-	}

-	

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Delete a session variable from the "userdata" array

-	 *

-	 * @access	array

-	 * @return	void

-	 */		

-	function unset_userdata($newdata = array())

-	{

-		if (is_string($newdata))

-		{

-			$newdata = array($newdata => '');

-		}

-	

-		if (count($newdata) > 0)

-		{

-			foreach ($newdata as $key => $val)

-			{

-				unset($this->userdata[$key]);

-			}

-		}

-	

-		$this->sess_write();

-	}

-	

-	// ------------------------------------------------------------------------

-

-	/**

-	 * Add or change flashdata, only available

-	 * until the next request

-	 *

-	 * @access	public

-	 * @param	mixed

-	 * @param	string

-	 * @return	void

-	 */

-	function set_flashdata($newdata = array(), $newval = '')

-	{

-		if (is_string($newdata))

-		{

-			$newdata = array($newdata => $newval);

-		}

-		

-		if (count($newdata) > 0)

-		{

-			foreach ($newdata as $key => $val)

-			{

-				$flashdata_key = $this->flashdata_key.':new:'.$key;

-				$this->set_userdata($flashdata_key, $val);

-			}

-		}

-	} 

-	

-	// ------------------------------------------------------------------------

-

-	/**

-	 * Keeps existing flashdata available to next request.

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	void

-	 */

-	function keep_flashdata($key)

-	{

-		// 'old' flashdata gets removed.  Here we mark all 

-		// flashdata as 'new' to preserve it from _flashdata_sweep()

-		// Note the function will return FALSE if the $key 

-		// provided cannot be found

-		$old_flashdata_key = $this->flashdata_key.':old:'.$key;

-		$value = $this->userdata($old_flashdata_key);

-

-		$new_flashdata_key = $this->flashdata_key.':new:'.$key;

-		$this->set_userdata($new_flashdata_key, $value);

-	}

-	

-	// ------------------------------------------------------------------------

-

-	/**

-	 * Fetch a specific flashdata item from the session array

-	 *

-	 * @access	public

-	 * @param	string

-	 * @return	string

-	 */	

-	function flashdata($key)

-	{

-		$flashdata_key = $this->flashdata_key.':old:'.$key;

-		return $this->userdata($flashdata_key);

-	}

-

-	// ------------------------------------------------------------------------

-

-	/**

-	 * Identifies flashdata as 'old' for removal

-	 * when _flashdata_sweep() runs.

-	 *

-	 * @access	private

-	 * @return	void

-	 */

-	function _flashdata_mark()

-	{

-		$userdata = $this->all_userdata();

-		foreach ($userdata as $name => $value)

-		{

-			$parts = explode(':new:', $name);

-			if (is_array($parts) && count($parts) === 2)

-			{

-				$new_name = $this->flashdata_key.':old:'.$parts[1];

-				$this->set_userdata($new_name, $value);

-				$this->unset_userdata($name);

-			}

-		}

-	}

-

-	// ------------------------------------------------------------------------

-

-	/**

-	 * Removes all flashdata marked as 'old'

-	 *

-	 * @access	private

-	 * @return	void

-	 */

-

-	function _flashdata_sweep()

-	{

-		$userdata = $this->all_userdata();

-		foreach ($userdata as $key => $value)

-		{

-			if (strpos($key, ':old:'))

-			{

-				$this->unset_userdata($key);

-			}

-		}

-

-	}

-

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Get the "now" time

-	 *

-	 * @access	private

-	 * @return	string

-	 */

-	function _get_time()

-	{

-		if (strtolower($this->time_reference) == 'gmt')

-		{

-			$now = time();

-			$time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now));

-		}

-		else

-		{

-			$time = time();

-		}

-	

-		return $time;

-	}

-

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Write the session cookie

-	 *

-	 * @access	public

-	 * @return	void

-	 */

-	function _set_cookie($cookie_data = NULL)

-	{

-		if (is_null($cookie_data))

-		{

-			$cookie_data = $this->userdata;

-		}

-	

-		// Serialize the userdata for the cookie

-		$cookie_data = $this->_serialize($cookie_data);

-		

-		if ($this->sess_encrypt_cookie == TRUE)

-		{

-			$cookie_data = $this->CI->encrypt->encode($cookie_data);

-		}

-		else

-		{

-			// if encryption is not used, we provide an md5 hash to prevent userside tampering

-			$cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);

-		}

-		

-		// Set the cookie

-		setcookie(

-					$this->sess_cookie_name,

-					$cookie_data,

-					$this->sess_expiration + time(),

-					$this->cookie_path,

-					$this->cookie_domain,

-					0

-				);

-	}

-

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Serialize an array

-	 *

-	 * This function first converts any slashes found in the array to a temporary

-	 * marker, so when it gets unserialized the slashes will be preserved

-	 *

-	 * @access	private

-	 * @param	array

-	 * @return	string

-	 */	

-	function _serialize($data)

-	{

-		if (is_array($data))

-		{

-			foreach ($data as $key => $val)

-			{

-				$data[$key] = str_replace('\\', '{{slash}}', $val);

-			}

-		}

-		else

-		{

-			$data = str_replace('\\', '{{slash}}', $data);

-		}

-		

-		return serialize($data);

-	}

-

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Unserialize

-	 *

-	 * This function unserializes a data string, then converts any

-	 * temporary slash markers back to actual slashes

-	 *

-	 * @access	private

-	 * @param	array

-	 * @return	string

-	 */		

-	function _unserialize($data)

-	{

-		$data = @unserialize(strip_slashes($data));

-		

-		if (is_array($data))

-		{

-			foreach ($data as $key => $val)

-			{

-				$data[$key] = str_replace('{{slash}}', '\\', $val);

-			}

-			

-			return $data;

-		}

-		

-		return str_replace('{{slash}}', '\\', $data);

-	}

-

-	// --------------------------------------------------------------------

-	

-	/**

-	 * Garbage collection

-	 *

-	 * This deletes expired session rows from database

-	 * if the probability percentage is met

-	 *

-	 * @access	public

-	 * @return	void

-	 */

-	function _sess_gc()

-	{

-		if ($this->sess_use_database != TRUE)

-		{

-			return;

-		}

-		

-		srand(time());

-		if ((rand() % 100) < $this->gc_probability)

-		{

-			$expire = $this->now - $this->sess_expiration;

-			

-			$this->CI->db->where("last_activity < {$expire}");

-			$this->CI->db->delete($this->sess_table_name);

-

-			log_message('debug', 'Session garbage collection performed.');

-		}

-	}

-

-	

-}

-// END Session Class

-

-/* End of file Session.php */

+<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2008, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * Session Class
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Sessions
+ * @author		ExpressionEngine Dev Team
+ * @link		http://codeigniter.com/user_guide/libraries/sessions.html
+ */
+class CI_Session {
+
+	var $sess_encrypt_cookie		= FALSE;
+	var $sess_use_database			= FALSE;
+	var $sess_table_name			= '';
+	var $sess_expiration			= 7200;
+	var $sess_match_ip				= FALSE;
+	var $sess_match_useragent		= TRUE;
+	var $sess_cookie_name			= 'ci_session';
+	var $cookie_prefix				= '';
+	var $cookie_path				= '';
+	var $cookie_domain				= '';
+	var $sess_time_to_update		= 300;
+	var $encryption_key				= '';
+	var $flashdata_key 				= 'flash';
+	var $time_reference				= 'time';
+	var $gc_probability				= 5;
+	var $userdata					= array();
+	var $CI;
+	var $now;
+
+	/**
+	 * Session Constructor
+	 *
+	 * The constructor runs the session routines automatically
+	 * whenever the class is instantiated.
+	 */
+	function CI_Session($params = array())
+	{
+		log_message('debug', "Session Class Initialized");
+
+		// Set the super object to a local variable for use throughout the class
+		$this->CI =& get_instance();
+
+		// Set all the session preferences, which can either be set
+		// manually via the $params array above or via the config file
+		foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path', 'cookie_domain', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key)
+		{
+			$this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key);
+		}
+
+		// Load the string helper so we can use the strip_slashes() function
+		$this->CI->load->helper('string');
+
+		// Do we need encryption? If so, load the encryption class
+		if ($this->sess_encrypt_cookie == TRUE)
+		{
+			$this->CI->load->library('encrypt');
+		}
+
+		// Are we using a database?  If so, load it
+		if ($this->sess_use_database === TRUE AND $this->sess_table_name != '')
+		{
+			$this->CI->load->database();
+		}
+
+		// Set the "now" time.  Can either be GMT or server time, based on the
+		// config prefs.  We use this to set the "last activity" time
+		$this->now = $this->_get_time();
+
+		// Set the session length. If the session expiration is
+		// set to zero we'll set the expiration two years from now.
+		if ($this->sess_expiration == 0)
+		{
+			$this->sess_expiration = (60*60*24*365*2);
+		}
+		 
+		// Set the cookie name
+		$this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
+
+		// Run the Session routine. If a session doesn't exist we'll
+		// create a new one.  If it does, we'll update it.
+		if ( ! $this->sess_read())
+		{
+			$this->sess_create();
+		}
+		else
+		{
+			$this->sess_update();
+		}
+
+		// Delete 'old' flashdata (from last request)
+	   	$this->_flashdata_sweep();
+
+		// Mark all new flashdata as old (data will be deleted before next request)
+	   	$this->_flashdata_mark();
+
+		// Delete expired sessions if necessary
+		$this->_sess_gc();
+
+		log_message('debug', "Session routines successfully run");
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch the current session data if it exists
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function sess_read()
+	{
+		// Fetch the cookie
+		$session = $this->CI->input->cookie($this->sess_cookie_name);
+
+		// No cookie?  Goodbye cruel world!...
+		if ($session === FALSE)
+		{
+			log_message('debug', 'A session cookie was not found.');
+			return FALSE;
+		}
+
+		// Decrypt the cookie data
+		if ($this->sess_encrypt_cookie == TRUE)
+		{
+			$session = $this->CI->encrypt->decode($session);
+		}
+		else
+		{
+			// encryption was not used, so we need to check the md5 hash
+			$hash	 = substr($session, strlen($session)-32); // get last 32 chars
+			$session = substr($session, 0, strlen($session)-32);
+
+			// Does the md5 hash match?  This is to prevent manipulation of session data in userspace
+			if ($hash !==  md5($session.$this->encryption_key))
+			{
+				log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.');
+				$this->sess_destroy();
+				return FALSE;
+			}
+		}
+
+		// Unserialize the session array
+		$session = $this->_unserialize($session);
+
+		// Is the session data we unserialized an array with the correct format?
+		if ( ! is_array($session) OR ! isset($session['session_id']) OR ! isset($session['ip_address']) OR ! isset($session['user_agent']) OR ! isset($session['last_activity']))
+		{
+			$this->sess_destroy();
+			return FALSE;
+		}
+
+		// Is the session current?
+		if (($session['last_activity'] + $this->sess_expiration) < $this->now)
+		{
+			$this->sess_destroy();
+			return FALSE;
+		}
+
+		// Does the IP Match?
+		if ($this->sess_match_ip == TRUE AND $session['ip_address'] != $this->CI->input->ip_address())
+		{
+			$this->sess_destroy();
+			return FALSE;
+		}
+
+		// Does the User Agent Match?
+		if ($this->sess_match_useragent == TRUE AND trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 50)))
+		{
+			$this->sess_destroy();
+			return FALSE;
+		}
+
+		// Is there a corresponding session in the DB?
+		if ($this->sess_use_database === TRUE)
+		{
+			$this->CI->db->where('session_id', $session['session_id']);
+
+			if ($this->sess_match_ip == TRUE)
+			{
+				$this->CI->db->where('ip_address', $session['ip_address']);
+			}
+
+			if ($this->sess_match_useragent == TRUE)
+			{
+				$this->CI->db->where('user_agent', $session['user_agent']);
+			}
+
+			$query = $this->CI->db->get($this->sess_table_name);
+
+			// No result?  Kill it!
+			if ($query->num_rows() == 0)
+			{
+				$this->sess_destroy();
+				return FALSE;
+			}
+
+			// Is there custom data?  If so, add it to the main session array
+			$row = $query->row();
+			if (isset($row->user_data) AND $row->user_data != '')
+			{
+				$custom_data = $this->_unserialize($row->user_data);
+
+				if (is_array($custom_data))
+				{
+					foreach ($custom_data as $key => $val)
+					{
+						$session[$key] = $val;
+					}
+				}
+			}
+		}
+
+		// Session is valid!
+		$this->userdata = $session;
+		unset($session);
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Write the session data
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function sess_write()
+	{
+		// Are we saving custom data to the DB?  If not, all we do is update the cookie
+		if ($this->sess_use_database === FALSE)
+		{
+			$this->_set_cookie();
+			return;
+		}
+
+		// set the custom userdata, the session data we will set in a second
+		$custom_userdata = $this->userdata;
+		$cookie_userdata = array();
+
+		// Before continuing, we need to determine if there is any custom data to deal with.
+		// Let's determine this by removing the default indexes to see if there's anything left in the array
+		// and set the session data while we're at it
+		foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
+		{
+			unset($custom_userdata[$val]);
+			$cookie_userdata[$val] = $this->userdata[$val];
+		}
+
+		// Did we find any custom data?  If not, we turn the empty array into a string
+		// since there's no reason to serialize and store an empty array in the DB
+		if (count($custom_userdata) === 0)
+		{
+			$custom_userdata = '';
+		}
+		else
+		{
+			// Serialize the custom data array so we can store it
+			$custom_userdata = $this->_serialize($custom_userdata);
+		}
+
+		// Run the update query
+		$this->CI->db->where('session_id', $this->userdata['session_id']);
+		$this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata));
+
+		// Write the cookie.  Notice that we manually pass the cookie data array to the
+		// _set_cookie() function. Normally that function will store $this->userdata, but
+		// in this case that array contains custom data, which we do not want in the cookie.
+		$this->_set_cookie($cookie_userdata);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Create a new session
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function sess_create()
+	{
+		$sessid = '';
+		while (strlen($sessid) < 32)
+		{
+			$sessid .= mt_rand(0, mt_getrandmax());
+		}
+
+		// To make the session ID even more secure we'll combine it with the user's IP
+		$sessid .= $this->CI->input->ip_address();
+
+		$this->userdata = array(
+							'session_id' 	=> md5(uniqid($sessid, TRUE)),
+							'ip_address' 	=> $this->CI->input->ip_address(),
+							'user_agent' 	=> substr($this->CI->input->user_agent(), 0, 50),
+							'last_activity'	=> $this->now
+							);
+
+
+		// Save the data to the DB if needed
+		if ($this->sess_use_database === TRUE)
+		{
+			$this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->userdata));
+		}
+
+		// Write the cookie
+		$this->_set_cookie();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update an existing session
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function sess_update()
+	{
+		// We only update the session every five minutes by default
+		if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
+		{
+			return;
+		}
+
+		// Save the old session id so we know which record to
+		// update in the database if we need it
+		$old_sessid = $this->userdata['session_id'];
+		$new_sessid = '';
+		while (strlen($new_sessid) < 32)
+		{
+			$new_sessid .= mt_rand(0, mt_getrandmax());
+		}
+
+		// To make the session ID even more secure we'll combine it with the user's IP
+		$new_sessid .= $this->CI->input->ip_address();
+
+		// Turn it into a hash
+		$new_sessid = md5(uniqid($new_sessid, TRUE));
+
+		// Update the session data in the session data array
+		$this->userdata['session_id'] = $new_sessid;
+		$this->userdata['last_activity'] = $this->now;
+
+		// _set_cookie() will handle this for us if we aren't using database sessions
+		// by pushing all userdata to the cookie.
+		$cookie_data = NULL;
+
+		// Update the session ID and last_activity field in the DB if needed
+		if ($this->sess_use_database === TRUE)
+		{
+			// set cookie explicitly to only have our session data
+			$cookie_data = array();
+			foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
+			{
+				$cookie_data[$val] = $this->userdata[$val];
+			}
+
+			$this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
+		}
+
+		// Write the cookie
+		$this->_set_cookie($cookie_data);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Destroy the current session
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function sess_destroy()
+	{
+		// Kill the session DB row
+		if ($this->sess_use_database === TRUE AND isset($this->userdata['session_id']))
+		{
+			$this->CI->db->where('session_id', $this->userdata['session_id']);
+			$this->CI->db->delete($this->sess_table_name);
+		}
+
+		// Kill the cookie
+		setcookie(
+					$this->sess_cookie_name,
+					addslashes(serialize(array())),
+					($this->now - 31500000),
+					$this->cookie_path,
+					$this->cookie_domain,
+					0
+				);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch a specific item from the session array
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	string
+	 */
+	function userdata($item)
+	{
+		return ( ! isset($this->userdata[$item])) ? FALSE : $this->userdata[$item];
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch all session data
+	 *
+	 * @access	public
+	 * @return	mixed
+	 */
+	function all_userdata()
+	{
+		return ( ! isset($this->userdata)) ? FALSE : $this->userdata;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Add or change data in the "userdata" array
+	 *
+	 * @access	public
+	 * @param	mixed
+	 * @param	string
+	 * @return	void
+	 */
+	function set_userdata($newdata = array(), $newval = '')
+	{
+		if (is_string($newdata))
+		{
+			$newdata = array($newdata => $newval);
+		}
+
+		if (count($newdata) > 0)
+		{
+			foreach ($newdata as $key => $val)
+			{
+				$this->userdata[$key] = $val;
+			}
+		}
+
+		$this->sess_write();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete a session variable from the "userdata" array
+	 *
+	 * @access	array
+	 * @return	void
+	 */
+	function unset_userdata($newdata = array())
+	{
+		if (is_string($newdata))
+		{
+			$newdata = array($newdata => '');
+		}
+
+		if (count($newdata) > 0)
+		{
+			foreach ($newdata as $key => $val)
+			{
+				unset($this->userdata[$key]);
+			}
+		}
+
+		$this->sess_write();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Add or change flashdata, only available
+	 * until the next request
+	 *
+	 * @access	public
+	 * @param	mixed
+	 * @param	string
+	 * @return	void
+	 */
+	function set_flashdata($newdata = array(), $newval = '')
+	{
+		if (is_string($newdata))
+		{
+			$newdata = array($newdata => $newval);
+		}
+
+		if (count($newdata) > 0)
+		{
+			foreach ($newdata as $key => $val)
+			{
+				$flashdata_key = $this->flashdata_key.':new:'.$key;
+				$this->set_userdata($flashdata_key, $val);
+			}
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Keeps existing flashdata available to next request.
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	void
+	 */
+	function keep_flashdata($key)
+	{
+		// 'old' flashdata gets removed.  Here we mark all
+		// flashdata as 'new' to preserve it from _flashdata_sweep()
+		// Note the function will return FALSE if the $key
+		// provided cannot be found
+		$old_flashdata_key = $this->flashdata_key.':old:'.$key;
+		$value = $this->userdata($old_flashdata_key);
+
+		$new_flashdata_key = $this->flashdata_key.':new:'.$key;
+		$this->set_userdata($new_flashdata_key, $value);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch a specific flashdata item from the session array
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	string
+	 */
+	function flashdata($key)
+	{
+		$flashdata_key = $this->flashdata_key.':old:'.$key;
+		return $this->userdata($flashdata_key);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Identifies flashdata as 'old' for removal
+	 * when _flashdata_sweep() runs.
+	 *
+	 * @access	private
+	 * @return	void
+	 */
+	function _flashdata_mark()
+	{
+		$userdata = $this->all_userdata();
+		foreach ($userdata as $name => $value)
+		{
+			$parts = explode(':new:', $name);
+			if (is_array($parts) && count($parts) === 2)
+			{
+				$new_name = $this->flashdata_key.':old:'.$parts[1];
+				$this->set_userdata($new_name, $value);
+				$this->unset_userdata($name);
+			}
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Removes all flashdata marked as 'old'
+	 *
+	 * @access	private
+	 * @return	void
+	 */
+
+	function _flashdata_sweep()
+	{
+		$userdata = $this->all_userdata();
+		foreach ($userdata as $key => $value)
+		{
+			if (strpos($key, ':old:'))
+			{
+				$this->unset_userdata($key);
+			}
+		}
+
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Get the "now" time
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _get_time()
+	{
+		if (strtolower($this->time_reference) == 'gmt')
+		{
+			$now = time();
+			$time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now));
+		}
+		else
+		{
+			$time = time();
+		}
+
+		return $time;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Write the session cookie
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function _set_cookie($cookie_data = NULL)
+	{
+		if (is_null($cookie_data))
+		{
+			$cookie_data = $this->userdata;
+		}
+
+		// Serialize the userdata for the cookie
+		$cookie_data = $this->_serialize($cookie_data);
+
+		if ($this->sess_encrypt_cookie == TRUE)
+		{
+			$cookie_data = $this->CI->encrypt->encode($cookie_data);
+		}
+		else
+		{
+			// if encryption is not used, we provide an md5 hash to prevent userside tampering
+			$cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);
+		}
+
+		// Set the cookie
+		setcookie(
+					$this->sess_cookie_name,
+					$cookie_data,
+					$this->sess_expiration + time(),
+					$this->cookie_path,
+					$this->cookie_domain,
+					0
+				);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Serialize an array
+	 *
+	 * This function first converts any slashes found in the array to a temporary
+	 * marker, so when it gets unserialized the slashes will be preserved
+	 *
+	 * @access	private
+	 * @param	array
+	 * @return	string
+	 */
+	function _serialize($data)
+	{
+		if (is_array($data))
+		{
+			foreach ($data as $key => $val)
+			{
+				$data[$key] = str_replace('\\', '{{slash}}', $val);
+			}
+		}
+		else
+		{
+			$data = str_replace('\\', '{{slash}}', $data);
+		}
+
+		return serialize($data);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Unserialize
+	 *
+	 * This function unserializes a data string, then converts any
+	 * temporary slash markers back to actual slashes
+	 *
+	 * @access	private
+	 * @param	array
+	 * @return	string
+	 */
+	function _unserialize($data)
+	{
+		$data = @unserialize(strip_slashes($data));
+
+		if (is_array($data))
+		{
+			foreach ($data as $key => $val)
+			{
+				$data[$key] = str_replace('{{slash}}', '\\', $val);
+			}
+
+			return $data;
+		}
+
+		return str_replace('{{slash}}', '\\', $data);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Garbage collection
+	 *
+	 * This deletes expired session rows from database
+	 * if the probability percentage is met
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function _sess_gc()
+	{
+		if ($this->sess_use_database != TRUE)
+		{
+			return;
+		}
+
+		srand(time());
+		if ((rand() % 100) < $this->gc_probability)
+		{
+			$expire = $this->now - $this->sess_expiration;
+
+			$this->CI->db->where("last_activity < {$expire}");
+			$this->CI->db->delete($this->sess_table_name);
+
+			log_message('debug', 'Session garbage collection performed.');
+		}
+	}
+
+
+}
+// END Session Class
+
+/* End of file Session.php */
 /* Location: ./system/libraries/Session.php */
\ No newline at end of file
diff --git a/user_guide/changelog.html b/user_guide/changelog.html
index f690c91..cde5f43 100644
--- a/user_guide/changelog.html
+++ b/user_guide/changelog.html
@@ -81,9 +81,10 @@
 	<li>Fixed a bug in the MySQLi driver when no port is specified</li>
 	<li>Fixed a bug (#5702), in which the field label was not being fetched properly, when "matching" one field to another.</li>
 	<li>Fixed a bug in which identifers were not being escaped properly when reserved characters were used.</li>
-	<li>Fixed a bug with the regular expression used to protect submitted paragraph tags in auto typography.</p>
+	<li>Fixed a bug with the regular expression used to protect submitted paragraph tags in auto typography.</li>
 	<li>Fixed a bug where double dashes within tag attributes were being converted to em dash entities.</li>
 	<li>Fixed a bug where double spaces within tag attributes were being converted to non-breaking space entities.</li>
+	<li>Changed a few docblock comments to reflect actual return values.</li>
 </ul>