Fixed conflict in url_helper docs.
diff --git a/application/config/config.php b/application/config/config.php
index 1ec6543..880393c 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -176,6 +176,10 @@
 |	3 = Informational Messages
 |	4 = All Messages
 |
+| You can also pass in a array with threshold levels to show individual error types
+| 
+| 	array(2) = Debug Messages, without Error Messages
+|
 | For a live site you'll usually only enable Errors (1) to be logged otherwise
 | your log files will fill up very fast.
 |
@@ -292,11 +296,13 @@
 | 'csrf_token_name' = The token name
 | 'csrf_cookie_name' = The cookie name
 | 'csrf_expire' = The number in seconds the token should expire.
+| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
 */
 $config['csrf_protection'] = FALSE;
 $config['csrf_token_name'] = 'csrf_test_name';
 $config['csrf_cookie_name'] = 'csrf_cookie_name';
 $config['csrf_expire'] = 7200;
+$config['csrf_exclude_uris'] = array();
 
 /*
 |--------------------------------------------------------------------------
diff --git a/application/config/database.php b/application/config/database.php
index fa541a7..b4b34bf 100644
--- a/application/config/database.php
+++ b/application/config/database.php
@@ -27,7 +27,8 @@
 |	['char_set'] The character set used in communicating with the database
 |	['dbcollat'] The character collation used in communicating with the database
 |				 NOTE: For MySQL and MySQLi databases, this setting is only used
-| 				 as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7.
+| 				 as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7
+|				 (and in table creation queries made with DB Forge).
 | 				 There is an incompatibility in PHP with mysql_real_escape_string() which
 | 				 can make your site vulnerable to SQL injection if you are using a
 | 				 multi-byte character set and are running versions lower than these.
diff --git a/application/config/migration.php b/application/config/migration.php
new file mode 100644
index 0000000..dba8700
--- /dev/null
+++ b/application/config/migration.php
@@ -0,0 +1,42 @@
+<?php defined('BASEPATH') OR exit('No direct script access allowed');

+/*

+|--------------------------------------------------------------------------

+| Enable/Disable Migrations

+|--------------------------------------------------------------------------

+|

+| Migrations are disabled by default for security reasons.

+| You should enable migrations whenever you intend to do a schema migration

+| and disable it back when you're done.

+|

+*/

+$config['migration_enabled'] = FALSE;

+

+

+/*

+|--------------------------------------------------------------------------

+| Migrations version

+|--------------------------------------------------------------------------

+|

+| This is used to set migration version that the file system should be on.

+| If you run $this->migration->latest() this is the version that schema will

+| be upgraded / downgraded to.

+|

+*/

+$config['migration_version'] = 0;

+

+

+/*

+|--------------------------------------------------------------------------

+| Migrations Path

+|--------------------------------------------------------------------------

+|

+| Path to your migrations folder.

+| Typically, it will be within your application path.

+| Also, writing permission is required within the migrations path.

+|

+*/

+$config['migration_path'] = APPPATH . 'migrations/';

+

+

+/* End of file migration.php */

+/* Location: ./application/config/migration.php */
\ No newline at end of file
diff --git a/application/config/mimes.php b/application/config/mimes.php
index 82767d7..90a1d18 100644
--- a/application/config/mimes.php
+++ b/application/config/mimes.php
@@ -8,10 +8,10 @@
 |
 */
 
-$mimes = array(	'hqx'	=>	'application/mac-binhex40',
+$mimes = array('hqx'   =>      array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'),
 				'cpt'	=>	'application/mac-compactpro',
 				'csv'	=>	array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'),
-				'bin'	=>	'application/macbinary',
+				'bin'	=>	array('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'),
 				'dms'	=>	'application/octet-stream',
 				'lha'	=>	'application/octet-stream',
 				'lzh'	=>	'application/octet-stream',
@@ -39,6 +39,7 @@
 				'dvi'	=>	'application/x-dvi',
 				'gtar'	=>	'application/x-gtar',
 				'gz'	=>	'application/x-gzip',
+				'gzip'  =>	'application/x-gzip',
 				'php'	=>	'application/x-httpd-php',
 				'php4'	=>	'application/x-httpd-php',
 				'php3'	=>	'application/x-httpd-php',
@@ -51,14 +52,14 @@
 				'tgz'	=>	array('application/x-tar', 'application/x-gzip-compressed'),
 				'xhtml'	=>	'application/xhtml+xml',
 				'xht'	=>	'application/xhtml+xml',
-				'zip'	=>  array('application/x-zip', 'application/zip', 'application/x-zip-compressed'),
+				'zip'	=>	array('application/x-zip', 'application/zip', 'application/x-zip-compressed'),
 				'mid'	=>	'audio/midi',
 				'midi'	=>	'audio/midi',
 				'mpga'	=>	'audio/mpeg',
 				'mp2'	=>	'audio/mpeg',
 				'mp3'	=>	array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),
-				'aif'	=>	'audio/x-aiff',
-				'aiff'	=>	'audio/x-aiff',
+				'aif'	=>	array('audio/x-aiff', 'audio/aiff'),
+				'aiff'	=>	array('audio/x-aiff', 'audio/aiff'),
 				'aifc'	=>	'audio/x-aiff',
 				'ram'	=>	'audio/x-pn-realaudio',
 				'rm'	=>	'audio/x-pn-realaudio',
@@ -66,7 +67,7 @@
 				'ra'	=>	'audio/x-realaudio',
 				'rv'	=>	'video/vnd.rn-realvideo',
 				'wav'	=>	'audio/x-wav',
-				'bmp'	=>	'image/bmp',
+				'bmp'	=>	array('image/bmp', 'image/x-windows-bmp'),
 				'gif'	=>	'image/gif',
 				'jpeg'	=>	array('image/jpeg', 'image/pjpeg'),
 				'jpg'	=>	array('image/jpeg', 'image/pjpeg'),
@@ -90,7 +91,7 @@
 				'mpe'	=>	'video/mpeg',
 				'qt'	=>	'video/quicktime',
 				'mov'	=>	'video/quicktime',
-				'avi'	=>	'video/x-msvideo',
+				'avi'	=>	array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
 				'movie'	=>	'video/x-sgi-movie',
 				'doc'	=>	'application/msword',
 				'docx'	=>	'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
@@ -98,7 +99,40 @@
 				'word'	=>	array('application/msword', 'application/octet-stream'),
 				'xl'	=>	'application/excel',
 				'eml'	=>	'message/rfc822',
-				'json' => array('application/json', 'text/json')
+				'json'  =>	array('application/json', 'text/json'),
+				'pem'   =>	array('application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'),
+				'p10'   =>	array('application/x-pkcs10', 'application/pkcs10'),
+				'p12'   =>	'application/x-pkcs12',
+				'p7a'   =>	'application/x-pkcs7-signature',
+				'p7c'   =>	array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
+				'p7m'   =>	array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
+				'p7r'   =>	'application/x-pkcs7-certreqresp',
+				'p7s'   =>	'application/pkcs7-signature',
+				'crt'   =>	array('application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'),
+				'crl'   =>	array('application/pkix-crl', 'application/pkcs-crl'),
+				'der'   =>	'application/x-x509-ca-cert',
+				'kdb'   =>	'application/octet-stream',
+				'pgp'   =>	'application/pgp',
+				'gpg'   =>	'application/gpg-keys',
+				'sst'   =>	'application/octet-stream',
+				'csr'   =>	'application/octet-stream',
+				'rsa'   =>	'application/x-pkcs7',
+				'cer'   =>	array('application/pkix-cert', 'application/x-x509-ca-cert'),
+				'3g2'   =>	'video/3gpp2',
+				'3gp'   =>	'video/3gp',
+				'mp4'   =>	'video/mp4',
+				'm4a'   =>	'audio/x-m4a',
+				'f4v'   =>	'video/mp4',
+				'aac'   =>	'audio/x-acc',
+				'm4u'   =>	'application/vnd.mpegurl',
+				'm3u'   =>	'text/plain',
+				'xspf'  =>	'application/xspf+xml',
+				'vlc'   =>	'application/videolan',
+				'wmv'   =>	'video/x-ms-wmv',
+				'au'    =>	'audio/x-au',
+				'ac3'   =>	'audio/ac3',
+				'flac'  =>	'audio/x-flac',
+				'ogg'   =>	'audio/ogg',
 			);
 
 
diff --git a/index.php b/index.php
index f4ac11a..c50cfed 100644
--- a/index.php
+++ b/index.php
@@ -33,7 +33,7 @@
 	switch (ENVIRONMENT)
 	{
 		case 'development':
-			error_reporting(E_ALL);
+			error_reporting(-1);
 		break;
 	
 		case 'testing':
@@ -73,6 +73,23 @@
  *
  */
 	$application_folder = 'application';
+		
+/*
+ *---------------------------------------------------------------
+ * VIEW FOLDER NAME
+ *---------------------------------------------------------------
+ * 
+ * If you want to move the view folder out of the application 
+ * folder set the path to the folder here. The folder can be renamed
+ * and relocated anywhere on your server. If blank, it will default 
+ * to the standard location inside your application folder.  If you 
+ * do move this, use the full server path to this folder 
+ *
+ * NO TRAILING SLASH!
+ *
+ */
+	$view_folder = '';	
+
 
 /*
  * --------------------------------------------------------------------
@@ -190,6 +207,22 @@
 
 		define('APPPATH', BASEPATH.$application_folder.'/');
 	}
+	
+	// The path to the "views" folder
+	if (is_dir($view_folder)) 
+	{
+		define ('VIEWPATH', $view_folder .'/');
+	}
+	else 
+	{
+		if ( ! is_dir(APPPATH.'views/'))
+		{
+			exit("Your view folder path does not appear to be set correctly. Please open the following file and correct this: ".SELF);
+		}
+				
+		define ('VIEWPATH', APPPATH.'views/' );	
+	}
+	
 
 /*
  * --------------------------------------------------------------------
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..be807db
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,11 @@
+# What is CodeIgniter
+
+CodeIgniter is an Application Development Framework - a toolkit - for people who build web sites using PHP. Its goal is to enable you to develop projects much faster than you could if you were writing code from scratch, by providing a rich set of libraries for commonly needed tasks, as well as a simple interface and logical structure to access these libraries. CodeIgniter lets you creatively focus on your project by minimizing the amount of code needed for a given task.
+
+# Resources
+
+ * [User Guide](http://codeigniter.com/user_guide/)
+ * [Community Forums](http://codeigniter.com/forums/)
+ * [User Voice](http://codeigniter.uservoice.com/forums/40508-codeigniter-reactor)
+ * [Community Wiki](http://codeigniter.com/wiki/)
+ * [Community IRC](http://webchat.freenode.net/?channels=codeigniter&uio=d4)
\ No newline at end of file
diff --git a/system/core/Benchmark.php b/system/core/Benchmark.php
old mode 100644
new mode 100755
index 515550e..a200727
--- a/system/core/Benchmark.php
+++ b/system/core/Benchmark.php
@@ -29,6 +29,11 @@
  */
 class CI_Benchmark {
 
+	/**
+	 * List of all benchmark markers and when they were added
+	 *
+	 * @var array
+	 */
 	var $marker = array();
 
 	// --------------------------------------------------------------------
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
old mode 100644
new mode 100755
index 94fecb5..0a1391d
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -27,18 +27,32 @@
  * @link		http://codeigniter.com/user_guide/
  */
 
-/*
- * ------------------------------------------------------
- *  Define the CodeIgniter Version
- * ------------------------------------------------------
+/**
+ * CodeIgniter Version
+ *
+ * @var string
+ *
  */
-	define('CI_VERSION', '2.0.3');
+	/**
+	 * CodeIgniter Version
+	 *
+	 * @var string
+	 *
+	 */
+	define('CI_VERSION', '2.0.2');
 
-/*
- * ------------------------------------------------------
- *  Define the CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
- * ------------------------------------------------------
+/**
+ * CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
+ *
+ * @var boolean
+ *
  */
+	/**
+	 * CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
+	 *
+	 * @var string
+	 *
+	 */
 	define('CI_CORE', FALSE);
 
 /*
@@ -267,7 +281,25 @@
 		OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))
 		)
 	{
-		show_404("{$class}/{$method}");
+		if ( ! empty($RTR->routes['404_override']))
+		{
+			$x = explode('/', $RTR->routes['404_override']);
+			$class = $x[0];
+			$method = (isset($x[1]) ? $x[1] : 'index');
+			if ( ! class_exists($class))
+			{
+				if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
+				{
+					show_404("{$class}/{$method}");
+				}
+
+				include_once(APPPATH.'controllers/'.$class.'.php');
+			}
+		}
+		else
+		{
+			show_404("{$class}/{$method}");
+		}
 	}
 
 /*
diff --git a/system/core/Common.php b/system/core/Common.php
index db9fbeb..d793754 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -132,9 +132,9 @@
 
 		$name = FALSE;
 
-		// Look for the class first in the native system/libraries folder
-		// thenin the local application/libraries folder
-		foreach (array(BASEPATH, APPPATH) as $path)
+		// Look for the class first in the local application/libraries folder
+		// then in the native system/libraries folder
+		foreach (array(APPPATH, BASEPATH) as $path)
 		{
 			if (file_exists($path.$directory.'/'.$class.'.php'))
 			{
@@ -536,5 +536,29 @@
 	}
 }
 
+// ------------------------------------------------------------------------
+
+/**
+* Returns HTML escaped variable
+*
+* @access	public
+* @param	mixed
+* @return	mixed
+*/
+if ( ! function_exists('html_escape'))
+{
+	function html_escape($var)
+	{
+		if (is_array($var))
+		{
+			return array_map('html_escape', $var);
+		}
+		else
+		{
+			return htmlspecialchars($var, ENT_QUOTES, config_item('charset'));
+		}
+	}
+}
+
 /* End of file Common.php */
 /* Location: ./system/core/Common.php */
\ No newline at end of file
diff --git a/system/core/Config.php b/system/core/Config.php
old mode 100644
new mode 100755
index 0e6f10e..714c466
--- a/system/core/Config.php
+++ b/system/core/Config.php
@@ -28,8 +28,23 @@
  */
 class CI_Config {
 
+	/**
+	 * List of all loaded config values
+	 *
+	 * @var array
+	 */
 	var $config = array();
+	/**
+	 * List of all loaded config files
+	 *
+	 * @var array
+	 */
 	var $is_loaded = array();
+	/**
+	 * List of paths to search when trying to load a config file
+	 *
+	 * @var array
+	 */
 	var $_config_paths = array(APPPATH);
 
 	/**
@@ -251,13 +266,13 @@
 			return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri);
 		}
 	}
-	
+
 	// -------------------------------------------------------------
-	
+
 	/**
 	 * Base URL
 	 * Returns base_url [. uri_string]
-	 * 
+	 *
 	 * @access public
 	 * @param string $uri
 	 * @return string
@@ -266,12 +281,12 @@
 	{
 		return $this->slash_item('base_url').ltrim($this->_uri_string($uri),'/');
 	}
-	
+
 	// -------------------------------------------------------------
-	
+
 	/**
 	 * Build URI string for use in Config::site_url() and Config::base_url()
-	 * 
+	 *
 	 * @access protected
 	 * @param  $uri
 	 * @return string
@@ -305,7 +320,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	/**
 	 * System URL
 	 *
diff --git a/system/core/Controller.php b/system/core/Controller.php
index ec86b79..fddb81e 100644
--- a/system/core/Controller.php
+++ b/system/core/Controller.php
@@ -48,7 +48,7 @@
 
 		$this->load =& load_class('Loader', 'core');
 
-		$this->load->set_base_classes()->ci_autoloader();
+		$this->load->initialize();
 		
 		log_message('debug', "Controller Class Initialized");
 	}
diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php
old mode 100644
new mode 100755
index bff86a9..869739a
--- a/system/core/Exceptions.php
+++ b/system/core/Exceptions.php
@@ -30,8 +30,21 @@
 	var $message;
 	var $filename;
 	var $line;
+
+	/**
+	 * Nesting level of the output buffering mechanism
+	 *
+	 * @var int
+	 * @access public
+	 */
 	var $ob_level;
 
+	/**
+	 * List if available error levels
+	 *
+	 * @var array
+	 * @access public
+	 */
 	var $levels = array(
 						E_ERROR				=>	'Error',
 						E_WARNING			=>	'Warning',
@@ -84,7 +97,8 @@
 	 * 404 Page Not Found Handler
 	 *
 	 * @access	private
-	 * @param	string
+	 * @param	string	the page
+	 * @param 	bool	log error yes/no
 	 * @return	string
 	 */
 	function show_404($page = '', $log_error = TRUE)
@@ -115,6 +129,7 @@
 	 * @param	string	the heading
 	 * @param	string	the message
 	 * @param	string	the template name
+	 * @param 	int		the status code
 	 * @return	string
 	 */
 	function show_error($heading, $message, $template = 'error_general', $status_code = 500)
diff --git a/system/core/Hooks.php b/system/core/Hooks.php
old mode 100644
new mode 100755
index fd6380f..33f1c03
--- a/system/core/Hooks.php
+++ b/system/core/Hooks.php
@@ -28,8 +28,23 @@
  */
 class CI_Hooks {
 
+	/**
+	 * Determines wether hooks are enabled
+	 *
+	 * @var bool
+	 */
 	var $enabled		= FALSE;
+	/**
+	 * List of all hooks set in config/hooks.php
+	 *
+	 * @var array
+	 */
 	var $hooks			= array();
+	/**
+	 * Determines wether hook is in progress, used to prevent infinte loops
+	 *
+	 * @var bool
+	 */
 	var $in_progress	= FALSE;
 
 	/**
diff --git a/system/core/Input.php b/system/core/Input.php
old mode 100644
new mode 100755
index cfbef94..0dc2c45
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -28,15 +28,51 @@
  */
 class CI_Input {
 
+	/**
+	 * IP address of the current user
+	 *
+	 * @var string
+	 */
 	var $ip_address				= FALSE;
+	/**
+	 * user agent (web browser) being used by the current user
+	 *
+	 * @var string
+	 */
 	var $user_agent				= FALSE;
+	/**
+	 * If FALSE, then $_GET will be set to an empty array
+	 *
+	 * @var bool
+	 */
 	var $_allow_get_array		= TRUE;
+	/**
+	 * If TRUE, then newlines are standardized
+	 *
+	 * @var bool
+	 */
 	var $_standardize_newlines	= TRUE;
-	var $_enable_xss			= FALSE; // Set automatically based on config setting
-	var $_enable_csrf			= FALSE; // Set automatically based on config setting
-
+	/**
+	 * Determines whether the XSS filter is always active when GET, POST or COOKIE data is encountered
+	 * Set automatically based on config setting
+	 *
+	 * @var bool
+	 */
+	var $_enable_xss			= FALSE;
+	/**
+	 * Enables a CSRF cookie token to be set.
+	 * Set automatically based on config setting
+	 *
+	 * @var bool
+	 */
+	var $_enable_csrf			= FALSE;
+	/**
+	 * List of all HTTP request headers
+	 *
+	 * @var array
+	 */
 	protected $headers			= array();
-	
+
 
 	/**
 	 * Constructor
@@ -147,7 +183,7 @@
 			}
 			return $post;
 		}
-		
+
 		return $this->_fetch_from_array($_POST, $index, $xss_clean);
 	}
 
@@ -287,14 +323,14 @@
 
 			$this->ip_address = in_array($_SERVER['REMOTE_ADDR'], $proxies) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
 		}
+		elseif (! $this->server('HTTP_CLIENT_IP') AND $this->server('REMOTE_ADDR'))
+		{
+			$this->ip_address = $_SERVER['REMOTE_ADDR'];
+		}
 		elseif ($this->server('REMOTE_ADDR') AND $this->server('HTTP_CLIENT_IP'))
 		{
 			$this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
 		}
-		elseif ($this->server('REMOTE_ADDR'))
-		{
-			$this->ip_address = $_SERVER['REMOTE_ADDR'];
-		}
 		elseif ($this->server('HTTP_CLIENT_IP'))
 		{
 			$this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
@@ -402,9 +438,9 @@
 	function _sanitize_globals()
 	{
 		// It would be "wrong" to unset any of these GLOBALS.
-		$protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST', 
+		$protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST',
 							'_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA',
-							'system_folder', 'application_folder', 'BM', 'EXT', 
+							'system_folder', 'application_folder', 'BM', 'EXT',
 							'CFG', 'URI', 'RTR', 'OUT', 'IN');
 
 		// Unset globals for securiy.
@@ -523,7 +559,7 @@
 		{
 			$str = $this->uni->clean_string($str);
 		}
-		
+
 		// Remove control characters
 		$str = remove_invisible_characters($str);
 
@@ -579,9 +615,11 @@
 	/**
 	 * Request Headers
 	 *
-	 * In Apache, you can simply call apache_request_headers(), however for 
+	 * In Apache, you can simply call apache_request_headers(), however for
 	 * people running other webservers the function is undefined.
 	 *
+	 * @param	bool XSS cleaning
+	 *
 	 * @return array
 	 */
 	public function request_headers($xss_clean = FALSE)
@@ -609,10 +647,10 @@
 		{
 			$key = str_replace('_', ' ', strtolower($key));
 			$key = str_replace(' ', '-', ucwords($key));
-			
+
 			$this->headers[$key] = $val;
 		}
-		
+
 		return $this->headers;
 	}
 
@@ -633,7 +671,7 @@
 		{
 			$this->request_headers();
 		}
-		
+
 		if ( ! isset($this->headers[$index]))
 		{
 			return FALSE;
@@ -644,7 +682,7 @@
 			return $this->security->xss_clean($this->headers[$index]);
 		}
 
-		return $this->headers[$index];		
+		return $this->headers[$index];
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/core/Lang.php b/system/core/Lang.php
old mode 100644
new mode 100755
index 170e6c7..d61d102
--- a/system/core/Lang.php
+++ b/system/core/Lang.php
@@ -26,7 +26,17 @@
  */
 class CI_Lang {
 
+	/**
+	 * List of translations
+	 *
+	 * @var array
+	 */
 	var $language	= array();
+	/**
+	 * List of loaded language files
+	 *
+	 * @var array
+	 */
 	var $is_loaded	= array();
 
 	/**
@@ -47,6 +57,9 @@
 	 * @access	public
 	 * @param	mixed	the name of the language file to be loaded. Can be an array
 	 * @param	string	the language (english, etc.)
+	 * @param	bool	return loaded array of translations
+	 * @param 	bool	add suffix to $langfile
+	 * @param 	string	alternative path to look for language file
 	 * @return	mixed
 	 */
 	function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '')
@@ -99,7 +112,7 @@
 		}
 
 
-		if ( ! isset($lang))
+		if ( ! isset($lang) OR ! is_array($lang))
 		{
 			log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile);
 			return;
@@ -111,7 +124,7 @@
 		}
 
 		$this->is_loaded[] = $langfile;
-		$this->language = array_merge($this->language, $lang);
+		$this->language = $this->language + $lang;
 		unset($lang);
 
 		log_message('debug', 'Language file loaded: language/'.$idiom.'/'.$langfile);
diff --git a/system/core/Loader.php b/system/core/Loader.php
old mode 100644
new mode 100755
index 7c8b298..de0fc06
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -29,18 +29,91 @@
 class CI_Loader {
 
 	// All these are set automatically. Don't mess with them.
+	/**
+	 * Nesting level of the output buffering mechanism
+	 *
+	 * @var int
+	 * @access protected
+	 */
 	protected $_ci_ob_level;
+	/**
+	 * List of paths to load views from
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_ci_view_paths		= array();
+	/**
+	 * List of paths to load libraries from
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_ci_library_paths	= array();
+	/**
+	 * List of paths to load models from
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_ci_model_paths		= array();
+	/**
+	 * List of paths to load helpers from
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_ci_helper_paths		= array();
+	/**
+	 * List of loaded base classes
+	 * Set by the controller class
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_base_classes		= array(); // Set by the controller class
+	/**
+	 * List of cached variables
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_ci_cached_vars		= array();
+	/**
+	 * List of loaded classes
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_ci_classes			= array();
+	/**
+	 * List of loaded files
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_ci_loaded_files		= array();
+	/**
+	 * List of loaded models
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_ci_models			= array();
+	/**
+	 * List of loaded helpers
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_ci_helpers			= array();
-	protected $_ci_varmap			= array('unit_test' => 'unit', 
+	/**
+	 * List of class name mappings
+	 *
+	 * @var array
+	 * @access protected
+	 */
+	protected $_ci_varmap			= array('unit_test' => 'unit',
 											'user_agent' => 'agent');
 
 	/**
@@ -54,25 +127,30 @@
 		$this->_ci_library_paths = array(APPPATH, BASEPATH);
 		$this->_ci_helper_paths = array(APPPATH, BASEPATH);
 		$this->_ci_model_paths = array(APPPATH);
-		$this->_ci_view_paths = array(APPPATH.'views/'	=> TRUE);
-		
+		$this->_ci_view_paths = array(VIEWPATH	=> TRUE);
+
 		log_message('debug', "Loader Class Initialized");
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	/**
-	 * Set _base_classes variable
+	 * Initialize the Loader
 	 *
 	 * This method is called once in CI_Controller.
 	 *
-	 * @param 	array 	
+	 * @param 	array
 	 * @return 	object
 	 */
-	public function set_base_classes()
+	public function initialize()
 	{
+		$this->_ci_classes = array();
+		$this->_ci_loaded_files = array();
+		$this->_ci_models = array();
 		$this->_base_classes =& is_loaded();
-		
+
+		$this->_ci_autoloader();
+
 		return $this;
 	}
 
@@ -96,7 +174,7 @@
 		{
 			return $this->_ci_classes[$class];
 		}
-				
+
 		return FALSE;
 	}
 
@@ -366,6 +444,7 @@
 	 * the controller class and its "view" files.
 	 *
 	 * @param	array
+	 * @param 	string
 	 * @return	void
 	 */
 	public function vars($vars = array(), $val = '')
@@ -507,6 +586,8 @@
 	 * Loads a config file
 	 *
 	 * @param	string
+	 * @param	bool
+	 * @param 	bool
 	 * @return	void
 	 */
 	public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
@@ -553,13 +634,13 @@
 	 * Prepends a parent path to the library, model, helper, and config path arrays
 	 *
 	 * @param	string
-	 * @param 	boolean 	
+	 * @param 	boolean
 	 * @return	void
 	 */
 	public function add_package_path($path, $view_cascade=TRUE)
 	{
 		$path = rtrim($path, '/').'/';
-		
+
 		array_unshift($this->_ci_library_paths, $path);
 		array_unshift($this->_ci_model_paths, $path);
 		array_unshift($this->_ci_helper_paths, $path);
@@ -595,6 +676,7 @@
 	 * If no path is provided, the most recently added path is removed.
 	 *
 	 * @param	type
+	 * @param 	bool
 	 * @return	type
 	 */
 	public function remove_package_path($path = '', $remove_config_path = TRUE)
@@ -619,7 +701,7 @@
 					unset($this->{$var}[$key]);
 				}
 			}
-			
+
 			if (isset($this->_ci_view_paths[$path.'views/']))
 			{
 				unset($this->_ci_view_paths[$path.'views/']);
@@ -658,7 +740,7 @@
 		{
 			$$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
 		}
-		
+
 		$file_exists = FALSE;
 
 		// Set the path to the requested file
@@ -680,11 +762,11 @@
 					$file_exists = TRUE;
 					break;
 				}
-				
+
 				if ( ! $cascade)
 				{
 					break;
-				}				
+				}
 			}
 		}
 
