Merge pull request #1760 from vlakoff/develop-2

Better old captcha image test
diff --git a/application/config/constants.php b/application/config/constants.php
index d22d296..62a18e7 100644
--- a/application/config/constants.php
+++ b/application/config/constants.php
@@ -52,14 +52,14 @@
 |
 */
 
-define('FOPEN_READ',							'rb');
-define('FOPEN_READ_WRITE',						'r+b');
-define('FOPEN_WRITE_CREATE_DESTRUCTIVE',		'wb'); // truncates existing file data, use with care
-define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE',	'w+b'); // truncates existing file data, use with care
-define('FOPEN_WRITE_CREATE',					'ab');
-define('FOPEN_READ_WRITE_CREATE',				'a+b');
-define('FOPEN_WRITE_CREATE_STRICT',				'xb');
-define('FOPEN_READ_WRITE_CREATE_STRICT',		'x+b');
+define('FOPEN_READ', 'rb');
+define('FOPEN_READ_WRITE', 'r+b');
+define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care
+define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care
+define('FOPEN_WRITE_CREATE', 'ab');
+define('FOPEN_READ_WRITE_CREATE', 'a+b');
+define('FOPEN_WRITE_CREATE_STRICT', 'xb');
+define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b');
 
 /*
 |--------------------------------------------------------------------------
diff --git a/system/helpers/text_helper.php b/system/helpers/text_helper.php
index 8a1f01b..b592f3c 100644
--- a/system/helpers/text_helper.php
+++ b/system/helpers/text_helper.php
@@ -89,7 +89,8 @@
 			return $str;
 		}
 
-		$str = preg_replace('/\s+/', ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $str));
+		// a bit complicated, but faster than preg_replace with \s+
+		$str = preg_replace('/ {2,}/', ' ', str_replace(array("\r", "\n", "\t", "\x0B", "\x0C"), ' ', $str));
 
 		if (strlen($str) <= $n)
 		{
diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php
index 1f24456..e6f6050 100755
--- a/system/libraries/Session/Session.php
+++ b/system/libraries/Session/Session.php
@@ -2,20 +2,31 @@
 /**
  * CodeIgniter
  *
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
  *
  * @package		CodeIgniter
- * @author		ExpressionEngine Dev Team
- * @copyright	Copyright (c) 2008 - 2010, EllisLab, Inc.
- * @license		http://codeigniter.com/user_guide/license.html
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2006 - 2012 EllisLab, Inc.
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
  * @link		http://codeigniter.com
  * @since		Version 2.0
  * @filesource
  */
 
-
 /**
- * CI_Session Class
+ * CodeIgniter Session Class
  *
  * The user interface defined by EllisLabs, now with puggable drivers to manage different storage mechanisms.
  * By default, the cookie session driver will load, but the 'sess_driver' config/param item (see above) can be
@@ -35,12 +46,13 @@
  * @package		CodeIgniter
  * @subpackage	Libraries
  * @category	Sessions
- * @author		ExpressionEngine Dev Team
+ * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/libraries/sessions.html
  */
 class CI_Session extends CI_Driver_Library {
+
 	public $params = array();
-	protected $current = null;
+	protected $current = NULL;
 	protected $userdata = array();
 
 	const FLASHDATA_KEY = 'flash';
@@ -69,10 +81,10 @@
 		   	'Session_cookie'
 		);
 		$key = 'sess_valid_drivers';
-		$drivers = (isset($params[$key])) ? $params[$key] : $CI->config->item($key);
+		$drivers = isset($params[$key]) ? $params[$key] : $CI->config->item($key);
 		if ($drivers)
 		{
-			if ( ! is_array($drivers)) $drivers = array($drivers);
+			is_array($drivers) OR $drivers = array($drivers);
 
 			// Add driver names to valid list
 			foreach ($drivers as $driver)
@@ -86,8 +98,12 @@
 
 		// Get driver to load
 		$key = 'sess_driver';
-		$driver = (isset($params[$key])) ? $params[$key] : $CI->config->item($key);
-		if ( ! $driver) $driver = 'cookie';
+		$driver = isset($params[$key]) ? $params[$key] : $CI->config->item($key);
+		if ( ! $driver)
+		{
+			$driver = 'cookie';
+		}
+
 		if ( ! in_array('session_'.strtolower($driver), array_map('strtolower', $this->valid_drivers)))
 		{
 			$this->valid_drivers[] = 'Session_'.$driver;
@@ -111,6 +127,8 @@
 		log_message('debug', 'CI_Session routines successfully run');
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Loads session storage driver
 	 *
@@ -125,6 +143,8 @@
 		return $this->current;
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Select default session storage driver
 	 *
@@ -142,7 +162,8 @@
 			if (isset($this->$child))
 			{
 				// See if driver is already current
-				if ($this->$child !== $this->current) {
+				if ($this->$child !== $this->current)
+				{
 					// Make driver current and sync userdata
 					$this->current = $this->$child;
 					$this->userdata =& $this->current->get_userdata();
@@ -156,6 +177,8 @@
 		}
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Destroy the current session
 	 *
@@ -167,19 +190,23 @@
 		$this->current->sess_destroy();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Regenerate the current session
 	 *
-	 * @param	boolean	Destroy session data flag (default: false)
+	 * @param	bool	Destroy session data flag (default: false)
 	 * @return	void
 	 */
-	public function sess_regenerate($destroy = false)
+	public function sess_regenerate($destroy = FALSE)
 	{
 		// Call regenerate on driver and resync userdata
 		$this->current->sess_regenerate($destroy);
 		$this->userdata =& $this->current->get_userdata();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Fetch a specific item from the session array
 	 *
@@ -188,10 +215,11 @@
 	 */
 	public function userdata($item)
 	{
-		// Return value or NULL if not found
-		return ( ! isset($this->userdata[$item])) ? NULL : $this->userdata[$item];
+		return isset($this->userdata[$item]) ? $this->userdata[$item] : NULL;
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Fetch all session data
 	 *
@@ -199,10 +227,11 @@
 	 */
 	public function all_userdata()
 	{
-		// Return entire array
-		return ( ! isset($this->userdata)) ? NULL : $this->userdata;
+		return isset($this->userdata) ? $this->userdata : NULL;
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Fetch all flashdata
 	 *
@@ -225,6 +254,8 @@
 		return $out;
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Add or change data in the "userdata" array
 	 *
@@ -253,6 +284,8 @@
 		$this->current->sess_save();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Delete a session variable from the "userdata" array
 	 *
@@ -270,7 +303,7 @@
 		// Unset each item name
 		if (count($newdata) > 0)
 		{
-			foreach ($newdata as $key => $val)
+			foreach (array_keys($newdata) as $key)
 			{
 				unset($this->userdata[$key]);
 			}
@@ -280,18 +313,21 @@
 		$this->current->sess_save();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Determine if an item exists
 	 *
 	 * @param	string	Item name
-	 * @return	boolean
+	 * @return	bool
 	 */
 	public function has_userdata($item)
 	{
-		// Check for item name
 		return isset($this->userdata[$item]);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Add or change flashdata, only available until the next request
 	 *
@@ -318,6 +354,8 @@
 		}
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Keeps existing flashdata available to next request.
 	 *
@@ -335,6 +373,8 @@
 		$this->set_userdata($new_flashdata_key, $value);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Fetch a specific flashdata item from the session array
 	 *
@@ -348,13 +388,14 @@
 		return $this->userdata($flashdata_key);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
-	 * Add or change tempdata, only available
-	 * until expiration
+	 * Add or change tempdata, only available until expiration
 	 *
 	 * @param	mixed	Item name or array of items
 	 * @param	string	Item value or empty string
-	 * @param	int		Item lifetime in seconds or 0 for default
+	 * @param	int	Item lifetime in seconds or 0 for default
 	 * @return	void
 	 */
 	public function set_tempdata($newdata = array(), $newval = '', $expire = 0)
@@ -390,6 +431,8 @@
 		$this->set_userdata(self::EXPIRATION_KEY, $expirations);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Delete a temporary session variable from the "userdata" array
 	 *
@@ -400,7 +443,7 @@
 	{
 		// Get expirations list
 		$expirations = $this->userdata(self::EXPIRATION_KEY);
-		if ( ! $expirations || ! count($expirations))
+		if (empty($expirations))
 		{
 			// Nothing to do
 			return;
@@ -415,7 +458,7 @@
 		// Prepend each item name and unset
 		if (count($newdata) > 0)
 		{
-			foreach ($newdata as $key => $val)
+			foreach (array_keys($newdata) as $key)
 			{
 				$tempdata_key = self::FLASHDATA_KEY.self::FLASHDATA_EXP.$key;
 				unset($expirations[$tempdata_key]);
@@ -427,6 +470,8 @@
 		$this->set_userdata(self::EXPIRATION_KEY, $expirations);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Fetch a specific tempdata item from the session array
 	 *
@@ -440,17 +485,17 @@
 		return $this->userdata($tempdata_key);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Identifies flashdata as 'old' for removal
 	 * when _flashdata_sweep() runs.
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _flashdata_mark()
 	{
-		$userdata = $this->all_userdata();
-		foreach ($userdata as $name => $value)
+		foreach ($this->all_userdata() as $name => $value)
 		{
 			$parts = explode(self::FLASHDATA_NEW, $name);
 			if (is_array($parts) && count($parts) === 2)
@@ -462,16 +507,17 @@
 		}
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Removes all flashdata marked as 'old'
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _flashdata_sweep()
 	{
 		$userdata = $this->all_userdata();
-		foreach ($userdata as $key => $value)
+		foreach (array_keys($userdata) as $key)
 		{
 			if (strpos($key, self::FLASHDATA_OLD))
 			{
@@ -480,17 +526,18 @@
 		}
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Removes all expired tempdata
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _tempdata_sweep()
 	{
 		// Get expirations list
 		$expirations = $this->userdata(self::EXPIRATION_KEY);
-		if ( ! $expirations ||  ! count($expirations))
+		if (empty($expirations))
 		{
 			// Nothing to do
 			return;
@@ -499,7 +546,7 @@
 		// Unset expired elements
 		$now = time();
 		$userdata = $this->all_userdata();
-		foreach ($userdata as $key => $value)
+		foreach (array_keys($userdata) as $key)
 		{
 			if (strpos($key, self::FLASHDATA_EXP) && $expirations[$key] < $now)
 			{
@@ -511,9 +558,10 @@
 		// Update expiration list
 		$this->set_userdata(self::EXPIRATION_KEY, $expirations);
 	}
-}
-// END CI_Session Class
 
+}
+
+// ------------------------------------------------------------------------
 
 /**
  * CI_Session_driver Class
@@ -535,9 +583,10 @@
  * @package		CodeIgniter
  * @subpackage	Libraries
  * @category	Sessions
- * @author		ExpressionEngine Dev Team
+ * @author		EllisLab Dev Team
  */
 abstract class CI_Session_driver extends CI_Driver {
+
 	/**
 	 * Decorate
 	 *
@@ -555,6 +604,8 @@
 		$this->initialize();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * __call magic method
 	 *
@@ -571,6 +622,8 @@
 		return parent::__call($method, $args);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Initialize driver
 	 *
@@ -581,50 +634,56 @@
 		// Overload this method to implement initialization
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Save the session data
 	 *
-	 * Data in the array has changed - perform any storage synchronization necessary
-	 * The child class MUST implement this abstract method!
+	 * Data in the array has changed - perform any storage synchronization
+	 * necessary. The child class MUST implement this abstract method!
 	 *
 	 * @return	void
 	 */
 	abstract public function sess_save();
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Destroy the current session
 	 *
-	 * Clean up storage for this session - it has been terminated
+	 * Clean up storage for this session - it has been terminated.
 	 * The child class MUST implement this abstract method!
 	 *
 	 * @return	void
 	 */
 	abstract public function sess_destroy();
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Regenerate the current session
 	 *
-	 * Regenerate the session id
+	 * Regenerate the session ID.
 	 * The child class MUST implement this abstract method!
 	 *
-	 * @param	boolean	Destroy session data flag (default: false)
+	 * @param	bool	Destroy session data flag (default: false)
 	 * @return	void
 	 */
-	abstract public function sess_regenerate($destroy = false);
+	abstract public function sess_regenerate($destroy = FALSE);
+
+	// ------------------------------------------------------------------------
 
 	/**
 	 * Get a reference to user data array
 	 *
-	 * Give array access to the main CI_Session object
+	 * Give array access to the main CI_Session object.
 	 * The child class MUST implement this abstract method!
 	 *
 	 * @return	array	Reference to userdata
 	 */
 	abstract public function &get_userdata();
-}
-// END CI_Session_driver Class
 
+}
 
 /* End of file Session.php */
-/* Location: ./system/libraries/Session/Session.php */
-?>
+/* Location: ./system/libraries/Session/Session.php */
\ No newline at end of file
diff --git a/system/libraries/Session/drivers/Session_cookie.php b/system/libraries/Session/drivers/Session_cookie.php
index 69e5fde..4f415cc 100755
--- a/system/libraries/Session/drivers/Session_cookie.php
+++ b/system/libraries/Session/drivers/Session_cookie.php
@@ -37,6 +37,7 @@
  * @link		http://codeigniter.com/user_guide/libraries/sessions.html
  */
 class CI_Session_cookie extends CI_Session_driver {
+
 	/**
 	 * Whether to encrypt the session cookie
 	 *
@@ -192,7 +193,6 @@
 	/**
 	 * Initialize session driver object
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function initialize()
@@ -220,16 +220,17 @@
 			'cookie_prefix',
 			'encryption_key'
 		);
+
 		foreach ($prefs as $key)
 		{
-			$this->$key = isset($this->_parent->params[$key]) ? $this->_parent->params[$key] :
-				$this->CI->config->item($key);
+			$this->$key = isset($this->_parent->params[$key])
+				? $this->_parent->params[$key]
+				: $this->CI->config->item($key);
 		}
 
 		if ($this->encryption_key === '')
 		{
-			show_error('In order to use the Cookie Session driver you are required to set an encryption key '.
-				'in your config file.');
+			show_error('In order to use the Cookie Session driver you are required to set an encryption key in your config file.');
 		}
 
 		// Load the string helper so we can use the strip_slashes() function
@@ -280,6 +281,8 @@
 		$this->_sess_gc();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Write the session data
 	 *
@@ -298,6 +301,8 @@
 		$this->_set_cookie();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Destroy the current session
 	 *
@@ -309,6 +314,7 @@
 		if ($this->sess_use_database === TRUE && isset($this->userdata['session_id']))
 		{
 			$this->CI->db->delete($this->sess_table_name, array('session_id' => $this->userdata['session_id']));
+			$this->data_dirty = FALSE;
 		}
 
 		// Kill the cookie
@@ -319,15 +325,17 @@
 		$this->userdata = array();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Regenerate the current session
 	 *
 	 * Regenerate the session id
 	 *
-	 * @param	boolean	Destroy session data flag (default: false)
+	 * @param	bool	Destroy session data flag (default: false)
 	 * @return	void
 	 */
-	public function sess_regenerate($destroy = false)
+	public function sess_regenerate($destroy = FALSE)
 	{
 		// Check destroy flag
 		if ($destroy)
@@ -339,25 +347,27 @@
 		else
 		{
 			// Just force an update to recreate the id
-			$this->_sess_update(true);
+			$this->_sess_update(TRUE);
 		}
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Get a reference to user data array
 	 *
-	 * @return	array - Reference to userdata
+	 * @return	array	Reference to userdata
 	 */
 	public function &get_userdata()
 	{
-		// Return reference to array
 		return $this->userdata;
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Fetch the current session data if it exists
 	 *
-	 * @access	protected
 	 * @return	bool
 	 */
 	protected function _sess_read()
@@ -388,8 +398,7 @@
 			// 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.');
+				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;
 			}
@@ -399,8 +408,7 @@
 		$session = $this->_unserialize($session);
 
 		// Is the session data we unserialized an array with the correct format?
-		if ( ! is_array($session) || ! isset($session['session_id'], $session['ip_address'], $session['user_agent'],
-		$session['last_activity']))
+		if ( ! is_array($session) OR ! isset($session['session_id'], $session['ip_address'], $session['user_agent'], $session['last_activity']))
 		{
 			$this->sess_destroy();
 			return FALSE;
@@ -422,7 +430,7 @@
 
 		// Does the User Agent Match?
 		if ($this->sess_match_useragent === TRUE &&
-		trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
+			trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
 		{
 			$this->sess_destroy();
 			return FALSE;
@@ -443,8 +451,19 @@
 				$this->CI->db->where('user_agent', $session['user_agent']);
 			}
 
+			// Is caching in effect? Turn it off
+			$db_cache = $this->CI->db->cache_on;
+			$this->CI->db->cache_off();
+
 			$query = $this->CI->db->limit(1)->get($this->sess_table_name);
 
+			// Was caching in effect?
+			if ($db_cache)
+			{
+				// Turn it back on
+				$this->CI->db->cache_on();
+			}
+
 			// No result? Kill it!
 			if ($query->num_rows() === 0)
 			{
@@ -470,10 +489,11 @@
 		return TRUE;
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Create a new session
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _sess_create()
@@ -497,11 +517,12 @@
 		$this->_set_cookie();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Update an existing session
 	 *
-	 * @access	protected
-	 * @param	boolean	Force update flag (default: false)
+	 * @param	bool	Force update flag (default: false)
 	 * @return	void
 	 */
 	protected function _sess_update($force = FALSE)
@@ -539,6 +560,8 @@
 		$this->_set_cookie();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Update database with current data
 	 *
@@ -547,6 +570,8 @@
 	 * so it's guaranteed to update even when a fatal error
 	 * occurs. The first call makes the update and clears the
 	 * dirty flag so it won't happen twice.
+	 *
+	 * @return	void
 	 */
 	public function _update_db()
 	{
@@ -583,6 +608,8 @@
 		}
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Generate a new session id
 	 *
@@ -604,15 +631,16 @@
 		return md5(uniqid($new_sessid, TRUE));
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Get the "now" time
 	 *
-	 * @access	protected
 	 * @return	int	 Time
 	 */
 	protected function _get_time()
 	{
-		if ($this->time_reference === 'local' || $this->time_reference === date_default_timezone_get())
+		if ($this->time_reference === 'local' OR $this->time_reference === date_default_timezone_get())
 		{
 			return time();
 		}
@@ -623,36 +651,27 @@
 		return mktime($hour, $minute, $second, $month, $day, $year);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Write the session cookie
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _set_cookie()
 	{
 		// Get userdata (only defaults if database)
-		if ($this->sess_use_database === TRUE)
-		{
-			$cookie_data = array_intersect_key($this->userdata, $this->defaults);
-		}
-		else
-		{
-			$cookie_data = $this->userdata;
-		}
+		$cookie_data = ($this->sess_use_database === TRUE)
+				? array_intersect_key($this->userdata, $this->defaults)
+				: $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
-		{
+		$cookie_data = ($this->sess_encrypt_cookie === TRUE)
+			? $this->CI->encrypt->encode($cookie_data)
 			// if encryption is not used, we provide an md5 hash to prevent userside tampering
-			$cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);
-		}
+			: $cookie_data.md5($cookie_data.$this->encryption_key);
 
 		$expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
 
@@ -661,35 +680,35 @@
 			$this->cookie_secure, $this->cookie_httponly);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Set a cookie with the system
 	 *
 	 * This abstraction of the setcookie call allows overriding for unit testing
 	 *
-	 * @access  protected
-	 * @param   string  Cookie name
-	 * @param   string  Cookie value
-	 * @param   int	 Expiration time
-	 * @param   string  Cookie path
-	 * @param   string  Cookie domain
-	 * @param   bool	Secure connection flag
-	 * @param   bool	HTTP protocol only flag
-	 * @return  void
+	 * @param	string	Cookie name
+	 * @param	string	Cookie value
+	 * @param	int	Expiration time
+	 * @param	string	Cookie path
+	 * @param	string	Cookie domain
+	 * @param	bool	Secure connection flag
+	 * @param	bool	HTTP protocol only flag
+	 * @return	void
 	 */
-	protected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = false,
-	$httponly = false)
+	protected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = FALSE, $httponly = FALSE)
 	{
-		// Set the cookie
 		setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * 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	protected
 	 * @param	mixed	Data to serialize
 	 * @return	string	Serialized data
 	 */
@@ -703,15 +722,17 @@
 		{
 			$data = str_replace('\\', '{{slash}}', $data);
 		}
+
 		return serialize($data);
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Escape slashes
 	 *
 	 * This function converts any slashes found into a temporary marker
 	 *
-	 * @access	protected
 	 * @param	string	Value
 	 * @param	string	Key
 	 * @return	void
@@ -724,13 +745,14 @@
 		}
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Unserialize
 	 *
 	 * This function unserializes a data string, then converts any
 	 * temporary slash markers back to actual slashes
 	 *
-	 * @access	protected
 	 * @param	mixed	Data to unserialize
 	 * @return	mixed	Unserialized data
 	 */
@@ -747,12 +769,13 @@
 		return is_string($data) ? str_replace('{{slash}}', '\\', $data) : $data;
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Unescape slashes
 	 *
 	 * This function converts any slash markers back into actual slashes
 	 *
-	 * @access	protected
 	 * @param	string	Value
 	 * @param	string	Key
 	 * @return	void
@@ -765,13 +788,14 @@
 		}
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Garbage collection
 	 *
 	 * This deletes expired session rows from database
 	 * if the probability percentage is met
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _sess_gc()
@@ -793,7 +817,8 @@
 			log_message('debug', 'Session garbage collection performed.');
 		}
 	}
+
 }
 
 /* End of file Session_cookie.php */
-/* Location: ./system/libraries/Session/drivers/Session_cookie.php */
+/* Location: ./system/libraries/Session/drivers/Session_cookie.php */
\ No newline at end of file
diff --git a/system/libraries/Session/drivers/Session_native.php b/system/libraries/Session/drivers/Session_native.php
index 8ba8e74..c97e153 100755
--- a/system/libraries/Session/drivers/Session_native.php
+++ b/system/libraries/Session/drivers/Session_native.php
@@ -2,18 +2,29 @@
 /**
  * CodeIgniter
  *
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst. It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
  *
  * @package		CodeIgniter
- * @author		ExpressionEngine	Dev Team
- * @copyright	Copyright	(c) 2008 - 2010, EllisLab, Inc.
- * @license		http://codeigniter.com/user_guide/license.html
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
  * @link		http://codeigniter.com
- * @since		Version	2.0
+ * @since		Version 1.0
  * @filesource
  */
 
-
 /**
  * Native PHP session management driver
  *
@@ -22,13 +33,13 @@
  * @package		CodeIgniter
  * @subpackage	Libraries
  * @category	Sessions
- * @author		ExpressionEngine Dev Team
+ * @author		EllisLab Dev Team
  */
 class CI_Session_native extends CI_Session_driver {
+
 	/**
 	 * Initialize session driver object
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function initialize()
@@ -47,10 +58,12 @@
 			'cookie_path',
 			'cookie_domain'
 		);
+
 		foreach ($prefs as $key)
 		{
-			$config[$key] = isset($this->_parent->params[$key]) ? $this->_parent->params[$key] :
-				$CI->config->item($key);
+			$config[$key] = isset($this->_parent->params[$key])
+				? $this->_parent->params[$key]
+				: $CI->config->item($key);
 		}
 
 		// Set session name, if specified
@@ -75,11 +88,13 @@
 			// Default to 2 years if expiration is "0"
 			$expire = ($config['sess_expiration'] == 0) ? (60*60*24*365*2) : $config['sess_expiration'];
 		}
+
 		if ($config['cookie_path'])
 		{
 			// Use specified path
 			$path = $config['cookie_path'];
 		}
+
 		if ($config['cookie_domain'])
 		{
 			// Use specified domain
@@ -98,14 +113,14 @@
 			// Expired - destroy
 			$destroy = TRUE;
 		}
-		else if ($config['sess_match_ip'] == TRUE && isset($_SESSION['ip_address']) &&
-		$_SESSION['ip_address'] != $CI->input->ip_address())
+		elseif ($config['sess_match_ip'] === TRUE && isset($_SESSION['ip_address'])
+			&& $_SESSION['ip_address'] !== $CI->input->ip_address())
 		{
 			// IP doesn't match - destroy
 			$destroy = TRUE;
 		}
-		else if ($config['sess_match_useragent'] == TRUE && isset($_SESSION['user_agent']) &&
-		$_SESSION['user_agent'] != trim(substr($CI->input->user_agent(), 0, 50)))
+		elseif ($config['sess_match_useragent'] === TRUE && isset($_SESSION['user_agent'])
+			&& $_SESSION['user_agent'] !== trim(substr($CI->input->user_agent(), 0, 50)))
 		{
 			// Agent doesn't match - destroy
 			$destroy = TRUE;
@@ -120,8 +135,8 @@
 		}
 
 		// Check for update time
-		if ($config['sess_time_to_update'] && isset($_SESSION['last_activity']) &&
-		($_SESSION['last_activity'] + $config['sess_time_to_update']) < $now)
+		if ($config['sess_time_to_update'] && isset($_SESSION['last_activity'])
+			&& ($_SESSION['last_activity'] + $config['sess_time_to_update']) < $now)
 		{
 			// Regenerate ID, but don't destroy session
 			$this->sess_regenerate(FALSE);
@@ -131,12 +146,13 @@
 		$_SESSION['last_activity'] = $now;
 
 		// Set matching values as required
-		if ($config['sess_match_ip'] == TRUE && !isset($_SESSION['ip_address']))
+		if ($config['sess_match_ip'] === TRUE && ! isset($_SESSION['ip_address']))
 		{
 			// Store user IP address
 			$_SESSION['ip_address'] = $CI->input->ip_address();
 		}
-		if ($config['sess_match_useragent'] == TRUE && !isset($_SESSION['user_agent']))
+
+		if ($config['sess_match_useragent'] === TRUE && ! isset($_SESSION['user_agent']))
 		{
 			// Store user agent string
 			$_SESSION['user_agent'] = trim(substr($CI->input->user_agent(), 0, 50));
@@ -146,10 +162,11 @@
 		$_SESSION['session_id'] = session_id();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Save the session data
 	 *
-	 * @access	public
 	 * @return	void
 	 */
 	public function sess_save()
@@ -157,10 +174,11 @@
 		// Nothing to do - changes to $_SESSION are automatically saved
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Destroy the current session
 	 *
-	 * @access	public
 	 * @return	void
 	 */
 	public function sess_destroy()
@@ -178,13 +196,14 @@
 		session_destroy();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Regenerate the current session
 	 *
 	 * Regenerate the session id
 	 *
-	 * @access	public
-	 * @param	boolean	Destroy session data flag (default: FALSE)
+	 * @param	bool	Destroy session data flag (default: FALSE)
 	 * @return	void
 	 */
 	public function sess_regenerate($destroy = FALSE)
@@ -194,10 +213,11 @@
 		$_SESSION['session_id'] = session_id();
 	}
 
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Get a reference to user data array
 	 *
-	 * @access	public
 	 * @return	array	Reference to userdata
 	 */
 	public function &get_userdata()
@@ -205,7 +225,8 @@
 		// Just return reference to $_SESSION
 		return $_SESSION;
 	}
+
 }
 
 /* End of file Session_native.php */
-/* Location: ./system/libraries/Session/drivers/Session_native.php */
+/* Location: ./system/libraries/Session/drivers/Session_native.php */
\ No newline at end of file
diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php
index cbb91c4..a8aaa20 100644
--- a/system/libraries/Xmlrpc.php
+++ b/system/libraries/Xmlrpc.php
@@ -1359,7 +1359,7 @@
 
 		if ($type === $this->xmlrpcBoolean)
 		{
-			$val = (int) (strcasecmp($val,'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false')));
+			$val = (int) (strcasecmp($val, 'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false')));
 		}
 
 		if ($this->mytype === 2)
diff --git a/tests/codeigniter/libraries/Calendar_test.php b/tests/codeigniter/libraries/Calendar_test.php
new file mode 100644
index 0000000..95668d7
--- /dev/null
+++ b/tests/codeigniter/libraries/Calendar_test.php
@@ -0,0 +1,204 @@
+<?php
+
+class Calendar_test extends CI_TestCase {
+
+	function __construct()
+	{
+		$obj = new stdClass;
+		$obj->calendar = new Mock_Libraries_Calendar();
+
+		$this->calendar = $obj->calendar;
+	}
+
+	function test_initialize()
+	{
+		$this->calendar->initialize(array(
+			'month_type'	=>	'short',
+			'start_day'	=>	'monday'
+		));
+		$this->assertEquals('short', $this->calendar->month_type);
+		$this->assertEquals('monday', $this->calendar->start_day);
+	}
+
+	/**
+	 * @covers Mock_Libraries_Calendar::parse_template
+	 */
+	function test_generate()
+	{
+		$no_events = '<table border="0" cellpadding="4" cellspacing="0">
+
+<tr>
+<th colspan="7">September&nbsp;2011</th>
+
+</tr>
+
+<tr>
+<td>Su</td><td>Mo</td><td>Tu</td><td>We</td><td>Th</td><td>Fr</td><td>Sa</td>
+</tr>
+
+<tr>
+<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>1</td><td>2</td><td>3</td>
+</tr>
+
+<tr>
+<td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td>
+</tr>
+
+<tr>
+<td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td>
+</tr>
+
+<tr>
+<td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td>
+</tr>
+
+<tr>
+<td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td><td>&nbsp;</td>
+</tr>
+
+</table>';
+
+		$this->assertEquals($no_events, $this->calendar->generate(2011, 9));
+
+		$data = array(
+			3  => 'http://example.com/news/article/2006/03/',
+			7  => 'http://example.com/news/article/2006/07/',
+			13 => 'http://example.com/news/article/2006/13/',
+			26 => 'http://example.com/news/article/2006/26/'
+		);
+
+		$events = '<table border="0" cellpadding="4" cellspacing="0">
+
+<tr>
+<th colspan="7">September&nbsp;2011</th>
+
+</tr>
+
+<tr>
+<td>Su</td><td>Mo</td><td>Tu</td><td>We</td><td>Th</td><td>Fr</td><td>Sa</td>
+</tr>
+
+<tr>
+<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>1</td><td>2</td><td><a href="http://example.com/news/article/2006/03/">3</a></td>
+</tr>
+
+<tr>
+<td>4</td><td>5</td><td>6</td><td><a href="http://example.com/news/article/2006/07/">7</a></td><td>8</td><td>9</td><td>10</td>
+</tr>
+
+<tr>
+<td>11</td><td>12</td><td><a href="http://example.com/news/article/2006/13/">13</a></td><td>14</td><td>15</td><td>16</td><td>17</td>
+</tr>
+
+<tr>
+<td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td>
+</tr>
+
+<tr>
+<td>25</td><td><a href="http://example.com/news/article/2006/26/">26</a></td><td>27</td><td>28</td><td>29</td><td>30</td><td>&nbsp;</td>
+</tr>
+
+</table>';
+
+		$this->assertEquals($events, $this->calendar->generate(2011, 9, $data));
+	}
+
+	function test_get_month_name()
+	{
+		$this->calendar->month_type = NULL;
+		$this->assertEquals('January', $this->calendar->get_month_name('01'));
+
+		$this->calendar->month_type = 'short';
+		$this->assertEquals('Jan', $this->calendar->get_month_name('01'));
+	}
+
+	function test_get_day_names()
+	{
+		$this->assertEquals(array(
+			'Sunday',
+			'Monday',
+			'Tuesday',
+			'Wednesday',
+			'Thursday',
+			'Friday',
+			'Saturday'
+		), $this->calendar->get_day_names('long'));
+
+		$this->assertEquals(array(
+			'Sun',
+			'Mon',
+			'Tue',
+			'Wed',
+			'Thu',
+			'Fri',
+			'Sat'
+		), $this->calendar->get_day_names('short'));
+
+		$this->calendar->day_type = NULL;
+
+		$this->assertEquals(array(
+			'Su',
+			'Mo',
+			'Tu',
+			'We',
+			'Th',
+			'Fr',
+			'Sa'
+		), $this->calendar->get_day_names());
+	}
+
+	function test_adjust_date()
+	{
+		$this->assertEquals(array('month' => 8, 'year' => 2012), $this->calendar->adjust_date(8, 2012));
+		$this->assertEquals(array('month' => 1, 'year' => 2013), $this->calendar->adjust_date(13, 2012));
+	}
+
+	function test_get_total_days()
+	{
+		$this->assertEquals(0, $this->calendar->get_total_days(13, 2012));
+
+		$this->assertEquals(31, $this->calendar->get_total_days(1, 2012));
+		$this->assertEquals(28, $this->calendar->get_total_days(2, 2011));
+		$this->assertEquals(29, $this->calendar->get_total_days(2, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(3, 2012));
+		$this->assertEquals(30, $this->calendar->get_total_days(4, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(5, 2012));
+		$this->assertEquals(30, $this->calendar->get_total_days(6, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(7, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(8, 2012));
+		$this->assertEquals(30, $this->calendar->get_total_days(9, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(10, 2012));
+		$this->assertEquals(30, $this->calendar->get_total_days(11, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(12, 2012));
+	}
+
+	function test_default_template()
+	{
+		$array = array(
+			'table_open'				=> '<table border="0" cellpadding="4" cellspacing="0">',
+			'heading_row_start'			=> '<tr>',
+			'heading_previous_cell'		=> '<th><a href="{previous_url}">&lt;&lt;</a></th>',
+			'heading_title_cell'		=> '<th colspan="{colspan}">{heading}</th>',
+			'heading_next_cell'			=> '<th><a href="{next_url}">&gt;&gt;</a></th>',
+			'heading_row_end'			=> '</tr>',
+			'week_row_start'			=> '<tr>',
+			'week_day_cell'				=> '<td>{week_day}</td>',
+			'week_row_end'				=> '</tr>',
+			'cal_row_start'				=> '<tr>',
+			'cal_cell_start'			=> '<td>',
+			'cal_cell_start_today'		=> '<td>',
+			'cal_cell_content'			=> '<a href="{content}">{day}</a>',
+			'cal_cell_content_today'	=> '<a href="{content}"><strong>{day}</strong></a>',
+			'cal_cell_no_content'		=> '{day}',
+			'cal_cell_no_content_today'	=> '<strong>{day}</strong>',
+			'cal_cell_blank'			=> '&nbsp;',
+			'cal_cell_end'				=> '</td>',
+			'cal_cell_end_today'		=> '</td>',
+			'cal_row_end'				=> '</tr>',
+			'table_close'				=> '</table>'
+		);
+
+		$this->assertEquals($array, $this->calendar->default_template());
+	}
+
+}
\ No newline at end of file
diff --git a/tests/mocks/libraries/calendar.php b/tests/mocks/libraries/calendar.php
new file mode 100644
index 0000000..8fee536
--- /dev/null
+++ b/tests/mocks/libraries/calendar.php
@@ -0,0 +1,25 @@
+<?php
+
+class Mock_Libraries_Calendar extends CI_Calendar {
+
+	public function __construct($config = array())
+	{
+		$this->CI = new stdClass;
+		$this->CI->lang = new Mock_Core_Lang();
+
+		if ( ! in_array('calendar_lang.php', $this->CI->lang->is_loaded, TRUE))
+		{
+			$this->CI->lang->load('calendar');
+		}
+
+		$this->local_time = time();
+
+		if (count($config) > 0)
+		{
+			$this->initialize($config);
+		}
+
+		log_message('debug', 'Calendar Class Initialized');
+	}
+
+}
\ No newline at end of file
diff --git a/user_guide_src/source/libraries/form_validation.rst b/user_guide_src/source/libraries/form_validation.rst
index 3bcad7b..39f7874 100644
--- a/user_guide_src/source/libraries/form_validation.rst
+++ b/user_guide_src/source/libraries/form_validation.rst
@@ -884,7 +884,6 @@
                                      0, 1, 2, 3, etc.
 **is_natural_no_zero**    No         Returns FALSE if the form element contains anything other than a natural
                                      number, but not zero: 1, 2, 3, etc.
-**is_unique**             Yes        Returns FALSE if the form element is not unique in a database table.                          is_unique[table.field] 
 **valid_email**           No         Returns FALSE if the form element does not contain a valid email address.
 **valid_emails**          No         Returns FALSE if any value provided in a comma separated list is not a valid email.
 **valid_ip**              No         Returns FALSE if the supplied IP is not valid.
diff --git a/user_guide_src/source/libraries/migration.rst b/user_guide_src/source/libraries/migration.rst
index 5192f1f..cb7d96a 100644
--- a/user_guide_src/source/libraries/migration.rst
+++ b/user_guide_src/source/libraries/migration.rst
@@ -2,4 +2,136 @@
 Migrations Class
 ################
 
-Coming soon.
\ No newline at end of file
+Migrations are a convenient way for you to alter your database in a 
+structured and organized manner. You could edit fragments of SQL by hand 
+but you would then be responsible for telling other developers that they 
+need to go and run them. You would also have to keep track of which changes 
+need to be run against the production machines next time you deploy.
+
+The database table **migration** tracks which migrations have already been 
+run so all you have to do is update your application files and 
+call **$this->migrate->current()** to work out which migrations should be run. 
+The current version is found in **config/migration.php**.
+
+******************
+Create a Migration
+******************
+
+.. note:: Each Migration is run in numerical order forward or backwards 
+	depending on the method taken. Use a prefix of 3 numbers followed by an 
+	underscore for the filename of your migration.
+	
+This will be the first migration for a new site which has a blog. All 
+migrations go in the folder **application/migrations/** and have names such 
+as: **001_add_blog.php**.::
+
+	defined('BASEPATH') OR exit('No direct script access allowed');
+
+	class Migration_Add_blog extends CI_Migration {
+
+		public function up()
+		{
+			$this->dbforge->add_field(array(
+				'blog_id' => array(
+					'type' => 'INT',
+					'constraint' => 5,
+					'unsigned' => TRUE,
+					'auto_increment' => TRUE
+				),
+				'blog_title' => array(
+					'type' => 'VARCHAR',
+					'constraint' => '100',
+				),
+				'blog_description' => array(
+					'type' => 'TEXT',
+					'null' => TRUE,
+				),
+			));
+			
+			$this->dbforge->create_table('blog');
+		}
+
+		public function down()
+		{
+			$this->dbforge->drop_table('blog');
+		}
+
+Then in **application/config/migration.php** set **$config['migration_version'] = 1;**.
+
+*************
+Usage Example
+*************
+
+In this example some simple code is placed in **application/controllers/migrate.php** 
+to update the schema.::
+
+	$this->load->library('migration');
+
+	if ( ! $this->migration->current())
+	{
+		show_error($this->migration->error_string());
+	}
+
+******************
+Function Reference
+******************
+
+There are five available methods for the Migration class:
+
+-  $this->migration->current();
+-  $this->migration->error_string();
+-  $this->migration->find_migrations();
+-  $this->migration->latest();
+-  $this->migration->version();
+
+$this->migration->current()
+============================
+
+The current migration is whatever is set for **$config['migration_version']** in 
+**application/config/migration.php**.
+
+$this->migration->error_string()
+=================================
+
+This returns a string of errors while performing a migration.
+
+$this->migration->find_migrations()
+====================================
+
+An array of migration filenames are returned that are found in the **migration_path** 
+property.
+
+$this->migration->latest()
+===========================
+
+This works much the same way as current() but instead of looking for 
+the **$config['migration_version']** the Migration class will use the very 
+newest migration found in the filesystem.
+
+$this->migration->version()
+============================
+
+Version can be used to roll back changes or step forwards programmatically to 
+specific versions. It works just like current but ignores **$config['migration_version']**.::
+
+	$this->load->library('migration');
+
+	$this->migration->version(5);
+
+*********************
+Migration Preferences
+*********************
+
+The following is a table of all the config options for migrations.
+
+========================== ====================== ============= =============================================
+Preference                 Default                Options       Description
+========================== ====================== ============= =============================================
+**migration_enabled**      FALSE                  TRUE / FALSE  Enable or disable migrations.
+**migration_path**         APPPATH.'migrations/'  None          The path to your migrations folder.
+**migration_version**      0                      None          The current version your database should use.
+**migration_table**        migrations             None          The table name for storing the shema
+                                                                version number.
+**migration_auto_latest**  FALSE                  TRUE / FALSE  Enable or disable automatically 
+                                                                running migrations.
+========================== ====================== ============= =============================================