@@ -913,6 +995,7 @@
 	 *
 	 * @param	string
 	 * @param	string
+	 * @param	bool
 	 * @param	string	an optional object name
 	 * @return	null
 	 */
@@ -935,22 +1018,22 @@
 					// first, global next
 					if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
 					{
-						include_once($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
+						include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
 						break;
 					}
 					elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
 					{
-						include_once($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
+						include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
 						break;
 					}
 					elseif (file_exists($path .'config/'.strtolower($class).'.php'))
 					{
-						include_once($path .'config/'.strtolower($class).'.php');
+						include($path .'config/'.strtolower($class).'.php');
 						break;
 					}
 					elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php'))
 					{
-						include_once($path .'config/'.ucfirst(strtolower($class)).'.php');
+						include($path .'config/'.ucfirst(strtolower($class)).'.php');
 						break;
 					}
 				}
@@ -1020,23 +1103,19 @@
 	 * The config/autoload.php file contains an array that permits sub-systems,
 	 * libraries, and helpers to be loaded automatically.
 	 *
-	 * This function is public, as it's used in the CI_Controller class.  
-	 * However, there is no reason you should ever needs to use it.
-	 *
 	 * @param	array
 	 * @return	void
 	 */
-	public function ci_autoloader()
+	protected function _ci_autoloader()
 	{
 		if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
 		{
-			include_once(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
+			include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
 		}
 		else
 		{
-			include_once(APPPATH.'config/autoload.php');
+			include(APPPATH.'config/autoload.php');
 		}
-		
 
 		if ( ! isset($autoload))
 		{
@@ -1122,6 +1201,7 @@
 	/**
 	 * Get a reference to a specific library or model
 	 *
+	 * @param 	string
 	 * @return	bool
 	 */
 	protected function &_ci_get_component($component)
@@ -1138,6 +1218,7 @@
 	 * This function preps the name of various items to make loading them more reliable.
 	 *
 	 * @param	mixed
+	 * @param 	string
 	 * @return	array
 	 */
 	protected function _ci_prep_filename($filename, $extension)
diff --git a/system/core/Model.php b/system/core/Model.php
old mode 100644
new mode 100755
index 8566a0b..e15ffbe
--- a/system/core/Model.php
+++ b/system/core/Model.php
@@ -42,6 +42,7 @@
 	 * Allows models to access CI's loaded classes using the same
 	 * syntax as controllers.
 	 *
+	 * @param	string
 	 * @access private
 	 */
 	function __get($key)
diff --git a/system/core/Output.php b/system/core/Output.php
old mode 100644
new mode 100755
index 05ace91..ccecafd
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -28,15 +28,67 @@
  */
 class CI_Output {
 
+	/**
+	 * Current output string
+	 *
+	 * @var string
+	 * @access 	protected
+	 */
 	protected $final_output;
+	/**
+	 * Cache expiration time
+	 *
+	 * @var int
+	 * @access 	protected
+	 */
 	protected $cache_expiration	= 0;
+	/**
+	 * List of server headers
+	 *
+	 * @var array
+	 * @access 	protected
+	 */
 	protected $headers			= array();
-	protected $mime_types			= array();
+	/**
+	 * List of mime types
+	 *
+	 * @var array
+	 * @access 	protected
+	 */
+	protected $mime_types		= array();
+	/**
+	 * Determines wether profiler is enabled
+	 *
+	 * @var book
+	 * @access 	protected
+	 */
 	protected $enable_profiler	= FALSE;
+	/**
+	 * Determines if output compression is enabled
+	 *
+	 * @var bool
+	 * @access 	protected
+	 */
 	protected $_zlib_oc			= FALSE;
+	/**
+	 * List of profiler sections
+	 *
+	 * @var array
+	 * @access 	protected
+	 */
 	protected $_profiler_sections = array();
-	protected $parse_exec_vars	= TRUE;	// whether or not to parse variables like {elapsed_time} and {memory_usage}
+	/**
+	 * Whether or not to parse variables like {elapsed_time} and {memory_usage}
+	 *
+	 * @var bool
+	 * @access 	protected
+	 */
+	protected $parse_exec_vars	= TRUE;
 
+	/**
+	 * Constructor
+	 *
+	 */
 	function __construct()
 	{
 		$this->_zlib_oc = @ini_get('zlib.output_compression');
@@ -127,6 +179,7 @@
 	 *
 	 * @access	public
 	 * @param	string
+	 * @param 	bool
 	 * @return	void
 	 */
 	function set_header($header, $replace = TRUE)
@@ -265,6 +318,7 @@
 	 * benchmark timer so the page rendering speed and memory usage can be shown.
 	 *
 	 * @access	public
+	 * @param 	string
 	 * @return	mixed
 	 */
 	function _display($output = '')
@@ -401,6 +455,7 @@
 	 * Write a Cache File
 	 *
 	 * @access	public
+	 * @param 	string
 	 * @return	void
 	 */
 	function _write_cache($output)
@@ -452,6 +507,8 @@
 	 * Update/serve a cached file
 	 *
 	 * @access	public
+	 * @param 	object	config class
+	 * @param 	object	uri class
 	 * @return	void
 	 */
 	function _display_cache(&$CFG, &$URI)
diff --git a/system/core/Router.php b/system/core/Router.php
old mode 100644
new mode 100755
index 5e92a04..6da6674
--- a/system/core/Router.php
+++ b/system/core/Router.php
@@ -28,12 +28,54 @@
  */
 class CI_Router {
 
+	/**
+	 * Config class
+	 *
+	 * @var object
+	 * @access public
+	 */
 	var $config;
+	/**
+	 * List of routes
+	 *
+	 * @var array
+	 * @access public
+	 */
 	var $routes			= array();
+	/**
+	 * List of error routes
+	 *
+	 * @var array
+	 * @access public
+	 */
 	var $error_routes	= array();
+	/**
+	 * Current class name
+	 *
+	 * @var string
+	 * @access public
+	 */
 	var $class			= '';
+	/**
+	 * Current method name
+	 *
+	 * @var string
+	 * @access public
+	 */
 	var $method			= 'index';
+	/**
+	 * Sub-directory that contains the requested controller class
+	 *
+	 * @var string
+	 * @access public
+	 */
 	var $directory		= '';
+	/**
+	 * Default controller (and method if specific)
+	 *
+	 * @var string
+	 * @access public
+	 */
 	var $default_controller;
 
 	/**
@@ -95,7 +137,7 @@
 		{
 			include(APPPATH.'config/routes.php');
 		}
-		
+
 		$this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route;
 		unset($route);
 
@@ -244,7 +286,20 @@
 				// Does the requested controller exist in the sub-folder?
 				if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php'))
 				{
-					show_404($this->fetch_directory().$segments[0]);
+					if ( ! empty($this->routes['404_override']))
+					{
+						$x = explode('/', $this->routes['404_override']);
+
+						$this->set_directory('');
+						$this->set_class($x[0]);
+						$this->set_method(isset($x[1]) ? $x[1] : 'index');
+
+						return $x;
+					}
+					else
+					{
+						show_404($this->fetch_directory().$segments[0]);
+					}
 				}
 			}
 			else
diff --git a/system/core/Security.php b/system/core/Security.php
old mode 100644
new mode 100755
index 3617cad..342455f
--- a/system/core/Security.php
+++ b/system/core/Security.php
@@ -25,14 +25,55 @@
  * @link		http://codeigniter.com/user_guide/libraries/security.html
  */
 class CI_Security {
-	
-	protected $_xss_hash			= '';
-	protected $_csrf_hash			= '';
-	protected $_csrf_expire			= 7200;  // Two hours (in seconds)
-	protected $_csrf_token_name		= 'ci_csrf_token';
-	protected $_csrf_cookie_name	= 'ci_csrf_token';
 
-	/* never allowed, string replacement */
+	/**
+	 * Random Hash for protecting URLs
+	 *
+	 * @var string
+	 * @access protected
+	 */
+	protected $_xss_hash			= '';
+	
+	/**
+	 * Random Hash for Cross Site Request Forgery Protection Cookie
+	 *
+	 * @var string
+	 * @access protected
+	 */
+	protected $_csrf_hash			= '';
+	
+	/**
+	 * Expiration time for Cross Site Request Forgery Protection Cookie
+	 * Defaults to two hours (in seconds)
+	 *
+	 * @var int
+	 * @access protected
+	 */
+	protected $_csrf_expire			= 7200;
+	
+	/**
+	 * Token name for Cross Site Request Forgery Protection Cookie
+	 *
+	 * @var string
+	 * @access protected
+	 */
+	protected $_csrf_token_name		= 'ci_csrf_token';
+	
+	/**
+	 * Cookie name for Cross Site Request Forgery Protection Cookie
+	 *
+	 * @var string
+	 * @access protected
+	 */
+	protected $_csrf_cookie_name	= 'ci_csrf_token';
+	
+	/**
+	 * List of never allowed strings
+	 *
+	 * @var array
+	 * @access protected
+	 */
+	
 	protected $_never_allowed_str = array(
 					'document.cookie'	=> '[removed]',
 					'document.write'	=> '[removed]',
@@ -45,14 +86,19 @@
 					'<![CDATA['			=> '&lt;![CDATA['
 	);
 
-	/* never allowed, regex replacement */
+	/**
+	 * List of never allowed regex replacement
+	 *
+	 * @var array
+	 * @access protected
+	 */
 	protected $_never_allowed_regex = array(
 					"javascript\s*:"			=> '[removed]',
 					"expression\s*(\(|&\#40;)"	=> '[removed]', // CSS and IE
 					"vbscript\s*:"				=> '[removed]', // IE, surprise!
 					"Redirect\s+302"			=> '[removed]'
 	);
-	
+
 	/**
 	 * Constructor
 	 */
@@ -93,9 +139,19 @@
 		{
 			return $this->csrf_set_cookie();
 		}
+		
+		// Check if URI has been whitelisted from CSRF checks
+		if ($exclude_uris = config_item('csrf_exclude_uris'))
+		{
+			$uri = load_class('URI', 'core');
+			if (in_array($uri->uri_string(), $exclude_uris))
+			{
+				return $this;
+			}
+		}
 
 		// Do the tokens exist in both the _POST and _COOKIE arrays?
-		if ( ! isset($_POST[$this->_csrf_token_name]) OR 
+		if ( ! isset($_POST[$this->_csrf_token_name]) OR
 			 ! isset($_COOKIE[$this->_csrf_cookie_name]))
 		{
 			$this->csrf_show_error();
@@ -107,7 +163,7 @@
 			$this->csrf_show_error();
 		}
 
-		// We kill this since we're done and we don't want to 
+		// We kill this since we're done and we don't want to
 		// polute the _POST array
 		unset($_POST[$this->_csrf_token_name]);
 
@@ -115,8 +171,8 @@
 		unset($_COOKIE[$this->_csrf_cookie_name]);
 		$this->_csrf_set_hash();
 		$this->csrf_set_cookie();
-
-		log_message('debug', "CSRF token verified ");
+		
+		log_message('debug', "CSRF token verified");
 		
 		return $this;
 	}
@@ -146,7 +202,7 @@
 		setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie);
 
 		log_message('debug', "CRSF cookie Set");
-		
+
 		return $this;
 	}
 
@@ -165,9 +221,9 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Get CSRF Hash 
+	 * Get CSRF Hash
 	 *
-	 * Getter Method 
+	 * Getter Method
 	 *
 	 * @return 	string 	self::_csrf_hash
 	 */
@@ -215,6 +271,7 @@
 	 * http://ha.ckers.org/xss.html
 	 *
 	 * @param	mixed	string or array
+	 * @param 	bool
 	 * @return	string
 	 */
 	public function xss_clean($str, $is_image = FALSE)
@@ -263,7 +320,7 @@
 		 */
 
 		$str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str);
-	
+
 		$str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_decode_entity'), $str);
 
 		/*
@@ -276,7 +333,7 @@
 		 *
 		 * This prevents strings like this: ja	vascript
 		 * NOTE: we deal with spaces between characters later.
-		 * NOTE: preg_replace was found to be amazingly slow here on 
+		 * NOTE: preg_replace was found to be amazingly slow here on
 		 * large blocks of data, so we use str_replace.
 		 */
 
@@ -304,8 +361,8 @@
 		 */
 		if ($is_image === TRUE)
 		{
-			// Images have a tendency to have the PHP short opening and 
-			// closing tags every so often so we skip those and only 
+			// Images have a tendency to have the PHP short opening and
+			// closing tags every so often so we skip those and only
 			// do the long opening tags.
 			$str = preg_replace('/<\?(php)/i', "&lt;?\\1", $str);
 		}
@@ -321,10 +378,10 @@
 		 * These words are compacted back to their correct state.
 		 */
 		$words = array(
-				'javascript', 'expression', 'vbscript', 'script', 
+				'javascript', 'expression', 'vbscript', 'script',
 				'applet', 'alert', 'document', 'write', 'cookie', 'window'
 			);
-			
+
 		foreach ($words as $word)
 		{
 			$temp = '';
@@ -341,8 +398,8 @@
 
 		/*
 		 * Remove disallowed Javascript in links or img tags
-		 * We used to do some version comparisons and use of stripos for PHP5, 
-		 * but it is dog slow compared to these simplified non-capturing 
+		 * We used to do some version comparisons and use of stripos for PHP5,
+		 * but it is dog slow compared to these simplified non-capturing
 		 * preg_match(), especially if the pattern exists in the string
 		 */
 		do
@@ -405,11 +462,11 @@
 
 		/*
 		 * Images are Handled in a Special Way
-		 * - Essentially, we want to know that after all of the character 
-		 * conversion is done whether any unwanted, likely XSS, code was found.  
+		 * - Essentially, we want to know that after all of the character
+		 * conversion is done whether any unwanted, likely XSS, code was found.
 		 * If not, we return TRUE, as the image is clean.
-		 * However, if the string post-conversion does not matched the 
-		 * string post-removal of XSS, then it fails, as there was unwanted XSS 
+		 * However, if the string post-conversion does not matched the
+		 * string post-removal of XSS, then it fails, as there was unwanted XSS
 		 * code found and removed/changed during processing.
 		 */
 
@@ -478,7 +535,7 @@
 		// correctly.  html_entity_decode() does not convert entities without
 		// semicolons, so we are left with our own little solution here. Bummer.
 
-		if (function_exists('html_entity_decode') && 
+		if (function_exists('html_entity_decode') &&
 			(strtolower($charset) != 'utf-8'))
 		{
 			$str = html_entity_decode($str, ENT_COMPAT, $charset);
@@ -505,6 +562,7 @@
 	 * Filename Security
 	 *
 	 * @param	string
+	 * @param 	bool
 	 * @return	string
 	 */
 	public function sanitize_filename($str, $relative_path = FALSE)
@@ -542,7 +600,7 @@
 						"%3b",		// ;
 						"%3d"		// =
 					);
-		
+
 		if ( ! $relative_path)
 		{
 			$bad[] = './';
@@ -570,7 +628,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	/*
 	 * Remove Evil HTML Attributes (like evenhandlers and style)
 	 *
@@ -578,7 +636,7 @@
 	 * 	- Everything up until a space
 	 *		For example, everything between the pipes:
 	 *		<a |style=document.write('hello');alert('world');| class=link>
-	 * 	- Everything inside the quotes 
+	 * 	- Everything inside the quotes
 	 *		For example, everything between the pipes:
 	 *		<a |style="document.write('hello'); alert('world');"| class="link">
 	 *
@@ -594,12 +652,12 @@
 		if ($is_image === TRUE)
 		{
 			/*
-			 * Adobe Photoshop puts XML metadata into JFIF images, 
+			 * Adobe Photoshop puts XML metadata into JFIF images,
 			 * including namespacing, so we have to allow this for images.
 			 */
 			unset($evil_attributes[array_search('xmlns', $evil_attributes)]);
 		}
-		
+
 		do {
 			$str = preg_replace(
 				"#<(/?[^><]+?)([^A-Za-z\-])(".implode('|', $evil_attributes).")(\s*=\s*)([\"][^>]*?[\"]|[\'][^>]*?[\']|[^>]*?)([\s><])([><]*)#i",
@@ -607,10 +665,10 @@
 				$str, -1, $count
 			);
 		} while ($count);
-		
+
 		return $str;
 	}
-	
+
 	// --------------------------------------------------------------------
 
 	/**
@@ -627,7 +685,7 @@
 		$str = '&lt;'.$matches[1].$matches[2].$matches[3];
 
 		// encode captured opening or closing brace to prevent recursive vectors
-		$str .= str_replace(array('>', '<'), array('&gt;', '&lt;'), 
+		$str .= str_replace(array('>', '<'), array('&gt;', '&lt;'),
 							$matches[4]);
 
 		return $str;
@@ -649,7 +707,7 @@
 	protected function _js_link_removal($match)
 	{
 		$attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]));
-		
+
 		return str_replace($match[1], preg_replace("#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si", "", $attributes), $match[0]);
 	}
 
@@ -669,7 +727,7 @@
 	protected function _js_img_removal($match)
 	{
 		$attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]));
-		
+
 		return str_replace($match[1], preg_replace("#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si", "", $attributes), $match[0]);
 	}
 
@@ -729,13 +787,13 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	/**
 	 * Validate URL entities
 	 *
 	 * Called by xss_clean()
 	 *
-	 * @param 	string	
+	 * @param 	string
 	 * @return 	string
 	 */
 	protected function _validate_entities($str)
@@ -743,9 +801,9 @@
 		/*
 		 * Protect GET variables in URLs
 		 */
-		
+
 		 // 901119URL5918AMP18930PROTECT8198
-		
+
 		$str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash()."\\1=\\2", $str);
 
 		/*
@@ -769,7 +827,7 @@
 		 * Un-Protect GET variables in URLs
 		 */
 		$str = str_replace($this->xss_hash(), '&', $str);
-		
+
 		return $str;
 	}
 
@@ -794,7 +852,7 @@
 		{
 			$str = preg_replace("#".$key."#i", $val, $str);
 		}
-		
+
 		return $str;
 	}
 
@@ -809,16 +867,16 @@
 	{
 		if ($this->_csrf_hash == '')
 		{
-			// If the cookie exists we will use it's value.  
+			// If the cookie exists we will use it's value.
 			// We don't necessarily want to regenerate it with
-			// each page load since a page could contain embedded 
+			// each page load since a page could contain embedded
 			// sub-pages causing this feature to fail
-			if (isset($_COOKIE[$this->_csrf_cookie_name]) && 
+			if (isset($_COOKIE[$this->_csrf_cookie_name]) &&
 				$_COOKIE[$this->_csrf_cookie_name] != '')
 			{
 				return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];
 			}
-			
+
 			return $this->_csrf_hash = md5(uniqid(rand(), TRUE));
 		}
 
@@ -826,7 +884,6 @@
 	}
 
 }
-// END Security Class
 
 /* End of file Security.php */
-/* Location: ./system/libraries/Security.php */
+/* Location: ./system/libraries/Security.php */
\ No newline at end of file
diff --git a/system/core/URI.php b/system/core/URI.php
old mode 100644
new mode 100755
index 20f0f00..8946bc7
--- a/system/core/URI.php
+++ b/system/core/URI.php
@@ -28,9 +28,34 @@
  */
 class CI_URI {
 
+	/**
+	 * List of cached uri segments
+	 *
+	 * @var array
+	 * @access public
+	 */
 	var	$keyval			= array();
+	/**
+	 * Current uri string
+	 *
+	 * @var string
+	 * @access public
+	 */
 	var $uri_string;
+	/**
+	 * List of uri segments
+	 *
+	 * @var array
+	 * @access public
+	 */
 	var $segments		= array();
+	/**
+	 * Re-indexed list of uri segments
+	 * Starts at 1 instead of 0
+	 *
+	 * @var array
+	 * @access public
+	 */
 	var $rsegments		= array();
 
 	/**
@@ -127,6 +152,7 @@
 	 * Set the URI String
 	 *
 	 * @access	public
+	 * @param 	string
 	 * @return	string
 	 */
 	function _set_uri_string($str)
@@ -149,7 +175,7 @@
 	 * @access	private
 	 * @return	string
 	 */
-	private function _detect_uri()
+	protected function _detect_uri()
 	{
 		if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME']))
 		{
@@ -206,7 +232,7 @@
 	 * @access	private
 	 * @return	string
 	 */
-	private function _parse_cli_args()
+	protected function _parse_cli_args()
 	{
 		$args = array_slice($_SERVER['argv'], 1);
 
@@ -366,6 +392,11 @@
 	/**
 	 * Identical to above only it uses the re-routed segment array
 	 *
+	 * @access 	public
+	 * @param 	integer	the starting segment number
+	 * @param 	array	an array of default values
+	 * @return 	array
+	 *
 	 */
 	function ruri_to_assoc($n = 3, $default = array())
 	{
diff --git a/system/database/DB.php b/system/database/DB.php
old mode 100644
new mode 100755
index 33207d8..8314d3b
--- a/system/database/DB.php
+++ b/system/database/DB.php
@@ -21,6 +21,8 @@
  * @category	Database
  * @author		ExpressionEngine Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @param 	string
+ * @param 	bool	Determines if active record should be used or not
  */
 function &DB($params = '', $active_record_override = NULL)
 {
@@ -35,7 +37,7 @@
 				show_error('The configuration file database.php does not exist.');
 			}
 		}
-		
+
 		include($file_path);
 
 		if ( ! isset($db) OR count($db) == 0)
diff --git a/system/database/DB_active_rec.php b/system/database/DB_active_rec.php
index 2af3553..37d162b 100644
--- a/system/database/DB_active_rec.php
+++ b/system/database/DB_active_rec.php
@@ -196,7 +196,7 @@
 			$alias = $this->_create_alias_from_table(trim($select));
 		}
 
-		$sql = $type.'('.$this->_protect_identifiers(trim($select)).') AS '.$alias;
+		$sql = $type.'('.$this->_protect_identifiers(trim($select)).') AS '.$this->_protect_identifiers(trim($alias));
 
 		$this->ar_select[] = $sql;
 
@@ -660,8 +660,12 @@
 			$prefix = (count($this->ar_like) == 0) ? '' : $type;
 
 			$v = $this->escape_like_str($v);
-
-			if ($side == 'before')
+			
+			if ($side == 'none')
+			{
+				$like_statement = $prefix." $k $not LIKE '{$v}'";
+			}
+			elseif ($side == 'before')
 			{
 				$like_statement = $prefix." $k $not LIKE '%{$v}'";
 			}
@@ -870,11 +874,11 @@
 	 */
 	public function limit($value, $offset = '')
 	{
-		$this->ar_limit = $value;
+		$this->ar_limit = (int) $value;
 
 		if ($offset != '')
 		{
-			$this->ar_offset = $offset;
+			$this->ar_offset = (int) $offset;
 		}
 
 		return $this;
@@ -1322,7 +1326,7 @@
 		{
 			if ($this->db_debug)
 			{
-				return $this->display_error('db_myst_use_index');
+				return $this->display_error('db_must_use_index');
 			}
 
 			return FALSE;
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index 10e8ed0..f3e824d 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -218,7 +218,7 @@
 
 		// Some DBs have functions that return the version, and don't run special
 		// SQL queries per se. In these instances, just return the result.
-		$driver_version_exceptions = array('oci8', 'sqlite');
+		$driver_version_exceptions = array('oci8', 'sqlite', 'cubrid');
 
 		if (in_array($this->dbdriver, $driver_version_exceptions))
 		{
diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php
new file mode 100644
index 0000000..d011404
--- /dev/null
+++ b/system/database/drivers/cubrid/cubrid_driver.php
@@ -0,0 +1,792 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package		CodeIgniter
+ * @author		Esen Sagynov
+ * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 2.0.2
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CUBRID Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the active record
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		Esen Sagynov
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_cubrid_driver extends CI_DB {
+
+	// Default CUBRID Broker port. Will be used unless user
+	// explicitly specifies another one.
+	const DEFAULT_PORT = 33000;
+
+	var $dbdriver = 'cubrid';
+
+	// The character used for escaping - no need in CUBRID
+	var	$_escape_char = '';
+
+	// clause and character used for LIKE escape sequences - not used in CUBRID
+	var $_like_escape_str = '';
+	var $_like_escape_chr = '';
+
+	/**
+	 * The syntax to count rows is slightly different across different
+	 * database engines, so this string appears in each driver and is
+	 * used for the count_all() and count_all_results() functions.
+	 */
+	var $_count_string = 'SELECT COUNT(*) AS ';
+	var $_random_keyword = ' RAND()'; // database specific random keyword
+
+	/**
+	 * Non-persistent database connection
+	 *
+	 * @access	private called by the base class
+	 * @return	resource
+	 */
+	function db_connect()
+	{
+		// If no port is defined by the user, use the default value
+		if ($this->port == '')
+		{
+			$this->port = self::DEFAULT_PORT;
+		}
+
+		$conn = cubrid_connect($this->hostname, $this->port, $this->database, $this->username, $this->password);
+
+		if ($conn)
+		{
+			// Check if a user wants to run queries in dry, i.e. run the
+			// queries but not commit them.
+			if (isset($this->auto_commit) && ! $this->auto_commit)
+			{
+				cubrid_set_autocommit($conn, CUBRID_AUTOCOMMIT_FALSE);
+			}
+			else
+			{
+				cubrid_set_autocommit($conn, CUBRID_AUTOCOMMIT_TRUE);
+				$this->auto_commit = TRUE;
+			}
+		}
+
+		return $conn;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Persistent database connection
+	 * In CUBRID persistent DB connection is supported natively in CUBRID
+	 * engine which can be configured in the CUBRID Broker configuration
+	 * file by setting the CCI_PCONNECT parameter to ON. In that case, all
+	 * connections established between the client application and the
+	 * server will become persistent. This is calling the same
+	 * @cubrid_connect function will establish persisten connection
+	 * considering that the CCI_PCONNECT is ON.
+	 *
+	 * @access	private called by the base class
+	 * @return	resource
+	 */
+	function db_pconnect()
+	{
+		return $this->db_connect();
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Reconnect
+	 *
+	 * Keep / reestablish the db connection if no queries have been
+	 * sent for a length of time exceeding the server's idle timeout
+	 *
+	 * @access	public
+	 * @return	void
+	 */
+	function reconnect()
+	{
+		if (cubrid_ping($this->conn_id) === FALSE)
+		{
+			$this->conn_id = FALSE;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Select the database
+	 *
+	 * @access	private called by the base class
+	 * @return	resource
+	 */
+	function db_select()
+	{
+		// In CUBRID there is no need to select a database as the database
+		// is chosen at the connection time.
+		// So, to determine if the database is "selected", all we have to
+		// do is ping the server and return that value.
+		return cubrid_ping($this->conn_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set client character set
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	string
+	 * @return	resource
+	 */
+	function db_set_charset($charset, $collation)
+	{
+		// In CUBRID, there is no need to set charset or collation.
+		// This is why returning true will allow the application continue
+		// its normal process.
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Version number query string
+	 *
+	 * @access	public
+	 * @return	string
+	 */
+	function _version()
+	{
+		// To obtain the CUBRID Server version, no need to run the SQL query.
+		// CUBRID PHP API provides a function to determin this value.
+		// This is why we also need to add 'cubrid' value to the list of
+		// $driver_version_exceptions array in DB_driver class in
+		// version() function.
+		return cubrid_get_server_info($this->conn_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Execute the query
+	 *
+	 * @access	private called by the base class
+	 * @param	string	an SQL query
+	 * @return	resource
+	 */
+	function _execute($sql)
+	{
+		$sql = $this->_prep_query($sql);
+		return @cubrid_query($sql, $this->conn_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Prep the query
+	 *
+	 * If needed, each database adapter can prep the query string
+	 *
+	 * @access	private called by execute()
+	 * @param	string	an SQL query
+	 * @return	string
+	 */
+	function _prep_query($sql)
+	{
+		// No need to prepare
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Begin Transaction
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function trans_begin($test_mode = FALSE)
+	{
+		if ( ! $this->trans_enabled)
+		{
+			return TRUE;
+		}
+
+		// When transactions are nested we only begin/commit/rollback the outermost ones
+		if ($this->_trans_depth > 0)
+		{
+			return TRUE;
+		}
+
+		// Reset the transaction failure flag.
+		// If the $test_mode flag is set to TRUE transactions will be rolled back
+		// even if the queries produce a successful result.
+		$this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
+
+		if (cubrid_get_autocommit($this->conn_id))
+		{
+			cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE);
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Commit Transaction
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function trans_commit()
+	{
+		if ( ! $this->trans_enabled)
+		{
+			return TRUE;
+		}
+
+		// When transactions are nested we only begin/commit/rollback the outermost ones
+		if ($this->_trans_depth > 0)
+		{
+			return TRUE;
+		}
+
+		cubrid_commit($this->conn_id);
+
+		if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id))
+		{
+			cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE);
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Rollback Transaction
+	 *
+	 * @access	public
+	 * @return	bool
+	 */
+	function trans_rollback()
+	{
+		if ( ! $this->trans_enabled)
+		{
+			return TRUE;
+		}
+
+		// When transactions are nested we only begin/commit/rollback the outermost ones
+		if ($this->_trans_depth > 0)
+		{
+			return TRUE;
+		}
+
+		cubrid_rollback($this->conn_id);
+
+		if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id))
+		{
+			cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE);
+		}
+
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Escape String
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	bool	whether or not the string will be used in a LIKE condition
+	 * @return	string
+	 */
+	function escape_str($str, $like = FALSE)
+	{
+		if (is_array($str))
+		{
+			foreach ($str as $key => $val)
+			{
+				$str[$key] = $this->escape_str($val, $like);
+			}
+
+			return $str;
+		}
+
+		if (function_exists('cubrid_real_escape_string') AND is_resource($this->conn_id))
+		{
+			$str = cubrid_real_escape_string($str, $this->conn_id);
+		}
+		else
+		{
+			$str = addslashes($str);
+		}
+
+		// escape LIKE condition wildcards
+		if ($like === TRUE)
+		{
+			$str = str_replace(array('%', '_'), array('\\%', '\\_'), $str);
+		}
+
+		return $str;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Affected Rows
+	 *
+	 * @access	public
+	 * @return	integer
+	 */
+	function affected_rows()
+	{
+		return @cubrid_affected_rows($this->conn_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Insert ID
+	 *
+	 * @access	public
+	 * @return	integer
+	 */
+	function insert_id()
+	{
+		return @cubrid_insert_id($this->conn_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * "Count All" query
+	 *
+	 * Generates a platform-specific query string that counts all records in
+	 * the specified table
+	 *
+	 * @access	public
+	 * @param	string
+	 * @return	string
+	 */
+	function count_all($table = '')
+	{
+		if ($table == '')
+		{
+			return 0;
+		}
+		
+		$query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
+
+		if ($query->num_rows() == 0)
+		{
+			return 0;
+		}
+
+		$row = $query->row();
+		$this->_reset_select();
+		return (int) $row->numrows;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * List table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @access	private
+	 * @param	boolean
+	 * @return	string
+	 */
+	function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = "SHOW TABLES";
+
+		if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+		{
+			$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @access	public
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	function _list_columns($table = '')
+	{
+		return "SHOW COLUMNS FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @access	public
+	 * @param	string	the table name
+	 * @return	object
+	 */
+	function _field_data($table)
+	{
+		return "SELECT * FROM ".$table." LIMIT 1";
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * The error message string
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _error_message()
+	{
+		return cubrid_error($this->conn_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * The error message number
+	 *
+	 * @access	private
+	 * @return	integer
+	 */
+	function _error_number()
+	{
+		return cubrid_errno($this->conn_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Escape the SQL Identifiers
+	 *
+	 * This function escapes column and table names
+	 *
+	 * @access	private
+	 * @param	string
+	 * @return	string
+	 */
+	function _escape_identifiers($item)
+	{
+		if ($this->_escape_char == '')
+		{
+			return $item;
+		}
+
+		foreach ($this->_reserved_identifiers as $id)
+		{
+			if (strpos($item, '.'.$id) !== FALSE)
+			{
+				$str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
+
+				// remove duplicates if the user already included the escape
+				return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+			}
+		}
+
+		if (strpos($item, '.') !== FALSE)
+		{
+			$str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
+		}
+		else
+		{
+			$str = $this->_escape_char.$item.$this->_escape_char;
+		}
+
+		// remove duplicates if the user already included the escape
+		return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @access	public
+	 * @param	type
+	 * @return	type
+	 */
+	function _from_tables($tables)
+	{
+		if ( ! is_array($tables))
+		{
+			$tables = array($tables);
+		}
+
+		return '('.implode(', ', $tables).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Insert statement
+	 *
+	 * Generates a platform-specific insert string from the supplied data
+	 *
+	 * @access	public
+	 * @param	string	the table name
+	 * @param	array	the insert keys
+	 * @param	array	the insert values
+	 * @return	string
+	 */
+	function _insert($table, $keys, $values)
+	{
+		return "INSERT INTO ".$table." (\"".implode('", "', $keys)."\") VALUES (".implode(', ', $values).")";
+	}
+
+	// --------------------------------------------------------------------
+
+
+	/**
+	 * Replace statement
+	 *
+	 * Generates a platform-specific replace string from the supplied data
+	 *
+	 * @access	public
+	 * @param	string	the table name
+	 * @param	array	the insert keys
+	 * @param	array	the insert values
+	 * @return	string
+	 */
+	function _replace($table, $keys, $values)
+	{
+		return "REPLACE INTO ".$table." (\"".implode('", "', $keys)."\") VALUES (".implode(', ', $values).")";
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Insert_batch statement
+	 *
+	 * Generates a platform-specific insert string from the supplied data
+	 *
+	 * @access	public
+	 * @param	string	the table name
+	 * @param	array	the insert keys
+	 * @param	array	the insert values
+	 * @return	string
+	 */
+	function _insert_batch($table, $keys, $values)
+	{
+		return "INSERT INTO ".$table." (\"".implode('", "', $keys)."\") VALUES ".implode(', ', $values);
+	}
+
+	// --------------------------------------------------------------------
+
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @access	public
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause
+	 * @param	array	the limit clause
+	 * @return	string
+	 */
+	function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = sprintf('"%s" = %s', $key, $val);
+		}
+
+		$limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
+
+		$orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
+
+		$sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
+
+		$sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
+
+		$sql .= $orderby.$limit;
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+
+	/**
+	 * Update_Batch statement
+	 *
+	 * Generates a platform-specific batch update string from the supplied data
+	 *
+	 * @access	public
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @return	string
+	 */
+	function _update_batch($table, $values, $index, $where = NULL)
+	{
+		$ids = array();
+		$where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : '';
+
+		foreach ($values as $key => $val)
+		{
+			$ids[] = $val[$index];
+
+			foreach (array_keys($val) as $field)
+			{
+				if ($field != $index)
+				{
+					$final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
+				}
+			}
+		}
+
+		$sql = "UPDATE ".$table." SET ";
+		$cases = '';
+
+		foreach ($final as $k => $v)
+		{
+			$cases .= $k.' = CASE '."\n";
+			foreach ($v as $row)
+			{
+				$cases .= $row."\n";
+			}
+
+			$cases .= 'ELSE '.$k.' END, ';
+		}
+
+		$sql .= substr($cases, 0, -2);
+
+		$sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')';
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 * If the database does not support the truncate() command
+	 * This function maps to "DELETE FROM table"
+	 *
+	 * @access	public
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	function _truncate($table)
+	{
+		return "TRUNCATE ".$table;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @access	public
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	string	the limit clause
+	 * @return	string
+	 */
+	function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = '';
+
+		if (count($where) > 0 OR count($like) > 0)
+		{
+			$conditions = "\nWHERE ";
+			$conditions .= implode("\n", $this->ar_where);
+
+			if (count($where) > 0 && count($like) > 0)
+			{
+				$conditions .= " AND ";
+			}
+			$conditions .= implode("\n", $like);
+		}
+
+		$limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
+
+		return "DELETE FROM ".$table.$conditions.$limit;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @access	public
+	 * @param	string	the sql query string
+	 * @param	integer	the number of rows to limit the query to
+	 * @param	integer	the offset value
+	 * @return	string
+	 */
+	function _limit($sql, $limit, $offset)
+	{
+		if ($offset == 0)
+		{
+			$offset = '';
+		}
+		else
+		{
+			$offset .= ", ";
+		}
+
+		return $sql."LIMIT ".$offset.$limit;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Close DB Connection
+	 *
+	 * @access	public
+	 * @param	resource
+	 * @return	void
+	 */
+	function _close($conn_id)
+	{
+		@cubrid_close($conn_id);
+	}
+
+}
+
+
+/* End of file cubrid_driver.php */
+/* Location: ./system/database/drivers/cubrid/cubrid_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/cubrid/cubrid_forge.php b/system/database/drivers/cubrid/cubrid_forge.php
new file mode 100644
index 0000000..bab03f7
--- /dev/null
+++ b/system/database/drivers/cubrid/cubrid_forge.php
@@ -0,0 +1,288 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package		CodeIgniter
+ * @author		Esen Sagynov
+ * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CUBRID Forge Class
+ *
+ * @category	Database
+ * @author		Esen Sagynov
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_cubrid_forge extends CI_DB_forge {
+
+	/**
+	 * Create database
+	 *
+	 * @access	private
+	 * @param	string	the database name
+	 * @return	bool
+	 */
+	function _create_database($name)
+	{
+		// CUBRID does not allow to create a database in SQL. The GUI tools
+		// have to be used for this purpose.
+		return FALSE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Drop database
+	 *
+	 * @access	private
+	 * @param	string	the database name
+	 * @return	bool
+	 */
+	function _drop_database($name)
+	{
+		// CUBRID does not allow to drop a database in SQL. The GUI tools
+		// have to be used for this purpose.
+		return FALSE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Process Fields
+	 *
+	 * @access	private
+	 * @param	mixed	the fields
+	 * @return	string
+	 */
+	function _process_fields($fields)
+	{
+		$current_field_count = 0;
+		$sql = '';
+
+		foreach ($fields as $field=>$attributes)
+		{
+			// Numeric field names aren't allowed in databases, so if the key is
+			// numeric, we know it was assigned by PHP and the developer manually
+			// entered the field information, so we'll simply add it to the list
+			if (is_numeric($field))
+			{
+				$sql .= "\n\t$attributes";
+			}
+			else
+			{
+				$attributes = array_change_key_case($attributes, CASE_UPPER);
+
+				$sql .= "\n\t\"" . $this->db->_protect_identifiers($field) . "\"";
+
+				if (array_key_exists('NAME', $attributes))
+				{
+					$sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' ';
+				}
+
+				if (array_key_exists('TYPE', $attributes))
+				{
+					$sql .= ' '.$attributes['TYPE'];
+
+					if (array_key_exists('CONSTRAINT', $attributes))
+					{
+						switch ($attributes['TYPE'])
+						{
+							case 'decimal':
+							case 'float':
+							case 'numeric':
+								$sql .= '('.implode(',', $attributes['CONSTRAINT']).')';
+								break;
+							case 'enum': 	// As of version 8.4.0 CUBRID does not support
+											// enum data type.
+											break;
+							case 'set':
+								$sql .= '("'.implode('","', $attributes['CONSTRAINT']).'")';
+								break;
+							default:
+								$sql .= '('.$attributes['CONSTRAINT'].')';
+						}
+					}
+				}
+
+				if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
+				{
+					//$sql .= ' UNSIGNED';
+					// As of version 8.4.0 CUBRID does not support UNSIGNED INTEGER data type.
+					// Will be supported in the next release as a part of MySQL Compatibility.
+				}
+
+				if (array_key_exists('DEFAULT', $attributes))
+				{
+					$sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
+				}
+
+				if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
+				{
+					$sql .= ' NULL';
+				}
+				else
+				{
+					$sql .= ' NOT NULL';
+				}
+
+				if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
+				{
+					$sql .= ' AUTO_INCREMENT';
+				}
+
+				if (array_key_exists('UNIQUE', $attributes) && $attributes['UNIQUE'] === TRUE)
+				{
+					$sql .= ' UNIQUE';
+				}
+			}
+
+			// don't add a comma on the end of the last field
+			if (++$current_field_count < count($fields))
+			{
+				$sql .= ',';
+			}
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Create Table
+	 *
+	 * @access	private
+	 * @param	string	the table name
+	 * @param	mixed	the fields
+	 * @param	mixed	primary key(s)
+	 * @param	mixed	key(s)
+	 * @param	boolean	should 'IF NOT EXISTS' be added to the SQL
+	 * @return	bool
+	 */
+	function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
+	{
+		$sql = 'CREATE TABLE ';
+
+		if ($if_not_exists === TRUE)
+		{
+			//$sql .= 'IF NOT EXISTS ';
+			// As of version 8.4.0 CUBRID does not support this SQL syntax.
+		}
+
+		$sql .= $this->db->_escape_identifiers($table)." (";
+
+		$sql .= $this->_process_fields($fields);
+
+		// If there is a PK defined
+		if (count($primary_keys) > 0)
+		{
+			$key_name = "pk_" . $table . "_" .
+				$this->db->_protect_identifiers(implode('_', $primary_keys));
+			
+			$primary_keys = $this->db->_protect_identifiers($primary_keys);
+			$sql .= ",\n\tCONSTRAINT " . $key_name . " PRIMARY KEY(" . implode(', ', $primary_keys) . ")";
+		}
+
+		if (is_array($keys) && count($keys) > 0)
+		{
+			foreach ($keys as $key)
+			{
+				if (is_array($key))
+				{
+					$key_name = $this->db->_protect_identifiers(implode('_', $key));
+					$key = $this->db->_protect_identifiers($key);
+				}
+				else
+				{
+					$key_name = $this->db->_protect_identifiers($key);
+					$key = array($key_name);
+				}
+				
+				$sql .= ",\n\tKEY \"{$key_name}\" (" . implode(', ', $key) . ")";
+			}
+		}
+
+		$sql .= "\n);";
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Drop Table
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _drop_table($table)
+	{
+		return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Alter table query
+	 *
+	 * Generates a platform-specific query so that a table can be altered
+	 * Called by add_column(), drop_column(), and column_alter(),
+	 *
+	 * @access	private
+	 * @param	string	the ALTER type (ADD, DROP, CHANGE)
+	 * @param	string	the column name
+	 * @param	array	fields
+	 * @param	string	the field after which we should add the new field
+	 * @return	object
+	 */
+	function _alter_table($alter_type, $table, $fields, $after_field = '')
+	{
+		$sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ";
+
+		// DROP has everything it needs now.
+		if ($alter_type == 'DROP')
+		{
+			return $sql.$this->db->_protect_identifiers($fields);
+		}
+
+		$sql .= $this->_process_fields($fields);
+
+		if ($after_field != '')
+		{
+			$sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Rename a table
+	 *
+	 * Generates a platform-specific query so that a table can be renamed
+	 *
+	 * @access	private
+	 * @param	string	the old table name
+	 * @param	string	the new table name
+	 * @return	string
+	 */
+	function _rename_table($table_name, $new_table_name)
+	{
+		$sql = 'RENAME TABLE '.$this->db->_protect_identifiers($table_name)." AS ".$this->db->_protect_identifiers($new_table_name);
+		return $sql;
+	}
+
+}
+
+/* End of file cubrid_forge.php */
+/* Location: ./system/database/drivers/cubrid/cubrid_forge.php */
\ No newline at end of file
diff --git a/system/database/drivers/cubrid/cubrid_result.php b/system/database/drivers/cubrid/cubrid_result.php
new file mode 100644
index 0000000..6f0c2b5
--- /dev/null
+++ b/system/database/drivers/cubrid/cubrid_result.php
@@ -0,0 +1,202 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package		CodeIgniter
+ * @author		Esen Sagynov
+ * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 2.0.2
+ * @filesource
+ */
+
+// --------------------------------------------------------------------
+
+/**
+ * CUBRID Result Class
+ *
+ * This class extends the parent result class: CI_DB_result
+ *
+ * @category	Database
+ * @author		Esen Sagynov
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_cubrid_result extends CI_DB_result {
+
+	/**
+	 * Number of rows in the result set
+	 *
+	 * @access	public
+	 * @return	integer
+	 */
+	function num_rows()
+	{
+		return @cubrid_num_rows($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Number of fields in the result set
+	 *
+	 * @access	public
+	 * @return	integer
+	 */
+	function num_fields()
+	{
+		return @cubrid_num_fields($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch Field Names
+	 *
+	 * Generates an array of column names
+	 *
+	 * @access	public
+	 * @return	array
+	 */
+	function list_fields()
+	{
+		return cubrid_column_names($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data
+	 *
+	 * Generates an array of objects containing field meta-data
+	 *
+	 * @access	public
+	 * @return	array
+	 */
+	function field_data()
+	{
+		$retval = array();
+
+		$tablePrimaryKeys = array();
+
+		while ($field = cubrid_fetch_field($this->result_id))
+		{
+			$F				= new stdClass();
+			$F->name		= $field->name;
+			$F->type		= $field->type;
+			$F->default		= $field->def;
+			$F->max_length	= $field->max_length;
+
+			// At this moment primary_key property is not returned when
+			// cubrid_fetch_field is called. The following code will
+			// provide a patch for it. primary_key property will be added
+			// in the next release.
+
+			// TODO: later version of CUBRID will provide primary_key
+			// property.
+			// When PK is defined in CUBRID, an index is automatically
+			// created in the db_index system table in the form of
+			// pk_tblname_fieldname. So the following will count how many
+			// columns are there which satisfy this format.
+			// The query will search for exact single columns, thus
+			// compound PK is not supported.
+			$res = cubrid_query($this->conn_id,
+				"SELECT COUNT(*) FROM db_index WHERE class_name = '" . $field->table .
+				"' AND is_primary_key = 'YES' AND index_name = 'pk_" .
+				$field->table . "_" . $field->name . "'"
+			);
+
+			if ($res)
+			{
+				$row = cubrid_fetch_array($res, CUBRID_NUM);
+				$F->primary_key = ($row[0] > 0 ? 1 : null);
+			}
+			else
+			{
+				$F->primary_key = null;
+			}
+
+			if (is_resource($res))
+			{
+				cubrid_close_request($res);
+				$this->result_id = FALSE;
+			}
+
+			$retval[] = $F;
+		}
+
+		return $retval;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Free the result
+	 *
+	 * @return	null
+	 */
+	function free_result()
+	{
+		if(is_resource($this->result_id) ||
+			get_resource_type($this->result_id) == "Unknown" &&
+			preg_match('/Resource id #/', strval($this->result_id)))
+		{
+			cubrid_close_request($this->result_id);
+			$this->result_id = FALSE;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Data Seek
+	 *
+	 * Moves the internal pointer to the desired offset. We call
+	 * this internally before fetching results to make sure the
+	 * result set starts at zero
+	 *
+	 * @access	private
+	 * @return	array
+	 */
+	function _data_seek($n = 0)
+	{
+		return cubrid_data_seek($this->result_id, $n);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Result - associative array
+	 *
+	 * Returns the result set as an array
+	 *
+	 * @access	private
+	 * @return	array
+	 */
+	function _fetch_assoc()
+	{
+		return cubrid_fetch_assoc($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Result - object
+	 *
+	 * Returns the result set as an object
+	 *
+	 * @access	private
+	 * @return	object
+	 */
+	function _fetch_object()
+	{
+		return cubrid_fetch_object($this->result_id);
+	}
+
+}
+
+
+/* End of file cubrid_result.php */
+/* Location: ./system/database/drivers/cubrid/cubrid_result.php */
\ No newline at end of file
diff --git a/system/database/drivers/cubrid/cubrid_utility.php b/system/database/drivers/cubrid/cubrid_utility.php
new file mode 100644
index 0000000..cd16d1e
--- /dev/null
+++ b/system/database/drivers/cubrid/cubrid_utility.php
@@ -0,0 +1,108 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package		CodeIgniter
+ * @author		Esen Sagynov
+ * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CUBRID Utility Class
+ *
+ * @category	Database
+ * @author		Esen Sagynov
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_cubrid_utility extends CI_DB_utility {
+
+	/**
+	 * List databases
+	 *
+	 * @access	private
+	 * @return	array
+	 */
+	function _list_databases()
+	{
+		// CUBRID does not allow to see the list of all databases on the
+		// server. It is the way its architecture is designed. Every
+		// database is independent and isolated.
+		// For this reason we can return only the name of the currect
+		// connected database.
+		if ($this->conn_id)
+		{
+			return "SELECT '" . $this->database . "'";
+		}
+		else
+		{
+			return FALSE;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Optimize table query
+	 *
+	 * Generates a platform-specific query so that a table can be optimized
+	 *
+	 * @access	private
+	 * @param	string	the table name
+	 * @return	object
+	 * @link 	http://www.cubrid.org/manual/840/en/Optimize%20Database
+	 */
+	function _optimize_table($table)
+	{
+		// No SQL based support in CUBRID as of version 8.4.0. Database or
+		// table optimization can be performed using CUBRID Manager
+		// database administration tool. See the link above for more info.
+		return FALSE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Repair table query
+	 *
+	 * Generates a platform-specific query so that a table can be repaired
+	 *
+	 * @access	private
+	 * @param	string	the table name
+	 * @return	object
+	 * @link 	http://www.cubrid.org/manual/840/en/Checking%20Database%20Consistency
+	 */
+	function _repair_table($table)
+	{
+		// Not supported in CUBRID as of version 8.4.0. Database or
+		// table consistency can be checked using CUBRID Manager
+		// database administration tool. See the link above for more info.
+		return FALSE;
+	}
+
+	// --------------------------------------------------------------------
+	/**
+	 * CUBRID Export
+	 *
+	 * @access	private
+	 * @param	array	Preferences
+	 * @return	mixed
+	 */
+	function _backup($params = array())
+	{
+		// No SQL based support in CUBRID as of version 8.4.0. Database or
+		// table backup can be performed using CUBRID Manager
+		// database administration tool.
+		return $this->db->display_error('db_unsuported_feature');
+	}
+}
+
+/* End of file cubrid_utility.php */
+/* Location: ./system/database/drivers/cubrid/cubrid_utility.php */
\ No newline at end of file
diff --git a/system/database/drivers/cubrid/index.html b/system/database/drivers/cubrid/index.html
new file mode 100644
index 0000000..c942a79
--- /dev/null
+++ b/system/database/drivers/cubrid/index.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+	<title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php
index 65397ed..b39bd93 100644
--- a/system/database/drivers/mssql/mssql_driver.php
+++ b/system/database/drivers/mssql/mssql_driver.php
@@ -367,6 +367,7 @@
 		}
 
 		$row = $query->row();
+		$this->_reset_select();
 		return (int) $row->numrows;
 	}
 
diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php
index b7d547c..f87cfea 100644
--- a/system/database/drivers/mysql/mysql_driver.php
+++ b/system/database/drivers/mysql/mysql_driver.php
@@ -54,6 +54,9 @@
 	var $_count_string = 'SELECT COUNT(*) AS ';
 	var $_random_keyword = ' RAND()'; // database specific random keyword
 
+	// whether SET NAMES must be used to set the character set
+	var $use_set_names;
+	
 	/**
 	 * Non-persistent database connection
 	 *
@@ -132,15 +135,13 @@
 	 */
 	function db_set_charset($charset, $collation)
 	{
-		static $use_set_names;
-		
-		if ( ! isset($use_set_names))
+		if ( ! isset($this->use_set_names))
 		{
 			// mysql_set_charset() requires PHP >= 5.2.3 and MySQL >= 5.0.7, use SET NAMES as fallback
-			$use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysql_get_server_info(), '5.0.7', '>=')) ? FALSE : TRUE;
+			$this->use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysql_get_server_info(), '5.0.7', '>=')) ? FALSE : TRUE;
 		}
 
-		if ($use_set_names)
+		if ($this->use_set_names === TRUE)
 		{
 			return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id);
 		}
@@ -384,6 +385,7 @@
 		}
 
 		$row = $query->row();
+		$this->_reset_select();
 		return (int) $row->numrows;
 	}
 
@@ -439,7 +441,7 @@
 	 */
 	function _field_data($table)
 	{
-		return "SELECT * FROM ".$table." LIMIT 1";
+		return "DESCRIBE ".$table;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/mysql/mysql_result.php b/system/database/drivers/mysql/mysql_result.php
index 5073896..2d2905c 100644
--- a/system/database/drivers/mysql/mysql_result.php
+++ b/system/database/drivers/mysql/mysql_result.php
@@ -84,14 +84,19 @@
 	function field_data()
 	{
 		$retval = array();
-		while ($field = mysql_fetch_field($this->result_id))
+		while ($field = mysql_fetch_object($this->result_id))
 		{
+			preg_match('/([a-zA-Z]+)\((\d+)\)/', $field->Type, $matches);
+
+			$type = $matches[1];
+			$length = (int)$matches[2];
+
 			$F				= new stdClass();
-			$F->name		= $field->name;
-			$F->type		= $field->type;
-			$F->default		= $field->def;
-			$F->max_length	= $field->max_length;
-			$F->primary_key = $field->primary_key;
+			$F->name		= $field->Field;
+			$F->type		= $type;
+			$F->default		= $field->Default;
+			$F->max_length	= $length;
+			$F->primary_key = ( $field->Key == 'PRI' ? 1 : 0 );
 
 			$retval[] = $F;
 		}
diff --git a/system/database/drivers/mysql/mysql_utility.php b/system/database/drivers/mysql/mysql_utility.php
index e9747c5..48c4d63 100644
--- a/system/database/drivers/mysql/mysql_utility.php
+++ b/system/database/drivers/mysql/mysql_utility.php
@@ -96,7 +96,7 @@
 			}
 
 			// Get the table schema
-			$query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.'.$table);
+			$query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.`'.$table.'`');
 
 			// No result means the table name was invalid
 			if ($query === FALSE)
diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php
index b8586c2..ccd110f 100644
--- a/system/database/drivers/mysqli/mysqli_driver.php
+++ b/system/database/drivers/mysqli/mysqli_driver.php
@@ -54,6 +54,9 @@
 	 */
 	var $delete_hack = TRUE;
 
+	// whether SET NAMES must be used to set the character set
+	var $use_set_names;
+	
 	// --------------------------------------------------------------------
 
 	/**
@@ -132,15 +135,13 @@
 	 */
 	function _db_set_charset($charset, $collation)
 	{
-		static $use_set_names;
-		
-		if ( ! isset($use_set_names))
+		if ( ! isset($this->use_set_names))
 		{
 			// mysqli_set_charset() requires MySQL >= 5.0.7, use SET NAMES as fallback
-			$use_set_names = (version_compare(mysqli_get_server_info($this->conn_id), '5.0.7', '>=')) ? FALSE : TRUE;
+			$this->use_set_names = (version_compare(mysqli_get_server_info($this->conn_id), '5.0.7', '>=')) ? FALSE : TRUE;
 		}
 
-		if ($use_set_names)
+		if ($this->use_set_names === TRUE)
 		{
 			return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'");
 		}
@@ -385,6 +386,7 @@
 		}
 
 		$row = $query->row();
+		$this->_reset_select();
 		return (int) $row->numrows;
 	}
 
@@ -440,7 +442,7 @@
 	 */
 	function _field_data($table)
 	{
-		return "SELECT * FROM ".$table." LIMIT 1";
+		return "DESCRIBE ".$table;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/mysqli/mysqli_result.php b/system/database/drivers/mysqli/mysqli_result.php
index c4d8f5d..ac86305 100644
--- a/system/database/drivers/mysqli/mysqli_result.php
+++ b/system/database/drivers/mysqli/mysqli_result.php
@@ -84,21 +84,26 @@
 	function field_data()
 	{
 		$retval = array();
-		while ($field = mysqli_fetch_field($this->result_id))
+		while ($field = mysqli_fetch_object($this->result_id))
 		{
+			preg_match('/([a-zA-Z]+)\((\d+)\)/', $field->Type, $matches);
+
+			$type = $matches[1];
+			$length = (int)$matches[2];
+
 			$F				= new stdClass();
-			$F->name		= $field->name;
-			$F->type		= $field->type;
-			$F->default		= $field->def;
-			$F->max_length	= $field->max_length;
-			$F->primary_key = ($field->flags & MYSQLI_PRI_KEY_FLAG) ? 1 : 0;
+			$F->name		= $field->Field;
+			$F->type		= $type;
+			$F->default		= $field->Default;
+			$F->max_length	= $length;
+			$F->primary_key = ( $field->Key == 'PRI' ? 1 : 0 );
 
 			$retval[] = $F;
 		}
 
 		return $retval;
 	}
-
+	
 	// --------------------------------------------------------------------
 
 	/**
diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php
index 14df104..42cfaae 100644
--- a/system/database/drivers/oci8/oci8_driver.php
+++ b/system/database/drivers/oci8/oci8_driver.php
@@ -470,6 +470,7 @@
 		}
 
 		$row = $query->row();
+		$this->_reset_select();
 		return (int) $row->numrows;
 	}
 
diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php
index 81e0d7c..08cd27b 100644
--- a/system/database/drivers/odbc/odbc_driver.php
+++ b/system/database/drivers/odbc/odbc_driver.php
@@ -50,7 +50,7 @@
 
 	function CI_DB_odbc_driver($params)
 	{
-		parent::CI_DB($params);
+		parent::CI_DB_driver($params);
 
 		$this->_random_keyword = ' RND('.time().')'; // database specific random keyword
 	}
@@ -339,6 +339,7 @@
 		}
 
 		$row = $query->row();
+		$this->_reset_select();
 		return (int) $row->numrows;
 	}
 
diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php
index 1403968..5367f97 100644
--- a/system/database/drivers/postgre/postgre_driver.php
+++ b/system/database/drivers/postgre/postgre_driver.php
@@ -385,6 +385,7 @@
 		}
 
 		$row = $query->row();
+		$this->_reset_select();
 		return (int) $row->numrows;
 	}
 
diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php
index eb4e585..0cc898b 100644
--- a/system/database/drivers/sqlite/sqlite_driver.php
+++ b/system/database/drivers/sqlite/sqlite_driver.php
@@ -354,6 +354,7 @@
 		}
 
 		$row = $query->row();
+		$this->_reset_select();
 		return (int) $row->numrows;
 	}
 
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
index 1d32792..400fd31 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_driver.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -344,6 +344,7 @@
 			return '0';
 
 		$row = $query->row();
+		$this->_reset_select();
 		return $row->numrows;
 	}
 
diff --git a/system/helpers/date_helper.php b/system/helpers/date_helper.php
index 553e8d7..6c559bb 100644
--- a/system/helpers/date_helper.php
+++ b/system/helpers/date_helper.php
@@ -491,6 +491,72 @@
 // ------------------------------------------------------------------------
 
 /**
+ * Turns many "reasonably-date-like" strings into something
+ * that is actually useful. This only works for dates after unix epoch.
+ * 
+ * @access  public
+ * @param   string  The terribly formatted date-like string
+ * @param   string  Date format to return (same as php date function)
+ * @return  string
+ */
+if ( ! function_exists('nice_date'))
+{
+	function nice_date($bad_date='', $format=false) 
+	{
+		if (empty($bad_date))
+		{
+			return 'Unknown';
+		}
+		// Date like: YYYYMM
+		if (preg_match('/^\d{6}$/',$bad_date)) 
+		{
+			//echo $bad_date." ";
+			if (in_array(substr($bad_date, 0, 2),array('19', '20'))) 
+			{
+				$year  = substr($bad_date, 0, 4);
+				$month = substr($bad_date, 4, 2);
+			} 
+			else 
+			{
+				$month  = substr($bad_date, 0, 2);
+				$year   = substr($bad_date, 2, 4);
+			}
+			return date($format, strtotime($year . '-' . $month . '-01'));
+		    
+		}
+		
+		// Date Like: YYYYMMDD
+		if (preg_match('/^\d{8}$/',$bad_date)) 
+		{
+			$month = substr($bad_date, 0, 2);
+			$day   = substr($bad_date, 2, 2);
+			$year  = substr($bad_date, 4, 4);
+			return date($format, strtotime($month . '/01/' . $year));
+		}
+		
+		// Date Like: MM-DD-YYYY __or__ M-D-YYYY (or anything in between)
+		if (preg_match('/^\d{1,2}-\d{1,2}-\d{4}$/',$bad_date))
+		{ 
+			list($m, $d, $y) = explode('-', $bad_date);
+			return date($format, strtotime("{$y}-{$m}-{$d}"));
+		}
+		
+		// Any other kind of string, when converted into UNIX time,
+		// produces "0 seconds after epoc..." is probably bad...
+		// return "Invalid Date".
+		if (date('U', strtotime($bad_date)) == '0')
+		{ 
+			return "Invalid Date";
+		}
+		
+		// It's probably a valid-ish date format already
+		return date($format, strtotime($bad_date));
+	}
+}
+
+// ------------------------------------------------------------------------
+
+/**
  * Timezone Menu
  *
  * Generates a drop-down menu of timezones.
diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php
index 2925d3c..d9305c0 100644
--- a/system/helpers/form_helper.php
+++ b/system/helpers/form_helper.php
@@ -64,8 +64,8 @@
 
 		$form .= '>';
 
-		// CSRF
-		if ($CI->config->item('csrf_protection') === TRUE)
+		// Add CSRF field if enabled, but leave it out for GET requests and requests to external websites	
+		if ($CI->config->item('csrf_protection') === TRUE AND ! (strpos($action, $CI->config->site_url()) === FALSE OR strpos($form, 'method="get"')))	
 		{
 			$hidden[$CI->security->get_csrf_token_name()] = $CI->security->get_csrf_hash();
 		}
@@ -94,7 +94,7 @@
  */
 if ( ! function_exists('form_open_multipart'))
 {
-	function form_open_multipart($action, $attributes = array(), $hidden = array())
+	function form_open_multipart($action = '', $attributes = array(), $hidden = array())
 	{
 		if (is_string($attributes))
 		{
@@ -249,7 +249,7 @@
 {
 	function form_textarea($data = '', $value = '', $extra = '')
 	{
-		$defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '90', 'rows' => '12');
+		$defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '40', 'rows' => '10');
 
 		if ( ! is_array($data) OR ! isset($data['value']))
 		{
diff --git a/system/helpers/string_helper.php b/system/helpers/string_helper.php
index 7765bba..9fa69f4 100644
--- a/system/helpers/string_helper.php
+++ b/system/helpers/string_helper.php
@@ -243,6 +243,23 @@
 // ------------------------------------------------------------------------
 
 /**
+ * Add's _1 to a string or increment the ending number to allow _2, _3, etc
+ *
+ * @param   string  $str  required
+ * @param   string  $separator  What should the duplicate number be appended with
+ * @param   string  $first  Which number should be used for the first dupe increment
+ * @return  string
+ */
+function increment_string($str, $separator = '_', $first = 1)
+{
+	preg_match('/(.+)'.$separator.'([0-9]+)$/', $str, $match);
+
+	return isset($match[2]) ? $match[1].$separator.($match[2] + 1) : $str.$separator.$first;
+}
+
+// ------------------------------------------------------------------------
+
+/**
  * Alternator
  *
  * Allows strings to be alternated.  See docs...
diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php
old mode 100644
new mode 100755
index 9f4b852..c524ddd
--- a/system/helpers/url_helper.php
+++ b/system/helpers/url_helper.php
@@ -512,7 +512,7 @@
 			$str = strtolower($str);
 		}
 
-		return trim(stripslashes($str));
+		return trim(trim(stripslashes($str)), $replace);
 	}
 }
 
@@ -527,7 +527,7 @@
  *
  * @access	public
  * @param	string	the URL
- * @param	string	the method: location or redirect
+ * @param	string	the method: location or refresh
  * @return	string
  */
 if ( ! function_exists('redirect'))
diff --git a/system/language/english/form_validation_lang.php b/system/language/english/form_validation_lang.php
index 3f24090..d1cf039 100644
--- a/system/language/english/form_validation_lang.php
+++ b/system/language/english/form_validation_lang.php
@@ -7,7 +7,7 @@
 $lang['valid_url']			= "The %s field must contain a valid URL.";
 $lang['valid_ip']			= "The %s field must contain a valid IP.";
 $lang['min_length']			= "The %s field must be at least %s characters in length.";
-$lang['max_length']			= "The %s field can not exceed %s characters in length.";
+$lang['max_length']			= "The %s field cannot exceed %s characters in length.";
 $lang['exact_length']		= "The %s field must be exactly %s characters in length.";
 $lang['alpha']				= "The %s field may only contain alphabetical characters.";
 $lang['alpha_numeric']		= "The %s field may only contain alpha-numeric characters.";
@@ -17,6 +17,7 @@
 $lang['integer']			= "The %s field must contain an integer.";
 $lang['regex_match']		= "The %s field is not in the correct format.";
 $lang['matches']			= "The %s field does not match the %s field.";
+$lang['is_unique'] 			= "The %s field must contain a unique value.";
 $lang['is_natural']			= "The %s field must contain only positive numbers.";
 $lang['is_natural_no_zero']	= "The %s field must contain a number greater than zero.";
 $lang['decimal']			= "The %s field must contain a decimal number.";
diff --git a/system/language/english/migration_lang.php b/system/language/english/migration_lang.php
new file mode 100644
index 0000000..4763ca2
--- /dev/null
+++ b/system/language/english/migration_lang.php
@@ -0,0 +1,13 @@
+<?php
+
+$lang['migration_none_found']			= "No migrations were found.";
+$lang['migration_not_found']			= "This migration could not be found.";
+$lang['migration_multiple_version']		= "This are multiple migrations with the same version number: %d.";
+$lang['migration_class_doesnt_exist']	= "The migration class \"%s\" could not be found.";
+$lang['migration_missing_up_method']	= "The migration class \"%s\" is missing an 'up' method.";
+$lang['migration_missing_down_method']	= "The migration class \"%s\" is missing an 'up' method.";
+$lang['migration_invalid_filename']		= "Migration \"%s\" has an invalid filename.";
+
+
+/* End of file migration_lang.php */
+/* Location: ./system/language/english/migration_lang.php */
\ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php
index 13e2d1a..6c37e70 100644
--- a/system/libraries/Cache/drivers/Cache_file.php
+++ b/system/libraries/Cache/drivers/Cache_file.php
@@ -157,17 +157,16 @@
 		
 		if (is_array($data))
 		{
-			$data = $data['data'];
 			$mtime = filemtime($this->_cache_path.$id);
 
-			if ( ! isset($data['ttl']))
+			if ( ! isset($data['data']['ttl']))
 			{
 				return FALSE;
 			}
 
 			return array(
-				'expire' 	=> $mtime + $data['ttl'],
-				'mtime'		=> $mtime
+				'expire' => $mtime + $data['data']['ttl'],
+				'mtime'	 => $mtime
 			);
 		}
 		
diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php
index ec2fd21..04aa81a 100644
--- a/system/libraries/Cache/drivers/Cache_memcached.php
+++ b/system/libraries/Cache/drivers/Cache_memcached.php
@@ -10,19 +10,19 @@
  * @license		http://codeigniter.com/user_guide/license.html
  * @link		http://codeigniter.com
  * @since		Version 2.0
- * @filesource	
+ * @filesource
  */
 
 // ------------------------------------------------------------------------
 
 /**
- * CodeIgniter Memcached Caching Class 
+ * CodeIgniter Memcached Caching Class
  *
  * @package		CodeIgniter
  * @subpackage	Libraries
  * @category	Core
  * @author		ExpressionEngine Dev Team
- * @link		
+ * @link
  */
 
 class CI_Cache_memcached extends CI_Driver {
@@ -37,18 +37,18 @@
 					)
 				);
 
-	// ------------------------------------------------------------------------	
+	// ------------------------------------------------------------------------
 
 	/**
 	 * Fetch from cache
 	 *
 	 * @param 	mixed		unique key id
 	 * @return 	mixed		data on success/false on failure
-	 */	
+	 */
 	public function get($id)
-	{	
+	{
 		$data = $this->_memcached->get($id);
-		
+
 		return (is_array($data)) ? $data[0] : FALSE;
 	}
 
@@ -64,11 +64,11 @@
 	 */
 	public function save($id, $data, $ttl = 60)
 	{
-		return $this->_memcached->add($id, array($data, time(), $ttl), $ttl);
+		return $this->_memcached->set($id, array($data, time(), $ttl), $ttl);
 	}
 
 	// ------------------------------------------------------------------------
-	
+
 	/**
 	 * Delete from Cache
 	 *
@@ -81,7 +81,7 @@
 	}
 
 	// ------------------------------------------------------------------------
-	
+
 	/**
 	 * Clean the Cache
 	 *
@@ -106,7 +106,7 @@
 	}
 
 	// ------------------------------------------------------------------------
-	
+
 	/**
 	 * Get Cache Metadata
 	 *
@@ -140,6 +140,7 @@
 	{
 		// Try to load memcached server info from the config file.
 		$CI =& get_instance();
+
 		if ($CI->config->load('memcached', TRUE, TRUE))
 		{
 			if (is_array($CI->config->config['memcached']))
@@ -149,11 +150,24 @@
 				foreach ($CI->config->config['memcached'] as $name => $conf)
 				{
 					$this->_memcache_conf[$name] = $conf;
-				}				
-			}			
+				}
+			}
 		}
-		
-		$this->_memcached = new Memcached();
+
+		if (class_exists('Memcached'))
+		{
+			$this->_memcached = new Memcached();
+		}
+		else if (class_exists('Memcache'))
+		{
+			$this->_memcached = new Memcache();
+		}
+		else
+		{
+			log_message('error', 'Failed to create object for Memcached Cache; extension not loaded?');
+
+			return FALSE;
+		}
 
 		foreach ($this->_memcache_conf as $name => $cache_server)
 		{
@@ -161,26 +175,42 @@
 			{
 				$cache_server['hostname'] = $this->_default_options['default_host'];
 			}
-	
+
 			if ( ! array_key_exists('port', $cache_server))
 			{
 				$cache_server['port'] = $this->_default_options['default_port'];
 			}
-	
+
 			if ( ! array_key_exists('weight', $cache_server))
 			{
 				$cache_server['weight'] = $this->_default_options['default_weight'];
 			}
-	
-			$this->_memcached->addServer(
-					$cache_server['hostname'], $cache_server['port'], $cache_server['weight']
-			);
+
+			if (get_class($this->_memcached) == 'Memcache')
+			{
+				// Third parameter is persistance and defaults to TRUE.
+				$this->_memcached->addServer(
+					$cache_server['hostname'],
+					$cache_server['port'],
+					TRUE,
+					$cache_server['weight']
+				);
+			}
+			else
+			{
+				$this->_memcached->addServer(
+					$cache_server['hostname'],
+					$cache_server['port'],
+					$cache_server['weight']
+				);
+			}
 		}
+
+		return TRUE;
 	}
 
 	// ------------------------------------------------------------------------
 
-
 	/**
 	 * Is supported
 	 *
@@ -189,15 +219,14 @@
 	 */
 	public function is_supported()
 	{
-		if ( ! extension_loaded('memcached'))
+		if ( ! extension_loaded('memcached') && ! extension_loaded('memcache'))
 		{
 			log_message('error', 'The Memcached Extension must be loaded to use Memcached Cache.');
-			
+
 			return FALSE;
 		}
-		
-		$this->_setup_memcached();
-		return TRUE;
+
+		return $this->_setup_memcached();
 	}
 
 	// ------------------------------------------------------------------------
diff --git a/system/libraries/Cart.php b/system/libraries/Cart.php
index b2eaa9a..ab5a70c 100644
--- a/system/libraries/Cart.php
+++ b/system/libraries/Cart.php
@@ -99,7 +99,7 @@
 		$save_cart = FALSE;
 		if (isset($items['id']))
 		{
-			if ($this->_insert($items) == TRUE)
+			if (($rowid = $this->_insert($items)))
 			{
 				$save_cart = TRUE;
 			}
@@ -110,7 +110,7 @@
 			{
 				if (is_array($val) AND isset($val['id']))
 				{
-					if ($this->_insert($val) == TRUE)
+					if ($this->_insert($val))
 					{
 						$save_cart = TRUE;
 					}
@@ -122,7 +122,7 @@
 		if ($save_cart == TRUE)
 		{
 			$this->_save_cart();
-			return TRUE;
+			return isset($rowid) ? $rowid : TRUE;
 		}
 
 		return FALSE;
@@ -244,7 +244,7 @@
 		}
 
 		// Woot!
-		return TRUE;
+		return $rowid;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php
index c32f2c1..9881c1e 100644
--- a/system/libraries/Driver.php
+++ b/system/libraries/Driver.php
@@ -54,7 +54,7 @@
 			if ( ! class_exists($child_class))
 			{
 				// check application path first
-				foreach (array(APPPATH, BASEPATH) as $path)
+				foreach (get_instance()->load->get_package_paths(TRUE) as $path)
 				{
 					// loves me some nesting!
 					foreach (array(ucfirst($driver_name), $driver_name) as $class)
@@ -226,4 +226,4 @@
 // END CI_Driver CLASS
 
 /* End of file Driver.php */
-/* Location: ./system/libraries/Driver.php */
+/* Location: ./system/libraries/Driver.php */
\ No newline at end of file
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index 03eccea..28a3d17 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -405,12 +405,12 @@
 	/**
 	 * Add a Header Item
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	string
 	 * @param	string
 	 * @return	void
 	 */
-	private function _set_header($header, $value)
+	protected function _set_header($header, $value)
 	{
 		$this->_headers[$header] = $value;
 	}
@@ -420,11 +420,11 @@
 	/**
 	 * Convert a String to an Array
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	string
 	 * @return	array
 	 */
-	private function _str_to_array($email)
+	protected function _str_to_array($email)
 	{
 		if ( ! is_array($email))
 		{
@@ -452,7 +452,7 @@
 	 */
 	public function set_alt_message($str = '')
 	{
-		$this->alt_message = ($str == '') ? '' : $str;
+		$this->alt_message = (string) $str;
 		return $this;
 	}
 
@@ -477,12 +477,12 @@
 	 * Set Wordwrap
 	 *
 	 * @access	public
-	 * @param	string
+	 * @param	bool
 	 * @return	void
 	 */
 	public function set_wordwrap($wordwrap = TRUE)
 	{
-		$this->wordwrap = ($wordwrap === FALSE) ? FALSE : TRUE;
+		$this->wordwrap = (bool) $wordwrap;
 		return $this;
 	}
 
@@ -577,10 +577,10 @@
 	/**
 	 * Set Message Boundary
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	void
 	 */
-	private function _set_boundaries()
+	protected function _set_boundaries()
 	{
 		$this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative
 		$this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary
@@ -591,10 +591,10 @@
 	/**
 	 * Get the Message ID
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	string
 	 */
-	private function _get_message_id()
+	protected function _get_message_id()
 	{
 		$from = $this->_headers['Return-Path'];
 		$from = str_replace(">", "", $from);
@@ -608,11 +608,11 @@
 	/**
 	 * Get Mail Protocol
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	bool
 	 * @return	string
 	 */
-	private function _get_protocol($return = TRUE)
+	protected function _get_protocol($return = TRUE)
 	{
 		$this->protocol = strtolower($this->protocol);
 		$this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol;
@@ -628,11 +628,11 @@
 	/**
 	 * Get Mail Encoding
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	bool
 	 * @return	string
 	 */
-	private function _get_encoding($return = TRUE)
+	protected function _get_encoding($return = TRUE)
 	{
 		$this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding;
 
@@ -655,10 +655,10 @@
 	/**
 	 * Get content type (text/html/attachment)
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	string
 	 */
-	private function _get_content_type()
+	protected function _get_content_type()
 	{
 		if	($this->mailtype == 'html' &&  count($this->_attach_name) == 0)
 		{
@@ -683,10 +683,10 @@
 	/**
 	 * Set RFC 822 Date
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	string
 	 */
-	private function _set_date()
+	protected function _set_date()
 	{
 		$timezone = date("Z");
 		$operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+';
@@ -701,10 +701,10 @@
 	/**
 	 * Mime message
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	string
 	 */
-	private function _get_mime_message()
+	protected function _get_mime_message()
 	{
 		return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";
 	}
@@ -802,10 +802,10 @@
 	 * If the user hasn't specified his own alternative message
 	 * it creates one by stripping the HTML
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	string
 	 */
-	private function _get_alt_message()
+	protected function _get_alt_message()
 	{
 		if ($this->alt_message != "")
 		{
@@ -941,11 +941,11 @@
 	/**
 	 * Build final headers
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	string
 	 * @return	string
 	 */
-	private function _build_headers()
+	protected function _build_headers()
 	{
 		$this->_set_header('X-Sender', $this->clean_email($this->_headers['From']));
 		$this->_set_header('X-Mailer', $this->useragent);
@@ -959,10 +959,10 @@
 	/**
 	 * Write Headers as a string
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	void
 	 */
-	private function _write_headers()
+	protected function _write_headers()
 	{
 		if ($this->protocol == 'mail')
 		{
@@ -994,10 +994,10 @@
 	/**
 	 * Build Final Body and attachments
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	void
 	 */
-	private function _build_message()
+	protected function _build_message()
 	{
 		if ($this->wordwrap === TRUE  AND  $this->mailtype != 'html')
 		{
@@ -1177,12 +1177,12 @@
 	 * Prepares string for Quoted-Printable Content-Transfer-Encoding
 	 * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	string
 	 * @param	integer
 	 * @return	string
 	 */
-	private function _prep_quoted_printable($str, $charlim = '')
+	protected function _prep_quoted_printable($str, $charlim = '')
 	{
 		// Set the character limit
 		// Don't allow over 76, as that will make servers and MUAs barf
@@ -1275,7 +1275,7 @@
 	 * @param	bool	// set to TRUE for processing From: headers
 	 * @return	str
 	 */
-	private function _prep_q_encoding($str, $from = FALSE)
+	protected function _prep_q_encoding($str, $from = FALSE)
 	{
 		$str = str_replace(array("\r", "\n"), array('', ''), $str);
 
@@ -1440,10 +1440,10 @@
 	/**
 	 * Unwrap special elements
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	void
 	 */
-	private function _unwrap_specials()
+	protected function _unwrap_specials()
 	{
 		$this->_finalbody = preg_replace_callback("/\{unwrap\}(.*?)\{\/unwrap\}/si", array($this, '_remove_nl_callback'), $this->_finalbody);
 	}
@@ -1453,10 +1453,10 @@
 	/**
 	 * Strip line-breaks via callback
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	string
 	 */
-	private function _remove_nl_callback($matches)
+	protected function _remove_nl_callback($matches)
 	{
 		if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE)
 		{
@@ -1471,10 +1471,10 @@
 	/**
 	 * Spool mail to the mail server
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	bool
 	 */
-	private function _spool_email()
+	protected function _spool_email()
 	{
 		$this->_unwrap_specials();
 
@@ -1516,10 +1516,10 @@
 	/**
 	 * Send using mail()
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	bool
 	 */
-	private function _send_with_mail()
+	protected function _send_with_mail()
 	{
 		if ($this->_safe_mode == TRUE)
 		{
@@ -1553,10 +1553,10 @@
 	/**
 	 * Send using Sendmail
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	bool
 	 */
-	private function _send_with_sendmail()
+	protected function _send_with_sendmail()
 	{
 		$fp = @popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w');
 
@@ -1591,10 +1591,10 @@
 	/**
 	 * Send using SMTP
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	bool
 	 */
-	private function _send_with_smtp()
+	protected function _send_with_smtp()
 	{
 		if ($this->smtp_host == '')
 		{
@@ -1660,11 +1660,11 @@
 	/**
 	 * SMTP Connect
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	string
 	 * @return	string
 	 */
-	private function _smtp_connect()
+	protected function _smtp_connect()
 	{
 		$this->_smtp_connect = fsockopen($this->smtp_host,
 										$this->smtp_port,
@@ -1687,12 +1687,12 @@
 	/**
 	 * Send SMTP command
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	string
 	 * @param	string
 	 * @return	string
 	 */
-	private function _send_command($cmd, $data = '')
+	protected function _send_command($cmd, $data = '')
 	{
 		switch ($cmd)
 		{
@@ -1754,10 +1754,10 @@
 	/**
 	 *  SMTP Authenticate
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	bool
 	 */
-	private function _smtp_authenticate()
+	protected function _smtp_authenticate()
 	{
 		if ( ! $this->_smtp_auth)
 		{
@@ -1808,10 +1808,10 @@
 	/**
 	 * Send SMTP data
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	bool
 	 */
-	private function _send_data($data)
+	protected function _send_data($data)
 	{
 		if ( ! fwrite($this->_smtp_connect, $data . $this->newline))
 		{
@@ -1829,10 +1829,10 @@
 	/**
 	 * Get SMTP data
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	string
 	 */
-	private function _get_smtp_data()
+	protected function _get_smtp_data()
 	{
 		$data = "";
 
@@ -1854,10 +1854,10 @@
 	/**
 	 * Get Hostname
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	string
 	 */
-	private function _get_hostname()
+	protected function _get_hostname()
 	{
 		return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';
 	}
@@ -1867,10 +1867,10 @@
 	/**
 	 * Get IP
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @return	string
 	 */
-	private function _get_ip()
+	protected function _get_ip()
 	{
 		if ($this->_IP !== FALSE)
 		{
@@ -1933,11 +1933,11 @@
 	/**
 	 * Set Message
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	string
 	 * @return	string
 	 */
-	private function _set_error_message($msg, $val = '')
+	protected function _set_error_message($msg, $val = '')
 	{
 		$CI =& get_instance();
 		$CI->lang->load('email');
@@ -1957,11 +1957,11 @@
 	/**
 	 * Mime Types
 	 *
-	 * @access	private
+	 * @access	protected
 	 * @param	string
 	 * @return	string
 	 */
-	private function _mime_types($ext = "")
+	protected function _mime_types($ext = "")
 	{
 		$mimes = array(	'hqx'	=>	'application/mac-binhex40',
 						'cpt'	=>	'application/mac-compactpro',
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index 6f79a55..a34809e 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -26,16 +26,15 @@
  */
 class CI_Form_validation {
 
-	var $CI;
-	var $_field_data			= array();
-	var $_config_rules			= array();
-	var $_error_array			= array();
-	var $_error_messages		= array();
-	var $_error_prefix			= '<p>';
-	var $_error_suffix			= '</p>';
-	var $error_string			= '';
-	var $_safe_form_data		= FALSE;
-
+	protected $CI;
+	protected $_field_data			= array();
+	protected $_config_rules		= array();
+	protected $_error_array			= array();
+	protected $_error_messages		= array();
+	protected $_error_prefix		= '<p>';
+	protected $_error_suffix		= '</p>';
+	protected $error_string			= '';
+	protected $_safe_form_data		= FALSE;
 
 	/**
 	 * Constructor
@@ -72,7 +71,7 @@
 	 * @param	string
 	 * @return	void
 	 */
-	function set_rules($field, $label = '', $rules = '')
+	public function set_rules($field, $label = '', $rules = '')
 	{
 		// No reason to set rules if we have no POST data
 		if (count($_POST) == 0)
@@ -163,7 +162,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function set_message($lang, $val = '')
+	public function set_message($lang, $val = '')
 	{
 		if ( ! is_array($lang))
 		{
@@ -187,7 +186,7 @@
 	 * @param	string
 	 * @return	void
 	 */
-	function set_error_delimiters($prefix = '<p>', $suffix = '</p>')
+	public function set_error_delimiters($prefix = '<p>', $suffix = '</p>')
 	{
 		$this->_error_prefix = $prefix;
 		$this->_error_suffix = $suffix;
@@ -206,7 +205,7 @@
 	 * @param	string	the field name
 	 * @return	void
 	 */
-	function error($field = '', $prefix = '', $suffix = '')
+	public function error($field = '', $prefix = '', $suffix = '')
 	{
 		if ( ! isset($this->_field_data[$field]['error']) OR $this->_field_data[$field]['error'] == '')
 		{
@@ -238,7 +237,7 @@
 	 * @param	string
 	 * @return	str
 	 */
-	function error_string($prefix = '', $suffix = '')
+	public function error_string($prefix = '', $suffix = '')
 	{
 		// No errrors, validation passes!
 		if (count($this->_error_array) === 0)
@@ -279,7 +278,7 @@
 	 * @access	public
 	 * @return	bool
 	 */
-	function run($group = '')
+	public function run($group = '')
 	{
 		// Do we even have any data to process?  Mm?
 		if (count($_POST) == 0)
@@ -374,7 +373,7 @@
 	 * @param	integer
 	 * @return	mixed
 	 */
-	function _reduce_array($array, $keys, $i = 0)
+	protected function _reduce_array($array, $keys, $i = 0)
 	{
 		if (is_array($array))
 		{
@@ -406,7 +405,7 @@
 	 * @access	private
 	 * @return	null
 	 */
-	function _reset_post_array()
+	protected function _reset_post_array()
 	{
 		foreach ($this->_field_data as $field => $row)
 		{
@@ -468,7 +467,7 @@
 	 * @param	integer
 	 * @return	mixed
 	 */
-	function _execute($row, $rules, $postdata = NULL, $cycles = 0)
+	protected function _execute($row, $rules, $postdata = NULL, $cycles = 0)
 	{
 		// If the $_POST data is an array we will run a recursive call
 		if (is_array($postdata))
@@ -489,7 +488,7 @@
 		if ( ! in_array('required', $rules) AND is_null($postdata))
 		{
 			// Before we bail out, does the rule contain a callback?
-			if (preg_match("/(callback_\w+)/", implode(' ', $rules), $match))
+			if (preg_match("/(callback_\w+(\[.*?\])?)/", implode(' ', $rules), $match))
 			{
 				$callback = TRUE;
 				$rules = (array('1' => $match[1]));
@@ -695,7 +694,7 @@
 	 * @param	string	the field name
 	 * @return	string
 	 */
-	function _translate_fieldname($fieldname)
+	protected function _translate_fieldname($fieldname)
 	{
 		// Do we need to translate the field name?
 		// We look for the prefix lang: to determine this
@@ -727,7 +726,7 @@
 	 * @param	string
 	 * @return	void
 	 */
-	function set_value($field = '', $default = '')
+	public function set_value($field = '', $default = '')
 	{
 		if ( ! isset($this->_field_data[$field]))
 		{
@@ -757,7 +756,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function set_select($field = '', $value = '', $default = FALSE)
+	public function set_select($field = '', $value = '', $default = FALSE)
 	{
 		if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata']))
 		{
@@ -801,7 +800,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function set_radio($field = '', $value = '', $default = FALSE)
+	public function set_radio($field = '', $value = '', $default = FALSE)
 	{
 		if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata']))
 		{
@@ -845,7 +844,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function set_checkbox($field = '', $value = '', $default = FALSE)
+	public function set_checkbox($field = '', $value = '', $default = FALSE)
 	{
 		if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata']))
 		{
@@ -885,7 +884,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function required($str)
+	public function required($str)
 	{
 		if ( ! is_array($str))
 		{
@@ -907,7 +906,7 @@
 	 * @param	regex
 	 * @return	bool
 	 */
-	function regex_match($str, $regex)
+	public function regex_match($str, $regex)
 	{
 		if ( ! preg_match($regex, $str))
 		{
@@ -927,7 +926,7 @@
 	 * @param	field
 	 * @return	bool
 	 */
-	function matches($str, $field)
+	public function matches($str, $field)
 	{
 		if ( ! isset($_POST[$field]))
 		{
@@ -938,6 +937,24 @@
 
 		return ($str !== $field) ? FALSE : TRUE;
 	}
+	
+	// --------------------------------------------------------------------
+
+	/**
+	 * Match one field to another
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	field
+	 * @return	bool
+	 */
+	public function is_unique($str, $field)
+	{
+		list($table, $field)=explode('.', $field);
+		$query = $this->CI->db->limit(1)->get_where($table, array($field => $str));
+		
+		return $query->num_rows() === 0;
+    }
 
 	// --------------------------------------------------------------------
 
@@ -949,7 +966,7 @@
 	 * @param	value
 	 * @return	bool
 	 */
-	function min_length($str, $val)
+	public function min_length($str, $val)
 	{
 		if (preg_match("/[^0-9]/", $val))
 		{
@@ -974,7 +991,7 @@
 	 * @param	value
 	 * @return	bool
 	 */
-	function max_length($str, $val)
+	public function max_length($str, $val)
 	{
 		if (preg_match("/[^0-9]/", $val))
 		{
@@ -999,7 +1016,7 @@
 	 * @param	value
 	 * @return	bool
 	 */
-	function exact_length($str, $val)
+	public function exact_length($str, $val)
 	{
 		if (preg_match("/[^0-9]/", $val))
 		{
@@ -1023,7 +1040,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function valid_email($str)
+	public function valid_email($str)
 	{
 		return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $str)) ? FALSE : TRUE;
 	}
@@ -1037,7 +1054,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function valid_emails($str)
+	public function valid_emails($str)
 	{
 		if (strpos($str, ',') === FALSE)
 		{
@@ -1064,7 +1081,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function valid_ip($ip)
+	public function valid_ip($ip)
 	{
 		return $this->CI->input->valid_ip($ip);
 	}
@@ -1078,7 +1095,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function alpha($str)
+	public function alpha($str)
 	{
 		return ( ! preg_match("/^([a-z])+$/i", $str)) ? FALSE : TRUE;
 	}
@@ -1092,7 +1109,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function alpha_numeric($str)
+	public function alpha_numeric($str)
 	{
 		return ( ! preg_match("/^([a-z0-9])+$/i", $str)) ? FALSE : TRUE;
 	}
@@ -1106,7 +1123,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function alpha_dash($str)
+	public function alpha_dash($str)
 	{
 		return ( ! preg_match("/^([-a-z0-9_-])+$/i", $str)) ? FALSE : TRUE;
 	}
@@ -1120,7 +1137,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function numeric($str)
+	public function numeric($str)
 	{
 		return (bool)preg_match( '/^[\-+]?[0-9]*\.?[0-9]+$/', $str);
 
@@ -1135,7 +1152,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function is_numeric($str)
+	public function is_numeric($str)
 	{
 		return ( ! is_numeric($str)) ? FALSE : TRUE;
 	}
@@ -1149,7 +1166,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function integer($str)
+	public function integer($str)
 	{
 		return (bool) preg_match('/^[\-+]?[0-9]+$/', $str);
 	}
@@ -1163,7 +1180,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function decimal($str)
+	public function decimal($str)
 	{
 		return (bool) preg_match('/^[\-+]?[0-9]+\.[0-9]+$/', $str);
 	}
@@ -1177,7 +1194,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function greater_than($str, $min)
+	public function greater_than($str, $min)
 	{
 		if ( ! is_numeric($str))
 		{
@@ -1195,7 +1212,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function less_than($str, $max)
+	public function less_than($str, $max)
 	{
 		if ( ! is_numeric($str))
 		{
@@ -1213,7 +1230,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function is_natural($str)
+	public function is_natural($str)
 	{
 		return (bool) preg_match( '/^[0-9]+$/', $str);
 	}
@@ -1227,7 +1244,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function is_natural_no_zero($str)
+	public function is_natural_no_zero($str)
 	{
 		if ( ! preg_match( '/^[0-9]+$/', $str))
 		{
@@ -1254,7 +1271,7 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function valid_base64($str)
+	public function valid_base64($str)
 	{
 		return (bool) ! preg_match('/[^a-zA-Z0-9\/\+=]/', $str);
 	}
@@ -1271,7 +1288,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function prep_for_form($data = '')
+	public function prep_for_form($data = '')
 	{
 		if (is_array($data))
 		{
@@ -1300,7 +1317,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function prep_url($str = '')
+	public function prep_url($str = '')
 	{
 		if ($str == 'http://' OR $str == '')
 		{
@@ -1324,7 +1341,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function strip_image_tags($str)
+	public function strip_image_tags($str)
 	{
 		return $this->CI->input->strip_image_tags($str);
 	}
@@ -1338,7 +1355,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function xss_clean($str)
+	public function xss_clean($str)
 	{
 		return $this->CI->security->xss_clean($str);
 	}
@@ -1352,7 +1369,7 @@
 	 * @param	string
 	 * @return	string
 	 */
-	function encode_php_tags($str)
+	public function encode_php_tags($str)
 	{
 		return str_replace(array('<?php', '<?PHP', '<?', '?>'),  array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $str);
 	}
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index 8902f52..a8a0387 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -1334,7 +1334,7 @@
 			return FALSE;
 		}
 
-		$vals = @getimagesize($path);
+		$vals = getimagesize($path);
 
 		$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
 
diff --git a/system/libraries/Log.php b/system/libraries/Log.php
index 9f1db76..bf10d47 100644
--- a/system/libraries/Log.php
+++ b/system/libraries/Log.php
@@ -27,10 +27,12 @@
 class CI_Log {
 
 	protected $_log_path;
-	protected $_threshold	= 1;
-	protected $_date_fmt	= 'Y-m-d H:i:s';
-	protected $_enabled	= TRUE;
-	protected $_levels	= array('ERROR' => '1', 'DEBUG' => '2',  'INFO' => '3', 'ALL' => '4');
+	protected $_threshold		= 1;
+	protected $_threshold_max	= 0;
+	protected $_threshold_array	= array();
+	protected $_date_fmt		= 'Y-m-d H:i:s';
+	protected $_enabled			= TRUE;
+	protected $_levels			= array('ERROR' => '1', 'DEBUG' => '2',  'INFO' => '3', 'ALL' => '4');
 
 	/**
 	 * Constructor
@@ -50,6 +52,11 @@
 		{
 			$this->_threshold = $config['log_threshold'];
 		}
+		elseif (is_array($config['log_threshold']))
+		{
+			$this->_threshold = $this->_threshold_max;
+			$this->_threshold_array = array_flip($config['log_threshold']);
+		}
 
 		if ($config['log_date_format'] != '')
 		{
@@ -80,9 +87,13 @@
 
 		if ( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold))
 		{
-			return FALSE;
+			if (empty($this->_threshold_array) OR ! isset($this->_threshold_array[$this->_levels[$level]]))
+			{
+				return FALSE;
+			}
 		}
 
+
 		$filepath = $this->_log_path.'log-'.date('Y-m-d').'.php';
 		$message  = '';
 
diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php
new file mode 100644
index 0000000..3943ec1
--- /dev/null
+++ b/system/libraries/Migration.php
@@ -0,0 +1,338 @@
+<?php defined('BASEPATH') OR exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2006 - 2011, EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * Migration Class
+ *
+ * All migrations should implement this, forces up() and down() and gives
+ * access to the CI super-global.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Libraries
+ * @author		Reactor Engineers
+ * @link
+ */
+class CI_Migration {
+
+	protected $_migration_enabled = FALSE;
+	protected $_migration_path = NULL;
+	protected $_migration_version = 0;
+
+	protected $_error_string = '';
+
+	public function __construct($config = array())
+	{
+		# Only run this constructor on main library load
+		if (get_parent_class($this) !== FALSE)
+		{
+			return;
+		}
+
+		foreach ($config as $key => $val)
+		{
+			$this->{'_' . $key} = $val;
+		}
+
+		log_message('debug', 'Migrations class initialized');
+
+		// Are they trying to use migrations while it is disabled?
+		if ($this->_migration_enabled !== TRUE)
+		{
+			show_error('Migrations has been loaded but is disabled or set up incorrectly.');
+		}
+
+		// If not set, set it
+		$this->_migration_path == '' OR $this->_migration_path = APPPATH . 'migrations/';
+
+		// Add trailing slash if not set
+		$this->_migration_path = rtrim($this->_migration_path, '/').'/';
+
+		// Load migration language
+		$this->lang->load('migration');
+
+		// They'll probably be using dbforge
+		$this->load->dbforge();
+
+		// If the migrations table is missing, make it
+		if ( ! $this->db->table_exists('migrations'))
+		{
+			$this->dbforge->add_field(array(
+				'version' => array('type' => 'INT', 'constraint' => 3),
+			));
+
+			$this->dbforge->create_table('migrations', TRUE);
+
+			$this->db->insert('migrations', array('version' => 0));
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Migrate to a schema version
+	 *
+	 * Calls each migration step required to get to the schema version of
+	 * choice
+	 *
+	 * @access	public
+	 * @param $version integer	Target schema version
+	 * @return	mixed	TRUE if already latest, FALSE if failed, int if upgraded
+	 */
+	public function version($target_version)
+	{
+		$start = $current_version = $this->_get_version();
+		$stop = $target_version;
+
+		if ($target_version > $current_version)
+		{
+			// Moving Up
+			++$start;
+			++$stop;
+			$step = 1;
+		}
+
+		else
+		{
+			// Moving Down
+			$step = -1;
+		}
+		
+		$method = $step === 1 ? 'up' : 'down';
+		$migrations = array();
+
+		// We now prepare to actually DO the migrations
+		// But first let's make sure that everything is the way it should be
+		for ($i = $start; $i != $stop; $i += $step)
+		{
+			$f = glob(sprintf($this->_migration_path . '%03d_*.php', $i));
+
+			// Only one migration per step is permitted
+			if (count($f) > 1)
+			{
+				$this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i);
+				return FALSE;
+			}
+
+			// Migration step not found
+			if (count($f) == 0)
+			{
+				// If trying to migrate up to a version greater than the last
+				// existing one, migrate to the last one.
+				if ($step == 1)
+				{
+					break;
+				}
+
+				// If trying to migrate down but we're missing a step,
+				// something must definitely be wrong.
+				$this->_error_string = sprintf($this->lang->line('migration_not_found'), $i);
+				return FALSE;
+			}
+
+			$file = basename($f[0]);
+			$name = basename($f[0], '.php');
+
+			// Filename validations
+			if (preg_match('/^\d{3}_(\w+)$/', $name, $match))
+			{
+				$match[1] = strtolower($match[1]);
+
+				// Cannot repeat a migration at different steps
+				if (in_array($match[1], $migrations))
+				{
+					$this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]);
+					return FALSE;
+				}
+
+				include $f[0];
+				$class = 'Migration_' . ucfirst($match[1]);
+
+				if ( ! class_exists($class))
+				{
+					$this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
+					return FALSE;
+				}
+
+				if ( ! is_callable(array($class, $method)))
+				{
+					$this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
+					return FALSE;
+				}
+
+				$migrations[] = $match[1];
+			}
+			else
+			{
+				$this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file);
+				return FALSE;
+			}
+		}
+
+		log_message('debug', 'Current migration: ' . $current_version);
+
+		$version = $i + ($step == 1 ? -1 : 0);
+
+		// If there is nothing to do so quit
+		if ($migrations === array())
+		{
+			return TRUE;
+		}
+
+		log_message('debug', 'Migrating from ' . $method . ' to version ' . $version);
+
+		// Loop through the migrations
+		foreach ($migrations AS $migration)
+		{
+			// Run the migration class
+			$class = 'Migration_' . ucfirst(strtolower($migration));
+			call_user_func(array(new $class, $method));
+
+			$current_version += $step;
+			$this->_update_version($current_version);
+		}
+
+		log_message('debug', 'Finished migrating to '.$current_version);
+
+		return $current_version;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set's the schema to the latest migration
+	 *
+	 * @access	public
+	 * @return	mixed	true if already latest, false if failed, int if upgraded
+	 */
+	public function latest()
+	{
+		if ( ! $migrations = $this->find_migrations())
+		{
+			$this->_error_string = $this->line->lang('migration_none_found');
+			return false;
+		}
+
+		$last_migration = basename(end($migrations));
+		
+		// Calculate the last migration step from existing migration
+		// filenames and procceed to the standard version migration
+		return $this->version((int) substr($last_migration, 0, 3));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set's the schema to the migration version set in config
+	 *
+	 * @access	public
+	 * @return	mixed	true if already current, false if failed, int if upgraded
+	 */
+	public function current()
+	{
+		return $this->version($this->_migration_version);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Error string
+	 *
+	 * @access	public
+	 * @return	string	Error message returned as a string
+	 */
+	public function error_string()
+	{
+		return $this->_error_string;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set's the schema to the latest migration
+	 *
+	 * @access	protected
+	 * @return	mixed	true if already latest, false if failed, int if upgraded
+	 */
+	protected function find_migrations()
+	{
+		// Load all *_*.php files in the migrations path
+		$files = glob($this->_migration_path . '*_*.php');
+		$file_count = count($files);
+		
+		for ($i = 0; $i < $file_count; $i++)
+		{
+			// Mark wrongly formatted files as false for later filtering
+			$name = basename($files[$i], '.php');
+			if ( ! preg_match('/^\d{3}_(\w+)$/', $name))
+			{
+				$files[$i] = FALSE;
+			}
+		}
+		
+		sort($files);
+
+		return $files;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Retrieves current schema version
+	 *
+	 * @access	protected
+	 * @return	integer	Current Migration
+	 */
+	protected function _get_version()
+	{
+		$row = $this->db->get('migrations')->row();
+		return $row ? $row->version : 0;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Stores the current schema version
+	 *
+	 * @access	protected
+	 * @param $migrations integer	Migration reached
+	 * @return	void					Outputs a report of the migration
+	 */
+	protected function _update_version($migrations)
+	{
+		return $this->db->update('migrations', array(
+			'version' => $migrations
+		));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Enable the use of CI super-global
+	 *
+	 * @access	public
+	 * @param $var
+	 * @return	mixed
+	 */
+	public function __get($var)
+	{
+		return get_instance()->$var;
+	}
+}
+
+/* End of file Migration.php */
+/* Location: ./system/libraries/Migration.php */
\ No newline at end of file
diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php
index 082a5ee..330acce 100644
--- a/system/libraries/Profiler.php
+++ b/system/libraries/Profiler.php
@@ -493,7 +493,7 @@
 	 *
 	 * @return 	string
 	 */
-	private function _compile_session_data()
+	protected function _compile_session_data()
 	{
 		if ( ! isset($this->CI->session))
 		{
@@ -555,4 +555,4 @@
 // END CI_Profiler class
 
 /* End of file Profiler.php */
-/* Location: ./system/libraries/Profiler.php */
\ No newline at end of file
+/* Location: ./system/libraries/Profiler.php */
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index 3177424..8f324de 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -30,6 +30,7 @@
 	public $max_width				= 0;
 	public $max_height				= 0;
 	public $max_filename			= 0;
+	public $max_filename_increment 	= 100;
 	public $allowed_types			= "";
 	public $file_temp				= "";
 	public $file_name				= "";
@@ -80,31 +81,32 @@
 	public function initialize($config = array())
 	{
 		$defaults = array(
-							'max_size'			=> 0,
-							'max_width'			=> 0,
-							'max_height'		=> 0,
-							'max_filename'		=> 0,
-							'allowed_types'		=> "",
-							'file_temp'			=> "",
-							'file_name'			=> "",
-							'orig_name'			=> "",
-							'file_type'			=> "",
-							'file_size'			=> "",
-							'file_ext'			=> "",
-							'upload_path'		=> "",
-							'overwrite'			=> FALSE,
-							'encrypt_name'		=> FALSE,
-							'is_image'			=> FALSE,
-							'image_width'		=> '',
-							'image_height'		=> '',
-							'image_type'		=> '',
-							'image_size_str'	=> '',
-							'error_msg'			=> array(),
-							'mimes'				=> array(),
-							'remove_spaces'		=> TRUE,
-							'xss_clean'			=> FALSE,
-							'temp_prefix'		=> "temp_file_",
-							'client_name'		=> ''
+							'max_size'					=> 0,
+							'max_width'					=> 0,
+							'max_height'				=> 0,
+							'max_filename'				=> 0,
+							'max_filename_increment'	=> 100,
+							'allowed_types'				=> "",
+							'file_temp'					=> "",
+							'file_name'					=> "",
+							'orig_name'					=> "",
+							'file_type'					=> "",
+							'file_size'					=> "",
+							'file_ext'					=> "",
+							'upload_path'				=> "",
+							'overwrite'					=> FALSE,
+							'encrypt_name'				=> FALSE,
+							'is_image'					=> FALSE,
+							'image_width'				=> '',
+							'image_height'				=> '',
+							'image_type'				=> '',
+							'image_size_str'			=> '',
+							'error_msg'					=> array(),
+							'mimes'						=> array(),
+							'remove_spaces'				=> TRUE,
+							'xss_clean'					=> FALSE,
+							'temp_prefix'				=> "temp_file_",
+							'client_name'				=> ''
 						);
 
 
@@ -402,7 +404,7 @@
 		$filename = str_replace($this->file_ext, '', $filename);
 
 		$new_filename = '';
-		for ($i = 1; $i < 100; $i++)
+		for ($i = 1; $i < $this->max_filename_increment; $i++)
 		{
 			if ( ! file_exists($path.$filename.$i.$this->file_ext))
 			{
diff --git a/system/libraries/User_agent.php b/system/libraries/User_agent.php
index 016102a..0b77a7d 100644
--- a/system/libraries/User_agent.php
+++ b/system/libraries/User_agent.php
@@ -142,7 +142,7 @@
 	{
 		$this->_set_platform();
 
-		foreach (array('_set_browser', '_set_robot', '_set_mobile') as $function)
+		foreach (array('_set_robot', '_set_browser', '_set_mobile') as $function)
 		{
 			if ($this->$function() === TRUE)
 			{
diff --git a/user_guide/changelog.html b/user_guide/changelog.html
index c52a33e..c030ce7 100644
--- a/user_guide/changelog.html
+++ b/user_guide/changelog.html
@@ -59,6 +59,65 @@
 
 <p>The <img src="images/reactor-bullet.png" width="16" height="16" alt="Reactor Marker" /> indicates items that were contributed to CodeIgniter via CodeIgniter Reactor.</p>
 
+<h2>Version 2.1.0 (planned)</h2>
+<p>Release Date: Not Released</p>
+
+<ul>
+	<li>General Changes
+		<ul>
+			<li class="reactor">Callback validation rules can now accept parameters like any other validation rule.</li>
+			<li class="reactor">Ability to log certain error types, not all under a threshold.</li>
+			<li class="reactor">Added html_escape() to <a href="general/common_functions.html">Common functions</a> to escape HTML output for preventing XSS.</li>
+		</ul>
+	</li>
+	<li>Helpers
+		<ul>
+			<li class="reactor">Added <samp>increment_string()</samp> to <a href="helpers/string_helper.html">String Helper</a> to turn "foo" into "foo-1" or "foo-1" into "foo-2".</li>
+        	<li>Altered form helper - made action on form_open_multipart helper function call optional.  Fixes (#65)</li>
+			<li><samp>url_title()</samp> will now trim extra dashes from beginning and end.</li>
+		</ul>
+	</li>
+	<li>Database
+		<ul>
+			<li class="reactor">Added a <a href="http://www.cubrid.org/" target="_blank">CUBRID</a> driver to the <a href="database/index.html">Database Driver</a>. Thanks to the CUBRID team for supplying this patch.</li>
+			<li class="reactor">Typecast limit and offset in the <a href="database/queries.html">Database Driver</a> to integers to avoid possible injection.</li>
+			<li class="reactor">
+				Added additional option 'none' for the optional third argument for  <kbd>$this->db->like()</kbd> in the <a href="database/active_record.html">Database Driver</a>.
+			</li>
+		</ul>
+	</li>
+	<li>Libraries
+		<ul>
+			<li class="reactor">Changed <kbd>$this->cart->insert()</kbd> in the <a href="libraries/cart.html">Cart Library</a> to return the Row ID if a single item was inserted successfully.</li>
+			<li class="reactor">Added support to set an optional parameter in your callback rules of validation using the <a href="libraries/form_validation.html">Form Validation Library</a>.</li>
+			<li class="reactor">Added a <a href="libraries/migration.html">Migration Library</a> to assist with applying incremental updates to your database schema.</li>
+			<li class="reactor">Driver children can be located in any package path.</li>
+			<li class="reactor">Added max_filename_increment config setting for Upload library.</li>
+			<li><samp>CI_Loader::_ci_autoloader()</samp> is now a protected method.</li>
+			<li class="reactor">Added <kbd>is_unique</kbd> to the <a href="libraries/form_validation.html">Form Validation library</a>.</li>
+		</ul>
+	</li>
+	<li>Core
+		<ul>
+			<li class="reactor">Changed private functions in CI_URI to protected so MY_URI can override them.</li>
+		</ul>
+	</li>
+</ul>
+
+<h3>Bug fixes for 2.1.0</h3>
+<ul>
+	<li class="reactor">Fixed #378 Robots identified as regular browsers by the User Agent class.</li>
+	<li class="reactor">If a config class was loaded first then a library with the same name is loaded, the config would be ignored.</li>
+	<li class="reactor">Fixed a bug (Reactor #19) where 1) the 404_override route was being ignored in some cases, and 2) auto-loaded libraries were not available to the 404_override controller when a controller existed but the requested method did not.</li>
+	<li class="rector">Fixed a bug (Reactor #89) where MySQL export would fail if the table had hyphens or other non alphanumeric/underscore characters.</li>
+    <li class="reactor">Fixed a bug (#200) where MySQL queries would be malformed after calling <samp>count_all()</samp> then <samp>db->get()</samp></li>
+    <li>Fixed a bug (#181) where a mis-spelling was in the form validation language file.</li>
+	<li>Fixed a bug (#160) - Removed unneeded array copy in the file cache driver.</li>
+	<li>Fixed a bug (#150) - <samp>field_data()</samp> now correctly returns column length.</li>
+	<li>Fixed a bug (#8) - <samp>load_class()</samp> now looks for core classes in <samp>APPPATH</samp> first, allowing them to be replaced.</li>
+	<li>Fixed a bug (#24) - ODBC database driver called incorrect parent in __construct().</li>
+</ul>
+
 <h2>Version 2.0.3</h2>
 <p>Release Date: August 20, 2011</p>
 
@@ -67,6 +126,7 @@
 		<ul>
 			<li>An improvement was made to the MySQL and MySQLi drivers to prevent exposing a potential vector for SQL injection on sites using multi-byte character sets in the database client connection.  <p>An incompatibility in PHP versions &lt; 5.2.3 and MySQL &lt; 5.0.7 with <em>mysql_set_charset()</em> creates a situation where using multi-byte  character sets on these environments may potentially expose a SQL injection attack vector.  Latin-1, UTF-8, and other "low ASCII" character sets are unaffected on all environments.</p>  <p class="critical">If you are running or considering running a multi-byte character set for your database connection, please pay close attention to the server environment you are deploying on to ensure you are not vulnerable.</p></li>
 		</ul>
+	</li>
 	<li>General Changes
 		<ul>
 			<li>Fixed a bug where there was a misspelling within a code comment in the index.php file.</li>
@@ -75,22 +135,29 @@
 			<li>Visual updates to the welcome_message view file and default error templates. Thanks to <a href="https://bitbucket.org/danijelb">danijelb</a> for the pull request.</li>
 			<li class="reactor">Added <samp>insert_batch()</samp> function to the PostgreSQL database driver.  Thanks to epallerols for the patch.</li>
 			<li class="reactor">Added "application/x-csv" to mimes.php.</li>
+			<li class="reactor">Added CSRF protection URI whitelisting.</li>
 			<li>Fixed a bug where <a href="libraries/email.html">Email library</a> attachments with a "." in the name would using invalid MIME-types.</li>
+            <li>Added support for pem,p10,p12,p7a,p7c,p7m,p7r,p7s,crt,crl,der,kdb,rsa,cer,sst,csr Certs to mimes.php.</li>
+            <li>Added support pgp,gpg to mimes.php.</li>
+            <li>Added support 3gp, 3g2, mp4, wmv, f4v, vlc Video files to mimes.php.</li>
+            <li>Added support m4a, aac, m4u, xspf, au, ac3, flac, ogg Audio files to mimes.php.</li>
+
 		</ul>
 	</li>
 	<li>Helpers
 		<ul>
 			<li>Added an optional third parameter to <samp>heading()</samp> which allows adding html attributes to the rendered heading tag.</li>
+			<li class="reactor"><kbd>form_open()</kbd> now only adds a hidden (Cross-site Reference Forgery) protection field when the form's action is internal and is set to the post method. (Reactor #165)</li>
+			<li class="reactor">Re-worked <samp>plural()</samp> and <samp>singular()</samp> functions in the <a href="helpers/inflector_helper.html">Inflector helper</a> to support considerably more words.</li>
 		</ul>
 	</li>
 	<li>Libraries
 		<ul>
 			<li>Altered Session to use a longer match against the user_agent string. See upgrade notes if using database sessions.</li>
-			<li class="reactor">Added <kbd>is_unique</kbd> to the <a href="libraries/form_validation.html">Form Validation library</a>.</li>
 			<li class="reactor">Added <kbd>$this->db->set_dbprefix()</kbd> to the <a href="database/queries.html">Database Driver</a>.</li>
 			<li class="reactor">Changed <kbd>$this->cart->insert()</kbd> in the <a href="libraries/cart.html">Cart Library</a> to return the Row ID if a single item was inserted successfully.</li>
 			<li class="reactor">Added <kbd>$this->load->get_var()</kbd> to the <a href="libraries/loader.html">Loader library</a> to retrieve global vars set with <kbd>$this->load->view()</kbd> and <kbd>$this->load->vars()</kbd>.</li>
-			<li>Changed <kbd>$this->db->having()</kbd> to insert quotes using escape() rather than escape_str().</li>
+			<li class="reactor">Changed <kbd>$this->db->having()</kbd> to insert quotes using escape() rather than escape_str().</li>
 		</ul>
 	</li>
 </ul>
diff --git a/user_guide/database/active_record.html b/user_guide/database/active_record.html
index 3f44fcd..92d9614 100644
--- a/user_guide/database/active_record.html
+++ b/user_guide/database/active_record.html
@@ -334,6 +334,13 @@
 	$this-&gt;db-&gt;like('title', 'match', 'both'); <br />
 // Produces: WHERE title LIKE '%match%' </code>	</li>
 
+If you do not want to use the wildcard (%) you can pass to the optional third argument the option 'none'.
+
+<code>
+	$this-&gt;db-&gt;like('title', 'match', 'none'); <br />
+// Produces: WHERE title LIKE 'match'
+</code>
+
 	<li><strong>Associative array method:</strong>
 
 	<code>
@@ -525,7 +532,7 @@
 <p>Generates an insert string based on the data you supply, and runs the query. You can either pass an
 <strong>array</strong> or an <strong>object</strong> to the function.  Here is an example using an array:</p>
 
-<code>
+<code> 
 $data = array(<br/>
 &nbsp;&nbsp;&nbsp;array(<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'title' => 'My title' ,<br />
@@ -537,7 +544,7 @@
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'name' => 'Another Name' ,<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'date' => 'Another date'<br />
 &nbsp;&nbsp;&nbsp;)<br/>
-);<br />
+);<br /> 
 <br />
 $this->db->update_batch('mytable', $data);
 <br /><br />
diff --git a/user_guide/database/configuration.html b/user_guide/database/configuration.html
index bbdd0a3..f06b08f 100644
--- a/user_guide/database/configuration.html
+++ b/user_guide/database/configuration.html
@@ -132,8 +132,8 @@
 <li><strong>cache_on</strong> - TRUE/FALSE (boolean) - Whether database query caching is enabled, see also <a href="caching.html">Database Caching Class</a>.</li>
 <li><strong>cachedir</strong> - The absolute server path to your database query cache directory.</li>
 <li><strong>char_set</strong> - The character set used in communicating with the database.</li>
-<li><strong>dbcollat</strong> - The character collation used in communicating with the database. <p class="important"><strong>Note:</strong> For MySQL and MySQLi databases, this setting is only used as a backup if your server is running PHP &lt; 5.2.3 or MySQL &lt; 5.0.7.  There is an incompatibility in PHP with mysql_real_escape_string() which can make your site vulnerable to SQL injection if you are using a multi-byte character set and are running versions lower than these.  Sites using Latin-1 or UTF-8 database character set and collation are unaffected.</p></li>
-<li><strong>swap_pre</strong> - A default table prefix that should be swapped with <var>dbprefix</var>.  This is useful for distributed applications where you might run manually written queries, and need the prefix to still be customizable by the end user.</li>
+<li><strong>dbcollat</strong> - The character collation used in communicating with the database. <p class="important"><strong>Note:</strong> For MySQL and MySQLi databases, this setting is only used as a backup if your server is running PHP &lt; 5.2.3 or MySQL &lt; 5.0.7 (and in table creation queries made with DB Forge). There is an incompatibility in PHP with mysql_real_escape_string() which can make your site vulnerable to SQL injection if you are using a multi-byte character set and are running versions lower than these. Sites using Latin-1 or UTF-8 database character set and collation are unaffected.</p></li>
+<li><strong>swap_pre</strong> - A default table prefix that should be swapped with <var>dbprefix</var>. This is useful for distributed applications where you might run manually written queries, and need the prefix to still be customizable by the end user.</li>
 <li><strong>autoinit</strong> - Whether or not to automatically connect to the database when the library loads. If set to false, the connection will take place prior to executing the first query.</li>
 <li><strong>stricton</strong> - TRUE/FALSE (boolean) - Whether to force "Strict Mode" connections, good for ensuring strict SQL while developing an application.</li>
 <li><strong>port</strong> - The database port number.  To use this value you have to add a line to the database config array.<code>$db['default']['port'] =  5432;</code>
diff --git a/user_guide/general/common_functions.html b/user_guide/general/common_functions.html
index 6545775..7cff632 100644
--- a/user_guide/general/common_functions.html
+++ b/user_guide/general/common_functions.html
@@ -104,6 +104,8 @@
 <p>This function prevents inserting null characters between ascii characters, like Java\0script.</p>
 
 
+<h2>html_escape(<var>$mixed</var>)</h2>
+<p>This function provides short cut for htmlspecialchars() function. It accepts string and array. To prevent Cross Site Scripting (XSS), it is very useful.</p>
 
 </div>
 
diff --git a/user_guide/helpers/date_helper.html b/user_guide/helpers/date_helper.html
index f930ea3..5b00e25 100644
--- a/user_guide/helpers/date_helper.html
+++ b/user_guide/helpers/date_helper.html
@@ -234,6 +234,20 @@
 
 
 
+<h2>nice_date()</h2>
+
+<p>This function can take a number poorly-formed date formats and convert them into something useful. It also accepts well-formed dates.</p> 
+<p>The function will return a Unix timestamp by default. You can, optionally, pass a format string (the same type as the PHP date function accepts) as the second parameter. Example:</p>
+
+<code>$bad_time = 199605<br />
+<br />
+// Should Produce: 1996-05-01<br />
+$better_time = nice_date($bad_time,'Y-m-d');<br />
+<br />
+$bad_time = 9-11-2001<br />
+// Should Produce: 2001-09-11<br />
+$better_time = nice_date($human,'Y-m-d');</code>
+
 
 
 <h2>timespan()</h2>
diff --git a/user_guide/helpers/string_helper.html b/user_guide/helpers/string_helper.html
index a3dc165..3141240 100644
--- a/user_guide/helpers/string_helper.html
+++ b/user_guide/helpers/string_helper.html
@@ -90,6 +90,17 @@
 <code>echo random_string('alnum', 16);</code>
 
 
+<h2>increment_string()</h2>
+
+<p>Increments a string by appending a number to it or increasing the number. Useful for creating "copies" or a file or duplicating database content which has unique titles or slugs.</p>
+
+<p>Usage example:</p>
+
+<code>echo increment_string('file', '_'); // "file_1"<br/>
+echo increment_string('file', '-', 2); // "file-2"<br/>
+echo increment_string('file-4'); // "file-5"<br/></code>
+
+
 <h2>alternator()</h2>
 
 <p>Allows two or more items to be alternated between, when cycling through a loop.  Example:</p>
diff --git a/user_guide/images/appflowchart.gif b/user_guide/images/appflowchart.gif
index 422332c..4328e48 100644
--- a/user_guide/images/appflowchart.gif
+++ b/user_guide/images/appflowchart.gif
Binary files differ
diff --git a/user_guide/installation/index.html b/user_guide/installation/index.html
index 5e8ab38..84338e2 100644
--- a/user_guide/installation/index.html
+++ b/user_guide/installation/index.html
@@ -72,7 +72,9 @@
 
 <p>For the best security, both the <dfn>system</dfn> and any <dfn>application</dfn> folders should be placed above web root so that they are not directly accessible via a browser.  By default, .htaccess files are included in each folder to help prevent direct access, but it is best to remove them from public access entirely in case the web server configuration changes or doesn't abide by the .htaccess.</p>
 
-<p>After moving them, open your main <kdb>index.php</kbd> file and set the <samp>$system_folder</samp> and <samp>$application_folder</samp> variables, preferably with a full path, e.g. '<dfn>/www/MyUser/system</dfn>'.</p>
+<p>If you would like to keep your views public it is also possible to move the <dfn>views</dfn> folder out of your application folder.</p>
+
+<p>After moving them, open your main <kdb>index.php</kbd> file and set the <samp>$system_folder</samp>, <samp>$application_folder</samp> and <samp>$view_folder</samp> variables, preferably with a full path, e.g. '<dfn>/www/MyUser/system</dfn>'.</p>
 
 <p>
     One additional measure to take in production environments is to disable
diff --git a/user_guide/installation/upgrade_203.html b/user_guide/installation/upgrade_203.html
index 1d37a05..0489983 100644
--- a/user_guide/installation/upgrade_203.html
+++ b/user_guide/installation/upgrade_203.html
@@ -81,7 +81,7 @@
 
 <h2>Step 5: Remove APPPATH.'third_party' from autoload.php</h2>
 
-<p>Open application/autoload.php, and look for the following:</p>
+<p>Open application/config/autoload.php, and look for the following:</p>
 
 <code>$autoload['packages'] = array(APPPATH.'third_party');</code>
 
diff --git a/user_guide/libraries/cart.html b/user_guide/libraries/cart.html
index 3471eeb..f1e8473 100644
--- a/user_guide/libraries/cart.html
+++ b/user_guide/libraries/cart.html
@@ -61,7 +61,7 @@
 <p>The Cart Class permits items to be added to a session that stays active while a user is browsing your site.
 These items can be retrieved and displayed in a standard "shopping cart" format, allowing the user to update the quantity or remove items from the cart.</p>
 
-<p>Please note that the Cart Class ONLY provides the core "cart" functionality.  It does not provide shipping, credit card authorization, or other processing components.</p>
+<p>Please note that the Cart Class ONLY provides the core "cart" functionality. It does not provide shipping, credit card authorization, or other processing components.</p>
 
 
 <h2>Initializing the Shopping Cart Class</h2>
@@ -106,20 +106,20 @@
 <li><strong>qty</strong> - The quantity being purchased.
 <li><strong>price</strong> - The price of the item.
 <li><strong>name</strong> - The name of the item.
-<li><strong>options</strong> - Any additional attributes that are needed to identify the product.  These must be passed via an array.
+<li><strong>options</strong> - Any additional attributes that are needed to identify the product. These must be passed via an array.
 </ul>
 
 <p>In addition to the five indexes above, there are two reserved words: <dfn>rowid</dfn> and <dfn>subtotal</dfn>.  These are used internally by the Cart class, so
 please do NOT use those words as index names when inserting data into the cart.</p>
 
-<p>Your array may contain additional data.  Anything you include in your array will be stored in the session.  However, it is best to standardize your data among
-all your products in order to make displaying the information in a table easier.</p>
+<p>Your array may contain additional data. Anything you include in your array will be stored in the session. However, it is best to standardize your data among all your products in order to make displaying the information in a table easier.</p>
+
+<p>The insert() method will return the $rowid if you successfully insert a single item.</p>
 
 
 <h2>Adding Multiple Items to The Cart</h2>
 
-<p>By using a multi-dimensional array, as shown below, it is possible to add multiple products to the cart in one action.  This is useful in cases where you wish to allow
-people to select from among several items on the same page.</p>
+<p>By using a multi-dimensional array, as shown below, it is possible to add multiple products to the cart in one action. This is useful in cases where you wish to allow people to select from among several items on the same page.</p>
 
 
 <code>
@@ -268,8 +268,8 @@
 <p><strong>What is a Row ID?</strong>&nbsp; The <kbd>row ID</kbd> is a unique identifier that is generated by the cart code when an item is added to the cart.  The reason a
 unique ID is created is so that identical products with different options can be managed by the cart.</p>
 
-<p>For example, let's say someone buys two identical t-shirts (same product ID), but in different sizes.  The product ID (and other attributes) will be
-identical for both sizes because it's the same shirt. The only difference will be the size.  The cart must therefore have a means of identifying this
+<p>For example, let's say someone buys two identical t-shirts (same product ID), but in different sizes. The product ID (and other attributes) will be
+identical for both sizes because it's the same shirt. The only difference will be the size. The cart must therefore have a means of identifying this
 difference so that the two sizes of shirts can be managed independently. It does so by creating a unique "row ID" based on the product ID and any options associated with it.</p>
 
 <p>In nearly all cases, updating the cart will be something the user does via the "view cart" page, so as a developer, it is unlikely that you will ever have to concern yourself
@@ -311,7 +311,7 @@
 
 <h2>$this->cart->has_options(rowid);</h2>
 
-<p>Returns TRUE (boolean) if a particular row in the cart contains options.  This function is designed to be used in a loop with <dfn>$this->cart->contents()</dfn>, since you must pass the <kbd>rowid</kbd> to this function, as shown in the <dfn>Displaying the Cart</dfn> example above.</p>
+<p>Returns TRUE (boolean) if a particular row in the cart contains options. This function is designed to be used in a loop with <dfn>$this->cart->contents()</dfn>, since you must pass the <kbd>rowid</kbd> to this function, as shown in the <dfn>Displaying the Cart</dfn> example above.</p>
 
 
 <h2>$this->cart->product_options(rowid);</h2>
@@ -322,7 +322,7 @@
 
 <h2>$this->cart->destroy();</h2>
 
-<p>Permits you to destroy the cart.  This function will likely be called when you are finished processing the customer's order.</p>
+<p>Permits you to destroy the cart. This function will likely be called when you are finished processing the customer's order.</p>
 
 
 
diff --git a/user_guide/libraries/file_uploading.html b/user_guide/libraries/file_uploading.html
index a88c672..94b2193 100644
--- a/user_guide/libraries/file_uploading.html
+++ b/user_guide/libraries/file_uploading.html
@@ -305,6 +305,13 @@
 </tr>
 
 <tr>
+<td class="td"><strong>max_filename_increment</strong></td>
+<td class="td">100</td>
+<td class="td">None</td>
+<td class="td">When overwrite is set to FALSE, use this to set the maximum filename increment for CodeIgniter to append to the filename.</td>
+</tr>
+
+<tr>
 <td class="td"><strong>encrypt_name</strong></td>
 <td class="td">FALSE</td>
 <td class="td">TRUE/FALSE (boolean)</td>
diff --git a/user_guide/libraries/form_validation.html b/user_guide/libraries/form_validation.html
index 7c544b6..ede1913 100644
--- a/user_guide/libraries/form_validation.html
+++ b/user_guide/libraries/form_validation.html
@@ -390,10 +390,10 @@
 <p>CodeIgniter lets you pipe multiple rules together.  Let's try it. Change your rules in the third parameter of rule setting function, like this:</p>
 
 <code>
-$this->form_validation->set_rules('username', 'Username', 'required|min_length[5]|max_length[12]');<br />
+$this->form_validation->set_rules('username', 'Username', 'required|min_length[5]|max_length[12]|is_unique[users.username]');<br />
 $this->form_validation->set_rules('password', 'Password', 'required|matches[passconf]');<br />
 $this->form_validation->set_rules('passconf', 'Password Confirmation', 'required');<br />
-$this->form_validation->set_rules('email', 'Email', 'required|valid_email');<br />
+$this->form_validation->set_rules('email', 'Email', 'required|valid_email|is_unique[users.email]');<br />
 </code>
 
 <p>The above code sets the following rules:</p>
@@ -508,15 +508,13 @@
 
 <code>$this->form_validation->set_rules('username', 'Username', '<kbd>callback_username_check</kbd>');</code>
 
-
 <p>Then add a new function called <dfn>username_check</dfn> to your controller.  Here's how your controller should now look:</p>
 
-
-<textarea class="textarea" style="width:100%" cols="50" rows="44">&lt;?php
+<textarea class="textarea" style="width:100%" cols="50" rows="40">&lt;?php
 
 class Form extends CI_Controller {
 
-	function index()
+	public function index()
 	{
 		$this->load->helper(array('form', 'url'));
 
@@ -525,7 +523,7 @@
 		$this->form_validation->set_rules('username', 'Username', 'callback_username_check');
 		$this->form_validation->set_rules('password', 'Password', 'required');
 		$this->form_validation->set_rules('passconf', 'Password Confirmation', 'required');
-		$this->form_validation->set_rules('email', 'Email', 'required');
+		$this->form_validation->set_rules('email', 'Email', 'required|is_unique[users.email]');
 
 		if ($this->form_validation->run() == FALSE)
 		{
@@ -537,7 +535,7 @@
 		}
 	}
 
-	function username_check($str)
+	public function username_check($str)
 	{
 		if ($str == 'test')
 		{
@@ -556,14 +554,13 @@
 <p><dfn>Reload your form and submit it with the word "test" as the username.  You can see that the form field data was passed to your
 callback function for you to process.</dfn></p>
 
-<p><strong>To invoke a callback just put the function name in a rule, with "callback_" as the rule prefix.</strong></p>
+<p>To invoke a callback just put the function name in a rule, with "callback_" as the rule <strong>prefix</strong>. If you need
+to receive an extra parameter in your callback function, just add it normally after the function name between square brackets,
+as in: "callback_foo<strong>[bar]</strong>", then it will be passed as the second argument of your callback function.</p>
 
-<p>You can also process the form data that is passed to your callback and return it.  If your callback returns anything other than a boolean TRUE/FALSE
+<p><strong>Note:</strong> You can also process the form data that is passed to your callback and return it.  If your callback returns anything other than a boolean TRUE/FALSE
 it is assumed that the data is your newly processed form data.</p>
 
-
-
-
 <a name="settingerrors"></a>
 <h2>Setting Error Messages</h2>
 
@@ -947,6 +944,13 @@
 	</tr>
 
 	<tr>
+		<td class="td"><strong>is_unique</strong></td>
+		<td class="td">Yes</td>
+		<td class="td">Returns FALSE if the form element is not unique to the table and field name in the parameter.</td>
+		<td class="td">is_unique[table.field]</td>
+	</tr>
+
+	<tr>
 		<td class="td"><strong>min_length</strong></td>
 		<td class="td">Yes</td>
 		<td class="td">Returns FALSE if the form element is shorter then the parameter value.</td>
@@ -1038,6 +1042,13 @@
 	</tr>
 
 	<tr>
+		<td class="td"><strong>is_unique</strong></td>
+		<td class="td">Yes</td>
+		<td class="td">Returns FALSE if the form element is not unique in a database table.</td>
+		<td class="td">is_unique[table.field]</td>
+	</tr>
+
+	<tr>
 		<td class="td"><strong>valid_email</strong></td>
 		<td class="td">No</td>
 		<td class="td">Returns FALSE if the form element does not contain a valid email address.</td>
diff --git a/user_guide/libraries/input.html b/user_guide/libraries/input.html
index 311f76e..77e2848 100644
--- a/user_guide/libraries/input.html
+++ b/user_guide/libraries/input.html
@@ -73,11 +73,11 @@
 <p>The security filtering function is called automatically when a new <a href="../general/controllers.html">controller</a> is invoked.  It does the following:</p>
 
 <ul>
-<li>Destroys the global GET array.  Since CodeIgniter does not utilize GET strings, there is no reason to allow it.</li>
+<li>If $config['allow_get_array'] is FALSE(default is TRUE), destroys the global GET array.</li>
 <li>Destroys all global variables in the event register_globals is turned on.</li>
-<li>Filters the POST/COOKIE array keys, permitting only alpha-numeric (and a few other) characters.</li>
+<li>Filters the GET/POST/COOKIE array keys, permitting only alpha-numeric (and a few other) characters.</li>
 <li>Provides XSS (Cross-site Scripting Hacks) filtering.  This can be enabled globally, or upon request.</li>
-<li>Standardizes newline characters to \n</li>
+<li>Standardizes newline characters to \n(In Windows \r\n)</li>
 </ul>
 
 
@@ -133,13 +133,13 @@
 <code>$this->input->post('some_data', TRUE);</code>
 
 <p>To return an array of all POST items call without any parameters.</p>
-<p>To return all POST items and pass them through the XSS filter leave the first parameter blank while setting the second parameter to boolean;</p>
+<p>To return all POST items and pass them through the XSS filter set the first parameter NULL while setting the second parameter to boolean;</p>
 <p>The function returns FALSE (boolean) if there are no items in the POST.</p>
 
 <code>
-	$this->input->post(); // returns all POST items with XSS filter
+	$this->input->post(NULL, TRUE); // returns all POST items with XSS filter
 	<br />
-	$this->input->post(NULL, FALSE); // returns all POST items without XSS
+	$this->input->post(); // returns all POST items without XSS filter
 </code>
 
 <h2>$this->input->get()</h2>
@@ -149,13 +149,13 @@
 <code>$this->input->get('some_data', TRUE);</code>
 
 <p>To return an array of all GET items call without any parameters.</p>
-<p>To return all GET items and pass them through the XSS filter leave the first parameter blank while setting the second parameter to boolean;</p>
+<p>To return all GET items and pass them through the XSS filter set the first parameter NULL while setting the second parameter to boolean;</p>
 <p>The function returns FALSE (boolean) if there are no items in the GET.</p>
 
 <code>
-	$this->input->get(); // returns all GET items with XSS filter
+	$this->input->get(NULL, TRUE); // returns all GET items with XSS filter
 	<br />
-	$this->input->get(NULL, FALSE); // returns all GET items without XSS filtering
+	$this->input->get(); // returns all GET items without XSS filtering
 </code>
 
 <h2>$this->input->get_post()</h2>
diff --git a/user_guide/libraries/security.html b/user_guide/libraries/security.html
index dd62a43..cbe12d8 100644
--- a/user_guide/libraries/security.html
+++ b/user_guide/libraries/security.html
@@ -116,6 +116,9 @@
 
 <p>If you use the <a href="../helpers/form_helper.html">form helper</a> the <var>form_open()</var> function will automatically insert a hidden csrf field in your forms.</p>
 
+<p>Select URIs can be whitelisted from csrf protection (for example API endpoints expecting externally POSTed content). You can add these URIs by editing the 'csrf_exclude_uris' config parameter:</p>
+<code>$config['csrf_exclude_uris'] = array('api/person/add');</code>
+
 </div>
 <!-- END CONTENT -->
 
diff --git a/user_guide/overview/appflow.html b/user_guide/overview/appflow.html
index c6bf857..fbc68fa 100644
--- a/user_guide/overview/appflow.html
+++ b/user_guide/overview/appflow.html
@@ -60,7 +60,7 @@
 
 <p>The following graphic illustrates how data flows throughout the system:</p>
 
-<div><img src="../images/appflowchart.gif" width="697" height="205" border="0" alt="CodeIgniter application flow" /></div>
+<div><img src="../images/appflowchart.gif" width="769" height="212" alt="CodeIgniter application flow"></div>
 
 
 <ol>