Merge changes from develop
diff --git a/.gitignore b/.gitignore
index 11fb6d6..a035c2b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,5 @@
 user_guide_src/build/*
 user_guide_src/cilexer/build/*
 user_guide_src/cilexer/dist/*
-user_guide_src/cilexer/pycilexer.egg-info/*
\ No newline at end of file
+user_guide_src/cilexer/pycilexer.egg-info/*
+/vendor/
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 0a76229..1c7e876 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,11 @@
 php:
   - 5.3
   - 5.4
+  
+services:
+  - mysql
+  - postgresql
+  - sqlite
 
 env:
   - DB=mysql
@@ -24,4 +29,4 @@
 branches:
   only:
     - develop
-    - /^feature.+/
\ No newline at end of file
+    - /^feature.+/
diff --git a/application/config/autoload.php b/application/config/autoload.php
index b3e63cb..ff153fb 100644
--- a/application/config/autoload.php
+++ b/application/config/autoload.php
@@ -46,10 +46,11 @@
 |
 | 1. Packages
 | 2. Libraries
-| 3. Helper files
-| 4. Custom config files
-| 5. Language files
-| 6. Models
+| 3. Drivers
+| 4. Helper files
+| 5. Custom config files
+| 6. Language files
+| 7. Models
 |
 */
 
@@ -75,7 +76,7 @@
 |
 | Prototype:
 |
-|	$autoload['libraries'] = array('database', 'session', 'xmlrpc');
+|	$autoload['libraries'] = array('database', 'email', 'xmlrpc');
 */
 
 $autoload['libraries'] = array();
@@ -83,6 +84,22 @@
 
 /*
 | -------------------------------------------------------------------
+|  Auto-load Drivers
+| -------------------------------------------------------------------
+| These classes are located in the system/libraries folder or in your
+| application/libraries folder within their own subdirectory. They
+| offer multiple interchangeable driver options.
+|
+| Prototype:
+|
+|	$autoload['drivers'] = array('session', 'cache');
+*/
+
+$autoload['drivers'] = array();
+
+
+/*
+| -------------------------------------------------------------------
 |  Auto-load Helper Files
 | -------------------------------------------------------------------
 | Prototype:
@@ -139,4 +156,4 @@
 
 
 /* End of file autoload.php */
-/* Location: ./application/config/autoload.php */
\ No newline at end of file
+/* Location: ./application/config/autoload.php */
diff --git a/application/config/config.php b/application/config/config.php
index 28fc406..ab1508e 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -265,6 +265,9 @@
 | Session Variables
 |--------------------------------------------------------------------------
 |
+| 'sess_driver'				= the driver to load: cookie (Classic), native (PHP sessions),
+|	or your custom driver name
+| 'sess_valid_drivers'		= additional valid drivers which may be loaded
 | 'sess_cookie_name'		= the name you want for the cookie
 | 'sess_expiration'			= the number of SECONDS you want the session to last.
 |   by default sessions last 7200 seconds (two hours).  Set to zero for no expiration.
@@ -278,6 +281,8 @@
 | 'sess_time_to_update'		= how many seconds between CI refreshing Session Information
 |
 */
+$config['sess_driver']			= 'cookie';
+$config['sess_valid_drivers']	= array();
 $config['sess_cookie_name']		= 'ci_session';
 $config['sess_expiration']		= 7200;
 $config['sess_expire_on_close']	= FALSE;
@@ -401,15 +406,19 @@
 | Reverse Proxy IPs
 |--------------------------------------------------------------------------
 |
-| If your server is behind a reverse proxy, you must whitelist the proxy IP
-| addresses from which CodeIgniter should trust the HTTP_X_FORWARDED_FOR
-| header in order to properly identify the visitor's IP address.
-| Comma-delimited, e.g. '10.0.1.200,10.0.1.201'
+| If your server is behind a reverse proxy, you must whitelist the proxy
+| IP addresses from which CodeIgniter should trust headers such as
+| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify
+| the visitor's IP address.
 |
+| You can use both an array or a comma-separated list of proxy addresses,
+| as well as specifying whole subnets. Here are a few examples:
+|
+| Comma-separated:	'10.0.1.200,192.168.5.0/24'
+| Array:		array('10.0.1.200', '192.168.5.0/24')
 */
 $config['proxy_ips'] = '';
 
 
-
 /* End of file config.php */
-/* Location: ./application/config/config.php */
+/* Location: ./application/config/config.php */
\ No newline at end of file
diff --git a/application/config/constants.php b/application/config/constants.php
index d22d296..62a18e7 100644
--- a/application/config/constants.php
+++ b/application/config/constants.php
@@ -52,14 +52,14 @@
 |
 */
 
-define('FOPEN_READ',							'rb');
-define('FOPEN_READ_WRITE',						'r+b');
-define('FOPEN_WRITE_CREATE_DESTRUCTIVE',		'wb'); // truncates existing file data, use with care
-define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE',	'w+b'); // truncates existing file data, use with care
-define('FOPEN_WRITE_CREATE',					'ab');
-define('FOPEN_READ_WRITE_CREATE',				'a+b');
-define('FOPEN_WRITE_CREATE_STRICT',				'xb');
-define('FOPEN_READ_WRITE_CREATE_STRICT',		'x+b');
+define('FOPEN_READ', 'rb');
+define('FOPEN_READ_WRITE', 'r+b');
+define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care
+define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care
+define('FOPEN_WRITE_CREATE', 'ab');
+define('FOPEN_READ_WRITE_CREATE', 'a+b');
+define('FOPEN_WRITE_CREATE_STRICT', 'xb');
+define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b');
 
 /*
 |--------------------------------------------------------------------------
diff --git a/application/config/database.php b/application/config/database.php
index bb0d87b..4c5cad0 100644
--- a/application/config/database.php
+++ b/application/config/database.php
@@ -63,6 +63,7 @@
 | 				 Sites using Latin-1 or UTF-8 database character set and collation are unaffected.
 |	['swap_pre'] A default table prefix that should be swapped with the dbprefix
 |	['autoinit'] Whether or not to automatically initialize the database.
+|	['compress'] Whether or not to use client compression (only MySQL and MySQLi)
 |	['stricton'] TRUE/FALSE - forces 'Strict Mode' connections
 |							- good for ensuring strict SQL while developing
 |	['failover'] array - A array with 0 or more data for connections if the main should fail.
@@ -93,6 +94,7 @@
 	'dbcollat' => 'utf8_general_ci',
 	'swap_pre' => '',
 	'autoinit' => TRUE,
+	'compress' => TRUE,
 	'stricton' => FALSE,
 	'failover' => array()
 );
diff --git a/application/config/foreign_chars.php b/application/config/foreign_chars.php
index 41de123..6614caa 100644
--- a/application/config/foreign_chars.php
+++ b/application/config/foreign_chars.php
@@ -40,20 +40,20 @@
 	'/Ä/' => 'Ae',
 	'/Ü/' => 'Ue',
 	'/Ö/' => 'Oe',
-	'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|Α|Ά/' => 'A',
-	'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª|α|ά/' => 'a',
+	'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|Α|Ά|Ả|Ạ|Ầ|Ẫ|Ẩ|Ậ|Ằ|Ắ|Ẵ|Ẳ|Ặ/' => 'A',
+	'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª|α|ά|ả|ạ|ầ|ấ|ẫ|ẩ|ậ|ằ|ắ|ẵ|ẳ|ặ/' => 'a',
 	'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
 	'/ç|ć|ĉ|ċ|č/' => 'c',
 	'/Ð|Ď|Đ|Δ/' => 'Dj',
 	'/ð|ď|đ|δ/' => 'dj',
-	'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Ε|Έ/' => 'E',
-	'/è|é|ê|ë|ē|ĕ|ė|ę|ě|έ|ε/' => 'e',
+	'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Ε|Έ|Ẽ|Ẻ|Ẹ|Ề|Ế|Ễ|Ể|Ệ/' => 'E',
+	'/è|é|ê|ë|ē|ĕ|ė|ę|ě|έ|ε|ẽ|ẻ|ẹ|ề|ế|ễ|ể|ệ/' => 'e',
 	'/Ĝ|Ğ|Ġ|Ģ|Γ/' => 'G',
 	'/ĝ|ğ|ġ|ģ|γ/' => 'g',
 	'/Ĥ|Ħ/' => 'H',
 	'/ĥ|ħ/' => 'h',
-	'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|Η|Ή|Ί|Ι|Ϊ/' => 'I',
-	'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|η|ή|ί|ι|ϊ/' => 'i',
+	'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|Η|Ή|Ί|Ι|Ϊ|Ỉ|Ị/' => 'I',
+	'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|η|ή|ί|ι|ϊ|ỉ|ị/' => 'i',
 	'/Ĵ/' => 'J',
 	'/ĵ/' => 'j',
 	'/Ķ|Κ/' => 'K',
@@ -62,18 +62,18 @@
 	'/ĺ|ļ|ľ|ŀ|ł|λ/' => 'l',
 	'/Ñ|Ń|Ņ|Ň|Ν/' => 'N',
 	'/ñ|ń|ņ|ň|ʼn|ν/' => 'n',
-	'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|Ο|Ό|Ω|Ώ/' => 'O',
-	'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|ο|ό|ω|ώ/' => 'o',
+	'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|Ο|Ό|Ω|Ώ|Ỏ|Ọ|Ồ|Ố|Ỗ|Ổ|Ộ|Ờ|Ớ|Ỡ|Ở|Ợ/' => 'O',
+	'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|ο|ό|ω|ώ|ỏ|ọ|ồ|ố|ỗ|ổ|ộ|ờ|ớ|ỡ|ở|ợ/' => 'o',
 	'/Ŕ|Ŗ|Ř|Ρ/' => 'R',
 	'/ŕ|ŗ|ř|ρ/' => 'r',
 	'/Ś|Ŝ|Ş|Ș|Š|Σ/' => 'S',
 	'/ś|ŝ|ş|ș|š|ſ|σ|ς/' => 's',
 	'/Ț|Ţ|Ť|Ŧ|τ/' => 'T',
 	'/ț|ţ|ť|ŧ/' => 't',
-	'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
-	'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ/' => 'u',
-	'/Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ/' => 'Y',
-	'/ý|ÿ|ŷ/' => 'y',
+	'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|Ũ|Ủ|Ụ|Ừ|Ứ|Ữ|Ử|Ự/' => 'U',
+	'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ|ủ|ụ|ừ|ứ|ữ|ử|ự/' => 'u',
+	'/Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ|Ỳ|Ỹ|Ỷ|Ỵ/' => 'Y',
+	'/ý|ÿ|ŷ|ỳ|ỹ|ỷ|ỵ/' => 'y',
 	'/Ŵ/' => 'W',
 	'/ŵ/' => 'w',
 	'/Ź|Ż|Ž|Ζ/' => 'Z',
diff --git a/application/config/mimes.php b/application/config/mimes.php
index 4b1d6a8..90ffe61 100644
--- a/application/config/mimes.php
+++ b/application/config/mimes.php
@@ -37,7 +37,7 @@
 return 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'),
+	'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', 'text/plain'),
 	'bin'	=>	array('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'),
 	'dms'	=>	'application/octet-stream',
 	'lha'	=>	'application/octet-stream',
@@ -49,14 +49,14 @@
 	'sea'	=>	'application/octet-stream',
 	'dll'	=>	'application/octet-stream',
 	'oda'	=>	'application/oda',
-	'pdf'	=>	array('application/pdf', 'application/x-download'),
+	'pdf'	=>	array('application/pdf', 'application/x-download', 'binary/octet-stream'),
 	'ai'	=>	'application/postscript',
 	'eps'	=>	'application/postscript',
 	'ps'	=>	'application/postscript',
 	'smi'	=>	'application/smil',
 	'smil'	=>	'application/smil',
 	'mif'	=>	'application/vnd.mif',
-	'xls'	=>	array('application/excel', 'application/vnd.ms-excel', 'application/msexcel'),
+	'xls'	=>	array('application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/excel', 'application/download', 'application/vnd.ms-office', 'application/msword'),
 	'ppt'	=>	array('application/powerpoint', 'application/vnd.ms-powerpoint'),
 	'pptx'	=> 	'application/vnd.openxmlformats-officedocument.presentationml.presentation',
 	'wbxml'	=>	'application/wbxml',
@@ -123,8 +123,10 @@
 	'avi'	=>	array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
 	'movie'	=>	'video/x-sgi-movie',
 	'doc'	=>	array('application/msword', 'application/vnd.ms-office'),
-	'docx'	=>	array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'),
-	'xlsx'	=>	array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'),
+	'docx'	=>	array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword'),
+	'dot'	=>	array('application/msword', 'application/vnd.ms-office'),
+	'dotx'	=>	array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword'),
+	'xlsx'	=>	array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword'),
 	'word'	=>	array('application/msword', 'application/octet-stream'),
 	'xl'	=>	'application/excel',
 	'eml'	=>	'message/rfc822',
diff --git a/application/config/user_agents.php b/application/config/user_agents.php
index 416ef56..78e4c8c 100644
--- a/application/config/user_agents.php
+++ b/application/config/user_agents.php
@@ -36,6 +36,7 @@
 */
 
 $platforms = array(
+	'windows nt 6.2'	=> 'Windows 8',
 	'windows nt 6.1'	=> 'Windows 7',
 	'windows nt 6.0'	=> 'Windows Vista',
 	'windows nt 5.2'	=> 'Windows 2003',
@@ -146,6 +147,7 @@
 	'ipaq'			=> 'HP iPaq',
 	'mot-'			=> 'Motorola',
 	'playstation portable'	=> 'PlayStation Portable',
+	'playstation 3'		=> 'PlayStation 3',
 	'hiptop'		=> 'Danger Hiptop',
 	'nec-'			=> 'NEC',
 	'panasonic'		=> 'Panasonic',
@@ -155,10 +157,10 @@
 	'spv'			=> 'SPV',
 	'zte'			=> 'ZTE',
 	'sendo'			=> 'Sendo',
-	'dsi'			=> 'Nintendo DSi',
-	'ds'			=> 'Nintendo DS',
+	'nintendo dsi'	=> 'Nintendo DSi',
+	'nintendo ds'	=> 'Nintendo DS',
+	'nintendo 3ds'	=> 'Nintendo 3DS',
 	'wii'			=> 'Nintendo Wii',
-	'3ds'			=> 'Nintendo 3DS',
 	'open web'		=> 'Open Web',
 	'openweb'		=> 'OpenWeb',
 
diff --git a/application/views/errors/error_404.php b/application/views/errors/error_404.php
index c19bedf..fe48fd5 100644
--- a/application/views/errors/error_404.php
+++ b/application/views/errors/error_404.php
@@ -31,9 +31,9 @@
 <title>404 Page Not Found</title>
 <style type="text/css">
 
-::selection{ background-color: #E13300; color: white; }
-::moz-selection{ background-color: #E13300; color: white; }
-::webkit-selection{ background-color: #E13300; color: white; }
+::selection { background-color: #E13300; color: white; }
+::-moz-selection { background-color: #E13300; color: white; }
+::-webkit-selection { background-color: #E13300; color: white; }
 
 body {
 	background-color: #fff;
@@ -73,6 +73,8 @@
 	margin: 10px;
 	border: 1px solid #D0D0D0;
 	box-shadow: 0 0 8px #D0D0D0;
+	-moz-box-shadow: 0 0 8px #D0D0D0;
+	-webkit-box-shadow: 0 0 8px #D0D0D0;
 }
 
 p {
diff --git a/application/views/errors/error_db.php b/application/views/errors/error_db.php
index 3b244e0..76ca6df 100644
--- a/application/views/errors/error_db.php
+++ b/application/views/errors/error_db.php
@@ -31,9 +31,9 @@
 <title>Database Error</title>
 <style type="text/css">
 
-::selection{ background-color: #E13300; color: white; }
-::moz-selection{ background-color: #E13300; color: white; }
-::webkit-selection{ background-color: #E13300; color: white; }
+::selection { background-color: #E13300; color: white; }
+::-moz-selection { background-color: #E13300; color: white; }
+::-webkit-selection { background-color: #E13300; color: white; }
 
 body {
 	background-color: #fff;
@@ -73,6 +73,8 @@
 	margin: 10px;
 	border: 1px solid #D0D0D0;
 	box-shadow: 0 0 8px #D0D0D0;
+	-moz-box-shadow: 0 0 8px #D0D0D0;
+	-webkit-box-shadow: 0 0 8px #D0D0D0;
 }
 
 p {
diff --git a/application/views/errors/error_general.php b/application/views/errors/error_general.php
index c88afe1..e9baf16 100644
--- a/application/views/errors/error_general.php
+++ b/application/views/errors/error_general.php
@@ -31,9 +31,9 @@
 <title>Error</title>
 <style type="text/css">
 
-::selection{ background-color: #E13300; color: white; }
-::moz-selection{ background-color: #E13300; color: white; }
-::webkit-selection{ background-color: #E13300; color: white; }
+::selection { background-color: #E13300; color: white; }
+::-moz-selection { background-color: #E13300; color: white; }
+::-webkit-selection { background-color: #E13300; color: white; }
 
 body {
 	background-color: #fff;
@@ -73,6 +73,8 @@
 	margin: 10px;
 	border: 1px solid #D0D0D0;
 	box-shadow: 0 0 8px #D0D0D0;
+	-moz-box-shadow: 0 0 8px #D0D0D0;
+	-webkit-box-shadow: 0 0 8px #D0D0D0;
 }
 
 p {
diff --git a/application/views/welcome_message.php b/application/views/welcome_message.php
index 65f62a9..d227f82 100644
--- a/application/views/welcome_message.php
+++ b/application/views/welcome_message.php
@@ -32,9 +32,9 @@
 
 	<style type="text/css">
 
-	::selection{ background-color: #E13300; color: white; }
-	::moz-selection{ background-color: #E13300; color: white; }
-	::webkit-selection{ background-color: #E13300; color: white; }
+	::selection { background-color: #E13300; color: white; }
+	::-moz-selection { background-color: #E13300; color: white; }
+	::-webkit-selection { background-color: #E13300; color: white; }
 
 	body {
 		background-color: #fff;
@@ -70,11 +70,11 @@
 		padding: 12px 10px 12px 10px;
 	}
 
-	#body{
+	#body {
 		margin: 0 15px 0 15px;
 	}
 
-	p.footer{
+	p.footer {
 		text-align: right;
 		font-size: 11px;
 		border-top: 1px solid #D0D0D0;
@@ -83,9 +83,10 @@
 		margin: 20px 0 0 0;
 	}
 
-	#container{
+	#container {
 		margin: 10px;
 		border: 1px solid #D0D0D0;
+		-moz-box-shadow: 0 0 8px #D0D0D0;
 		-webkit-box-shadow: 0 0 8px #D0D0D0;
 	}
 	</style>
diff --git a/composer.json b/composer.json
index fa6dc02..dc098ac 100644
--- a/composer.json
+++ b/composer.json
@@ -1,5 +1,8 @@
 {
     "require": {
         "mikey179/vfsStream": "*"
-    }
+    },
+    "require-dev": {
+		"EHER/PHPUnit": "*"
+	}
 }
\ No newline at end of file
diff --git a/contributing.md b/contributing.md
new file mode 100644
index 0000000..f3f94fb
--- /dev/null
+++ b/contributing.md
@@ -0,0 +1,92 @@
+# Contributing to CodeIgniter
+
+
+CodeIgniter is a community driven project and accepts contributions of code and documentation from the community. These contributions are made in the form of Issues or [Pull Requests](http://help.github.com/send-pull-requests/) on the [EllisLab CodeIgniter repository](https://github.com/EllisLab/CodeIgniter>) on GitHub.
+
+Issues are a quick way to point out a bug. If you find a bug or documentation error in CodeIgniter then please check a few things first:
+
+1. There is not already an open Issue
+2. The issue has already been fixed (check the develop branch, or look for closed Issues)
+3. Is it something really obvious that you fix it yourself?
+
+Reporting issues is helpful but an even better approach is to send a Pull Request, which is done by "Forking" the main repository and committing to your own copy. This will require you to use the version control system called Git.
+
+## Guidelines
+
+Before we look into how, here are the guidelines. If your Pull Requests fail
+to pass these guidelines it will be declined and you will need to re-submit
+when you’ve made the changes. This might sound a bit tough, but it is required
+for us to maintain quality of the code-base.
+
+### PHP Style
+
+All code must meet the [Style Guide](http://codeigniter.com/user_guide/general/styleguide.html), which is
+essentially the [Allman indent style](http://en.wikipedia.org/wiki/Indent_style#Allman_style), underscores and readable operators. This makes certain that all code is the same format as the existing code and means it will be as readable as possible.
+
+### Documentation
+
+If you change anything that requires a change to documentation then you will need to add it. New classes, methods, parameters, changing default values, etc are all things that will require a change to documentation. The change-log must also be updated for every change. Also PHPDoc blocks must be maintained.
+
+### Compatibility
+
+CodeIgniter is compatible with PHP 5.2.4 so all code supplied must stick to
+this requirement. If PHP 5.3 or 5.4 functions or features are used then there
+must be a fallback for PHP 5.2.4.
+
+### Branching
+
+CodeIgniter uses the [Git-Flow](http://nvie.com/posts/a-successful-git-branching-model/) branching model which requires all pull requests to be sent to the "develop" branch. This is
+where the next planned version will be developed. The "master" branch will always contain the latest stable version and is kept clean so a "hotfix" (e.g: an emergency security patch) can be applied to master to create a new version, without worrying about other features holding it up. For this reason all commits need to be made to "develop" and any sent to "master" will be closed automatically. If you have multiple changes to submit, please place all changes into their own branch on your fork.
+
+One thing at a time: A pull request should only contain one change. That does not mean only one commit, but one change - however many commits it took. The reason for this is that if you change X and Y but send a pull request for both at the same time, we might really want X but disagree with Y, meaning we cannot merge the request. Using the Git-Flow branching model you can create new branches for both of these features and send two requests.
+
+### Signing
+
+You must sign your work, certifying that you either wrote the work or otherwise have the right to pass it on to an open source project. git makes this trivial as you merely have to use `--signoff` on your commits to your CodeIgniter fork.
+
+`git commit --signoff`
+
+or simply
+
+`git commit -s`
+
+This will sign your commits with the information setup in your git config, e.g.
+
+`Signed-off-by: John Q Public <john.public@example.com>`
+
+If you are using [Tower](http://www.git-tower.com/) there is a "Sign-Off" checkbox in the commit window. You could even alias git commit to use the `-s` flag so you don’t have to think about it.
+
+By signing your work in this manner, you certify to a "Developer's Certificate of Origin". The current version of this certificate is in the `DCO.txt` file in the root of this repository.
+
+
+## How-to Guide
+
+There are two ways to make changes, the easy way and the hard way. Either way you will need to [create a GitHub account](https://github.com/signup/free).
+
+Easy way GitHub allows in-line editing of files for making simple typo changes and quick-fixes. This is not the best way as you are unable to test the code works. If you do this you could be introducing syntax errors, etc, but for a Git-phobic user this is good for a quick-fix.
+
+Hard way The best way to contribute is to "clone" your fork of CodeIgniter to your development area. That sounds like some jargon, but "forking" on GitHub means "making a copy of that repo to your account" and "cloning" means "copying that code to your environment so you can work on it".
+
+1. Set up Git (Windows, Mac & Linux)
+2. Go to the CodeIgniter repo
+3. Fork it
+4. Clone your CodeIgniter repo: git@github.com:<your-name>/CodeIgniter.git
+5. Checkout the "develop" branch At this point you are ready to start making changes. 
+6. Fix existing bugs on the Issue tracker after taking a look to see nobody else is working on them.
+7. Commit the files
+8. Push your develop branch to your fork
+9. Send a pull request [http://help.github.com/send-pull-requests/](http://help.github.com/send-pull-requests/)
+
+The Reactor Engineers will now be alerted about the change and at least one of the team will respond. If your change fails to meet the guidelines it will be bounced, or feedback will be provided to help you improve it.
+
+Once the Reactor Engineer handling your pull request is happy with it they will post it to the internal EllisLab discussion area to be double checked by the other Engineers and EllisLab developers. If nobody has a problem with the change then it will be merged into develop and will be part of the next release. Keeping your fork up-to-date
+
+Unlike systems like Subversion, Git can have multiple remotes. A remote is the name for a URL of a Git repository. By default your fork will have a remote named "origin" which points to your fork, but you can add another remote named "codeigniter" which points to `git://github.com/EllisLab/CodeIgniter.git`. This is a read-only remote but you can pull from this develop branch to update your own.
+
+If you are using command-line you can do the following:
+
+1. `git remote add codeigniter git://github.com/EllisLab/CodeIgniter.git`
+2. `git pull codeigniter develop`
+3. `git push origin develop`
+
+Now your fork is up to date. This should be done regularly, or before you send a pull request at least.
\ No newline at end of file
diff --git a/readme.rst b/readme.rst
index b211ad7..8628645 100644
--- a/readme.rst
+++ b/readme.rst
@@ -38,166 +38,6 @@
 Please see the `installation section <http://codeigniter.com/user_guide/installation/index.html>`_
 of the CodeIgniter User Guide.
 
-************
-Contributing
-************
-
-CodeIgniter is a community driven project and accepts contributions of code
-and documentation from the community. These contributions are made in the form
-of Issues or `Pull Requests <http://help.github.com/send-pull-requests/>`_ on
-the `EllisLab CodeIgniter repository
-<https://github.com/EllisLab/CodeIgniter>`_ on GitHub.
-
-Issues are a quick way to point out a bug. If you find a bug or documentation
-error in CodeIgniter then please check a few things first:
-
-- There is not already an open Issue
-- The issue has already been fixed (check the develop branch, or look for
-  closed Issues)
-- Is it something really obvious that you fix it yourself?
-
-Reporting issues is helpful but an even better approach is to send a Pull
-Request, which is done by "Forking" the main repository and committing to your
-own copy. This will require you to use the version control system called Git.
-
-**********
-Guidelines
-**********
-
-Before we look into how, here are the guidelines. If your Pull Requests fail
-to pass these guidelines it will be declined and you will need to re-submit
-when you’ve made the changes. This might sound a bit tough, but it is required
-for us to maintain quality of the code-base.
-
-PHP Style
-=========
-
-All code must meet the `Style Guide
-<http://codeigniter.com/user_guide/general/styleguide.html>`_, which is
-essentially the `Allman indent style
-<http://en.wikipedia.org/wiki/Indent_style#Allman_style>`_, underscores and
-readable operators. This makes certain that all code is the same format as the
-existing code and means it will be as readable as possible.
-
-Documentation
-=============
-
-If you change anything that requires a change to documentation then you will
-need to add it. New classes, methods, parameters, changing default values, etc
-are all things that will require a change to documentation. The change-log
-must also be updated for every change. Also PHPDoc blocks must be maintained.
-
-Compatibility
-=============
-
-CodeIgniter is compatible with PHP 5.2.4 so all code supplied must stick to
-this requirement. If PHP 5.3 or 5.4 functions or features are used then there
-must be a fallback for PHP 5.2.4.
-
-Branching
-=========
-
-CodeIgniter uses the `Git-Flow
-<http://nvie.com/posts/a-successful-git-branching-model/>`_ branching model
-which requires all pull requests to be sent to the "develop" branch. This is
-where the next planned version will be developed. The "master" branch will
-always contain the latest stable version and is kept clean so a "hotfix" (e.g:
-an emergency security patch) can be applied to master to create a new version,
-without worrying about other features holding it up. For this reason all
-commits need to be made to "develop" and any sent to "master" will be closed
-automatically. If you have multiple changes to submit, please place all
-changes into their own branch on your fork.
-
-One thing at a time: A pull request should only contain one change. That does
-not mean only one commit, but one change - however many commits it took. The
-reason for this is that if you change X and Y but send a pull request for both
-at the same time, we might really want X but disagree with Y, meaning we
-cannot merge the request. Using the Git-Flow branching model you can create
-new branches for both of these features and send two requests.
-
-Signing
-=======
-You must sign your work, certifying that you either wrote the work or
-otherwise have the right to pass it on to an open source project. git makes
-this trivial as you merely have to use `--signoff` on your commits to your
-CodeIgniter fork.
-
-::
-
-	git commit --signoff
-
-or simply::
-
-	git commit -s
-
-This will sign your commits with the information setup in your git config, e.g.
-
-	Signed-off-by: John Q Public <john.public@example.com>
-
-If you are using Tower there is a "Sign-Off" checkbox in the commit window. You 
-could even alias git commit to use the -s flag so you don’t have to think about 
-it.
-
-By signing your work in this manner, you certify to a "Developer's Certificate 
-or Origin". The current version of this certificate is in the `DCO.txt` file
-in the root of this repository.
-
-
-************
-How-to Guide
-************
-
-There are two ways to make changes, the easy way and the hard way. Either way
-you will need to `create a GitHub account <https://github.com/signup/free>`_.
-
-Easy way GitHub allows in-line editing of files for making simple typo changes
-and quick-fixes. This is not the best way as you are unable to test the code
-works. If you do this you could be introducing syntax errors, etc, but for a
-Git-phobic user this is good for a quick-fix.
-
-Hard way The best way to contribute is to "clone" your fork of CodeIgniter to
-your development area. That sounds like some jargon, but "forking" on GitHub
-means "making a copy of that repo to your account" and "cloning" means
-"copying that code to your environment so you can work on it".
-
-#. Set up Git (Windows, Mac & Linux)
-#. Go to the CodeIgniter repo
-#. Fork it
-#. Clone your CodeIgniter repo: git@github.com:<your-name>/CodeIgniter.git
-#. Checkout the "develop" branch At this point you are ready to start making
-   changes. 
-#. Fix existing bugs on the Issue tracker after taking a look to see nobody
-   else is working on them.
-#. Commit the files
-#. Push your develop branch to your fork
-#. Send a pull request http://help.github.com/send-pull-requests/
-
-The Reactor Engineers will now be alerted about the change and at least one of
-the team will respond. If your change fails to meet the guidelines it will be
-bounced, or feedback will be provided to help you improve it.
-
-Once the Reactor Engineer handling your pull request is happy with it they
-will post it to the internal EllisLab discussion area to be double checked by
-the other Engineers and EllisLab developers. If nobody has a problem with the
-change then it will be merged into develop and will be part of the next
-release. Keeping your fork up-to-date
-
-Unlike systems like Subversion, Git can have multiple remotes. A remote is the
-name for a URL of a Git repository. By default your fork will have a remote
-named "origin" which points to your fork, but you can add another remote named
-"codeigniter" which points to git://github.com/EllisLab/CodeIgniter.git. This
-is a read-only remote but you can pull from this develop branch to update your
-own.
-
-If you are using command-line you can do the following:
-
-#. git remote add codeigniter git://github.com/EllisLab/CodeIgniter.git
-#. git pull codeigniter develop
-#. git push origin develop
-
-Now your fork is up to date. This should be done regularly, or before you send
-a pull request at least.
-
 *******
 License
 *******
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
index 8159b19..f3592ea 100644
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -50,7 +50,7 @@
  *  Load the global functions
  * ------------------------------------------------------
  */
-	require(BASEPATH.'core/Common.php');
+	require_once(BASEPATH.'core/Common.php');
 
 /*
  * ------------------------------------------------------
diff --git a/system/core/Common.php b/system/core/Common.php
index 06b1622..341402c 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -150,7 +150,7 @@
 
 				if (class_exists($name) === FALSE)
 				{
-					require($path.$directory.'/'.$class.'.php');
+					require_once($path.$directory.'/'.$class.'.php');
 				}
 
 				break;
@@ -164,7 +164,7 @@
 
 			if (class_exists($name) === FALSE)
 			{
-				require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php');
+				require_once(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php');
 			}
 		}
 
@@ -488,13 +488,9 @@
 		{
 			header('Status: '.$code.' '.$text, TRUE);
 		}
-		elseif ($server_protocol === 'HTTP/1.0')
-		{
-			header('HTTP/1.0 '.$code.' '.$text, TRUE, $code);
-		}
 		else
 		{
-			header('HTTP/1.1 '.$code.' '.$text, TRUE, $code);
+			header(($server_protocol ? $server_protocol : 'HTTP/1.1').' '.$code.' '.$text, TRUE, $code);
 		}
 	}
 }
@@ -524,20 +520,19 @@
 	{
 		$_error =& load_class('Exceptions', 'core');
 
-		// Should we display the error? We'll get the current error_reporting
+		// Should we ignore the error? We'll get the current error_reporting
 		// level and add its bits with the severity bits to find out.
-		// And respect display_errors
-		if (($severity & error_reporting()) === $severity && (bool) ini_get('display_errors') === TRUE)
-		{
-			$_error->show_php_error($severity, $message, $filepath, $line);
-		}
-
-		// Should we log the error? No? We're done...
-		if (config_item('log_threshold') === 0)
+		if (($severity & error_reporting()) !== $severity)
 		{
 			return;
 		}
 
+		// Should we display the error?
+		if ((bool) ini_get('display_errors') === TRUE)
+		{
+			$_error->show_php_error($severity, $message, $filepath, $line);
+		}
+
 		$_error->log_exception($severity, $message, $filepath, $line);
 	}
 }
@@ -598,5 +593,44 @@
 	}
 }
 
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('_stringify_attributes'))
+{
+	/**
+	 * Stringify attributes for use in HTML tags.
+	 *
+	 * Helper function used to convert a string, array, or object
+	 * of attributes to a string.
+	 *
+	 * @param	mixed	string, array, object
+	 * @param	bool
+	 * @return	string
+	 */
+	function _stringify_attributes($attributes, $js = FALSE)
+	{
+		$atts = NULL;
+
+		if (empty($attributes))
+		{
+			return $atts;
+		}
+
+		if (is_string($attributes))
+		{
+			return ' '.$attributes;
+		}
+
+		$attributes = (array) $attributes;
+
+		foreach ($attributes as $key => $val)
+		{
+			$atts .= ($js) ? $key.'='.$val.',' : ' '.$key.'="'.$val.'"';
+		}
+
+		return rtrim($atts, ',');
+	}
+}
+
 /* 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
index 2f6a9e0..8e4f998 100644
--- a/system/core/Config.php
+++ b/system/core/Config.php
@@ -102,7 +102,7 @@
 	{
 		$file = ($file === '') ? 'config' : str_replace('.php', '', $file);
 		$found = $loaded = FALSE;
-		
+
 		$check_locations = defined('ENVIRONMENT')
 			? array(ENVIRONMENT.'/'.$file, $file)
 			: array($file);
diff --git a/system/core/Input.php b/system/core/Input.php
index 162e40c..82482f2 100644
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -328,39 +328,113 @@
 			return $this->ip_address;
 		}
 
-		if (config_item('proxy_ips') != '' && $this->server('HTTP_X_FORWARDED_FOR') && $this->server('REMOTE_ADDR'))
+		$proxy_ips = config_item('proxy_ips');
+		if ( ! empty($proxy_ips) && ! is_array($proxy_ips))
 		{
-			$proxies = preg_split('/[\s,]/', config_item('proxy_ips'), -1, PREG_SPLIT_NO_EMPTY);
-			$proxies = is_array($proxies) ? $proxies : array($proxies);
-
-			$this->ip_address = in_array($_SERVER['REMOTE_ADDR'], $proxies) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
-		}
-		elseif ( ! $this->server('HTTP_CLIENT_IP') && $this->server('REMOTE_ADDR'))
-		{
-			$this->ip_address = $_SERVER['REMOTE_ADDR'];
-		}
-		elseif ($this->server('REMOTE_ADDR') && $this->server('HTTP_CLIENT_IP'))
-		{
-			$this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
-		}
-		elseif ($this->server('HTTP_CLIENT_IP'))
-		{
-			$this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
-		}
-		elseif ($this->server('HTTP_X_FORWARDED_FOR'))
-		{
-			$this->ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
+			$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
 		}
 
-		if ($this->ip_address === FALSE)
-		{
-			return $this->ip_address = '0.0.0.0';
-		}
+		$this->ip_address = $this->server('REMOTE_ADDR');
 
-		if (strpos($this->ip_address, ',') !== FALSE)
+		if ($proxy_ips)
 		{
-			$x = explode(',', $this->ip_address);
-			$this->ip_address = trim(end($x));
+			foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)
+			{
+				if (($spoof = $this->server($header)) !== NULL)
+				{
+					// Some proxies typically list the whole chain of IP
+					// addresses through which the client has reached us.
+					// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
+					if (strpos($spoof, ',') !== FALSE)
+					{
+						$spoof = explode(',', $spoof, 2);
+						$spoof = $spoof[0];
+					}
+
+					if ( ! $this->valid_ip($spoof))
+					{
+						$spoof = NULL;
+					}
+					else
+					{
+						break;
+					}
+				}
+			}
+
+			if ($spoof)
+			{
+				for ($i = 0, $c = count($proxy_ips); $i < $c; $i++)
+				{
+					// Check if we have an IP address or a subnet
+					if (strpos($proxy_ips[$i], '/') === FALSE)
+					{
+						// An IP address (and not a subnet) is specified.
+						// We can compare right away.
+						if ($proxy_ips[$i] === $this->ip_address)
+						{
+							$this->ip_address = $spoof;
+							break;
+						}
+
+						continue;
+					}
+
+					// We have a subnet ... now the heavy lifting begins
+					isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.';
+
+					// If the proxy entry doesn't match the IP protocol - skip it
+					if (strpos($proxy_ips[$i], $separator) === FALSE)
+					{
+						continue;
+					}
+
+					// Convert the REMOTE_ADDR IP address to binary, if needed
+					if ( ! isset($ip, $convert_func))
+					{
+						if ($separator === ':')
+						{
+							// Make sure we're have the "full" IPv6 format
+							$ip = str_replace('::', str_repeat(':', 9 - substr_count($this->ip_address, ':')), $this->ip_address);
+							$convert_func = is_php('5.3')
+								? function ($value)
+									{
+										return str_pad(base_convert($value, 16, 2), 16, '0', STR_PAD_LEFT);
+									}
+								: create_function('$value', 'return str_pad(base_convert($value, 16, 2), 16, "0", STR_PAD_LEFT);');
+						}
+						else
+						{
+							$ip = $this->ip_address;
+							$convert_func = is_php('5.3')
+								? function ($value)
+									{
+										return str_pad(decbin($value), 8, '0', STR_PAD_LEFT);
+									}
+								: create_function('$value', 'return str_pad(decbin($value), 8, "0", STR_PAD_LEFT);');
+						}
+
+						$ip = implode(array_map($convert_func, explode($separator, $ip)));
+					}
+
+					// Split the netmask length off the network address
+					list($netaddr, $masklen) = explode('/', $proxy_ips[$i], 2);
+
+					// Again, an IPv6 address is most likely in a compressed form
+					if ($separator === ':')
+					{
+						$netaddr = str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr);
+					}
+
+					// Convert to a binary form and finally compare
+					$netaddr = implode(array_map($convert_func, explode($separator, $netaddr)));
+					if (strncmp($ip, $netaddr, $masklen) === 0)
+					{
+						$this->ip_address = $spoof;
+						break;
+					}
+				}
+			}
 		}
 
 		if ( ! $this->valid_ip($this->ip_address))
@@ -518,7 +592,7 @@
 		$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
 
 		// CSRF Protection check
-		if ($this->_enable_csrf === TRUE)
+		if ($this->_enable_csrf === TRUE && ! $this->is_cli_request())
 		{
 			$this->security->csrf_verify();
 		}
diff --git a/system/core/Loader.php b/system/core/Loader.php
index 0bc6e84..75e9360 100644
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -409,8 +409,8 @@
 	 * 1. The name of the "view" file to be included.
 	 * 2. An associative array of data to be extracted for use in the view.
 	 * 3. TRUE/FALSE - whether to return the data or load it. In
-	 *    some cases it's advantageous to be able to return data so that
-	 *    a developer can process it in some way.
+	 *	some cases it's advantageous to be able to return data so that
+	 *	a developer can process it in some way.
 	 *
 	 * @param	string
 	 * @param	array
@@ -633,13 +633,7 @@
 			{
 				$this->driver($driver);
 			}
-			return FALSE;
-		}
-
-		if ( ! class_exists('CI_Driver_Library'))
-		{
-			// we aren't instantiating an object here, that'll be done by the Library itself
-			require BASEPATH.'libraries/Driver.php';
+			return;
 		}
 
 		if ($library === '')
@@ -785,11 +779,11 @@
 			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
 			$_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
 
-			foreach ($this->_ci_view_paths as $view_file => $cascade)
+			foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
 			{
-				if (file_exists($view_file.$_ci_file))
+				if (file_exists($_ci_view_file.$_ci_file))
 				{
-					$_ci_path = $view_file.$_ci_file;
+					$_ci_path = $_ci_view_file.$_ci_file;
 					$file_exists = TRUE;
 					break;
 				}
@@ -837,10 +831,10 @@
 		 * We buffer the output for two reasons:
 		 * 1. Speed. You get a significant speed boost.
 		 * 2. So that the final rendered template can be post-processed by
-		 *    the output class. Why do we need post processing? For one thing,
-		 *    in order to show the elapsed page load time. Unless we can
-		 *    intercept the content right before it's sent to the browser and
-		 *    then stop the timer it won't be accurate.
+		 *	the output class. Why do we need post processing? For one thing,
+		 *	in order to show the elapsed page load time. Unless we can
+		 *	intercept the content right before it's sent to the browser and
+		 *	then stop the timer it won't be accurate.
 		 */
 		ob_start();
 
@@ -915,6 +909,13 @@
 
 			// Get the filename from the path
 			$class = substr($class, $last_slash);
+
+			// Check for match and driver base class
+			if (strtolower(trim($subdir, '/')) == strtolower($class) && ! class_exists('CI_Driver_Library'))
+			{
+				// We aren't instantiating an object here, just making the base class available
+				require BASEPATH.'libraries/Driver.php';
+			}
 		}
 
 		// We'll test for both lowercase and capitalized versions of the file name
@@ -996,14 +997,19 @@
 				$this->_ci_loaded_files[] = $filepath;
 				return $this->_ci_init_class($class, '', $params, $object_name);
 			}
-
 		} // END FOREACH
 
 		// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
 		if ($subdir === '')
 		{
 			$path = strtolower($class).'/'.$class;
-			return $this->_ci_load_class($path, $params);
+			return $this->_ci_load_class($path, $params, $object_name);
+		}
+		else if (ucfirst($subdir) != $subdir)
+		{
+			// Lowercase subdir failed - retry capitalized
+			$path = ucfirst($subdir).$class;
+			return $this->_ci_load_class($path, $params, $object_name);
 		}
 
 		// If we got this far we were unable to find the requested class.
@@ -1193,6 +1199,15 @@
 			}
 		}
 
+		// Autoload drivers
+		if (isset($autoload['drivers']))
+		{
+			foreach ($autoload['drivers'] as $item)
+			{
+				$this->driver($item);
+			}
+		}
+
 		// Autoload models
 		if (isset($autoload['model']))
 		{
diff --git a/system/core/Output.php b/system/core/Output.php
index 5ec8c4b..052367e 100644
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -552,13 +552,13 @@
 		fclose($fp);
 
 		// Strip out the embedded timestamp
-		if ( ! preg_match('/(\d+TS--->)/', $cache, $match))
+		if ( ! preg_match('/^(\d+)TS--->/', $cache, $match))
 		{
 			return FALSE;
 		}
 
 		$last_modified = filemtime($cache_path);
-		$expire = trim(str_replace('TS--->', '', $match[1]));
+		$expire = $match[1];
 
 		// Has the file expired?
 		if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
@@ -575,7 +575,7 @@
 		}
 
 		// Display the cache
-		$this->_display(str_replace($match[0], '', $cache));
+		$this->_display(substr($cache, strlen($match[0])));
 		log_message('debug', 'Cache file is current. Sending it to browser.');
 		return TRUE;
 	}
diff --git a/system/core/Utf8.php b/system/core/Utf8.php
index 0a7ec50..1ff0298 100644
--- a/system/core/Utf8.php
+++ b/system/core/Utf8.php
@@ -49,30 +49,31 @@
 	{
 		log_message('debug', 'Utf8 Class Initialized');
 
-		global $CFG;
+		$charset = strtoupper(config_item('charset'));
+
+		// set internal encoding for multibyte string functions if necessary
+		// and set a flag so we don't have to repeatedly use extension_loaded()
+		// or function_exists()
+		if (extension_loaded('mbstring'))
+		{
+			define('MB_ENABLED', TRUE);
+			mb_internal_encoding($charset);
+		}
+		else
+		{
+			define('MB_ENABLED', FALSE);
+		}
+
 
 		if (
-			@preg_match('/./u', 'é') === 1		// PCRE must support UTF-8
-			&& function_exists('iconv')			// iconv must be installed
-			&& (bool) @ini_get('mbstring.func_overload') !== TRUE	// Multibyte string function overloading cannot be enabled
-			&& $CFG->item('charset') === 'UTF-8'		// Application charset must be UTF-8
+			@preg_match('/./u', 'é') === 1	// PCRE must support UTF-8
+			&& function_exists('iconv')	// iconv must be installed
+			&& MB_ENABLED === TRUE		// mbstring must be enabled
+			&& $charset === 'UTF-8'		// Application charset must be UTF-8
 			)
 		{
 			define('UTF8_ENABLED', TRUE);
 			log_message('debug', 'UTF-8 Support Enabled');
-
-			// set internal encoding for multibyte string functions if necessary
-			// and set a flag so we don't have to repeatedly use extension_loaded()
-			// or function_exists()
-			if (extension_loaded('mbstring'))
-			{
-				define('MB_ENABLED', TRUE);
-				mb_internal_encoding('UTF-8');
-			}
-			else
-			{
-				define('MB_ENABLED', FALSE);
-			}
 		}
 		else
 		{
@@ -135,7 +136,7 @@
 		{
 			return @iconv($encoding, 'UTF-8', $str);
 		}
-		elseif (function_exists('mb_convert_encoding'))
+		elseif (MB_ENABLED === TRUE)
 		{
 			return @mb_convert_encoding($str, 'UTF-8', $encoding);
 		}
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index f848cfe..ea56d38 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -51,6 +51,7 @@
 	public $char_set		= 'utf8';
 	public $dbcollat		= 'utf8_general_ci';
 	public $autoinit		= TRUE; // Whether to automatically initialize the DB
+	public $compress		= TRUE;
 	public $swap_pre		= '';
 	public $port			= '';
 	public $pconnect		= FALSE;
@@ -78,6 +79,10 @@
 	protected $_protect_identifiers		= TRUE;
 	protected $_reserved_identifiers	= array('*'); // Identifiers that should NOT be escaped
 
+	// clause and character used for LIKE escape sequences
+	protected $_like_escape_str = " ESCAPE '%s' ";
+	protected $_like_escape_chr = '!';
+
 	/**
 	 * The syntax to count rows is slightly different across different
 	 * database engines, so this string appears in each driver and is
@@ -669,7 +674,7 @@
 	 */
 	public function is_write_type($sql)
 	{
-		return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s+/i', $sql);
+		return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s+/i', $sql);
 	}
 
 	// --------------------------------------------------------------------
@@ -1355,7 +1360,7 @@
 		}
 		else
 		{
-			$message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error;
+			$message = is_array($error) ? $error : array(str_replace('%s', $swap, $LANG->line($error)));
 		}
 
 		// Find the most likely culprit of the error by going through
@@ -1364,7 +1369,13 @@
 		$trace = debug_backtrace();
 		foreach ($trace as $call)
 		{
-			if (isset($call['file']) && strpos($call['file'], BASEPATH.'database') === FALSE)
+			// We'll need this on Windows, as APPPATH and BASEPATH will always use forward slashes
+			if (DIRECTORY_SEPARATOR !== '/')
+			{
+				$call['file'] = str_replace('\\', '/', $call['file']);
+			}
+
+			if (isset($call['file'], $call['class']) && strpos($call['file'], BASEPATH.'database') === FALSE && strpos($call['class'], 'Loader') !== FALSE)
 			{
 				// Found it - use a relative path for safety
 				$message[] = 'Filename: '.str_replace(array(APPPATH, BASEPATH), '', $call['file']);
diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php
index 416132e..139f467 100644
--- a/system/database/DB_query_builder.php
+++ b/system/database/DB_query_builder.php
@@ -1483,19 +1483,18 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
+	 * FROM tables
 	 *
-	 * This public function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
+	 * Groups tables in FROM clauses if needed, so there is no confusion
+	 * about operator precedence.
 	 *
-	 * @param       array
-	 * @return      string
+	 * Note: This is only used (and overriden) by MySQL and CUBRID.
+	 *
+	 * @return	string
 	 */
-	protected function _from_tables($tables)
+	protected function _from_tables()
 	{
-		is_array($tables) OR $tables = array($tables);
-
-		return (count($tables) === 1) ? $tables[0] : '('.implode(', ', $tables).')';
+		return implode(', ', $this->qb_from);
 	}
 
 	// --------------------------------------------------------------------
@@ -2010,7 +2009,7 @@
 		// Write the "FROM" portion of the query
 		if (count($this->qb_from) > 0)
 		{
-			$sql .= "\nFROM ".$this->_from_tables($this->qb_from);
+			$sql .= "\nFROM ".$this->_from_tables();
 		}
 
 		// Write the "JOIN" portion of the query
diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php
index e243aae..7f8f297 100644
--- a/system/database/drivers/cubrid/cubrid_driver.php
+++ b/system/database/drivers/cubrid/cubrid_driver.php
@@ -45,10 +45,6 @@
 	// The character used for escaping - no need in CUBRID
 	protected $_escape_char = '`';
 
-	// clause and character used for LIKE escape sequences - not used in CUBRID
-	protected $_like_escape_str = '';
-	protected $_like_escape_chr = '';
-
 	protected $_random_keyword = ' RAND()'; // database specific random keyword
 
 	// CUBRID-specific properties
@@ -72,6 +68,8 @@
 		}
 	}
 
+	// --------------------------------------------------------------------
+
 	/**
 	 * Non-persistent database connection
 	 *
@@ -431,6 +429,26 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * FROM tables
+	 *
+	 * Groups tables in FROM clauses if needed, so there is no confusion
+	 * about operator precedence.
+	 *
+	 * @return	string
+	 */
+	protected function _from_tables()
+	{
+		if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
+		{
+			return '('.implode(', ', $this->qb_from).')';
+		}
+
+		return implode(', ', $this->qb_from);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Close DB Connection
 	 *
 	 * @return	void
diff --git a/system/database/drivers/ibase/ibase_driver.php b/system/database/drivers/ibase/ibase_driver.php
index 7b37b99..96d6f65 100644
--- a/system/database/drivers/ibase/ibase_driver.php
+++ b/system/database/drivers/ibase/ibase_driver.php
@@ -45,10 +45,6 @@
 	// The character used to escape with
 	protected $_escape_char = '"';
 
-	// clause and character used for LIKE escape sequences
-	protected $_like_escape_str = " ESCAPE '%s' ";
-	protected $_like_escape_chr = '!';
-
 	protected $_random_keyword = ' Random()'; // database specific random keyword
 
 	// Keeps track of the resource for the current transaction
@@ -309,22 +305,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This public function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php
index edc6a84..4369bbe 100644
--- a/system/database/drivers/mssql/mssql_driver.php
+++ b/system/database/drivers/mssql/mssql_driver.php
@@ -45,10 +45,6 @@
 	// The character used for escaping
 	protected $_escape_char = '"';
 
-	// clause and character used for LIKE escape sequences
-	protected $_like_escape_str = " ESCAPE '%s' ";
-	protected $_like_escape_chr = '!';
-
 	protected $_random_keyword = ' NEWID()';
 
 	// MSSQL-specific properties
@@ -366,22 +362,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php
index 0a15fe4..60bcf3c 100644
--- a/system/database/drivers/mysql/mysql_driver.php
+++ b/system/database/drivers/mysql/mysql_driver.php
@@ -45,10 +45,6 @@
 	// The character used for escaping
 	protected $_escape_char = '`';
 
-	// clause and character used for LIKE escape sequences - not used in MySQL
-	protected $_like_escape_str = '';
-	protected $_like_escape_chr = '\\';
-
 	protected $_random_keyword = ' RAND()'; // database specific random keyword
 
 	/**
@@ -83,7 +79,14 @@
 	 */
 	public function db_connect()
 	{
-		return @mysql_connect($this->hostname, $this->username, $this->password, TRUE);
+		if ($this->compress === TRUE)
+		{
+			return @mysql_connect($this->hostname, $this->username, $this->password, TRUE, MYSQL_CLIENT_COMPRESS);
+		}
+		else
+		{
+			return @mysql_connect($this->hostname, $this->username, $this->password, TRUE);
+		}
 	}
 
 	// --------------------------------------------------------------------
@@ -95,7 +98,14 @@
 	 */
 	public function db_pconnect()
 	{
-		return @mysql_pconnect($this->hostname, $this->username, $this->password);
+		if ($this->compress === TRUE)
+		{
+			return @mysql_pconnect($this->hostname, $this->username, $this->password, MYSQL_CLIENT_COMPRESS);
+		}
+		else
+		{
+			return @mysql_pconnect($this->hostname, $this->username, $this->password);
+		}
 	}
 
 	// --------------------------------------------------------------------
@@ -455,6 +465,26 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * FROM tables
+	 *
+	 * Groups tables in FROM clauses if needed, so there is no confusion
+	 * about operator precedence.
+	 *
+	 * @return	string
+	 */
+	protected function _from_tables()
+	{
+		if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
+		{
+			return '('.implode(', ', $this->qb_from).')';
+		}
+
+		return implode(', ', $this->qb_from);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Close DB Connection
 	 *
 	 * @return	void
diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php
index 5f5a31d..934694c 100644
--- a/system/database/drivers/mysqli/mysqli_driver.php
+++ b/system/database/drivers/mysqli/mysqli_driver.php
@@ -45,10 +45,6 @@
 	// The character used for escaping
 	protected $_escape_char = '`';
 
-	// clause and character used for LIKE escape sequences - not used in MySQL
-	protected $_like_escape_str = '';
-	protected $_like_escape_chr = '\\';
-
 	protected $_random_keyword = ' RAND()'; // database specific random keyword
 
 	/**
@@ -65,6 +61,17 @@
 	 */
 	public function db_connect()
 	{
+		// Use MySQL client compression?
+		if ($this->compress === TRUE)
+		{
+			$port = empty($this->port) ? NULL : $this->port;
+
+			$mysqli = new mysqli();
+			@$mysqli->real_connect($this->hostname, $this->username, $this->password, $this->database, $port, NULL, MYSQLI_CLIENT_COMPRESS);
+
+			return $mysqli;
+		}
+
 		return empty($this->port)
 			? @new mysqli($this->hostname, $this->username, $this->password, $this->database)
 			: @new mysqli($this->hostname, $this->username, $this->password, $this->database, $this->port);
@@ -85,6 +92,17 @@
 			return $this->db_connect();
 		}
 
+		// Use MySQL client compression?
+		if ($this->compress === TRUE)
+		{
+			$port = empty($this->port) ? NULL : $this->port;
+
+			$mysqli = mysqli_init();
+			$mysqli->real_connect('p:'.$this->hostname, $this->username, $this->password, $this->database, $port, NULL, MYSQLI_CLIENT_COMPRESS);
+
+			return $mysqli;
+		}
+
 		return empty($this->port)
 			? @new mysqli('p:'.$this->hostname, $this->username, $this->password, $this->database)
 			: @new mysqli('p:'.$this->hostname, $this->username, $this->password, $this->database, $this->port);
@@ -400,6 +418,14 @@
 	 */
 	public function error()
 	{
+		if ( ! empty($this->conn_id->connect_errno))
+		{
+			return array(
+				'code' => $this->conn_id->connect_errno,
+				'message' => is_php('5.2.9') ? $this->conn_id->connect_error : mysqli_connect_error()
+			);
+		}
+
 		return array('code' => $this->conn_id->errno, 'message' => $this->conn_id->error);
 	}
 
@@ -447,6 +473,26 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * FROM tables
+	 *
+	 * Groups tables in FROM clauses if needed, so there is no confusion
+	 * about operator precedence.
+	 *
+	 * @return	string
+	 */
+	protected function _from_tables()
+	{
+		if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
+		{
+			return '('.implode(', ', $this->qb_from).')';
+		}
+
+		return implode(', ', $this->qb_from);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Close DB Connection
 	 *
 	 * @return	void
diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php
index dcc4652..8e4f4ef 100644
--- a/system/database/drivers/oci8/oci8_driver.php
+++ b/system/database/drivers/oci8/oci8_driver.php
@@ -54,10 +54,6 @@
 	// The character used for excaping
 	protected $_escape_char = '"';
 
-	// clause and character used for LIKE escape sequences
-	protected $_like_escape_str = " ESCAPE '%s' ";
-	protected $_like_escape_chr = '!';
-
 	/**
 	 * The syntax to count rows is slightly different across different
 	 * database engines, so this string appears in each driver and is
@@ -547,22 +543,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Insert_batch statement
 	 *
 	 * Generates a platform-specific insert string from the supplied data
diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php
index f624002..741b741 100644
--- a/system/database/drivers/odbc/odbc_driver.php
+++ b/system/database/drivers/odbc/odbc_driver.php
@@ -45,9 +45,7 @@
 	// the character used to excape - not necessary for ODBC
 	protected $_escape_char = '';
 
-	// clause and character used for LIKE escape sequences
 	protected $_like_escape_str = " {escape '%s'} ";
-	protected $_like_escape_chr = '!';
 
 	protected $_random_keyword;
 
@@ -291,22 +289,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php
index ee5af78..f4509b1 100644
--- a/system/database/drivers/pdo/pdo_driver.php
+++ b/system/database/drivers/pdo/pdo_driver.php
@@ -45,10 +45,6 @@
 	// The character used to escaping
 	protected $_escape_char = '"';
 
-	// clause and character used for LIKE escape sequences
-	protected $_like_escape_str = " ESCAPE '%s' ";
-	protected $_like_escape_chr = '!';
-
 	protected $_random_keyword;
 
 	public $trans_enabled = FALSE;
diff --git a/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php
index 0e6877c..438d312 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php
@@ -130,22 +130,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
index 7411263..d2a484d 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
@@ -44,10 +44,6 @@
 
 	protected $_escape_char = '`';
 
-	// clause and character used for LIKE escape sequences - not used in CUBRID
-	protected $_like_escape_str = '';
-	protected $_like_escape_chr = '\\';
-
 	protected $_random_keyword = ' RAND()';
 
 	/**
@@ -183,6 +179,26 @@
 		return 'TRUNCATE '.$table;
 	}
 
+	// --------------------------------------------------------------------
+
+	/**
+	 * FROM tables
+	 *
+	 * Groups tables in FROM clauses if needed, so there is no confusion
+	 * about operator precedence.
+	 *
+	 * @return	string
+	 */
+	protected function _from_tables()
+	{
+		if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
+		{
+			return '('.implode(', ', $this->qb_from).')';
+		}
+
+		return implode(', ', $this->qb_from);
+	}
+
 }
 
 /* End of file pdo_cubrid_driver.php */
diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
index d6465cd..782bb53 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
@@ -153,22 +153,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
@@ -239,4 +223,4 @@
 }
 
 /* End of file pdo_dblib_driver.php */
-/* Location: ./system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php */
diff --git a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
index 5b36342..32d1f21 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
@@ -139,22 +139,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return 	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php b/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php
index 7563a42..22a5f92 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php
@@ -165,22 +165,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
index 8248049..8dd4301 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
@@ -159,22 +159,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
index 67da156..9ffb93e 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
@@ -44,10 +44,6 @@
 
 	protected $_escape_char = '`';
 
-	// clause and character used for LIKE escape sequences - not used in MySQL
-	protected $_like_escape_str = '';
-	protected $_like_escape_chr = '\\';
-
 	protected $_random_keyword = ' RAND()';
 
 	/**
@@ -211,6 +207,26 @@
 		return 'TRUNCATE '.$table;
 	}
 
+	// --------------------------------------------------------------------
+
+	/**
+	 * FROM tables
+	 *
+	 * Groups tables in FROM clauses if needed, so there is no confusion
+	 * about operator precedence.
+	 *
+	 * @return	string
+	 */
+	protected function _from_tables()
+	{
+		if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
+		{
+			return '('.implode(', ', $this->qb_from).')';
+		}
+
+		return implode(', ', $this->qb_from);
+	}
+
 }
 
 /* End of file pdo_mysql_driver.php */
diff --git a/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
index cfbb639..b03218f 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
@@ -146,22 +146,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Insert_batch statement
 	 *
 	 * @param	string	the table name
diff --git a/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
index 0c34674..5944d55 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
@@ -46,7 +46,6 @@
 	protected $_escape_char = '';
 
 	// clause and character used for LIKE escape sequences
-	protected $_like_escape_chr = '!';
 	protected $_like_escape_str = " {escape '%s'} ";
 
 	protected $_random_keyword = ' RAND()';
@@ -157,22 +156,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
index 07cf8f5..74d56e6 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
@@ -142,22 +142,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
index 4b5747d..5b1cd97 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
@@ -182,22 +182,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
@@ -273,4 +257,4 @@
 }
 
 /* End of file pdo_sqlsrv_driver.php */
-/* Location: ./system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php */
diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php
index ddcf3f7..19f384c 100644
--- a/system/database/drivers/postgre/postgre_driver.php
+++ b/system/database/drivers/postgre/postgre_driver.php
@@ -44,10 +44,6 @@
 
 	protected $_escape_char = '"';
 
-	// clause and character used for LIKE escape sequences
-	protected $_like_escape_str = " ESCAPE '%s' ";
-	protected $_like_escape_chr = '!';
-
 	protected $_random_keyword = ' RANDOM()'; // database specific random keyword
 
 	/**
@@ -132,7 +128,15 @@
 	 */
 	public function db_pconnect()
 	{
-		return @pg_pconnect($this->dsn);
+		$conn = @pg_pconnect($this->dsn);
+		if ($conn && pg_connection_status($conn) === PGSQL_CONNECTION_BAD)
+		{
+			if (pg_ping($conn) === FALSE)
+			{
+				return FALSE;
+			}
+		}
+		return $conn;
 	}
 
 	// --------------------------------------------------------------------
@@ -453,22 +457,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php
index 19824db..2744a63 100644
--- a/system/database/drivers/sqlite/sqlite_driver.php
+++ b/system/database/drivers/sqlite/sqlite_driver.php
@@ -45,10 +45,6 @@
 	// The character used to escape with - not needed for SQLite
 	protected $_escape_char = '"';
 
-	// clause and character used for LIKE escape sequences
-	protected $_like_escape_str = " ESCAPE '%s' ";
-	protected $_like_escape_chr = '!';
-
 	protected $_random_keyword = ' Random()'; // database specific random keyword
 
 	/**
diff --git a/system/database/drivers/sqlite3/sqlite3_driver.php b/system/database/drivers/sqlite3/sqlite3_driver.php
index cc35d31..d03be15 100644
--- a/system/database/drivers/sqlite3/sqlite3_driver.php
+++ b/system/database/drivers/sqlite3/sqlite3_driver.php
@@ -46,10 +46,6 @@
 	// The character used for escaping
 	protected $_escape_char = '"';
 
-	// clause and character used for LIKE escape sequences
-	protected $_like_escape_str = ' ESCAPE \'%s\' ';
-	protected $_like_escape_chr = '!';
-
 	protected $_random_keyword = ' RANDOM()';
 
 	/**
@@ -288,25 +284,16 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * The error message string
+	 * Error
 	 *
-	 * @return	string
-	 */
-	protected function _error_message()
-	{
-		return $this->conn_id->lastErrorMsg();
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * The error message number
+	 * Returns an array containing code and message of the last
+	 * database error that has occured.
 	 *
-	 * @return	int
+	 * @return	array
 	 */
-	protected function _error_number()
+	public function error()
 	{
-		return $this->conn_id->lastErrorCode();
+		return array('code' => $this->conn_id->lastErrorCode(), 'message' => $this->conn_id->lastErrorMsg());
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
index badbb8e..6594292 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_driver.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -45,10 +45,6 @@
 	// The character used for escaping
 	protected $_escape_char = '"';
 
-	// clause and character used for LIKE escape sequences
-	protected $_like_escape_str = " ESCAPE '%s' ";
-	protected $_like_escape_chr = '!';
-
 	protected $_random_keyword = ' NEWID()';
 
 	// SQLSRV-specific properties
@@ -225,7 +221,7 @@
 	 */
 	public function affected_rows()
 	{
-		return sqlrv_rows_affected($this->result_id);
+		return sqlsrv_rows_affected($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -362,22 +358,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		return is_array($tables) ? implode(', ', $tables) : $tables;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/helpers/captcha_helper.php b/system/helpers/captcha_helper.php
index a4383c9..3aac14d 100644
--- a/system/helpers/captcha_helper.php
+++ b/system/helpers/captcha_helper.php
@@ -80,8 +80,7 @@
 		$current_dir = @opendir($img_path);
 		while ($filename = @readdir($current_dir))
 		{
-			if ($filename !== '.' && $filename !== '..' && $filename !== 'index.html'
-				&& (str_replace('.jpg', '', $filename) + $expiration) < $now)
+			if (substr($filename, -4) === '.jpg' && (str_replace('.jpg', '', $filename) + $expiration) < $now)
 			{
 				@unlink($img_path.$filename);
 			}
diff --git a/system/helpers/date_helper.php b/system/helpers/date_helper.php
index a45b3d7..955d745 100644
--- a/system/helpers/date_helper.php
+++ b/system/helpers/date_helper.php
@@ -575,22 +575,7 @@
 			$menu .= ' class="'.$class.'"';
 		}
 
-		// Generate a string from the attributes submitted, if any
-		if (is_array($attributes))
-		{
-			$atts = '';
-			foreach ($attributes as $key => $val)
-			{
-				$atts .= ' '.$key.'="'.$val.'"';
-			}
-			$attributes = $atts;
-		}
-		elseif (is_string($attributes) && strlen($attributes) > 0)
-		{
-			$attributes = ' '.$attributes;
-		}
-
-		$menu .= $attributes.">\n";
+		$menu .= _stringify_attributes($attributes).">\n";
 
 		foreach (timezones() as $key => $val)
 		{
@@ -672,5 +657,136 @@
 	}
 }
 
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('date_range'))
+{
+	/**
+	 * Date range
+	 *
+	 * Returns a list of dates within a specified period.
+	 *
+	 * @param	int	unix_start	UNIX timestamp of period start date
+	 * @param	int	unix_end|days	UNIX timestamp of period end date
+	 *					or interval in days.
+	 * @param	mixed	is_unix		Specifies wether the second parameter
+	 *					is a UNIX timestamp or a day interval
+	 *					 - TRUE or 'unix' for a timestamp
+	 *					 - FALSE or 'days' for an interval
+	 * @param	string  date_format	Output date format, same as in date()
+	 * @return	array
+	 */
+	function date_range($unix_start = '', $mixed = '', $is_unix = TRUE, $format = 'Y-m-d')
+	{
+		if ($unix_start == '' OR $mixed == '' OR $format == '')
+		{
+			return FALSE;
+		}
+
+		$is_unix = ! ( ! $is_unix OR $is_unix === 'days');
+
+		// Validate input and try strtotime() on invalid timestamps/intervals, just in case
+		if ( ( ! preg_match('/^[0-9]+$/', $unix_start) && ($unix_start = @strtotime($unix_time)) === FALSE)
+			OR ( ! preg_match('/^[0-9]+$/', $mixed) && ($is_unix === FALSE OR ($mixed = @strtotime($mixed)) === FALSE))
+			OR ($is_unix === TRUE && $mixed < $unix_start))
+		{
+			return FALSE;
+		}
+
+		if ($is_unix && ($unix_start == $mixed OR date($format, $unix_start) === date($format, $mixed)))
+		{
+			return array($start_date);
+		}
+
+		$range = array();
+
+		/* NOTE: Even though the DateTime object has many useful features, it appears that
+		 *	 it doesn't always handle properly timezones, when timestamps are passed
+		 *	 directly to its constructor. Neither of the following gave proper results:
+		 *
+		 *		new DateTime('<timestamp>')
+		 *		new DateTime('<timestamp>', '<timezone>')
+		 *
+		 *	 --- available in PHP 5.3:
+		 *
+		 *		DateTime::createFromFormat('<format>', '<timestamp>')
+		 *		DateTime::createFromFormat('<format>', '<timestamp>', '<timezone')
+		 *
+		 *	 ... so we'll have to set the timestamp after the object is instantiated.
+		 *	 Furthermore, in PHP 5.3 we can use DateTime::setTimestamp() to do that and
+		 *	 given that we have UNIX timestamps - we should use it.
+		*/
+		$from = new DateTime();
+
+		if (is_php('5.3'))
+		{
+			$from->setTimestamp($unix_start);
+			if ($is_unix)
+			{
+				$arg = new DateTime();
+				$arg->setTimestamp($mixed);
+			}
+			else
+			{
+				$arg = (int) $mixed;
+			}
+
+			$period = new DatePeriod($from, new DateInterval('P1D'), $arg);
+			foreach ($period as $date)
+			{
+				$range[] = $date->format($format);
+			}
+
+			/* If a period end date was passed to the DatePeriod constructor, it might not
+			 * be in our results. Not sure if this is a bug or it's just possible because
+			 * the end date might actually be less than 24 hours away from the previously
+			 * generated DateTime object, but either way - we have to append it manually.
+			 */
+			if ( ! is_int($arg) && $range[count($range) - 1] !== $arg->format($format))
+			{
+				$range[] = $arg->format($format);
+			}
+
+			return $range;
+		}
+
+		$from->setDate(date('Y', $unix_start), date('n', $unix_start), date('j', $unix_start));
+		$from->setTime(date('G', $unix_start), date('i', $unix_start), date('s', $unix_start));
+		if ($is_unix)
+		{
+			$arg = new DateTime();
+			$arg->setDate(date('Y', $mixed), date('n', $mixed), date('j', $mixed));
+			$arg->setTime(date('G', $mixed), date('i', $mixed), date('s', $mixed));
+		}
+		else
+		{
+			$arg = (int) $mixed;
+		}
+		$range[] = $from->format($format);
+
+		if (is_int($arg)) // Day intervals
+		{
+			do
+			{
+				$from->modify('+1 day');
+				$range[] = $from->format($format);
+			}
+			while (--$arg > 0);
+		}
+		else // end date UNIX timestamp
+		{
+			for ($from->modify('+1 day'), $end_check = $arg->format('Ymd'); $from->format('Ymd') < $end_check; $from->modify('+1 day'))
+			{
+				$range[] = $from->format($format);
+			}
+
+			// Our loop only appended dates prior to our end date
+			$range[] = $arg->format($format);
+		}
+
+		return $range;
+	}
+}
+
 /* End of file date_helper.php */
 /* Location: ./system/helpers/date_helper.php */
\ No newline at end of file
diff --git a/system/helpers/directory_helper.php b/system/helpers/directory_helper.php
index e7d3b5e..7d6b677 100644
--- a/system/helpers/directory_helper.php
+++ b/system/helpers/directory_helper.php
@@ -62,7 +62,7 @@
 			while (FALSE !== ($file = readdir($fp)))
 			{
 				// Remove '.', '..', and hidden files [optional]
-				if ( ! trim($file, '.') OR ($hidden === FALSE && $file[0] === '.'))
+				if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.'))
 				{
 					continue;
 				}
diff --git a/system/helpers/download_helper.php b/system/helpers/download_helper.php
index 09c4de5..0232adf 100644
--- a/system/helpers/download_helper.php
+++ b/system/helpers/download_helper.php
@@ -95,7 +95,10 @@
 		}
 
 		// Clean output buffer
-		ob_clean();
+		if (ob_get_level() !== 0)
+		{
+			ob_clean();
+		}
 
 		// Generate the server headers
 		header('Content-Type: '.$mime);
diff --git a/system/helpers/file_helper.php b/system/helpers/file_helper.php
index 3834d48..e68bb7f 100644
--- a/system/helpers/file_helper.php
+++ b/system/helpers/file_helper.php
@@ -124,7 +124,7 @@
 				{
 					delete_files($path.DIRECTORY_SEPARATOR.$filename, $del_dir, $level + 1, $htdocs);
 				}
-				elseif ($htdocs === TRUE && ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename))
+				elseif ($htdocs !== TRUE OR ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename))
 				{
 					@unlink($path.DIRECTORY_SEPARATOR.$filename);
 				}
diff --git a/system/helpers/html_helper.php b/system/helpers/html_helper.php
index 68ce702..2372e81 100644
--- a/system/helpers/html_helper.php
+++ b/system/helpers/html_helper.php
@@ -51,7 +51,7 @@
 	 */
 	function heading($data = '', $h = '1', $attributes = '')
 	{
-		return '<h'.$h.($attributes !== '' ? ' ' : '').$attributes.'>'.$data.'</h'.$h.'>';
+		return '<h'.$h._stringify_attributes($attributes).'>'.$data.'</h'.$h.'>';
 	}
 }
 
@@ -119,23 +119,8 @@
 		// Set the indentation based on the depth
 		$out = str_repeat(' ', $depth);
 
-		// Were any attributes submitted?  If so generate a string
-		if (is_array($attributes))
-		{
-			$atts = '';
-			foreach ($attributes as $key => $val)
-			{
-				$atts .= ' '.$key.'="'.$val.'"';
-			}
-			$attributes = $atts;
-		}
-		elseif (is_string($attributes) && strlen($attributes) > 0)
-		{
-			$attributes = ' '.$attributes;
-		}
-
 		// Write the opening list tag
-		$out .= '<'.$type.$attributes.">\n";
+		$out .= '<'.$type._stringify_attributes($attributes).">\n";
 
 		// Cycle through the list elements.  If an array is
 		// encountered we will recursively call _list()
@@ -191,9 +176,10 @@
 	 *
 	 * @param	mixed
 	 * @param	bool
+	 * @param	mixed
 	 * @return	string
 	 */
-	function img($src = '', $index_page = FALSE)
+	function img($src = '', $index_page = FALSE, $attributes = '')
 	{
 		if ( ! is_array($src) )
 		{
@@ -229,7 +215,7 @@
 			}
 		}
 
-		return $img.'/>';
+		return $img._stringify_attributes($attributes).' />';
 	}
 }
 
@@ -242,9 +228,9 @@
 	 *
 	 * Generates a page document type declaration
 	 *
-	 * Valid options are xhtml-11, xhtml-strict, xhtml-trans, xhtml-frame,
-	 * html4-strict, html4-trans, and html4-frame. Values are saved in the
-	 * doctypes config file.
+	 * Examples of valid options: html5, xhtml-11, xhtml-strict, xhtml-trans,
+	 * xhtml-frame, html4-strict, html4-trans, and html4-frame.
+	 * All values are saved in the doctypes config file.
 	 *
 	 * @param	string	type	The doctype to be generated
 	 * @return	string
diff --git a/system/helpers/text_helper.php b/system/helpers/text_helper.php
index 8a1f01b..b592f3c 100644
--- a/system/helpers/text_helper.php
+++ b/system/helpers/text_helper.php
@@ -89,7 +89,8 @@
 			return $str;
 		}
 
-		$str = preg_replace('/\s+/', ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $str));
+		// a bit complicated, but faster than preg_replace with \s+
+		$str = preg_replace('/ {2,}/', ' ', str_replace(array("\r", "\n", "\t", "\x0B", "\x0C"), ' ', $str));
 
 		if (strlen($str) <= $n)
 		{
diff --git a/system/helpers/typography_helper.php b/system/helpers/typography_helper.php
index af9d16a..9dbba06 100644
--- a/system/helpers/typography_helper.php
+++ b/system/helpers/typography_helper.php
@@ -65,11 +65,11 @@
 	 * @param	bool	whether to reduce multiple instances of double newlines to two
 	 * @return	string
 	 */
-	function auto_typography($str, $strip_js_event_handlers = TRUE, $reduce_linebreaks = FALSE)
+	function auto_typography($str, $reduce_linebreaks = FALSE)
 	{
 		$CI =& get_instance();
 		$CI->load->library('typography');
-		return $CI->typography->auto_typography($str, $strip_js_event_handlers, $reduce_linebreaks);
+		return $CI->typography->auto_typography($str, $reduce_linebreaks);
 	}
 }
 
diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php
index 39e6343..b1f5ecc 100644
--- a/system/helpers/url_helper.php
+++ b/system/helpers/url_helper.php
@@ -165,7 +165,7 @@
 
 		if ($attributes !== '')
 		{
-			$attributes = _parse_attributes($attributes);
+			$attributes = _stringify_attributes($attributes);
 		}
 
 		return '<a href="'.$site_url.'"'.$attributes.'>'.$title.'</a>';
@@ -221,10 +221,10 @@
 			unset($attributes[$key]);
 		}
 
-		$attributes = empty($attributes) ? '' : _parse_attributes($attributes);
+		$attributes = _stringify_attributes($attributes);
 
 		return '<a href="'.$site_url
-			.'" onclick="window.open(\''.$site_url."', '".$window_name."', '"._parse_attributes($atts, TRUE)."'); return false;\""
+			.'" onclick="window.open(\''.$site_url."', '".$window_name."', '"._stringify_attributes($atts, TRUE)."'); return false;\""
 			.$attributes.'>'.$title.'</a>';
 	}
 }
@@ -250,7 +250,7 @@
 			$title = $email;
 		}
 
-		return '<a href="mailto:'.$email.'"'._parse_attributes($attributes).'>'.$title.'</a>';
+		return '<a href="mailto:'.$email.'"'._stringify_attributes($attributes).'>'.$title.'</a>';
 	}
 }
 
@@ -534,7 +534,7 @@
 		}
 
 		// IIS environment likely? Use 'refresh' for better compatibility
-		if (DIRECTORY_SEPARATOR !== '/' && $method === 'auto')
+		if ($method === 'auto' && isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== FALSE)
 		{
 			$method = 'refresh';
 		}
@@ -560,47 +560,5 @@
 	}
 }
 
-// ------------------------------------------------------------------------
-
-if ( ! function_exists('_parse_attributes'))
-{
-	/**
-	 * Parse out the attributes
-	 *
-	 * Some of the functions use this
-	 *
-	 * @param	array
-	 * @param	bool
-	 * @return	string
-	 */
-	function _parse_attributes($attributes, $javascript = FALSE)
-	{
-		if (is_string($attributes))
-		{
-			return ($attributes !== '') ? ' '.$attributes : '';
-		}
-
-		$att = '';
-		foreach ($attributes as $key => $val)
-		{
-			if ($javascript === TRUE)
-			{
-				$att .= $key.'='.$val.',';
-			}
-			else
-			{
-				$att .= ' '.$key.'="'.$val.'"';
-			}
-		}
-
-		if ($javascript === TRUE && $att !== '')
-		{
-			return substr($att, 0, -1);
-		}
-
-		return $att;
-	}
-}
-
 /* End of file url_helper.php */
 /* Location: ./system/helpers/url_helper.php */
\ No newline at end of file
diff --git a/system/language/english/date_lang.php b/system/language/english/date_lang.php
index 229d33d..6683e4c 100644
--- a/system/language/english/date_lang.php
+++ b/system/language/english/date_lang.php
@@ -25,20 +25,20 @@
  * @filesource
  */
 
-$lang['date_year'] = "Year";
-$lang['date_years'] = "Years";
-$lang['date_month'] = "Month";
-$lang['date_months'] = "Months";
-$lang['date_week'] = "Week";
-$lang['date_weeks'] = "Weeks";
-$lang['date_day'] = "Day";
-$lang['date_days'] = "Days";
-$lang['date_hour'] = "Hour";
-$lang['date_hours'] = "Hours";
-$lang['date_minute'] = "Minute";
-$lang['date_minutes'] = "Minutes";
-$lang['date_second'] = "Second";
-$lang['date_seconds'] = "Seconds";
+$lang['date_year'] = 'Year';
+$lang['date_years'] = 'Years';
+$lang['date_month'] = 'Month';
+$lang['date_months'] = 'Months';
+$lang['date_week'] = 'Week';
+$lang['date_weeks'] = 'Weeks';
+$lang['date_day'] = 'Day';
+$lang['date_days'] = 'Days';
+$lang['date_hour'] = 'Hour';
+$lang['date_hours'] = 'Hours';
+$lang['date_minute'] = 'Minute';
+$lang['date_minutes'] = 'Minutes';
+$lang['date_second'] = 'Second';
+$lang['date_seconds'] = 'Seconds';
 
 $lang['UM12']	= '(UTC -12:00) Baker/Howland Island';
 $lang['UM11']	= '(UTC -11:00) Niue';
diff --git a/system/language/english/form_validation_lang.php b/system/language/english/form_validation_lang.php
index 0217761..6ff0cc2 100644
--- a/system/language/english/form_validation_lang.php
+++ b/system/language/english/form_validation_lang.php
@@ -42,9 +42,10 @@
 $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['differs']        		= 'The %s field must differ from 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['is_natural']				= 'The %s field must only contain digits.';
+$lang['is_natural_no_zero']		= 'The %s field must only contain digits and must be greater than zero.';
 $lang['decimal']				= 'The %s field must contain a decimal number.';
 $lang['less_than']				= 'The %s field must contain a number less than %s.';
 $lang['less_than_equal_to']		= 'The %s field must contain a number less than or equal to %s.';
diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php
index d67ee25..769d892 100644
--- a/system/libraries/Driver.php
+++ b/system/libraries/Driver.php
@@ -54,14 +54,30 @@
 	protected $lib_name;
 
 	/**
+	 * Get magic method
+	 *
 	 * The first time a child is used it won't exist, so we instantiate it
 	 * subsequents calls will go straight to the proper child.
 	 *
-	 * @param	mixed	$child
-	 * @return	mixed
+	 * @param   string  Child class name
+	 * @return  object  Child class
 	 */
 	public function __get($child)
 	{
+		// Try to load the driver
+		return $this->load_driver($child);
+	}
+
+	/**
+	 * Load driver
+	 *
+	 * Separate load_driver call to support explicit driver load by library or user
+	 *
+	 * @param   string  Child class name
+	 * @return  object  Child class
+	 */
+	public function load_driver($child)
+	{
 		if ( ! isset($this->lib_name))
 		{
 			$this->lib_name = get_class($this);
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index fdb9be4..fa1d5e9 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -98,6 +98,8 @@
 	 */
 	public function __construct($config = array())
 	{
+		$this->charset = strtoupper(config_item('charset'));
+
 		if (count($config) > 0)
 		{
 			$this->initialize($config);
@@ -188,7 +190,7 @@
 	 * @param	string
 	 * @return	object
 	 */
-	public function from($from, $name = '')
+	public function from($from, $name = '', $return_path = '')
 	{
 		if (preg_match('/\<(.*)\>/', $from, $match))
 		{
@@ -198,6 +200,10 @@
 		if ($this->validate)
 		{
 			$this->validate_email($this->_str_to_array($from));
+			if ($return_path)
+			{
+				$this->validate_email($this->_str_to_array($return_path));
+			}
 		}
 
 		// prepare the display name
@@ -216,7 +222,12 @@
 		}
 
 		$this->set_header('From', $name.' <'.$from.'>');
-		$this->set_header('Return-Path', '<'.$from.'>');
+
+		if( ! $return_path)
+		{
+			$return_path = $from;
+		}
+		$this->set_header('Return-Path', '<'.$return_path.'>');
 
 		return $this;
 	}
@@ -971,7 +982,6 @@
 
 				$this->_finalbody = $body.$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline;
 
-
 				if ($this->_get_protocol() === 'mail')
 				{
 					$this->_header_str .= $hdr;
@@ -1091,17 +1101,24 @@
 	 * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
 	 *
 	 * @param	string
-	 * @param	int
 	 * @return	string
 	 */
-	protected function _prep_quoted_printable($str, $charlim = '')
+	protected function _prep_quoted_printable($str)
 	{
-		// Set the character limit
-		// Don't allow over 76, as that will make servers and MUAs barf
-		// all over quoted-printable data
-		if ($charlim === '' OR $charlim > 76)
+		// RFC 2045 specifies CRLF as "\r\n".
+		// However, many developers choose to override that and violate
+		// the RFC rules due to (apparently) a bug in MS Exchange,
+		// which only works with "\n".
+		if ($this->crlf === "\r\n")
 		{
-			$charlim = 76;
+			if (is_php('5.3'))
+			{
+				return quoted_printable_encode($str);
+			}
+			elseif (function_exists('imap_8bit'))
+			{
+				return imap_8bit($str);
+			}
 		}
 
 		// Reduce multiple spaces & remove nulls
@@ -1146,7 +1163,7 @@
 
 				// If we're at the character limit, add the line to the output,
 				// reset our temp variable, and keep on chuggin'
-				if ((strlen($temp) + strlen($char)) >= $charlim)
+				if ((strlen($temp) + strlen($char)) >= 76)
 				{
 					$output .= $temp.$escape.$this->crlf;
 					$temp = '';
@@ -1228,7 +1245,7 @@
 
 		// wrap each line with the shebang, charset, and transfer encoding
 		// the preceding space on successive lines is required for header "folding"
-		return trim(preg_replace('/^(.*)$/m', ' =?'.$this->charset.'?Q?$1?=', $output.$temp));
+		return trim(preg_replace('/^(.*?)(\r*)$/m', ' =?'.$this->charset.'?Q?$1?=$2', $output.$temp));
 	}
 
 	// --------------------------------------------------------------------
@@ -1238,7 +1255,7 @@
 	 *
 	 * @return	bool
 	 */
-	public function send()
+	public function send($auto_clear = TRUE)
 	{
 		if ($this->_replyto_flag === FALSE)
 		{
@@ -1257,11 +1274,25 @@
 
 		if ($this->bcc_batch_mode && count($this->_bcc_array) > $this->bcc_batch_size)
 		{
-			return $this->batch_bcc_send();
+			$result = $this->batch_bcc_send();
+
+			if ($result && $auto_clear)
+			{
+				$this->clear();
+			}
+
+			return $result;
 		}
 
 		$this->_build_message();
-		return $this->_spool_email();
+		$result = $this->_spool_email();
+
+		if ($result && $auto_clear)
+		{
+			$this->clear();
+		}
+
+		return $result;
 	}
 
 	// --------------------------------------------------------------------
@@ -1385,7 +1416,7 @@
 		{
 			// most documentation of sendmail using the "-f" flag lacks a space after it, however
 			// we've encountered servers that seem to require it to be in place.
-			return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$this->clean_email($this->_headers['From']));
+			return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$this->clean_email($this->_headers['Return-Path']));
 		}
 	}
 
@@ -1398,7 +1429,7 @@
 	 */
 	protected function _send_with_sendmail()
 	{
-		$fp = @popen($this->mailpath.' -oi -f '.$this->clean_email($this->_headers['From']).' -t', 'w');
+		$fp = @popen($this->mailpath.' -oi -f '.$this->clean_email($this->_headers['From']).' -t'.' -r '.$this->clean_email($this->_headers['Return-Path']), 'w');
 
 		if ($fp === FALSE OR $fp === NULL)
 		{
diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php
index 8ffd93a..73ab8ca 100644
--- a/system/libraries/Encrypt.php
+++ b/system/libraries/Encrypt.php
@@ -165,7 +165,7 @@
 	 */
 	public function decode($string, $key = '')
 	{
-		if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string))
+		if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string) OR base64_encode(base64_decode($string)) !== $string)
 		{
 			return FALSE;
 		}
@@ -484,7 +484,7 @@
 	 */
 	public function set_hash($type = 'sha1')
 	{
-		$this->_hash_type = ($type !== 'sha1' && $type !== 'md5') ? 'sha1' : $type;
+		$this->_hash_type = in_array($type, hash_algos()) ? $type : 'sha1';
 	}
 
 	// --------------------------------------------------------------------
@@ -497,7 +497,7 @@
 	 */
 	public function hash($str)
 	{
-		return ($this->_hash_type === 'sha1') ? sha1($str) : md5($str);
+		return hash($this->_hash_type, $str);
 	}
 
 }
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index b490a34..91f46b6 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -134,12 +134,6 @@
 		// Automatically load the form helper
 		$this->CI->load->helper('form');
 
-		// Set the character encoding in MB.
-		if (MB_ENABLED === TRUE)
-		{
-			mb_internal_encoding($this->CI->config->item('charset'));
-		}
-
 		log_message('debug', 'Form Validation Class Initialized');
 	}
 
@@ -977,6 +971,20 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Differs from another field
+	 *
+	 * @param	string
+	 * @param	string	field
+	 * @return	bool
+	 */
+	public function differs($str, $field)
+	{
+		return ! (isset($this->_field_data[$field]) && $this->_field_data[$field]['postdata'] === $str);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Is Unique
 	 *
 	 * Check if the input value doesn't already exist
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index 899b995..ef41878 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -1320,6 +1320,13 @@
 				imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);
 				imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);
 			}
+
+			// We can preserve transparency for PNG images
+			if ($this->image_type === 3)
+			{
+				imagealphablending($src_img, FALSE);
+				imagesavealpha($src_img, TRUE);
+			}
 		}
 
 		// Output the final image
diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php
index 5573f64..e1e729b 100644
--- a/system/libraries/Pagination.php
+++ b/system/libraries/Pagination.php
@@ -52,20 +52,20 @@
 	protected $full_tag_open		= '';
 	protected $full_tag_close		= '';
 	protected $first_tag_open		= '';
-	protected $first_tag_close		= '&nbsp;';
-	protected $last_tag_open		= '&nbsp;';
+	protected $first_tag_close		= '';
+	protected $last_tag_open		= '';
 	protected $last_tag_close		= '';
 	protected $first_url			= ''; // Alternative URL for the First Page.
-	protected $cur_tag_open			= '&nbsp;<strong>';
+	protected $cur_tag_open			= '<strong>';
 	protected $cur_tag_close		= '</strong>';
-	protected $next_tag_open		= '&nbsp;';
-	protected $next_tag_close		= '&nbsp;';
-	protected $prev_tag_open		= '&nbsp;';
+	protected $next_tag_open		= '';
+	protected $next_tag_close		= '';
+	protected $prev_tag_open		= '';
 	protected $prev_tag_close		= '';
-	protected $num_tag_open			= '&nbsp;';
+	protected $num_tag_open			= '';
 	protected $num_tag_close		= '';
 	protected $page_query_string	= FALSE;
-	protected $query_string_segment 	= 'per_page';
+	protected $query_string_segment = 'per_page';
 	protected $display_pages		= TRUE;
 	protected $_attributes			= '';
 	protected $_link_types			= array();
@@ -215,7 +215,8 @@
 		// string. If post, add a trailing slash to the base URL if needed
 		if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE)
 		{
-			$this->base_url = rtrim($this->base_url).'&amp;'.$this->query_string_segment.'=';
+			$segment = (strpos($this->base_url, '?')) ? '&amp;' : '?';
+			$this->base_url = rtrim($this->base_url).$segment.$this->query_string_segment.'=';
 		}
 		else
 		{
diff --git a/system/libraries/Session.php b/system/libraries/Session.php
deleted file mode 100644
index af38dc3..0000000
--- a/system/libraries/Session.php
+++ /dev/null
@@ -1,955 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.2.4 or newer
- *
- * NOTICE OF LICENSE
- *
- * Licensed under the Open Software License version 3.0
- *
- * This source file is subject to the Open Software License (OSL 3.0) that is
- * bundled with this package in the files license.txt / license.rst.  It is
- * also available through the world wide web at this URL:
- * http://opensource.org/licenses/OSL-3.0
- * If you did not receive a copy of the license and are unable to obtain it
- * through the world wide web, please send an email to
- * licensing@ellislab.com so we can send you a copy immediately.
- *
- * @package		CodeIgniter
- * @author		EllisLab Dev Team
- * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
- * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
- * @link		http://codeigniter.com
- * @since		Version 1.0
- * @filesource
- */
-
-/**
- * Session Class
- *
- * @package		CodeIgniter
- * @subpackage	Libraries
- * @category	Sessions
- * @author		EllisLab Dev Team
- * @link		http://codeigniter.com/user_guide/libraries/sessions.html
- */
-class CI_Session {
-
-	/**
-	 * Whether to encrypt the session cookie
-	 *
-	 * @var bool
-	 */
-	public $sess_encrypt_cookie		= FALSE;
-
-	/**
-	 * Whether to use to the database for session storage
-	 *
-	 * @var bool
-	 */
-	public $sess_use_database		= FALSE;
-
-	/**
-	 * Name of the database table in which to store sessions
-	 *
-	 * @var string
-	 */
-	public $sess_table_name			= '';
-
-	/**
-	 * Length of time (in seconds) for sessions to expire
-	 *
-	 * @var int
-	 */
-	public $sess_expiration			= 7200;
-
-	/**
-	 * Whether to kill session on close of browser window
-	 *
-	 * @var bool
-	 */
-	public $sess_expire_on_close		= FALSE;
-
-	/**
-	 * Whether to match session on ip address
-	 *
-	 * @var bool
-	 */
-	public $sess_match_ip			= FALSE;
-
-	/**
-	 * Whether to match session on user-agent
-	 *
-	 * @var bool
-	 */
-	public $sess_match_useragent		= TRUE;
-
-	/**
-	 * Name of session cookie
-	 *
-	 * @var string
-	 */
-	public $sess_cookie_name		= 'ci_session';
-
-	/**
-	 * Session cookie prefix
-	 *
-	 * @var string
-	 */
-	public $cookie_prefix			= '';
-
-	/**
-	 * Session cookie path
-	 *
-	 * @var string
-	 */
-	public $cookie_path			= '';
-
-	/**
-	 * Session cookie domain
-	 *
-	 * @var string
-	 */
-	public $cookie_domain			= '';
-
-	/**
-	 * Whether to set the cookie only on HTTPS connections
-	 *
-	 * @var bool
-	 */
-	public $cookie_secure			= FALSE;
-
-	/**
-	 * Whether cookie should be allowed only to be sent by the server
-	 *
-	 * @var bool
-	 */
-	public $cookie_httponly 		= FALSE;
-
-	/**
-	 * Interval at which to update session
-	 *
-	 * @var int
-	 */
-	public $sess_time_to_update		= 300;
-
-	/**
-	 * Key with which to encrypt the session cookie
-	 *
-	 * @var string
-	 */
-	public $encryption_key			= '';
-
-	/**
-	 * String to indicate flash data cookies
-	 *
-	 * @var string
-	 */
-	public $flashdata_key			= 'flash';
-
-	/**
-	 * Timezone to use for the current time
-	 *
-	 * @var string
-	 */
-	public $time_reference			= 'local';
-
-
-	/**
-	 * Session data
-	 *
-	 * @var array
-	 */
-	public $userdata			= array();
-
-	/**
-	 * Reference to CodeIgniter instance
-	 *
-	 * @var object
-	 */
-	public $CI;
-
-	/**
-	 * Current time
-	 *
-	 * @var int
-	 */
-	public $now;
-
-	/**
-	 * Session Constructor
-	 *
-	 * The constructor runs the session routines automatically
-	 * whenever the class is instantiated.
-	 *
-	 * @param	array
-	 * @return	void
-	 */
-	public function __construct($params = array())
-	{
-		log_message('debug', 'Session Class Initialized');
-
-		// Set the super object to a local variable for use throughout the class
-		$this->CI =& get_instance();
-
-		// Set all the session preferences, which can either be set
-		// manually via the $params array above or via the config file
-		foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration', 'sess_expire_on_close', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key)
-		{
-			$this->$key = isset($params[$key]) ? $params[$key] : $this->CI->config->item($key);
-		}
-
-		if ($this->encryption_key === '')
-		{
-			show_error('In order to use the Session class you are required to set an encryption key in your config file.');
-		}
-
-		// Load the string helper so we can use the strip_slashes() function
-		$this->CI->load->helper('string');
-
-		// Do we need encryption? If so, load the encryption class
-		if ($this->sess_encrypt_cookie === TRUE)
-		{
-			$this->CI->load->library('encrypt');
-		}
-
-		// Are we using a database? If so, load it
-		if ($this->sess_use_database === TRUE && $this->sess_table_name !== '')
-		{
-			$this->CI->load->database();
-		}
-
-		// Set the "now" time. Can either be GMT or server time, based on the
-		// config prefs. We use this to set the "last activity" time
-		$this->now = $this->_get_time();
-
-		// Set the session length. If the session expiration is
-		// set to zero we'll set the expiration two years from now.
-		if ($this->sess_expiration === 0)
-		{
-			$this->sess_expiration = (60*60*24*365*2);
-		}
-
-		// Set the cookie name
-		$this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
-
-		// Run the Session routine. If a session doesn't exist we'll
-		// create a new one. If it does, we'll update it.
-		if ( ! $this->sess_read())
-		{
-			$this->sess_create();
-		}
-		else
-		{
-			$this->sess_update();
-		}
-
-		// Delete 'old' flashdata (from last request)
-		$this->_flashdata_sweep();
-
-		// Mark all new flashdata as old (data will be deleted before next request)
-		$this->_flashdata_mark();
-
-		// Delete expired sessions if necessary
-		$this->_sess_gc();
-
-		log_message('debug', 'Session routines successfully run');
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Fetch the current session data if it exists
-	 *
-	 * @return	bool
-	 */
-	public function sess_read()
-	{
-		// Fetch the cookie
-		$session = $this->CI->input->cookie($this->sess_cookie_name);
-
-		// No cookie?  Goodbye cruel world!...
-		if ($session === NULL)
-		{
-			log_message('debug', 'A session cookie was not found.');
-			return FALSE;
-		}
-
-		// Decrypt the cookie data
-		if ($this->sess_encrypt_cookie === TRUE)
-		{
-			$session = $this->CI->encrypt->decode($session);
-		}
-		else
-		{
-			// encryption was not used, so we need to check the md5 hash
-			$hash	 = substr($session, strlen($session)-32); // get last 32 chars
-			$session = substr($session, 0, strlen($session)-32);
-
-			// Does the md5 hash match? This is to prevent manipulation of session data in userspace
-			if ($hash !==  md5($session.$this->encryption_key))
-			{
-				log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.');
-				$this->sess_destroy();
-				return FALSE;
-			}
-		}
-
-		// Unserialize the session array
-		$session = $this->_unserialize($session);
-
-		// Is the session data we unserialized an array with the correct format?
-		if ( ! is_array($session) OR ! isset($session['session_id'], $session['ip_address'], $session['user_agent'], $session['last_activity']))
-		{
-			$this->sess_destroy();
-			return FALSE;
-		}
-
-		// Is the session current?
-		if (($session['last_activity'] + $this->sess_expiration) < $this->now)
-		{
-			$this->sess_destroy();
-			return FALSE;
-		}
-
-		// Does the IP match?
-		if ($this->sess_match_ip === TRUE && $session['ip_address'] !== $this->CI->input->ip_address())
-		{
-			$this->sess_destroy();
-			return FALSE;
-		}
-
-		// Does the User Agent Match?
-		if ($this->sess_match_useragent === TRUE && trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
-		{
-			$this->sess_destroy();
-			return FALSE;
-		}
-
-		// Is there a corresponding session in the DB?
-		if ($this->sess_use_database === TRUE)
-		{
-			$this->CI->db->where('session_id', $session['session_id']);
-
-			if ($this->sess_match_ip === TRUE)
-			{
-				$this->CI->db->where('ip_address', $session['ip_address']);
-			}
-
-			if ($this->sess_match_useragent === TRUE)
-			{
-				$this->CI->db->where('user_agent', $session['user_agent']);
-			}
-
-			$query = $this->CI->db->limit(1)->get($this->sess_table_name);
-
-			// No result? Kill it!
-			if ($query->num_rows() === 0)
-			{
-				$this->sess_destroy();
-				return FALSE;
-			}
-
-			// Is there custom data?  If so, add it to the main session array
-			$row = $query->row();
-			if ( ! empty($row->user_data))
-			{
-				$custom_data = $this->_unserialize($row->user_data);
-
-				if (is_array($custom_data))
-				{
-					foreach ($custom_data as $key => $val)
-					{
-						$session[$key] = $val;
-					}
-				}
-			}
-		}
-
-		// Session is valid!
-		$this->userdata = $session;
-		unset($session);
-
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Write the session data
-	 *
-	 * @return	void
-	 */
-	public function sess_write()
-	{
-		// Are we saving custom data to the DB?  If not, all we do is update the cookie
-		if ($this->sess_use_database === FALSE)
-		{
-			$this->_set_cookie();
-			return;
-		}
-
-		// set the custom userdata, the session data we will set in a second
-		$custom_userdata = $this->userdata;
-		$cookie_userdata = array();
-
-		// Before continuing, we need to determine if there is any custom data to deal with.
-		// Let's determine this by removing the default indexes to see if there's anything left in the array
-		// and set the session data while we're at it
-		foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
-		{
-			unset($custom_userdata[$val]);
-			$cookie_userdata[$val] = $this->userdata[$val];
-		}
-
-		// Did we find any custom data? If not, we turn the empty array into a string
-		// since there's no reason to serialize and store an empty array in the DB
-		if (count($custom_userdata) === 0)
-		{
-			$custom_userdata = '';
-		}
-		else
-		{
-			// Serialize the custom data array so we can store it
-			$custom_userdata = $this->_serialize($custom_userdata);
-		}
-
-		// Run the update query
-		$this->CI->db->where('session_id', $this->userdata['session_id']);
-		$this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata));
-
-		// Write the cookie. Notice that we manually pass the cookie data array to the
-		// _set_cookie() function. Normally that function will store $this->userdata, but
-		// in this case that array contains custom data, which we do not want in the cookie.
-		$this->_set_cookie($cookie_userdata);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Create a new session
-	 *
-	 * @return	void
-	 */
-	public function sess_create()
-	{
-		$sessid = '';
-		do
-		{
-			$sessid .= mt_rand(0, mt_getrandmax());
-		}
-		while (strlen($sessid) < 32);
-
-		// To make the session ID even more secure we'll combine it with the user's IP
-		$sessid .= $this->CI->input->ip_address();
-
-		$this->userdata = array(
-					'session_id'	=> md5(uniqid($sessid, TRUE)),
-					'ip_address'	=> $this->CI->input->ip_address(),
-					'user_agent'	=> substr($this->CI->input->user_agent(), 0, 120),
-					'last_activity'	=> $this->now,
-					'user_data'	=> ''
-				);
-
-		// Save the data to the DB if needed
-		if ($this->sess_use_database === TRUE)
-		{
-			$this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->userdata));
-		}
-
-		// Write the cookie
-		$this->_set_cookie();
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Update an existing session
-	 *
-	 * @return	void
-	 */
-	public function sess_update()
-	{
-		// We only update the session every five minutes by default
-		if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
-		{
-			return;
-		}
-
-		// _set_cookie() will handle this for us if we aren't using database sessions
-		// by pushing all userdata to the cookie.
-		$cookie_data = NULL;
-
-		/* Changing the session ID during an AJAX call causes problems,
-		 * so we'll only update our last_activity
-		 */
-		if ($this->CI->input->is_ajax_request())
-		{
-			$this->userdata['last_activity'] = $this->now;
-
-			// Update the session ID and last_activity field in the DB if needed
-			if ($this->sess_use_database === TRUE)
-			{
-				// set cookie explicitly to only have our session data
-				$cookie_data = array();
-				foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
-				{
-					$cookie_data[$val] = $this->userdata[$val];
-				}
-
-				$this->CI->db->query($this->CI->db->update_string($this->sess_table_name,
-											array('last_activity' => $this->userdata['last_activity']),
-											array('session_id' => $this->userdata['session_id'])));
-			}
-
-			return $this->_set_cookie($cookie_data);
-		}
-
-		// Save the old session id so we know which record to
-		// update in the database if we need it
-		$old_sessid = $this->userdata['session_id'];
-		$new_sessid = '';
-		do
-		{
-			$new_sessid .= mt_rand(0, mt_getrandmax());
-		}
-		while (strlen($new_sessid) < 32);
-
-		// To make the session ID even more secure we'll combine it with the user's IP
-		$new_sessid .= $this->CI->input->ip_address();
-
-		// Turn it into a hash and update the session data array
-		$this->userdata['session_id'] = $new_sessid = md5(uniqid($new_sessid, TRUE));
-		$this->userdata['last_activity'] = $this->now;
-
-		// Update the session ID and last_activity field in the DB if needed
-		if ($this->sess_use_database === TRUE)
-		{
-			// set cookie explicitly to only have our session data
-			$cookie_data = array();
-			foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
-			{
-				$cookie_data[$val] = $this->userdata[$val];
-			}
-
-			$this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
-		}
-
-		// Write the cookie
-		$this->_set_cookie($cookie_data);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Destroy the current session
-	 *
-	 * @return	void
-	 */
-	public function sess_destroy()
-	{
-		// Kill the session DB row
-		if ($this->sess_use_database === TRUE && isset($this->userdata['session_id']))
-		{
-			$this->CI->db->where('session_id', $this->userdata['session_id']);
-			$this->CI->db->delete($this->sess_table_name);
-		}
-
-		// Kill the cookie
-		setcookie(
-				$this->sess_cookie_name,
-				addslashes(serialize(array())),
-				($this->now - 31500000),
-				$this->cookie_path,
-				$this->cookie_domain,
-				0
-			);
-
-		// Kill session data
-		$this->userdata = array();
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Fetch a specific item from the session array
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function userdata($item)
-	{
-		return isset($this->userdata[$item]) ? $this->userdata[$item] : NULL;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Fetch all session data
-	 *
-	 * @return	array
-	 */
-	public function all_userdata()
-	{
-		return $this->userdata;
-	}
-
-	// --------------------------------------------------------------------------
-
-	/**
-	 * Fetch all flashdata
-	 *
-	 * @return	array
-	 */
-	public function all_flashdata()
-	{
-		$out = array();
-
-		// loop through all userdata
-		foreach ($this->all_userdata() as $key => $val)
-		{
-			// if it contains flashdata, add it
-			if (strpos($key, 'flash:old:') !== FALSE)
-			{
-				$out[$key] = $val;
-			}
-		}
-		return $out;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Add or change data in the "userdata" array
-	 *
-	 * @param	mixed
-	 * @param	string
-	 * @return	void
-	 */
-	public function set_userdata($newdata = array(), $newval = '')
-	{
-		if (is_string($newdata))
-		{
-			$newdata = array($newdata => $newval);
-		}
-
-		if (count($newdata) > 0)
-		{
-			foreach ($newdata as $key => $val)
-			{
-				$this->userdata[$key] = $val;
-			}
-		}
-
-		$this->sess_write();
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Delete a session variable from the "userdata" array
-	 *
-	 * @param	array
-	 * @return	void
-	 */
-	public function unset_userdata($newdata = array())
-	{
-		if (is_string($newdata))
-		{
-			$newdata = array($newdata => '');
-		}
-
-		if (count($newdata) > 0)
-		{
-			foreach ($newdata as $key => $val)
-			{
-				unset($this->userdata[$key]);
-			}
-		}
-
-		$this->sess_write();
-	}
-
-	// ------------------------------------------------------------------------
-
-	/**
-	 * Add or change flashdata, only available
-	 * until the next request
-	 *
-	 * @param	mixed
-	 * @param	string
-	 * @return	void
-	 */
-	public function set_flashdata($newdata = array(), $newval = '')
-	{
-		if (is_string($newdata))
-		{
-			$newdata = array($newdata => $newval);
-		}
-
-		if (count($newdata) > 0)
-		{
-			foreach ($newdata as $key => $val)
-			{
-				$this->set_userdata($this->flashdata_key.':new:'.$key, $val);
-			}
-		}
-	}
-
-	// ------------------------------------------------------------------------
-
-	/**
-	 * Keeps existing flashdata available to next request.
-	 *
-	 * @param	string
-	 * @return	void
-	 */
-	public function keep_flashdata($key)
-	{
-		// 'old' flashdata gets removed. Here we mark all
-		// flashdata as 'new' to preserve it from _flashdata_sweep()
-		// Note the function will return NULL if the $key
-		// provided cannot be found
-		$value = $this->userdata($this->flashdata_key.':old:'.$key);
-
-		$this->set_userdata($this->flashdata_key.':new:'.$key, $value);
-	}
-
-	// ------------------------------------------------------------------------
-
-	/**
-	 * Fetch a specific flashdata item from the session array
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	public function flashdata($key)
-	{
-		return $this->userdata($this->flashdata_key.':old:'.$key);
-	}
-
-	// ------------------------------------------------------------------------
-
-	/**
-	 * Identifies flashdata as 'old' for removal
-	 * when _flashdata_sweep() runs.
-	 *
-	 * @return	void
-	 */
-	protected function _flashdata_mark()
-	{
-		$userdata = $this->all_userdata();
-		foreach ($userdata as $name => $value)
-		{
-			$parts = explode(':new:', $name);
-			if (is_array($parts) && count($parts) === 2)
-			{
-				$this->set_userdata($this->flashdata_key.':old:'.$parts[1], $value);
-				$this->unset_userdata($name);
-			}
-		}
-	}
-
-	// ------------------------------------------------------------------------
-
-	/**
-	 * Removes all flashdata marked as 'old'
-	 *
-	 * @return	void
-	 */
-	protected function _flashdata_sweep()
-	{
-		$userdata = $this->all_userdata();
-		foreach ($userdata as $key => $value)
-		{
-			if (strpos($key, ':old:'))
-			{
-				$this->unset_userdata($key);
-			}
-		}
-
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Get the "now" time
-	 *
-	 * @return	string
-	 */
-	protected function _get_time()
-	{
-		if ($this->time_reference === 'local' OR $this->time_reference === date_default_timezone_get())
-		{
-			return time();
-		}
-
-		$datetime = new DateTime('now', new DateTimeZone($this->time_reference));
-		sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
-
-		return mktime($hour, $minute, $second, $month, $day, $year);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Write the session cookie
-	 *
-	 * @param	mixed
-	 * @return	void
-	 */
-	protected function _set_cookie($cookie_data = NULL)
-	{
-		if (is_null($cookie_data))
-		{
-			$cookie_data = $this->userdata;
-		}
-
-		// Serialize the userdata for the cookie
-		$cookie_data = $this->_serialize($cookie_data);
-
-		if ($this->sess_encrypt_cookie === TRUE)
-		{
-			$cookie_data = $this->CI->encrypt->encode($cookie_data);
-		}
-		else
-		{
-			// if encryption is not used, we provide an md5 hash to prevent userside tampering
-			$cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);
-		}
-
-		$expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
-
-		// Set the cookie
-		setcookie(
-			$this->sess_cookie_name,
-			$cookie_data,
-			$expire,
-			$this->cookie_path,
-			$this->cookie_domain,
-			$this->cookie_secure,
-			$this->cookie_httponly
-		);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Serialize an array
-	 *
-	 * This function first converts any slashes found in the array to a temporary
-	 * marker, so when it gets unserialized the slashes will be preserved
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _serialize($data)
-	{
-		if (is_array($data))
-		{
-			array_walk_recursive($data, array(&$this, '_escape_slashes'));
-		}
-		elseif (is_string($data))
-		{
-			$data = str_replace('\\', '{{slash}}', $data);
-		}
-		return serialize($data);
-	}
-
-	/**
-	 * Escape slashes
-	 *
-	 * This function converts any slashes found into a temporary marker
-	 *
-	 * @param	string
-	 * @param	string
-	 * @return	void
-	 */
-	protected function _escape_slashes(&$val, $key)
-	{
-		if (is_string($val))
-		{
-			$val = str_replace('\\', '{{slash}}', $val);
-		}
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Unserialize
-	 *
-	 * This function unserializes a data string, then converts any
-	 * temporary slash markers back to actual slashes
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _unserialize($data)
-	{
-		$data = @unserialize(strip_slashes(trim($data)));
-
-		if (is_array($data))
-		{
-			array_walk_recursive($data, array(&$this, '_unescape_slashes'));
-			return $data;
-		}
-
-		return is_string($data) ? str_replace('{{slash}}', '\\', $data) : $data;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Unescape slashes
-	 *
-	 * This function converts any slash markers back into actual slashes
-	 *
-	 * @param	string
-	 * @param	string
-	 * @return	void
-	 */
-	protected function _unescape_slashes(&$val, $key)
-	{
-		if (is_string($val))
-		{
-	 		$val= str_replace('{{slash}}', '\\', $val);
-		}
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Garbage collection
-	 *
-	 * This deletes expired session rows from database
-	 * if the probability percentage is met
-	 *
-	 * @return	void
-	 */
-	protected function _sess_gc()
-	{
-		if ($this->sess_use_database !== TRUE)
-		{
-			return;
-		}
-
-		$probability = ini_get('session.gc_probability');
-		$divisor = ini_get('session.gc_divisor');
-
-		srand(time());
-		if ((mt_rand(0, $divisor) / $divisor) < $probability)
-		{
-			$expire = $this->now - $this->sess_expiration;
-
-			$this->CI->db->where('last_activity < '.$expire);
-			$this->CI->db->delete($this->sess_table_name);
-
-			log_message('debug', 'Session garbage collection performed.');
-		}
-	}
-
-}
-
-/* End of file Session.php */
-/* Location: ./system/libraries/Session.php */
\ No newline at end of file
diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php
new file mode 100755
index 0000000..9785060
--- /dev/null
+++ b/system/libraries/Session/Session.php
@@ -0,0 +1,714 @@
+<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2006 - 2012 EllisLab, Inc.
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 2.0
+ * @filesource
+ */
+
+/**
+ * CodeIgniter Session Class
+ *
+ * The user interface defined by EllisLabs, now with puggable drivers to manage different storage mechanisms.
+ * By default, the cookie session driver will load, but the 'sess_driver' config/param item (see above) can be
+ * used to specify the 'native' driver, or any other you might create.
+ * Once loaded, this driver setup is a drop-in replacement for the former CI_Session library, taking its place as the
+ * 'session' member of the global controller framework (e.g.: $CI->session or $this->session).
+ * In keeping with the CI_Driver methodology, multiple drivers may be loaded, although this might be a bit confusing.
+ * The CI_Session library class keeps track of the most recently loaded driver as "current" to call for driver methods.
+ * Ideally, one driver is loaded and all calls go directly through the main library interface. However, any methods
+ * called through the specific driver will switch the "current" driver to itself before invoking the library method
+ * (which will then call back into the driver for low-level operations). So, alternation between two drivers can be
+ * achieved by specifying which driver to use for each call (e.g.: $this->session->native->set_userdata('foo', 'bar');
+ * $this->session->cookie->userdata('foo'); $this->session->native->unset_userdata('foo');). Notice in the previous
+ * example that the _native_ userdata value 'foo' would be set to 'bar', which would NOT be returned by the call for
+ * the _cookie_ userdata 'foo', nor would the _cookie_ value be unset by the call to unset the _native_ 'foo' value.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Sessions
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/libraries/sessions.html
+ */
+class CI_Session extends CI_Driver_Library {
+
+	public $params = array();
+	protected $current = NULL;
+	protected $userdata = array();
+
+	const FLASHDATA_KEY = 'flash';
+	const FLASHDATA_NEW = ':new:';
+	const FLASHDATA_OLD = ':old:';
+	const FLASHDATA_EXP = ':exp:';
+	const EXPIRATION_KEY = '__expirations';
+	const TEMP_EXP_DEF = 300;
+
+	/**
+	 * CI_Session constructor
+	 *
+	 * The constructor loads the configured driver ('sess_driver' in config.php or as a parameter), running
+	 * routines in its constructor, and manages flashdata aging.
+	 *
+	 * @param	array	Configuration parameters
+	 * @return	void
+	 */
+	public function __construct(array $params = array())
+	{
+		$CI =& get_instance();
+
+		// No sessions under CLI
+		if ($CI->input->is_cli_request())
+		{
+			return;
+		}
+
+		log_message('debug', 'CI_Session Class Initialized');
+
+		// Get valid drivers list
+		$this->valid_drivers = array(
+			'Session_native',
+		   	'Session_cookie'
+		);
+		$key = 'sess_valid_drivers';
+		$drivers = isset($params[$key]) ? $params[$key] : $CI->config->item($key);
+		if ($drivers)
+		{
+			is_array($drivers) OR $drivers = array($drivers);
+
+			// Add driver names to valid list
+			foreach ($drivers as $driver)
+			{
+				if ( ! in_array(strtolower($driver), array_map('strtolower', $this->valid_drivers)))
+				{
+					$this->valid_drivers[] = $driver;
+				}
+			}
+		}
+
+		// Get driver to load
+		$key = 'sess_driver';
+		$driver = isset($params[$key]) ? $params[$key] : $CI->config->item($key);
+		if ( ! $driver)
+		{
+			$driver = 'cookie';
+		}
+
+		if ( ! in_array('session_'.strtolower($driver), array_map('strtolower', $this->valid_drivers)))
+		{
+			$this->valid_drivers[] = 'Session_'.$driver;
+		}
+
+		// Save a copy of parameters in case drivers need access
+		$this->params = $params;
+
+		// Load driver and get array reference
+		$this->load_driver($driver);
+
+		// Delete 'old' flashdata (from last request)
+		$this->_flashdata_sweep();
+
+		// Mark all new flashdata as old (data will be deleted before next request)
+		$this->_flashdata_mark();
+
+		// Delete expired tempdata
+		$this->_tempdata_sweep();
+
+		log_message('debug', 'CI_Session routines successfully run');
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Loads session storage driver
+	 *
+	 * @param	string	Driver classname
+	 * @return	object	Loaded driver object
+	 */
+	public function load_driver($driver)
+	{
+		// Save reference to most recently loaded driver as library default and sync userdata
+		$this->current = parent::load_driver($driver);
+		$this->userdata =& $this->current->get_userdata();
+		return $this->current;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Select default session storage driver
+	 *
+	 * @param	string	Driver classname
+	 * @return	void
+	 */
+	public function select_driver($driver)
+	{
+		// Validate driver name
+		$lowername = strtolower(str_replace('CI_', '', $driver));
+		if (in_array($lowername, array_map('strtolower', $this->valid_drivers)))
+		{
+			// See if driver is loaded
+			$child = str_replace($this->lib_name.'_', '', $driver);
+			if (isset($this->$child))
+			{
+				// See if driver is already current
+				if ($this->$child !== $this->current)
+				{
+					// Make driver current and sync userdata
+					$this->current = $this->$child;
+					$this->userdata =& $this->current->get_userdata();
+				}
+			}
+			else
+			{
+				// Load new driver
+				$this->load_driver($child);
+			}
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Destroy the current session
+	 *
+	 * @return	void
+	 */
+	public function sess_destroy()
+	{
+		// Just call destroy on driver
+		$this->current->sess_destroy();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Regenerate the current session
+	 *
+	 * @param	bool	Destroy session data flag (default: false)
+	 * @return	void
+	 */
+	public function sess_regenerate($destroy = FALSE)
+	{
+		// Call regenerate on driver and resync userdata
+		$this->current->sess_regenerate($destroy);
+		$this->userdata =& $this->current->get_userdata();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch a specific item from the session array
+	 *
+	 * @param	string	Item key
+	 * @return	string	Item value or NULL if not found
+	 */
+	public function userdata($item)
+	{
+		return isset($this->userdata[$item]) ? $this->userdata[$item] : NULL;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch all session data
+	 *
+	 * @return	array	User data array
+	 */
+	public function all_userdata()
+	{
+		return isset($this->userdata) ? $this->userdata : NULL;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch all flashdata
+	 *
+	 * @return	array   Flash data array
+	 */
+	public function all_flashdata()
+	{
+		$out = array();
+
+		// loop through all userdata
+		foreach ($this->all_userdata() as $key => $val)
+		{
+			// if it contains flashdata, add it
+			if (strpos($key, self::FLASHDATA_KEY.self::FLASHDATA_OLD) !== FALSE)
+			{
+				$key = str_replace(self::FLASHDATA_KEY.self::FLASHDATA_OLD, '', $key);
+				$out[$key] = $val;
+			}
+		}
+		return $out;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Add or change data in the "userdata" array
+	 *
+	 * @param	mixed	Item name or array of items
+	 * @param	string	Item value or empty string
+	 * @return	void
+	 */
+	public function set_userdata($newdata = array(), $newval = '')
+	{
+		// Wrap params as array if singular
+		if (is_string($newdata))
+		{
+			$newdata = array($newdata => $newval);
+		}
+
+		// Set each name/value pair
+		if (count($newdata) > 0)
+		{
+			foreach ($newdata as $key => $val)
+			{
+				$this->userdata[$key] = $val;
+			}
+		}
+
+		// Tell driver data changed
+		$this->current->sess_save();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Delete a session variable from the "userdata" array
+	 *
+	 * @param	mixed	Item name or array of item names
+	 * @return	void
+	 */
+	public function unset_userdata($newdata = array())
+	{
+		// Wrap single name as array
+		if (is_string($newdata))
+		{
+			$newdata = array($newdata => '');
+		}
+
+		// Unset each item name
+		if (count($newdata) > 0)
+		{
+			foreach (array_keys($newdata) as $key)
+			{
+				unset($this->userdata[$key]);
+			}
+		}
+
+		// Tell driver data changed
+		$this->current->sess_save();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Determine if an item exists
+	 *
+	 * @param	string	Item name
+	 * @return	bool
+	 */
+	public function has_userdata($item)
+	{
+		return isset($this->userdata[$item]);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Add or change flashdata, only available until the next request
+	 *
+	 * @param	mixed	Item name or array of items
+	 * @param	string	Item value or empty string
+	 * @return	void
+	 */
+	public function set_flashdata($newdata = array(), $newval = '')
+	{
+		// Wrap item as array if singular
+		if (is_string($newdata))
+		{
+			$newdata = array($newdata => $newval);
+		}
+
+		// Prepend each key name and set value
+		if (count($newdata) > 0)
+		{
+			foreach ($newdata as $key => $val)
+			{
+				$flashdata_key = self::FLASHDATA_KEY.self::FLASHDATA_NEW.$key;
+				$this->set_userdata($flashdata_key, $val);
+			}
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Keeps existing flashdata available to next request.
+	 *
+	 * @param	string	Item key
+	 * @return	void
+	 */
+	public function keep_flashdata($key)
+	{
+		// 'old' flashdata gets removed. Here we mark all flashdata as 'new' to preserve it from _flashdata_sweep()
+		// Note the function will return NULL if the $key provided cannot be found
+		$old_flashdata_key = self::FLASHDATA_KEY.self::FLASHDATA_OLD.$key;
+		$value = $this->userdata($old_flashdata_key);
+
+		$new_flashdata_key = self::FLASHDATA_KEY.self::FLASHDATA_NEW.$key;
+		$this->set_userdata($new_flashdata_key, $value);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch a specific flashdata item from the session array
+	 *
+	 * @param	string	Item key
+	 * @return	string
+	 */
+	public function flashdata($key)
+	{
+		// Prepend key and retrieve value
+		$flashdata_key = self::FLASHDATA_KEY.self::FLASHDATA_OLD.$key;
+		return $this->userdata($flashdata_key);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Add or change tempdata, only available until expiration
+	 *
+	 * @param	mixed	Item name or array of items
+	 * @param	string	Item value or empty string
+	 * @param	int	Item lifetime in seconds or 0 for default
+	 * @return	void
+	 */
+	public function set_tempdata($newdata = array(), $newval = '', $expire = 0)
+	{
+		// Set expiration time
+		$expire = time() + ($expire ? $expire : self::TEMP_EXP_DEF);
+
+		// Wrap item as array if singular
+		if (is_string($newdata))
+		{
+			$newdata = array($newdata => $newval);
+		}
+
+		// Get or create expiration list
+		$expirations = $this->userdata(self::EXPIRATION_KEY);
+		if ( ! $expirations)
+		{
+			$expirations = array();
+		}
+
+		// Prepend each key name and set value
+		if (count($newdata) > 0)
+		{
+			foreach ($newdata as $key => $val)
+			{
+				$tempdata_key = self::FLASHDATA_KEY.self::FLASHDATA_EXP.$key;
+				$expirations[$tempdata_key] = $expire;
+				$this->set_userdata($tempdata_key, $val);
+			}
+		}
+
+		// Update expiration list
+		$this->set_userdata(self::EXPIRATION_KEY, $expirations);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Delete a temporary session variable from the "userdata" array
+	 *
+	 * @param	mixed	Item name or array of item names
+	 * @return	void
+	 */
+	public function unset_tempdata($newdata = array())
+	{
+		// Get expirations list
+		$expirations = $this->userdata(self::EXPIRATION_KEY);
+		if (empty($expirations))
+		{
+			// Nothing to do
+			return;
+		}
+
+		// Wrap single name as array
+		if (is_string($newdata))
+		{
+			$newdata = array($newdata => '');
+		}
+
+		// Prepend each item name and unset
+		if (count($newdata) > 0)
+		{
+			foreach (array_keys($newdata) as $key)
+			{
+				$tempdata_key = self::FLASHDATA_KEY.self::FLASHDATA_EXP.$key;
+				unset($expirations[$tempdata_key]);
+				$this->unset_userdata($tempdata_key);
+			}
+		}
+
+		// Update expiration list
+		$this->set_userdata(self::EXPIRATION_KEY, $expirations);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch a specific tempdata item from the session array
+	 *
+	 * @param	string	Item key
+	 * @return	string
+	 */
+	public function tempdata($key)
+	{
+		// Prepend key and return value
+		$tempdata_key = self::FLASHDATA_KEY.self::FLASHDATA_EXP.$key;
+		return $this->userdata($tempdata_key);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Identifies flashdata as 'old' for removal
+	 * when _flashdata_sweep() runs.
+	 *
+	 * @return	void
+	 */
+	protected function _flashdata_mark()
+	{
+		foreach ($this->all_userdata() as $name => $value)
+		{
+			$parts = explode(self::FLASHDATA_NEW, $name);
+			if (is_array($parts) && count($parts) === 2)
+			{
+				$new_name = self::FLASHDATA_KEY.self::FLASHDATA_OLD.$parts[1];
+				$this->set_userdata($new_name, $value);
+				$this->unset_userdata($name);
+			}
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Removes all flashdata marked as 'old'
+	 *
+	 * @return	void
+	 */
+	protected function _flashdata_sweep()
+	{
+		$userdata = $this->all_userdata();
+		foreach (array_keys($userdata) as $key)
+		{
+			if (strpos($key, self::FLASHDATA_OLD))
+			{
+				$this->unset_userdata($key);
+			}
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Removes all expired tempdata
+	 *
+	 * @return	void
+	 */
+	protected function _tempdata_sweep()
+	{
+		// Get expirations list
+		$expirations = $this->userdata(self::EXPIRATION_KEY);
+		if (empty($expirations))
+		{
+			// Nothing to do
+			return;
+		}
+
+		// Unset expired elements
+		$now = time();
+		$userdata = $this->all_userdata();
+		foreach (array_keys($userdata) as $key)
+		{
+			if (strpos($key, self::FLASHDATA_EXP) && $expirations[$key] < $now)
+			{
+				unset($expirations[$key]);
+				$this->unset_userdata($key);
+			}
+		}
+
+		// Update expiration list
+		$this->set_userdata(self::EXPIRATION_KEY, $expirations);
+	}
+
+}
+
+// ------------------------------------------------------------------------
+
+/**
+ * CI_Session_driver Class
+ *
+ * Extend this class to make a new CI_Session driver.
+ * A CI_Session driver basically manages an array of name/value pairs with some sort of storage mechanism.
+ * To make a new driver, derive from (extend) CI_Session_driver. Overload the initialize method and read or create
+ * session data. Then implement a save handler to write changed data to storage (sess_save), a destroy handler
+ * to remove deleted data (sess_destroy), and an access handler to expose the data (get_userdata).
+ * Put your driver in the libraries/Session/drivers folder anywhere in the loader paths. This includes the
+ * application directory, the system directory, or any path you add with $CI->load->add_package_path().
+ * Your driver must be named CI_Session_<name>, and your filename must be Session_<name>.php,
+ * preferably also capitalized. (e.g.: CI_Session_foo in libraries/Session/drivers/Session_foo.php)
+ * Then specify the driver by setting 'sess_driver' in your config file or as a parameter when loading the CI_Session
+ * object. (e.g.: $config['sess_driver'] = 'foo'; OR $CI->load->driver('session', array('sess_driver' => 'foo')); )
+ * Already provided are the Native driver, which manages the native PHP $_SESSION array, and
+ * the Cookie driver, which manages the data in a browser cookie, with optional extra storage in a database table.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Sessions
+ * @author		EllisLab Dev Team
+ */
+abstract class CI_Session_driver extends CI_Driver {
+
+	protected $CI;
+
+	/**
+	 * Constructor
+	 *
+	 * Gets the CI singleton, so that individual drivers
+	 * don't have to do it separately.
+	 *
+	 * @return	void
+	 */
+	public function __construct()
+	{
+		$this->CI =& get_instance();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Decorate
+	 *
+	 * Decorates the child with the parent driver lib's methods and properties
+	 *
+	 * @param	object	Parent library object
+	 * @return	void
+	 */
+	public function decorate($parent)
+	{
+		// Call base class decorate first
+		parent::decorate($parent);
+
+		// Call initialize method now that driver has access to $this->_parent
+		$this->initialize();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * __call magic method
+	 *
+	 * Handles access to the parent driver library's methods
+	 *
+	 * @param	string	Library method name
+	 * @param	array	Method arguments (default: none)
+	 * @return	mixed
+	 */
+	public function __call($method, $args = array())
+	{
+		// Make sure the parent library uses this driver
+		$this->_parent->select_driver(get_class($this));
+		return parent::__call($method, $args);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Initialize driver
+	 *
+	 * @return	void
+	 */
+	protected function initialize()
+	{
+		// Overload this method to implement initialization
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Save the session data
+	 *
+	 * Data in the array has changed - perform any storage synchronization
+	 * necessary. The child class MUST implement this abstract method!
+	 *
+	 * @return	void
+	 */
+	abstract public function sess_save();
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Destroy the current session
+	 *
+	 * Clean up storage for this session - it has been terminated.
+	 * The child class MUST implement this abstract method!
+	 *
+	 * @return	void
+	 */
+	abstract public function sess_destroy();
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Regenerate the current session
+	 *
+	 * Regenerate the session ID.
+	 * The child class MUST implement this abstract method!
+	 *
+	 * @param	bool	Destroy session data flag (default: false)
+	 * @return	void
+	 */
+	abstract public function sess_regenerate($destroy = FALSE);
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get a reference to user data array
+	 *
+	 * Give array access to the main CI_Session object.
+	 * The child class MUST implement this abstract method!
+	 *
+	 * @return	array	Reference to userdata
+	 */
+	abstract public function &get_userdata();
+
+}
+
+/* End of file Session.php */
+/* Location: ./system/libraries/Session/Session.php */
\ No newline at end of file
diff --git a/system/libraries/Session/drivers/Session_cookie.php b/system/libraries/Session/drivers/Session_cookie.php
new file mode 100755
index 0000000..fb62c7e
--- /dev/null
+++ b/system/libraries/Session/drivers/Session_cookie.php
@@ -0,0 +1,814 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst. It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+/**
+ * Cookie-based session management driver
+ *
+ * This is the classic CI_Session functionality, as written by EllisLab, abstracted out to a driver.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Sessions
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/libraries/sessions.html
+ */
+class CI_Session_cookie extends CI_Session_driver {
+
+	/**
+	 * Whether to encrypt the session cookie
+	 *
+	 * @var bool
+	 */
+	public $sess_encrypt_cookie		= FALSE;
+
+	/**
+	 * Whether to use to the database for session storage
+	 *
+	 * @var bool
+	 */
+	public $sess_use_database		= FALSE;
+
+	/**
+	 * Name of the database table in which to store sessions
+	 *
+	 * @var string
+	 */
+	public $sess_table_name			= '';
+
+	/**
+	 * Length of time (in seconds) for sessions to expire
+	 *
+	 * @var int
+	 */
+	public $sess_expiration			= 7200;
+
+	/**
+	 * Whether to kill session on close of browser window
+	 *
+	 * @var bool
+	 */
+	public $sess_expire_on_close	= FALSE;
+
+	/**
+	 * Whether to match session on ip address
+	 *
+	 * @var bool
+	 */
+	public $sess_match_ip			= FALSE;
+
+	/**
+	 * Whether to match session on user-agent
+	 *
+	 * @var bool
+	 */
+	public $sess_match_useragent	= TRUE;
+
+	/**
+	 * Name of session cookie
+	 *
+	 * @var string
+	 */
+	public $sess_cookie_name		= 'ci_session';
+
+	/**
+	 * Session cookie prefix
+	 *
+	 * @var string
+	 */
+	public $cookie_prefix			= '';
+
+	/**
+	 * Session cookie path
+	 *
+	 * @var string
+	 */
+	public $cookie_path				= '';
+
+	/**
+	 * Session cookie domain
+	 *
+	 * @var string
+	 */
+	public $cookie_domain			= '';
+
+	/**
+	 * Whether to set the cookie only on HTTPS connections
+	 *
+	 * @var bool
+	 */
+	public $cookie_secure			= FALSE;
+
+	/**
+	 * Whether cookie should be allowed only to be sent by the server
+	 *
+	 * @var bool
+	 */
+	public $cookie_httponly 		= FALSE;
+
+	/**
+	 * Interval at which to update session
+	 *
+	 * @var int
+	 */
+	public $sess_time_to_update		= 300;
+
+	/**
+	 * Key with which to encrypt the session cookie
+	 *
+	 * @var string
+	 */
+	public $encryption_key			= '';
+
+	/**
+	 * Timezone to use for the current time
+	 *
+	 * @var string
+	 */
+	public $time_reference			= 'local';
+
+	/**
+	 * Session data
+	 *
+	 * @var array
+	 */
+	public $userdata				= array();
+
+	/**
+	 * Current time
+	 *
+	 * @var int
+	 */
+	public $now;
+
+	/**
+	 * Default userdata keys
+	 *
+	 * @var	array
+	 */
+	protected $defaults = array(
+		'session_id' => NULL,
+		'ip_address' => NULL,
+		'user_agent' => NULL,
+		'last_activity' => NULL
+	);
+
+	/**
+	 * Data needs DB update flag
+	 *
+	 * @var	bool
+	 */
+	protected $data_dirty = FALSE;
+
+	/**
+	 * Initialize session driver object
+	 *
+	 * @return	void
+	 */
+	protected function initialize()
+	{
+		// Set all the session preferences, which can either be set
+		// manually via the $params array or via the config file
+		$prefs = array(
+			'sess_encrypt_cookie',
+			'sess_use_database',
+			'sess_table_name',
+			'sess_expiration',
+			'sess_expire_on_close',
+			'sess_match_ip',
+			'sess_match_useragent',
+			'sess_cookie_name',
+			'cookie_path',
+			'cookie_domain',
+			'cookie_secure',
+			'cookie_httponly',
+			'sess_time_to_update',
+			'time_reference',
+			'cookie_prefix',
+			'encryption_key'
+		);
+
+		foreach ($prefs as $key)
+		{
+			$this->$key = isset($this->_parent->params[$key])
+				? $this->_parent->params[$key]
+				: $this->CI->config->item($key);
+		}
+
+		if ($this->encryption_key === '')
+		{
+			show_error('In order to use the Cookie Session driver you are required to set an encryption key in your config file.');
+		}
+
+		// Load the string helper so we can use the strip_slashes() function
+		$this->CI->load->helper('string');
+
+		// Do we need encryption? If so, load the encryption class
+		if ($this->sess_encrypt_cookie === TRUE)
+		{
+			$this->CI->load->library('encrypt');
+		}
+
+		// Check for database
+		if ($this->sess_use_database === TRUE && $this->sess_table_name !== '')
+		{
+			// Load database driver
+			$this->CI->load->database();
+
+			// Register shutdown function
+			register_shutdown_function(array($this, '_update_db'));
+		}
+
+		// Set the "now" time. Can either be GMT or server time, based on the config prefs.
+		// We use this to set the "last activity" time
+		$this->now = $this->_get_time();
+
+		// Set the session length. If the session expiration is
+		// set to zero we'll set the expiration two years from now.
+		if ($this->sess_expiration === 0)
+		{
+			$this->sess_expiration = (60*60*24*365*2);
+		}
+
+		// Set the cookie name
+		$this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
+
+		// Run the Session routine. If a session doesn't exist we'll
+		// create a new one. If it does, we'll update it.
+		if ( ! $this->_sess_read())
+		{
+			$this->_sess_create();
+		}
+		else
+		{
+			$this->_sess_update();
+		}
+
+		// Delete expired sessions if necessary
+		$this->_sess_gc();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Write the session data
+	 *
+	 * @return	void
+	 */
+	public function sess_save()
+	{
+		// Check for database
+		if ($this->sess_use_database === TRUE)
+		{
+			// Mark custom data as dirty so we know to update the DB
+			$this->data_dirty = TRUE;
+		}
+
+		// Write the cookie
+		$this->_set_cookie();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Destroy the current session
+	 *
+	 * @return	void
+	 */
+	public function sess_destroy()
+	{
+		// Kill the session DB row
+		if ($this->sess_use_database === TRUE && isset($this->userdata['session_id']))
+		{
+			$this->CI->db->delete($this->sess_table_name, array('session_id' => $this->userdata['session_id']));
+			$this->data_dirty = FALSE;
+		}
+
+		// Kill the cookie
+		$this->_setcookie($this->sess_cookie_name, addslashes(serialize(array())), ($this->now - 31500000),
+			$this->cookie_path, $this->cookie_domain, 0);
+
+		// Kill session data
+		$this->userdata = array();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Regenerate the current session
+	 *
+	 * Regenerate the session id
+	 *
+	 * @param	bool	Destroy session data flag (default: false)
+	 * @return	void
+	 */
+	public function sess_regenerate($destroy = FALSE)
+	{
+		// Check destroy flag
+		if ($destroy)
+		{
+			// Destroy old session and create new one
+			$this->sess_destroy();
+			$this->_sess_create();
+		}
+		else
+		{
+			// Just force an update to recreate the id
+			$this->_sess_update(TRUE);
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get a reference to user data array
+	 *
+	 * @return	array	Reference to userdata
+	 */
+	public function &get_userdata()
+	{
+		return $this->userdata;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch the current session data if it exists
+	 *
+	 * @return	bool
+	 */
+	protected function _sess_read()
+	{
+		// Fetch the cookie
+		$session = $this->CI->input->cookie($this->sess_cookie_name);
+
+		// No cookie? Goodbye cruel world!...
+		if ($session === NULL)
+		{
+			log_message('debug', 'A session cookie was not found.');
+			return FALSE;
+		}
+
+		// Check for encryption
+		if ($this->sess_encrypt_cookie === TRUE)
+		{
+			// Decrypt the cookie data
+			$session = $this->CI->encrypt->decode($session);
+		}
+		else
+		{
+			// Encryption was not used, so we need to check the md5 hash in the last 32 chars
+			$len	 = strlen($session)-32;
+			$hash	 = substr($session, $len);
+			$session = substr($session, 0, $len);
+
+			// Does the md5 hash match? This is to prevent manipulation of session data in userspace
+			if ($hash !== md5($session.$this->encryption_key))
+			{
+				log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.');
+				$this->sess_destroy();
+				return FALSE;
+			}
+		}
+
+		// Unserialize the session array
+		$session = $this->_unserialize($session);
+
+		// Is the session data we unserialized an array with the correct format?
+		if ( ! is_array($session) OR ! isset($session['session_id'], $session['ip_address'], $session['user_agent'], $session['last_activity']))
+		{
+			$this->sess_destroy();
+			return FALSE;
+		}
+
+		// Is the session current?
+		if (($session['last_activity'] + $this->sess_expiration) < $this->now)
+		{
+			$this->sess_destroy();
+			return FALSE;
+		}
+
+		// Does the IP match?
+		if ($this->sess_match_ip === TRUE && $session['ip_address'] !== $this->CI->input->ip_address())
+		{
+			$this->sess_destroy();
+			return FALSE;
+		}
+
+		// Does the User Agent Match?
+		if ($this->sess_match_useragent === TRUE &&
+			trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
+		{
+			$this->sess_destroy();
+			return FALSE;
+		}
+
+		// Is there a corresponding session in the DB?
+		if ($this->sess_use_database === TRUE)
+		{
+			$this->CI->db->where('session_id', $session['session_id']);
+
+			if ($this->sess_match_ip === TRUE)
+			{
+				$this->CI->db->where('ip_address', $session['ip_address']);
+			}
+
+			if ($this->sess_match_useragent === TRUE)
+			{
+				$this->CI->db->where('user_agent', $session['user_agent']);
+			}
+
+			// Is caching in effect? Turn it off
+			$db_cache = $this->CI->db->cache_on;
+			$this->CI->db->cache_off();
+
+			$query = $this->CI->db->limit(1)->get($this->sess_table_name);
+
+			// Was caching in effect?
+			if ($db_cache)
+			{
+				// Turn it back on
+				$this->CI->db->cache_on();
+			}
+
+			// No result? Kill it!
+			if ($query->num_rows() === 0)
+			{
+				$this->sess_destroy();
+				return FALSE;
+			}
+
+			// Is there custom data? If so, add it to the main session array
+			$row = $query->row();
+			if ( ! empty($row->user_data))
+			{
+				$custom_data = $this->_unserialize($row->user_data);
+
+				if (is_array($custom_data))
+				{
+					$session = $session + $custom_data;
+				}
+			}
+		}
+
+		// Session is valid!
+		$this->userdata = $session;
+		return TRUE;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Create a new session
+	 *
+	 * @return	void
+	 */
+	protected function _sess_create()
+	{
+		// Initialize userdata
+		$this->userdata = array(
+			'session_id'	=> $this->_make_sess_id(),
+			'ip_address'	=> $this->CI->input->ip_address(),
+			'user_agent'	=> substr($this->CI->input->user_agent(), 0, 120),
+			'last_activity'	=> $this->now,
+		);
+
+		// Check for database
+		if ($this->sess_use_database === TRUE)
+		{
+			// Add empty user_data field and save the data to the DB
+			$this->CI->db->set('user_data', '')->insert($this->sess_table_name, $this->userdata);
+		}
+
+		// Write the cookie
+		$this->_set_cookie();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Update an existing session
+	 *
+	 * @param	bool	Force update flag (default: false)
+	 * @return	void
+	 */
+	protected function _sess_update($force = FALSE)
+	{
+		// We only update the session every five minutes by default (unless forced)
+		if ( ! $force && ($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
+		{
+			return;
+		}
+
+		// Update last activity to now
+		$this->userdata['last_activity'] = $this->now;
+
+		// Save the old session id so we know which DB record to update
+		$old_sessid = $this->userdata['session_id'];
+
+		// Changing the session ID during an AJAX call causes problems
+		if ( ! $this->CI->input->is_ajax_request())
+		{
+			// Get new id
+			$this->userdata['session_id'] = $this->_make_sess_id();
+		}
+
+		// Check for database
+		if ($this->sess_use_database === TRUE)
+		{
+			// Update the session ID and last_activity field in the DB
+			$this->CI->db->update($this->sess_table_name, array(
+					 'last_activity' => $this->now,
+					 'session_id' => $this->userdata['session_id']
+			), array('session_id' => $old_sessid));
+		}
+
+		// Write the cookie
+		$this->_set_cookie();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Update database with current data
+	 *
+	 * This gets called from the shutdown function and also
+	 * registered with PHP to run at the end of the request
+	 * so it's guaranteed to update even when a fatal error
+	 * occurs. The first call makes the update and clears the
+	 * dirty flag so it won't happen twice.
+	 *
+	 * @return	void
+	 */
+	public function _update_db()
+	{
+		// Check for database and dirty flag and unsaved
+		if ($this->sess_use_database === TRUE && $this->data_dirty === TRUE)
+		{
+			// Set up activity and data fields to be set
+			// If we don't find custom data, user_data will remain an empty string
+			$set = array(
+				'last_activity' => $this->userdata['last_activity'],
+				'user_data' => ''
+			);
+
+			// Get the custom userdata, leaving out the defaults
+			// (which get stored in the cookie)
+			$userdata = array_diff_key($this->userdata, $this->defaults);
+
+			// Did we find any custom data?
+			if ( ! empty($userdata))
+			{
+				// Serialize the custom data array so we can store it
+				$set['user_data'] = $this->_serialize($userdata);
+			}
+
+			// Run the update query
+			// Any time we change the session id, it gets updated immediately,
+			// so our where clause below is always safe
+			$this->CI->db->update($this->sess_table_name, $set, array('session_id' => $this->userdata['session_id']));
+
+			// Clear dirty flag to prevent double updates
+			$this->data_dirty = FALSE;
+
+			log_message('debug', 'CI_Session Data Saved To DB');
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Generate a new session id
+	 *
+	 * @return	string	Hashed session id
+	 */
+	protected function _make_sess_id()
+	{
+		$new_sessid = '';
+		do
+		{
+			$new_sessid .= mt_rand(0, mt_getrandmax());
+		}
+		while (strlen($new_sessid) < 32);
+
+		// To make the session ID even more secure we'll combine it with the user's IP
+		$new_sessid .= $this->CI->input->ip_address();
+
+		// Turn it into a hash and return
+		return md5(uniqid($new_sessid, TRUE));
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get the "now" time
+	 *
+	 * @return	int	 Time
+	 */
+	protected function _get_time()
+	{
+		if ($this->time_reference === 'local' OR $this->time_reference === date_default_timezone_get())
+		{
+			return time();
+		}
+
+		$datetime = new DateTime('now', new DateTimeZone($this->time_reference));
+		sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
+
+		return mktime($hour, $minute, $second, $month, $day, $year);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Write the session cookie
+	 *
+	 * @return	void
+	 */
+	protected function _set_cookie()
+	{
+		// Get userdata (only defaults if database)
+		$cookie_data = ($this->sess_use_database === TRUE)
+				? array_intersect_key($this->userdata, $this->defaults)
+				: $this->userdata;
+
+		// Serialize the userdata for the cookie
+		$cookie_data = $this->_serialize($cookie_data);
+
+		$cookie_data = ($this->sess_encrypt_cookie === TRUE)
+			? $this->CI->encrypt->encode($cookie_data)
+			// if encryption is not used, we provide an md5 hash to prevent userside tampering
+			: $cookie_data.md5($cookie_data.$this->encryption_key);
+
+		$expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
+
+		// Set the cookie
+		$this->_setcookie($this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain,
+			$this->cookie_secure, $this->cookie_httponly);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Set a cookie with the system
+	 *
+	 * This abstraction of the setcookie call allows overriding for unit testing
+	 *
+	 * @param	string	Cookie name
+	 * @param	string	Cookie value
+	 * @param	int	Expiration time
+	 * @param	string	Cookie path
+	 * @param	string	Cookie domain
+	 * @param	bool	Secure connection flag
+	 * @param	bool	HTTP protocol only flag
+	 * @return	void
+	 */
+	protected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = FALSE, $httponly = FALSE)
+	{
+		setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Serialize an array
+	 *
+	 * This function first converts any slashes found in the array to a temporary
+	 * marker, so when it gets unserialized the slashes will be preserved
+	 *
+	 * @param	mixed	Data to serialize
+	 * @return	string	Serialized data
+	 */
+	protected function _serialize($data)
+	{
+		if (is_array($data))
+		{
+			array_walk_recursive($data, array(&$this, '_escape_slashes'));
+		}
+		elseif (is_string($data))
+		{
+			$data = str_replace('\\', '{{slash}}', $data);
+		}
+
+		return serialize($data);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Escape slashes
+	 *
+	 * This function converts any slashes found into a temporary marker
+	 *
+	 * @param	string	Value
+	 * @param	string	Key
+	 * @return	void
+	 */
+	protected function _escape_slashes(&$val, $key)
+	{
+		if (is_string($val))
+		{
+			$val = str_replace('\\', '{{slash}}', $val);
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Unserialize
+	 *
+	 * This function unserializes a data string, then converts any
+	 * temporary slash markers back to actual slashes
+	 *
+	 * @param	mixed	Data to unserialize
+	 * @return	mixed	Unserialized data
+	 */
+	protected function _unserialize($data)
+	{
+		$data = @unserialize(strip_slashes(trim($data)));
+
+		if (is_array($data))
+		{
+			array_walk_recursive($data, array(&$this, '_unescape_slashes'));
+			return $data;
+		}
+
+		return is_string($data) ? str_replace('{{slash}}', '\\', $data) : $data;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Unescape slashes
+	 *
+	 * This function converts any slash markers back into actual slashes
+	 *
+	 * @param	string	Value
+	 * @param	string	Key
+	 * @return	void
+	 */
+	protected function _unescape_slashes(&$val, $key)
+	{
+		if (is_string($val))
+		{
+	 		$val= str_replace('{{slash}}', '\\', $val);
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Garbage collection
+	 *
+	 * This deletes expired session rows from database
+	 * if the probability percentage is met
+	 *
+	 * @return	void
+	 */
+	protected function _sess_gc()
+	{
+		if ($this->sess_use_database !== TRUE)
+		{
+			return;
+		}
+
+		$probability = ini_get('session.gc_probability');
+		$divisor = ini_get('session.gc_divisor');
+
+		srand(time());
+		if ((mt_rand(0, $divisor) / $divisor) < $probability)
+		{
+			$expire = $this->now - $this->sess_expiration;
+			$this->CI->db->delete($this->sess_table_name, 'last_activity < '.$expire);
+
+			log_message('debug', 'Session garbage collection performed.');
+		}
+	}
+
+}
+
+/* End of file Session_cookie.php */
+/* Location: ./system/libraries/Session/drivers/Session_cookie.php */
\ No newline at end of file
diff --git a/system/libraries/Session/drivers/Session_native.php b/system/libraries/Session/drivers/Session_native.php
new file mode 100755
index 0000000..8d5e515
--- /dev/null
+++ b/system/libraries/Session/drivers/Session_native.php
@@ -0,0 +1,231 @@
+<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst. It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+/**
+ * Native PHP session management driver
+ *
+ * This is the driver that uses the native PHP $_SESSION array through the Session driver library.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Sessions
+ * @author		EllisLab Dev Team
+ */
+class CI_Session_native extends CI_Session_driver {
+
+	/**
+	 * Initialize session driver object
+	 *
+	 * @return	void
+	 */
+	protected function initialize()
+	{
+		// Get config parameters
+		$config = array();
+		$prefs = array(
+			'sess_cookie_name',
+			'sess_expire_on_close',
+			'sess_expiration',
+			'sess_match_ip',
+			'sess_match_useragent',
+			'sess_time_to_update',
+			'cookie_prefix',
+			'cookie_path',
+			'cookie_domain'
+		);
+
+		foreach ($prefs as $key)
+		{
+			$config[$key] = isset($this->_parent->params[$key])
+				? $this->_parent->params[$key]
+				: $this->CI->config->item($key);
+		}
+
+		// Set session name, if specified
+		if ($config['sess_cookie_name'])
+		{
+			// Differentiate name from cookie driver with '_id' suffix
+			$name = $config['sess_cookie_name'].'_id';
+			if ($config['cookie_prefix'])
+			{
+				// Prepend cookie prefix
+				$name = $config['cookie_prefix'].$name;
+			}
+			session_name($name);
+		}
+
+		// Set expiration, path, and domain
+		$expire = 7200;
+		$path = '/';
+		$domain = '';
+		if ($config['sess_expiration'] !== FALSE)
+		{
+			// Default to 2 years if expiration is "0"
+			$expire = ($config['sess_expiration'] == 0) ? (60*60*24*365*2) : $config['sess_expiration'];
+		}
+
+		if ($config['cookie_path'])
+		{
+			// Use specified path
+			$path = $config['cookie_path'];
+		}
+
+		if ($config['cookie_domain'])
+		{
+			// Use specified domain
+			$domain = $config['cookie_domain'];
+		}
+		session_set_cookie_params($config['sess_expire_on_close'] ? 0 : $expire, $path, $domain);
+
+		// Start session
+		session_start();
+
+		// Check session expiration, ip, and agent
+		$now = time();
+		$destroy = FALSE;
+		if (isset($_SESSION['last_activity']) && ($_SESSION['last_activity'] + $expire) < $now)
+		{
+			// Expired - destroy
+			$destroy = TRUE;
+		}
+		elseif ($config['sess_match_ip'] === TRUE && isset($_SESSION['ip_address'])
+			&& $_SESSION['ip_address'] !== $this->CI->input->ip_address())
+		{
+			// IP doesn't match - destroy
+			$destroy = TRUE;
+		}
+		elseif ($config['sess_match_useragent'] === TRUE && isset($_SESSION['user_agent'])
+			&& $_SESSION['user_agent'] !== trim(substr($this->CI->input->user_agent(), 0, 50)))
+		{
+			// Agent doesn't match - destroy
+			$destroy = TRUE;
+		}
+
+		// Destroy expired or invalid session
+		if ($destroy)
+		{
+			// Clear old session and start new
+			$this->sess_destroy();
+			session_start();
+		}
+
+		// Check for update time
+		if ($config['sess_time_to_update'] && isset($_SESSION['last_activity'])
+			&& ($_SESSION['last_activity'] + $config['sess_time_to_update']) < $now)
+		{
+			// Regenerate ID, but don't destroy session
+			$this->sess_regenerate(FALSE);
+		}
+
+		// Set activity time
+		$_SESSION['last_activity'] = $now;
+
+		// Set matching values as required
+		if ($config['sess_match_ip'] === TRUE && ! isset($_SESSION['ip_address']))
+		{
+			// Store user IP address
+			$_SESSION['ip_address'] = $this->CI->input->ip_address();
+		}
+
+		if ($config['sess_match_useragent'] === TRUE && ! isset($_SESSION['user_agent']))
+		{
+			// Store user agent string
+			$_SESSION['user_agent'] = trim(substr($this->CI->input->user_agent(), 0, 50));
+		}
+
+		// Make session ID available
+		$_SESSION['session_id'] = session_id();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Save the session data
+	 *
+	 * @return	void
+	 */
+	public function sess_save()
+	{
+		// Nothing to do - changes to $_SESSION are automatically saved
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Destroy the current session
+	 *
+	 * @return	void
+	 */
+	public function sess_destroy()
+	{
+		// Cleanup session
+		$_SESSION = array();
+		$name = session_name();
+		if (isset($_COOKIE[$name]))
+		{
+			// Clear session cookie
+			$params = session_get_cookie_params();
+			setcookie($name, '', time() - 42000, $params['path'], $params['domain']);
+			unset($_COOKIE[$name]);
+		}
+		session_destroy();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Regenerate the current session
+	 *
+	 * Regenerate the session id
+	 *
+	 * @param	bool	Destroy session data flag (default: FALSE)
+	 * @return	void
+	 */
+	public function sess_regenerate($destroy = FALSE)
+	{
+		// Just regenerate id, passing destroy flag
+		session_regenerate_id($destroy);
+		$_SESSION['session_id'] = session_id();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get a reference to user data array
+	 *
+	 * @return	array	Reference to userdata
+	 */
+	public function &get_userdata()
+	{
+		// Just return reference to $_SESSION
+		return $_SESSION;
+	}
+
+}
+
+/* End of file Session_native.php */
+/* Location: ./system/libraries/Session/drivers/Session_native.php */
\ No newline at end of file
diff --git a/system/libraries/Unit_test.php b/system/libraries/Unit_test.php
index 70ad8dc..c2c0175 100644
--- a/system/libraries/Unit_test.php
+++ b/system/libraries/Unit_test.php
@@ -240,6 +240,11 @@
 				{
 					foreach ($val as $k => $v)
 					{
+						if ( ! in_array($k, $this->_test_items_visible))
+						{
+							continue;
+						}
+
 						if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$v))))
 						{
 							$v = $line;
diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php
old mode 100644
new mode 100755
index cbb91c4..dc5d27f
--- a/system/libraries/Xmlrpc.php
+++ b/system/libraries/Xmlrpc.php
@@ -1317,15 +1317,15 @@
 		{
 			$type = $type === '' ? 'string' : $type;
 
-			if ($this->xmlrpcTypes[$type] === 1)
+			if ($this->xmlrpcTypes[$type] == 1)
 			{
 				$this->addScalar($val,$type);
 			}
-			elseif ($this->xmlrpcTypes[$type] === 2)
+			elseif ($this->xmlrpcTypes[$type] == 2)
 			{
 				$this->addArray($val);
 			}
-			elseif ($this->xmlrpcTypes[$type] === 3)
+			elseif ($this->xmlrpcTypes[$type] == 3)
 			{
 				$this->addStruct($val);
 			}
@@ -1351,7 +1351,7 @@
 			return 0;
 		}
 
-		if ($typeof !== 1)
+		if ($typeof != 1)
 		{
 			echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
 			return 0;
@@ -1359,7 +1359,7 @@
 
 		if ($type === $this->xmlrpcBoolean)
 		{
-			$val = (int) (strcasecmp($val,'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false')));
+			$val = (int) (strcasecmp($val, 'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false')));
 		}
 
 		if ($this->mytype === 2)
diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php
index 5216038..1c666d5 100644
--- a/tests/Bootstrap.php
+++ b/tests/Bootstrap.php
@@ -11,6 +11,7 @@
 defined('BASEPATH') OR define('BASEPATH', PROJECT_BASE.'system/');
 defined('APPPATH') OR define('APPPATH', PROJECT_BASE.'application/');
 defined('VIEWPATH') OR define('VIEWPATH', PROJECT_BASE.'');
+isset($_SERVER['REMOTE_ADDR']) OR $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
 
 // Get vfsStream either via PEAR or composer
 foreach (explode(PATH_SEPARATOR, get_include_path()) as $path)
diff --git a/tests/codeigniter/core/Common_test.php b/tests/codeigniter/core/Common_test.php
index f9bf6c2..27d48ef 100644
--- a/tests/codeigniter/core/Common_test.php
+++ b/tests/codeigniter/core/Common_test.php
@@ -10,4 +10,35 @@
 		$this->assertEquals(FALSE, is_php('9999.9.9'));
 	}
 
+	// ------------------------------------------------------------------------
+
+	public function test_stringify_attributes()
+	{
+		$this->assertEquals(' class="foo" id="bar"', _stringify_attributes(array('class' => 'foo', 'id' => 'bar')));
+
+		$atts = new Stdclass;
+		$atts->class = 'foo';
+		$atts->id = 'bar';
+		$this->assertEquals(' class="foo" id="bar"', _stringify_attributes($atts));
+
+		$atts = new Stdclass;
+		$this->assertEquals('', _stringify_attributes($atts));
+
+		$this->assertEquals(' class="foo" id="bar"', _stringify_attributes('class="foo" id="bar"'));
+
+		$this->assertEquals('', _stringify_attributes(array()));
+	}
+
+	// ------------------------------------------------------------------------
+
+	public function test_stringify_js_attributes()
+	{
+		$this->assertEquals('width=800,height=600', _stringify_attributes(array('width' => '800', 'height' => '600'), TRUE));
+
+		$atts = new Stdclass;
+		$atts->width = 800;
+		$atts->height = 600;
+		$this->assertEquals('width=800,height=600', _stringify_attributes($atts, TRUE));
+	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/escape_test.php b/tests/codeigniter/database/query_builder/escape_test.php
index c6380dd..27e678f 100644
--- a/tests/codeigniter/database/query_builder/escape_test.php
+++ b/tests/codeigniter/database/query_builder/escape_test.php
@@ -27,7 +27,7 @@
 
 		if (strpos(DB_DRIVER, 'mysql') !== FALSE)
 		{
-			$sql = "SELECT `value` FROM `misc` WHERE `key` LIKE '$string%' ESCAPE '';";
+			$sql = "SELECT `value` FROM `misc` WHERE `key` LIKE '$string%' ESCAPE '!';";
 		}
 		else
 		{
@@ -52,7 +52,7 @@
 
 		if (strpos(DB_DRIVER, 'mysql') !== FALSE)
 		{
-			$sql = "SELECT `value` FROM `misc` WHERE `key` LIKE '$string%' ESCAPE '';";
+			$sql = "SELECT `value` FROM `misc` WHERE `key` LIKE '$string%' ESCAPE '!';";
 		}
 		else
 		{
diff --git a/tests/codeigniter/helpers/date_helper_test.php b/tests/codeigniter/helpers/date_helper_test.php
index 1b79b94..9feade7 100644
--- a/tests/codeigniter/helpers/date_helper_test.php
+++ b/tests/codeigniter/helpers/date_helper_test.php
@@ -290,6 +290,29 @@
 		$this->assertEquals(0, timezones('non_existant'));
 	}
 
+	// ------------------------------------------------------------------------
+
+	public function test_date_range()
+	{
+		$dates = array(
+			'29-01-2012', '30-01-2012', '31-01-2012',
+			'01-02-2012', '02-02-2012', '03-02-2012',
+			'04-02-2012', '05-02-2012', '06-02-2012',
+			'07-02-2012', '08-02-2012', '09-02-2012',
+			'10-02-2012', '11-02-2012', '12-02-2012',
+			'13-02-2012', '14-02-2012', '15-02-2012',
+			'16-02-2012', '17-02-2012', '18-02-2012',
+			'19-02-2012', '20-02-2012', '21-02-2012',
+			'22-02-2012', '23-02-2012', '24-02-2012',
+			'25-02-2012', '26-02-2012', '27-02-2012',
+			'28-02-2012', '29-02-2012', '01-03-2012'
+		);
+
+		$this->assertEquals($dates, date_range(mktime(12, 0, 0, 1, 29, 2012), mktime(12, 0, 0, 3, 1, 2012), TRUE, 'd-m-Y'));
+		array_pop($dates);
+		$this->assertEquals($dates, date_range(mktime(12, 0, 0, 1, 29, 2012), 31, FALSE, 'd-m-Y'));
+	}
+
 }
 
 /* End of file date_helper_test.php */
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/directory_helper_test.php b/tests/codeigniter/helpers/directory_helper_test.php
index 176ff1d..c39ccd8 100644
--- a/tests/codeigniter/helpers/directory_helper_test.php
+++ b/tests/codeigniter/helpers/directory_helper_test.php
@@ -19,6 +19,7 @@
 				'benchmark.html' => '',
 				'database' => array('active_record.html' => '', 'binds.html' => ''),
 				'email.html' => '',
+				'0' => '',
 				'.hiddenfile.txt' => ''
 			)
 		);
@@ -30,7 +31,8 @@
 			'libraries' => array(
 				'benchmark.html',
 				'database' => array('active_record.html', 'binds.html'),
-				'email.html'
+				'email.html',
+				'0'
 			)
 		);
 
diff --git a/tests/codeigniter/helpers/html_helper_test.php b/tests/codeigniter/helpers/html_helper_test.php
index 9a7bb48..4dd717f 100644
--- a/tests/codeigniter/helpers/html_helper_test.php
+++ b/tests/codeigniter/helpers/html_helper_test.php
@@ -22,6 +22,22 @@
 		$this->assertEquals('<h2 class="bar">foobar</h2>', heading('foobar', 2, 'class="bar"'));
 	}
 
+	public function test_heading_array_attributes()
+	{
+		// Test array of attributes
+		$this->assertEquals('<h2 class="bar" id="foo">foobar</h2>', heading('foobar', 2, array('class' => 'bar', 'id' => 'foo')));
+	}
+
+	public function test_heading_object_attributes()
+	{
+		// Test array of attributes
+		$this->assertEquals('<h2 class="bar" id="foo">foobar</h2>', heading('foobar', 2, array('class' => 'bar', 'id' => 'foo')));
+		$test = new stdClass;
+		$test->class = "bar";
+		$test->id = "foo";
+		$this->assertEquals('<h2 class="bar" id="foo">foobar</h2>', heading('foobar', 2, $test));
+	}
+
 	// ------------------------------------------------------------------------
 
 	public function test_Ul()
@@ -72,5 +88,4 @@
 		$this->assertEquals($expect, meta(array('name' => 'foo')));
 
 	}
-
 }
\ No newline at end of file
diff --git a/tests/codeigniter/libraries/Calendar_test.php b/tests/codeigniter/libraries/Calendar_test.php
new file mode 100644
index 0000000..95668d7
--- /dev/null
+++ b/tests/codeigniter/libraries/Calendar_test.php
@@ -0,0 +1,204 @@
+<?php
+
+class Calendar_test extends CI_TestCase {
+
+	function __construct()
+	{
+		$obj = new stdClass;
+		$obj->calendar = new Mock_Libraries_Calendar();
+
+		$this->calendar = $obj->calendar;
+	}
+
+	function test_initialize()
+	{
+		$this->calendar->initialize(array(
+			'month_type'	=>	'short',
+			'start_day'	=>	'monday'
+		));
+		$this->assertEquals('short', $this->calendar->month_type);
+		$this->assertEquals('monday', $this->calendar->start_day);
+	}
+
+	/**
+	 * @covers Mock_Libraries_Calendar::parse_template
+	 */
+	function test_generate()
+	{
+		$no_events = '<table border="0" cellpadding="4" cellspacing="0">
+
+<tr>
+<th colspan="7">September&nbsp;2011</th>
+
+</tr>
+
+<tr>
+<td>Su</td><td>Mo</td><td>Tu</td><td>We</td><td>Th</td><td>Fr</td><td>Sa</td>
+</tr>
+
+<tr>
+<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>1</td><td>2</td><td>3</td>
+</tr>
+
+<tr>
+<td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td>
+</tr>
+
+<tr>
+<td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td>
+</tr>
+
+<tr>
+<td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td>
+</tr>
+
+<tr>
+<td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td><td>&nbsp;</td>
+</tr>
+
+</table>';
+
+		$this->assertEquals($no_events, $this->calendar->generate(2011, 9));
+
+		$data = array(
+			3  => 'http://example.com/news/article/2006/03/',
+			7  => 'http://example.com/news/article/2006/07/',
+			13 => 'http://example.com/news/article/2006/13/',
+			26 => 'http://example.com/news/article/2006/26/'
+		);
+
+		$events = '<table border="0" cellpadding="4" cellspacing="0">
+
+<tr>
+<th colspan="7">September&nbsp;2011</th>
+
+</tr>
+
+<tr>
+<td>Su</td><td>Mo</td><td>Tu</td><td>We</td><td>Th</td><td>Fr</td><td>Sa</td>
+</tr>
+
+<tr>
+<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>1</td><td>2</td><td><a href="http://example.com/news/article/2006/03/">3</a></td>
+</tr>
+
+<tr>
+<td>4</td><td>5</td><td>6</td><td><a href="http://example.com/news/article/2006/07/">7</a></td><td>8</td><td>9</td><td>10</td>
+</tr>
+
+<tr>
+<td>11</td><td>12</td><td><a href="http://example.com/news/article/2006/13/">13</a></td><td>14</td><td>15</td><td>16</td><td>17</td>
+</tr>
+
+<tr>
+<td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td>
+</tr>
+
+<tr>
+<td>25</td><td><a href="http://example.com/news/article/2006/26/">26</a></td><td>27</td><td>28</td><td>29</td><td>30</td><td>&nbsp;</td>
+</tr>
+
+</table>';
+
+		$this->assertEquals($events, $this->calendar->generate(2011, 9, $data));
+	}
+
+	function test_get_month_name()
+	{
+		$this->calendar->month_type = NULL;
+		$this->assertEquals('January', $this->calendar->get_month_name('01'));
+
+		$this->calendar->month_type = 'short';
+		$this->assertEquals('Jan', $this->calendar->get_month_name('01'));
+	}
+
+	function test_get_day_names()
+	{
+		$this->assertEquals(array(
+			'Sunday',
+			'Monday',
+			'Tuesday',
+			'Wednesday',
+			'Thursday',
+			'Friday',
+			'Saturday'
+		), $this->calendar->get_day_names('long'));
+
+		$this->assertEquals(array(
+			'Sun',
+			'Mon',
+			'Tue',
+			'Wed',
+			'Thu',
+			'Fri',
+			'Sat'
+		), $this->calendar->get_day_names('short'));
+
+		$this->calendar->day_type = NULL;
+
+		$this->assertEquals(array(
+			'Su',
+			'Mo',
+			'Tu',
+			'We',
+			'Th',
+			'Fr',
+			'Sa'
+		), $this->calendar->get_day_names());
+	}
+
+	function test_adjust_date()
+	{
+		$this->assertEquals(array('month' => 8, 'year' => 2012), $this->calendar->adjust_date(8, 2012));
+		$this->assertEquals(array('month' => 1, 'year' => 2013), $this->calendar->adjust_date(13, 2012));
+	}
+
+	function test_get_total_days()
+	{
+		$this->assertEquals(0, $this->calendar->get_total_days(13, 2012));
+
+		$this->assertEquals(31, $this->calendar->get_total_days(1, 2012));
+		$this->assertEquals(28, $this->calendar->get_total_days(2, 2011));
+		$this->assertEquals(29, $this->calendar->get_total_days(2, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(3, 2012));
+		$this->assertEquals(30, $this->calendar->get_total_days(4, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(5, 2012));
+		$this->assertEquals(30, $this->calendar->get_total_days(6, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(7, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(8, 2012));
+		$this->assertEquals(30, $this->calendar->get_total_days(9, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(10, 2012));
+		$this->assertEquals(30, $this->calendar->get_total_days(11, 2012));
+		$this->assertEquals(31, $this->calendar->get_total_days(12, 2012));
+	}
+
+	function test_default_template()
+	{
+		$array = array(
+			'table_open'				=> '<table border="0" cellpadding="4" cellspacing="0">',
+			'heading_row_start'			=> '<tr>',
+			'heading_previous_cell'		=> '<th><a href="{previous_url}">&lt;&lt;</a></th>',
+			'heading_title_cell'		=> '<th colspan="{colspan}">{heading}</th>',
+			'heading_next_cell'			=> '<th><a href="{next_url}">&gt;&gt;</a></th>',
+			'heading_row_end'			=> '</tr>',
+			'week_row_start'			=> '<tr>',
+			'week_day_cell'				=> '<td>{week_day}</td>',
+			'week_row_end'				=> '</tr>',
+			'cal_row_start'				=> '<tr>',
+			'cal_cell_start'			=> '<td>',
+			'cal_cell_start_today'		=> '<td>',
+			'cal_cell_content'			=> '<a href="{content}">{day}</a>',
+			'cal_cell_content_today'	=> '<a href="{content}"><strong>{day}</strong></a>',
+			'cal_cell_no_content'		=> '{day}',
+			'cal_cell_no_content_today'	=> '<strong>{day}</strong>',
+			'cal_cell_blank'			=> '&nbsp;',
+			'cal_cell_end'				=> '</td>',
+			'cal_cell_end_today'		=> '</td>',
+			'cal_row_end'				=> '</tr>',
+			'table_close'				=> '</table>'
+		);
+
+		$this->assertEquals($array, $this->calendar->default_template());
+	}
+
+}
\ No newline at end of file
diff --git a/tests/codeigniter/libraries/Session_test.php b/tests/codeigniter/libraries/Session_test.php
new file mode 100644
index 0000000..60d3a5b
--- /dev/null
+++ b/tests/codeigniter/libraries/Session_test.php
@@ -0,0 +1,400 @@
+<?php
+
+/**
+ * Session driver library unit test
+ */
+class Session_test extends CI_TestCase {
+	protected $settings = array(
+		'use_cookies' => 0,
+	   	'use_only_cookies' => 0,
+	   	'cache_limiter' => false
+	);
+	protected $setting_vals = array();
+	protected $cookie_vals;
+	protected $session;
+
+	/**
+	 * Set up test framework
+	 */
+	public function set_up()
+	{
+		// Override settings
+		foreach ($this->settings as $name => $value) {
+			$this->setting_vals[$name] = ini_get('session.'.$name);
+			ini_set('session.'.$name, $value);
+		}
+
+		// Start with clean environment
+		$this->cookie_vals = $_COOKIE;
+		$_COOKIE = array();
+
+		// Establish necessary support classes
+		$obj = new stdClass;
+		$cfg = $this->ci_core_class('cfg');
+		$obj->config = new $cfg();
+		$ldr = $this->ci_core_class('load');
+		$obj->load = new $ldr();
+		$obj->input = new Mock_Core_Input(NULL, NULL);
+		$this->ci_instance($obj);
+
+		// Attach session instance locally
+		$config = array(
+			'sess_encrypt_cookie' => FALSE,
+			'sess_use_database' => FALSE,
+			'sess_table_name' => '',
+			'sess_expiration' => 7200,
+			'sess_expire_on_close' => FALSE,
+			'sess_match_ip' => FALSE,
+			'sess_match_useragent' => TRUE,
+			'sess_cookie_name' => 'ci_session',
+			'cookie_path' => '',
+			'cookie_domain' => '',
+			'cookie_secure' => FALSE,
+			'cookie_httponly' => FALSE,
+			'sess_time_to_update' => 300,
+			'time_reference' => 'local',
+			'cookie_prefix' => '',
+			'encryption_key' => 'foobar',
+			'sess_valid_drivers' => array(
+				'Mock_Libraries_Session_native',
+			   	'Mock_Libraries_Session_cookie'
+			)
+		);
+		$this->session = new Mock_Libraries_Session($config);
+	}
+
+	/**
+	 * Tear down test framework
+	 */
+	public function tear_down()
+	{
+		// Restore environment
+		if (session_id()) session_destroy();
+		$_SESSION = array();
+		$_COOKIE = $this->cookie_vals;
+
+		// Restore settings
+		foreach ($this->settings as $name => $value) {
+			ini_set('session.'.$name, $this->setting_vals[$name]);
+		}
+	}
+
+	/**
+	 * Test set_userdata() function
+	 *
+	 * @covers  CI_Session::set_userdata
+	 * @covers  CI_Session::userdata
+	 */
+	public function test_set_userdata()
+	{
+		// Set userdata values for each driver
+		$key1 = 'test1';
+		$ckey2 = 'test2';
+		$nkey2 = 'test3';
+		$cmsg1 = 'Some test data';
+		$cmsg2 = 42;
+		$nmsg1 = 'Other test data';
+		$nmsg2 = true;
+		$this->session->cookie->set_userdata($key1, $cmsg1);
+		$this->session->set_userdata($ckey2, $cmsg2);
+		$this->session->native->set_userdata($key1, $nmsg1);
+		$this->session->set_userdata($nkey2, $nmsg2);
+
+		// Verify independent messages
+		$this->assertEquals($cmsg1, $this->session->cookie->userdata($key1));
+		$this->assertEquals($nmsg1, $this->session->native->userdata($key1));
+
+		// Verify pre-selected driver sets
+		$this->assertEquals($cmsg2, $this->session->cookie->userdata($ckey2));
+		$this->assertEquals($nmsg2, $this->session->native->userdata($nkey2));
+
+		// Verify no crossover
+		$this->assertNull($this->session->cookie->userdata($nkey2));
+		$this->assertNull($this->session->native->userdata($ckey2));
+	}
+
+	/**
+	 * Test the has_userdata() function
+	 *
+	 * @covers	CI_Session::has_userdata
+	 */
+	public function test_has_userdata()
+	{
+		// Set a userdata value for each driver
+		$key = 'hastest';
+		$cmsg = 'My test data';
+		$this->session->cookie->set_userdata($key, $cmsg);
+		$nmsg = 'Your test data';
+		$this->session->native->set_userdata($key, $nmsg);
+
+		// Verify values exist
+		$this->assertTrue($this->session->cookie->has_userdata($key));
+		$this->assertTrue($this->session->native->has_userdata($key));
+
+		// Verify non-existent values
+		$nokey = 'hasnot';
+		$this->assertFalse($this->session->cookie->has_userdata($nokey));
+		$this->assertFalse($this->session->native->has_userdata($nokey));
+	}
+
+	/**
+	 * Test the all_userdata() function
+	 *
+	 * @covers	CI_Session::all_userdata
+	 */
+	public function test_all_userdata()
+	{
+		// Set a specific series of data for each driver
+		$cdata = array(
+			'one' => 'first',
+			'two' => 'second',
+		   	'three' => 'third',
+		   	'foo' => 'bar',
+		   	'bar' => 'baz'
+		);
+		$ndata = array(
+			'one' => 'gold',
+		   	'two' => 'silver',
+		   	'three' => 'bronze',
+		   	'foo' => 'baz',
+		   	'bar' => 'foo'
+		);
+		$this->session->cookie->set_userdata($cdata);
+		$this->session->native->set_userdata($ndata);
+
+		// Make sure all values are present
+		$call = $this->session->cookie->all_userdata();
+		foreach ($cdata as $key => $value) {
+			$this->assertEquals($value, $call[$key]);
+		}
+		$nall = $this->session->native->all_userdata();
+		foreach ($ndata as $key => $value) {
+			$this->assertEquals($value, $nall[$key]);
+		}
+	}
+
+	/**
+	 * Test the unset_userdata() function
+	 *
+	 * @covers	CI_Session::unset_userdata
+	 */
+	public function test_unset_userdata()
+	{
+		// Set a userdata message for each driver
+		$key = 'untest';
+		$cmsg = 'Other test data';
+		$this->session->cookie->set_userdata($key, $cmsg);
+		$nmsg = 'Sundry test data';
+		$this->session->native->set_userdata($key, $nmsg);
+
+		// Verify independent messages
+		$this->assertEquals($this->session->cookie->userdata($key), $cmsg);
+		$this->assertEquals($this->session->native->userdata($key), $nmsg);
+
+		// Unset them and verify absence
+		$this->session->cookie->unset_userdata($key);
+		$this->session->native->unset_userdata($key);
+		$this->assertNull($this->session->cookie->userdata($key));
+		$this->assertNull($this->session->native->userdata($key));
+	}
+
+	/**
+	 * Test the flashdata() functions
+	 *
+	 * @covers	CI_Session::set_flashdata
+	 * @covers	CI_Session::flashdata
+	 */
+	public function test_flashdata()
+	{
+		// Set flashdata message for each driver
+		$key = 'fltest';
+		$cmsg = 'Some flash data';
+		$this->session->cookie->set_flashdata($key, $cmsg);
+		$nmsg = 'Other flash data';
+		$this->session->native->set_flashdata($key, $nmsg);
+
+		// Simulate page reload
+		$this->session->cookie->reload();
+		$this->session->native->reload();
+
+		// Verify independent messages
+		$this->assertEquals($cmsg, $this->session->cookie->flashdata($key));
+		$this->assertEquals($nmsg, $this->session->native->flashdata($key));
+
+		// Simulate next page reload
+		$this->session->cookie->reload();
+		$this->session->native->reload();
+
+		// Verify absence of messages
+		$this->assertNull($this->session->cookie->flashdata($key));
+		$this->assertNull($this->session->native->flashdata($key));
+	}
+
+	/**
+	 * Test the keep_flashdata() function
+	 *
+	 * @covers	CI_Session::keep_flashdata
+	 */
+	public function test_keep_flashdata()
+	{
+		// Set flashdata message for each driver
+		$key = 'kfltest';
+		$cmsg = 'My flash data';
+		$this->session->cookie->set_flashdata($key, $cmsg);
+		$nmsg = 'Your flash data';
+		$this->session->native->set_flashdata($key, $nmsg);
+
+		// Simulate page reload and verify independent messages
+		$this->session->cookie->reload();
+		$this->session->native->reload();
+		$this->assertEquals($cmsg, $this->session->cookie->flashdata($key));
+		$this->assertEquals($nmsg, $this->session->native->flashdata($key));
+
+		// Keep messages
+		$this->session->cookie->keep_flashdata($key);
+		$this->session->native->keep_flashdata($key);
+
+		// Simulate next page reload and verify message persistence
+		$this->session->cookie->reload();
+		$this->session->native->reload();
+		$this->assertEquals($cmsg, $this->session->cookie->flashdata($key));
+		$this->assertEquals($nmsg, $this->session->native->flashdata($key));
+
+		// Simulate next page reload and verify absence of messages
+		$this->session->cookie->reload();
+		$this->session->native->reload();
+		$this->assertNull($this->session->cookie->flashdata($key));
+		$this->assertNull($this->session->native->flashdata($key));
+	}
+
+	/**
+	 * Test the all_flashdata() function
+	 *
+	 * @covers	CI_Session::all_flashdata
+	 */
+	public function test_all_flashdata()
+	{
+		// Set a specific series of data for each driver
+		$cdata = array(
+			'one' => 'first',
+		   	'two' => 'second',
+		   	'three' => 'third',
+		   	'foo' => 'bar',
+		   	'bar' => 'baz'
+		);
+		$ndata = array(
+			'one' => 'gold',
+		   	'two' => 'silver',
+		   	'three' => 'bronze',
+		   	'foo' => 'baz',
+		   	'bar' => 'foo'
+		);
+		$this->session->cookie->set_flashdata($cdata);
+		$this->session->native->set_flashdata($ndata);
+
+		// Simulate page reload and make sure all values are present
+		$this->session->cookie->reload();
+		$this->session->native->reload();
+		$this->assertEquals($cdata, $this->session->cookie->all_flashdata());
+		$this->assertEquals($ndata, $this->session->native->all_flashdata());
+	}
+
+	/**
+	 * Test the tempdata() functions
+	 *
+	 * @covers	CI_Session::set_tempdata
+	 * @covers	CI_Session::tempdata
+	 */
+	public function test_set_tempdata()
+	{
+		// Set tempdata message for each driver - 1 second timeout
+		$key = 'tmptest';
+		$cmsg = 'Some temp data';
+		$this->session->cookie->set_tempdata($key, $cmsg, 1);
+		$nmsg = 'Other temp data';
+		$this->session->native->set_tempdata($key, $nmsg, 1);
+
+		// Simulate page reload and verify independent messages
+		$this->session->cookie->reload();
+		$this->session->native->reload();
+		$this->assertEquals($cmsg, $this->session->cookie->tempdata($key));
+		$this->assertEquals($nmsg, $this->session->native->tempdata($key));
+
+		// Wait 2 seconds, simulate page reload and verify message absence
+		sleep(2);
+		$this->session->cookie->reload();
+		$this->session->native->reload();
+		$this->assertNull($this->session->cookie->tempdata($key));
+		$this->assertNull($this->session->native->tempdata($key));
+	}
+
+	/**
+	 * Test the unset_tempdata() function
+	 *
+	 * @covers	CI_Session::unset_tempdata
+	 */
+	public function test_unset_tempdata()
+	{
+		// Set tempdata message for each driver - 1 second timeout
+		$key = 'utmptest';
+		$cmsg = 'My temp data';
+		$this->session->cookie->set_tempdata($key, $cmsg, 1);
+		$nmsg = 'Your temp data';
+		$this->session->native->set_tempdata($key, $nmsg, 1);
+
+		// Verify independent messages
+		$this->assertEquals($cmsg, $this->session->cookie->tempdata($key));
+		$this->assertEquals($nmsg, $this->session->native->tempdata($key));
+
+		// Unset data and verify message absence
+		$this->session->cookie->unset_tempdata($key);
+		$this->session->native->unset_tempdata($key);
+		$this->assertNull($this->session->cookie->tempdata($key));
+		$this->assertNull($this->session->native->tempdata($key));
+	}
+
+	/**
+	 * Test the sess_regenerate() function
+	 *
+	 * @covers	CI_Session::sess_regenerate
+	 */
+	public function test_sess_regenerate()
+	{
+		// Get current session id, regenerate, and compare
+		// Cookie driver
+		$oldid = $this->session->cookie->userdata('session_id');
+		$this->session->cookie->sess_regenerate();
+		$newid = $this->session->cookie->userdata('session_id');
+		$this->assertNotEquals($oldid, $newid);
+
+		// Native driver - bug #55267 (https://bugs.php.net/bug.php?id=55267) prevents testing this
+		// $oldid = session_id();
+		// $this->session->native->sess_regenerate();
+		// $oldid = session_id();
+		// $this->assertNotEquals($oldid, $newid);
+	}
+
+	/**
+	 * Test the sess_destroy() function
+	 *
+	 * @covers	CI_Session::sess_destroy
+	 */
+	public function test_sess_destroy()
+	{
+		// Set a userdata message, destroy session, and verify absence
+		$key = 'dsttest';
+		$msg = 'More test data';
+
+		// Cookie driver
+		$this->session->cookie->set_userdata($key, $msg);
+		$this->assertEquals($msg, $this->session->cookie->userdata($key));
+		$this->session->cookie->sess_destroy();
+		$this->assertNull($this->session->cookie->userdata($key));
+
+		// Native driver
+		$this->session->native->set_userdata($key, $msg);
+		$this->assertEquals($msg, $this->session->native->userdata($key));
+		$this->session->native->sess_destroy();
+		$this->assertNull($this->session->native->userdata($key));
+	}
+}
\ No newline at end of file
diff --git a/tests/codeigniter/libraries/Upload_test.php b/tests/codeigniter/libraries/Upload_test.php
new file mode 100644
index 0000000..d79a3ff
--- /dev/null
+++ b/tests/codeigniter/libraries/Upload_test.php
@@ -0,0 +1,270 @@
+<?php
+
+class Upload_test extends CI_TestCase {
+
+	function set_up()
+	{
+		$obj = new stdClass;
+		$obj->upload = new Mock_Libraries_Upload();
+		$obj->security = new Mock_Core_Security();
+		$obj->lang = new Mock_Core_Lang();
+
+		$this->ci_instance($obj);
+		$this->upload = $obj->upload;
+
+		vfsStreamWrapper::register();
+		vfsStreamWrapper::setRoot(new vfsStreamDirectory('testDir'));
+
+		$this->_test_dir = vfsStreamWrapper::getRoot();
+	}
+
+	function test_do_upload() 
+	{
+		$this->markTestIncomplete('We can\'t really test this at the moment because of the call to `is_uploaded_file` in do_upload which isn\'t supported by vfsStream');
+	}
+
+	function test_data()
+	{
+		$data = array(
+				'file_name'		=> 'hello.txt',
+				'file_type'		=> 'text/plain',
+				'file_path'		=> '/tmp/',
+				'full_path'		=> '/tmp/hello.txt',
+				'raw_name'		=> 'hello',
+				'orig_name'		=> 'hello.txt',
+				'client_name'		=> '',
+				'file_ext'		=> '.txt',
+				'file_size'		=> 100,
+				'is_image'		=> FALSE,
+				'image_width'		=> '',
+				'image_height'		=> '',
+				'image_type'		=> '',
+				'image_size_str'	=> ''
+			);
+
+		$this->upload->set_upload_path('/tmp/');
+
+		foreach ($data as $k => $v)
+		{
+			$this->upload->{$k}	= $v;
+		}
+
+		$this->assertEquals('hello.txt', $this->upload->data('file_name'));
+		$this->assertEquals($data, $this->upload->data());
+	}
+
+	function test_set_upload_path()
+	{
+		$this->upload->set_upload_path('/tmp/');
+		$this->assertEquals('/tmp/', $this->upload->upload_path);
+
+		$this->upload->set_upload_path('/tmp');
+		$this->assertEquals('/tmp/', $this->upload->upload_path);
+	}
+
+	function test_set_filename()
+	{
+		$file1 = vfsStream::newFile('hello-world.txt')->withContent('Hello world.')->at($this->_test_dir);
+		$this->upload->file_ext = '.txt';
+
+		$this->assertEquals('helloworld.txt', $this->upload->set_filename(vfsStream::url('testDir').'/', 'helloworld.txt'));
+		$this->assertEquals('hello-world1.txt', $this->upload->set_filename(vfsStream::url('testDir').'/', 'hello-world.txt'));
+	}
+
+	function test_set_max_filesize()
+	{
+		$this->upload->set_max_filesize(100);
+		$this->assertEquals(100, $this->upload->max_size);
+	}	
+
+	function test_set_max_filename()
+	{
+		$this->upload->set_max_filename(100);
+		$this->assertEquals(100, $this->upload->max_filename);
+	}
+
+	function test_set_max_width()
+	{
+		$this->upload->set_max_width(100);
+		$this->assertEquals(100, $this->upload->max_width);
+	}	
+
+	function test_set_max_height()
+	{
+		$this->upload->set_max_height(100);
+		$this->assertEquals(100, $this->upload->max_height);
+	}
+
+	function test_set_allowed_types()
+	{
+		$this->upload->set_allowed_types('*');
+		$this->assertEquals('*', $this->upload->allowed_types);
+
+		$this->upload->set_allowed_types('foo|bar');
+		$this->assertEquals(array('foo', 'bar'), $this->upload->allowed_types);
+	}
+
+	function test_set_image_properties()
+	{
+		$this->upload->file_type = 'image/gif';
+		$this->upload->file_temp = 'tests/mocks/uploads/ci_logo.gif';
+
+		$props = array(
+			'image_width'	=>	170,
+			'image_height'	=>	73,
+			'image_type'	=>	'gif',
+			'image_size_str'	=>	'width="170" height="73"'
+		);
+
+		$this->upload->set_image_properties($this->upload->file_temp);
+
+		$this->assertEquals($props['image_width'], $this->upload->image_width);
+		$this->assertEquals($props['image_height'], $this->upload->image_height);
+		$this->assertEquals($props['image_type'], $this->upload->image_type);
+		$this->assertEquals($props['image_size_str'], $this->upload->image_size_str);
+	}
+
+	function test_set_xss_clean()
+	{
+		$this->upload->set_xss_clean(TRUE);
+		$this->assertTrue($this->upload->xss_clean);
+
+		$this->upload->set_xss_clean(FALSE);
+		$this->assertFalse($this->upload->xss_clean);
+	}
+
+	function test_is_image()
+	{
+		$this->upload->file_type = 'image/x-png';
+		$this->assertTrue($this->upload->is_image());
+
+		$this->upload->file_type = 'text/plain';
+		$this->assertFalse($this->upload->is_image());
+	}
+
+	function test_is_allowed_filetype()
+	{
+		$this->upload->allowed_types = array('html', 'gif');
+
+		$this->upload->file_ext = '.txt';
+		$this->upload->file_type = 'text/plain';
+		$this->assertFalse($this->upload->is_allowed_filetype(FALSE));
+		$this->assertFalse($this->upload->is_allowed_filetype(TRUE));
+
+		$this->upload->file_ext = '.html';
+		$this->upload->file_type = 'text/html';
+		$this->assertTrue($this->upload->is_allowed_filetype(FALSE));
+		$this->assertTrue($this->upload->is_allowed_filetype(TRUE));
+
+		$this->upload->file_temp = 'tests/mocks/uploads/ci_logo.gif';
+		$this->upload->file_ext = '.gif';
+		$this->upload->file_type = 'image/gif';
+		$this->assertTrue($this->upload->is_allowed_filetype());
+	}
+
+	function test_is_allowed_filesize()
+	{
+		$this->upload->max_size = 100;
+		$this->upload->file_size = 99;
+
+		$this->assertTrue($this->upload->is_allowed_filesize());
+
+		$this->upload->file_size = 101;
+		$this->assertFalse($this->upload->is_allowed_filesize());
+	}
+
+	function test_is_allowed_dimensions()
+	{
+		$this->upload->file_type = 'text/plain';
+		$this->assertTrue($this->upload->is_allowed_dimensions());
+
+		$this->upload->file_type = 'image/gif';
+		$this->upload->file_temp = 'tests/mocks/uploads/ci_logo.gif';
+
+		$this->upload->max_width = 10;		
+		$this->assertFalse($this->upload->is_allowed_dimensions());
+
+		$this->upload->max_width = 170;
+		$this->upload->max_height = 10;
+		$this->assertFalse($this->upload->is_allowed_dimensions());
+
+		$this->upload->max_height = 73;
+		$this->assertTrue($this->upload->is_allowed_dimensions());
+	}
+
+	function test_validate_upload_path()
+	{
+		$this->upload->upload_path = '';
+		$this->assertFalse($this->upload->validate_upload_path());
+
+		$this->upload->upload_path = vfsStream::url('testDir');
+		$this->assertTrue($this->upload->validate_upload_path());
+	}
+
+	function test_get_extension()
+	{
+		$this->assertEquals('.txt', $this->upload->get_extension('hello.txt'));
+		$this->assertEquals('.htaccess', $this->upload->get_extension('.htaccess'));
+		$this->assertEquals('', $this->upload->get_extension('hello'));
+	}
+
+	function test_clean_file_name()
+	{
+		$this->assertEquals('hello.txt', $this->upload->clean_file_name('hello.txt'));
+		$this->assertEquals('hello.txt', $this->upload->clean_file_name('%253chell>o.txt'));
+	}
+
+	function test_limit_filename_length()
+	{
+		$this->assertEquals('hello.txt', $this->upload->limit_filename_length('hello.txt', 10));
+		$this->assertEquals('hello.txt', $this->upload->limit_filename_length('hello-world.txt', 9));
+	}
+
+	function test_do_xss_clean()
+	{
+		$file1 = vfsStream::newFile('file1.txt')->withContent('The billy goat was waiting for them.')->at($this->_test_dir);
+		$file2 = vfsStream::newFile('file2.txt')->at($this->_test_dir);
+		$file3 = vfsStream::newFile('file3.txt')->withContent('<script type="text/javascript">alert("Boo! said the billy goat")</script>')->at($this->_test_dir);
+
+		$this->upload->file_temp = vfsStream::url('file1.txt');
+		$this->assertTrue($this->upload->do_xss_clean());
+
+		$this->upload->file_temp = vfsStream::url('file2.txt');
+		$this->assertFalse($this->upload->do_xss_clean());
+
+		$this->upload->file_temp = vfsStream::url('file3.txt');
+		$this->assertFalse($this->upload->do_xss_clean());
+
+		$this->upload->file_temp = 'tests/mocks/uploads/ci_logo.gif';
+		$this->assertTrue($this->upload->do_xss_clean());
+	}
+
+	function test_set_error()
+	{
+		$errors = array(
+			'An error!'
+		);
+
+		$another = 'Another error!';
+
+		$this->upload->set_error($errors);
+		$this->assertEquals($errors, $this->upload->error_msg);
+
+		$errors[] = $another;
+		$this->upload->set_error($another);
+		$this->assertEquals($errors, $this->upload->error_msg);
+	}
+
+	function test_display_errors()
+	{
+		$this->upload->error_msg[] = 'Error test';
+		$this->assertEquals('<p>Error test</p>', $this->upload->display_errors());
+	}
+
+	function test_mimes_types()
+	{
+		$this->assertEquals('text/plain', $this->upload->mimes_types('txt'));
+		$this->assertFalse($this->upload->mimes_types('foobar'));
+	}
+
+}
\ No newline at end of file
diff --git a/tests/mocks/autoloader.php b/tests/mocks/autoloader.php
index be1c222..88d016b 100644
--- a/tests/mocks/autoloader.php
+++ b/tests/mocks/autoloader.php
@@ -26,10 +26,14 @@
 		'Email', 'Encrypt', 'Form_validation',
 		'Ftp', 'Image_lib', 'Javascript',
 		'Log', 'Migration', 'Pagination',
-		'Parser', 'Profiler', 'Session',
-		'Table', 'Trackback', 'Typography',
-		'Unit_test', 'Upload', 'User_agent',
-		'Xmlrpc', 'Zip',
+		'Parser', 'Profiler', 'Table',
+	   	'Trackback', 'Typography', 'Unit_test',
+	   	'Upload', 'User_agent', 'Xmlrpc',
+	   	'Zip',
+	);
+
+	$ci_drivers = array(
+		'Session',
 	);
 
 	if (strpos($class, 'Mock_') === 0)
@@ -52,6 +56,15 @@
 			$dir = BASEPATH.'libraries'.DIRECTORY_SEPARATOR;
 			$class = ($subclass === 'Driver_Library') ? 'Driver' : $subclass;
 		}
+		elseif (in_array($subclass, $ci_drivers))
+		{
+			$dir = BASEPATH.'libraries'.DIRECTORY_SEPARATOR.$subclass.DIRECTORY_SEPARATOR;
+			$class = $subclass;
+		}
+		elseif (in_array(($parent = strtok($subclass, '_')), $ci_drivers)) {
+			$dir = BASEPATH.'libraries'.DIRECTORY_SEPARATOR.$parent.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
+			$class = $subclass;
+		}
 		elseif (preg_match('/^CI_DB_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) === 3)
 		{
 			$driver_path = BASEPATH.'database'.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
@@ -91,4 +104,4 @@
 	}
 
 	include_once($file);
-}
\ No newline at end of file
+}
diff --git a/tests/mocks/core/input.php b/tests/mocks/core/input.php
index 2a4aa49..0d18738 100644
--- a/tests/mocks/core/input.php
+++ b/tests/mocks/core/input.php
@@ -28,4 +28,14 @@
 		return parent::_fetch_from_array($array, $index, $xss_clean);
 	}
 
+	/**
+	 * Lie about being a CLI request
+	 *
+	 * We take advantage of this in libraries/Session_test
+	 */
+	public function is_cli_request()
+	{
+		return FALSE;
+	}
+
 }
\ No newline at end of file
diff --git a/tests/mocks/core/lang.php b/tests/mocks/core/lang.php
new file mode 100644
index 0000000..1b99aed
--- /dev/null
+++ b/tests/mocks/core/lang.php
@@ -0,0 +1,15 @@
+<?php
+
+class Mock_Core_Lang extends CI_Lang {
+
+	function line($line = '')
+	{
+		return FALSE;
+	}
+
+	function load($langfile, $idiom = '', $return = false, $add_suffix = true, $alt_path = '')
+	{
+		return;
+	}
+
+}
\ No newline at end of file
diff --git a/tests/mocks/libraries/calendar.php b/tests/mocks/libraries/calendar.php
new file mode 100644
index 0000000..8fee536
--- /dev/null
+++ b/tests/mocks/libraries/calendar.php
@@ -0,0 +1,25 @@
+<?php
+
+class Mock_Libraries_Calendar extends CI_Calendar {
+
+	public function __construct($config = array())
+	{
+		$this->CI = new stdClass;
+		$this->CI->lang = new Mock_Core_Lang();
+
+		if ( ! in_array('calendar_lang.php', $this->CI->lang->is_loaded, TRUE))
+		{
+			$this->CI->lang->load('calendar');
+		}
+
+		$this->local_time = time();
+
+		if (count($config) > 0)
+		{
+			$this->initialize($config);
+		}
+
+		log_message('debug', 'Calendar Class Initialized');
+	}
+
+}
\ No newline at end of file
diff --git a/tests/mocks/libraries/session.php b/tests/mocks/libraries/session.php
new file mode 100644
index 0000000..9d6feee
--- /dev/null
+++ b/tests/mocks/libraries/session.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * Mock library to add testing features to Session driver library
+ */
+class Mock_Libraries_Session extends CI_Session {
+	/**
+	 * Simulate new page load
+	 */
+	public function reload()
+	{
+		$this->_flashdata_sweep();
+		$this->_flashdata_mark();
+		$this->_tempdata_sweep();
+	}
+}
+
+/**
+ * Mock cookie driver to overload cookie setting
+ */
+class Mock_Libraries_Session_cookie extends CI_Session_cookie {
+	/**
+	 * Overload _setcookie to manage $_COOKIE values, since actual cookies can't be set in unit testing
+	 */
+	protected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = false,
+	$httponly = false)
+	{
+		if (empty($value) || $expire <= time()) {
+			// Clear cookie
+			unset($_COOKIE[$name]);
+		}
+		else {
+			// Set cookie
+			$_COOKIE[$name] = $value;
+		}
+	}
+}
+
+/**
+ * Mock native driver (just for consistency in loading)
+ */
+class Mock_Libraries_Session_native extends CI_Session_native { }
+
diff --git a/tests/mocks/libraries/upload.php b/tests/mocks/libraries/upload.php
new file mode 100644
index 0000000..988723e
--- /dev/null
+++ b/tests/mocks/libraries/upload.php
@@ -0,0 +1,3 @@
+<?php
+
+class Mock_Libraries_Upload extends CI_Upload {}
\ No newline at end of file
diff --git a/tests/mocks/uploads/ci_logo.gif b/tests/mocks/uploads/ci_logo.gif
new file mode 100644
index 0000000..073ec14
--- /dev/null
+++ b/tests/mocks/uploads/ci_logo.gif
Binary files differ
diff --git a/tests/travis/mysql.phpunit.xml b/tests/travis/mysql.phpunit.xml
index 38c8eba..06d4a01 100644
--- a/tests/travis/mysql.phpunit.xml
+++ b/tests/travis/mysql.phpunit.xml
@@ -18,7 +18,7 @@
 		</testsuite>
 	</testsuites>
 	<filter>
-        <whitelist addUncoveredFilesFromWhitelist="true">
+        <whitelist addUncoveredFilesFromWhitelist="false">
             <directory suffix=".php">../../system</directory>
         </whitelist>
 	</filter>
diff --git a/tests/travis/pdo/mysql.phpunit.xml b/tests/travis/pdo/mysql.phpunit.xml
index c3113a6..7121edc 100644
--- a/tests/travis/pdo/mysql.phpunit.xml
+++ b/tests/travis/pdo/mysql.phpunit.xml
@@ -18,7 +18,7 @@
 		</testsuite>
 	</testsuites>
 	<filter>
-        <whitelist addUncoveredFilesFromWhitelist="true">
+        <whitelist addUncoveredFilesFromWhitelist="false">
             <directory suffix=".php">../../../system</directory>
         </whitelist>
 	</filter>
diff --git a/tests/travis/pdo/pgsql.phpunit.xml b/tests/travis/pdo/pgsql.phpunit.xml
index 2320255..df3ff98 100644
--- a/tests/travis/pdo/pgsql.phpunit.xml
+++ b/tests/travis/pdo/pgsql.phpunit.xml
@@ -18,7 +18,7 @@
 		</testsuite>
 	</testsuites>
 	<filter>
-        <whitelist addUncoveredFilesFromWhitelist="true">
+        <whitelist addUncoveredFilesFromWhitelist="false">
             <directory suffix=".php">../../../system</directory>
         </whitelist>
 	</filter>
diff --git a/tests/travis/pdo/sqlite.phpunit.xml b/tests/travis/pdo/sqlite.phpunit.xml
index 3d12567..7d867f6 100644
--- a/tests/travis/pdo/sqlite.phpunit.xml
+++ b/tests/travis/pdo/sqlite.phpunit.xml
@@ -18,7 +18,7 @@
 		</testsuite>
 	</testsuites>
 	<filter>
-        <whitelist addUncoveredFilesFromWhitelist="true">
+        <whitelist addUncoveredFilesFromWhitelist="false">
             <directory suffix=".php">../../../system</directory>
         </whitelist>
 	</filter>
diff --git a/tests/travis/pgsql.phpunit.xml b/tests/travis/pgsql.phpunit.xml
index 51e433d..bfddbf6 100644
--- a/tests/travis/pgsql.phpunit.xml
+++ b/tests/travis/pgsql.phpunit.xml
@@ -18,7 +18,7 @@
 		</testsuite>
 	</testsuites>
 	<filter>
-        <whitelist addUncoveredFilesFromWhitelist="true">
+        <whitelist addUncoveredFilesFromWhitelist="false">
             <directory suffix=".php">../../system</directory>
         </whitelist>
 	</filter>
diff --git a/tests/travis/sqlite.phpunit.xml b/tests/travis/sqlite.phpunit.xml
index 7011657..75c946a 100644
--- a/tests/travis/sqlite.phpunit.xml
+++ b/tests/travis/sqlite.phpunit.xml
@@ -18,7 +18,7 @@
 		</testsuite>
 	</testsuites>
 	<filter>
-        <whitelist addUncoveredFilesFromWhitelist="true">
+        <whitelist addUncoveredFilesFromWhitelist="false">
             <directory suffix=".php">../../system</directory>
         </whitelist>
 	</filter>
diff --git a/user_guide_src/source/_themes/eldocs/layout.html b/user_guide_src/source/_themes/eldocs/layout.html
index 01db07c..51d61b8 100644
--- a/user_guide_src/source/_themes/eldocs/layout.html
+++ b/user_guide_src/source/_themes/eldocs/layout.html
@@ -91,13 +91,7 @@
 		</div><!-- /#brand -->
 
 		<div id="header">
-			<form method="get" action="http://www.google.com/search">
-				<fieldset>
-					<input type="text" name="q" id="q" value="">
-					<input type="hidden" name="as_sitesearch" id="as_sitesearch" value="{{ project_domain }}/user_guide/" />
-					<input class="grades" type="submit" value="search">
-				</fieldset>
-			</form>
+            {%- include "searchbox.html" %}
 			<ul>
 				{%- block rootrellink %}
 				<li><a href="{{ pathto(master_doc) }}">User Guide Home</a>{%- if pagename != 'index' %}&nbsp;&nbsp;{{ reldelim1 }}{%- endif %}</li>
@@ -113,8 +107,10 @@
 			</ul>
 		</div><!-- /#header -->
 
-		<div class="section" id="content">
+		<div class="section body" id="content">
+			{%- block body %}
 			{{ body }}
+			{%- endblock %}
 		</div>
 	{%- endblock %}
 
@@ -125,8 +121,8 @@
 	{%- block footer %}			
 		<div id="footer">
 			<p class="top"><a href="#header" title="Return to top">Return to top</a></p>
-			<p><a href="{{ project_url }}">{{ project }}</a> &ndash; Copyright &copy; {{ copyright }}</a></p>
+			<p><a href="http://{{ project_domain }}/">{{ project }}</a> &ndash; Copyright &copy; {{ copyright }}</a></p>
 		</div><!-- /#footer -->
 	{%- endblock %}
 	</body>
-</html>
+</html>
\ No newline at end of file
diff --git a/user_guide_src/source/_themes/eldocs/searchbox.html b/user_guide_src/source/_themes/eldocs/searchbox.html
new file mode 100644
index 0000000..039590b
--- /dev/null
+++ b/user_guide_src/source/_themes/eldocs/searchbox.html
@@ -0,0 +1,21 @@
+<!--
+	--------------------------------
+	Original Google search box block
+	--------------------------------
+	
+<form method="get" action="http://www.google.com/search">
+	<fieldset>
+		<input type="text" name="q" id="q" value="">
+		<input type="hidden" name="as_sitesearch" id="as_sitesearch" value="{{ project_domain }}/user_guide/" />
+		<input class="grades" type="submit" value="search">
+	</fieldset>
+</form>
+-->
+
+
+<form class="search" action="{{ pathto('search') }}" method="get">
+  <input type="text" name="q" id="q" value="" />
+  <input type="submit" value="search" />
+  <input type="hidden" name="check_keywords" value="yes" />
+  <input type="hidden" name="area" value="default" />
+</form>
diff --git a/user_guide_src/source/_themes/eldocs/static/asset/css/common.css b/user_guide_src/source/_themes/eldocs/static/asset/css/common.css
index 6cabda0..0a63871 100644
--- a/user_guide_src/source/_themes/eldocs/static/asset/css/common.css
+++ b/user_guide_src/source/_themes/eldocs/static/asset/css/common.css
@@ -148,6 +148,8 @@
 
 .top{ float: right; }
 
+.highlight{ overflow-x: auto; }
+
 .admonition,
 .highlight-ee,
 .highlight-ci,
@@ -166,6 +168,8 @@
 	padding: 10px 10px 8px; 
 }
 
+.highlight-ci{ background-color: #FEFEFE; border-color: #E5E5E5; }
+
 	.admonition p{ margin: 0; }
 	
 	.codeblock{ margin: 10px 0; }
@@ -181,6 +185,8 @@
 }
 
 .admonition-title:after{ content: ':  '; }
+
+.highlighted{ background-color: #FFF09B; }
 	
 #table-contents{
 	bottom: 0;
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 68719ef..747791c 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -22,7 +22,7 @@
    -  ``$_SERVER['CI_ENV']`` can now be set to control the ``ENVIRONMENT`` constant.
    -  Added an optional backtrace to php-error template.
    -  Added Android to the list of user agents.
-   -  Added Windows 7, Android, Blackberry and iOS to the list of user platforms.
+   -  Added Windows 7, Windows 8, Android, Blackberry, iOS and PlayStation 3 to the list of user platforms.
    -  Added Fennec (Firefox for mobile) to the list of mobile user agents.
    -  Ability to log certain error types, not all under a threshold.
    -  Added support for pem, p10, p12, p7a, p7c, p7m, p7r, p7s, crt, crl, der, kdb, rsa, cer, sst, csr Certs to mimes.php.
@@ -34,10 +34,12 @@
    -  Added support for rar archives to mimes.php.
    -  Updated support for xml ('application/xml') and xsl ('application/xml', 'text/xsl') files in mimes.php.
    -  Updated support for doc files in mimes.php.
+   -  Updated support for docx files in mimes.php.
    -  Updated support for php files in mimes.php.
    -  Updated support for zip files in mimes.php.
+   -  Updated support for csv files in mimes.php.
    -  Added some more doctypes.
-   -  Added Romanian and Greek characters in foreign_characters.php.
+   -  Added Romanian, Greek and Vietnamese characters in *foreign_characters.php*.
    -  Changed logger to only chmod when file is first created.
    -  Removed previously deprecated SHA1 Library.
    -  Removed previously deprecated use of ``$autoload['core']`` in application/config/autoload.php.
@@ -49,7 +51,8 @@
    -  Changed detection of ``$view_folder`` so that if it's not found in the current path, it will now also be searched for under the application folder.
    -  Path constants BASEPATH, APPPATH and VIEWPATH are now (internally) defined as absolute paths.
    -  Updated email validation methods to use ``filter_var()`` instead of PCRE.
-   -  Changed environment defaults to report all errors in 'development' and only fatal ones in 'testing' and 'production' but only display them in 'development'.
+   -  Changed environment defaults to report all errors in *development* and only fatal ones in *testing*, *production* but only display them in *development*.
+   -  Updated *ip_address* database field lengths from 16 to 45 for supporting IPv6 address on :doc:`Trackback Library <libraries/trackback>` and :doc:`Captcha Helper <helpers/captcha_helper>`.
 
 -  Helpers
 
@@ -57,13 +60,15 @@
 	 - ``now()`` now works with all timezone strings supported by PHP.
 	 - Added an optional third parameter to ``timespan()`` that constrains the number of time units displayed.
 	 - Added an optional parameter to ``timezone_menu()`` that allows more attributes to be added to the generated select tag.
-	 - Deprecated ``standard_date()``, which now just uses the native ``date()`` with `DateTime constants <http://bg2.php.net/manual/en/class.datetime.php#datetime.constants.types>`_.
+	 - Deprecated ``standard_date()``, which now just uses the native ``date()`` with `DateTime constants <http://www.php.net/manual/en/class.datetime.php#datetime.constants.types>`_.
+	 - Added function ``date_range()`` that generates a list of dates between a specified period.
    -  ``create_captcha()`` accepts additional colors parameter, allowing for color customization.
    -  :doc:`URL Helper <helpers/url_helper>` changes include:
 	 - ``url_title()`` will now trim extra dashes from beginning and end.
 	 - ``anchor_popup()`` will now fill the "href" attribute with the URL and its JS code will return false instead.
 	 - Added JS window name support to ``anchor_popup()`` function.
 	 - Added support (auto-detection) for HTTP/1.1 response code 303 in ``redirect()``.
+	 - "auto" method in ``redirect()`` now chooses the "refresh" method only on IIS servers, instead of all servers on Windows.
    -  Added XHTML Basic 1.1 doctype to :doc:`HTML Helper <helpers/html_helper>`.
    -  Changed ``humanize()`` to include a second param for the separator.
    -  Refactored ``plural()`` and ``singular()`` to avoid double pluralization and support more words.
@@ -71,7 +76,7 @@
    -  Added a work-around in ``force_download()`` for a bug Android <= 2.1, where the filename extension needs to be in uppercase.
    -  ``form_dropdown()`` will now also take an array for unity with other form helpers.
    -  ``do_hash()`` now uses PHP's native ``hash()`` function (supporting more algorithms) and is deprecated.
-   -  Removed previously deprecated helper function ``js_insert_smiley()`` from smiley helper.
+   -  Removed previously deprecated helper function ``js_insert_smiley()`` from :doc:`Smiley Helper <helpers/smiley_helper>`.
    -  :doc:`File Helper <helpers/file_helper>` changes include:
 	 - ``set_realpath()`` can now also handle file paths as opposed to just directories.
 	 - Added an optional paramater to ``delete_files()`` to enable it to skip deleting files such as .htaccess and index.html.
@@ -101,12 +106,13 @@
    -  Replaced the _error_message() and _error_number() methods with error(), that returns an array containing the last database error code and message.
    -  Improved version() implementation so that drivers that have a native function to get the version number don't have to be defined in the core DB_driver class.
    -  Improved support of the PostgreSQL driver, including:
-	 - pg_version() is now used to get the database version number, when possible.
-	 - Added db_set_charset() support.
-	 - Added support for optimize_table() in :doc:`Database Utilities <database/utilities>` (rebuilds table indexes).
-	 - Added boolean data type support in escape().
-	 - Added update_batch() support.
-	 - Removed limit() and order_by() support for UPDATE and DELETE queries in as PostgreSQL does not support those features.
+	 - ``pg_version()`` is now used to get the database version number, when possible.
+	 - Added ``db_set_charset()`` support.
+	 - Added support for ``optimize_table()`` in :doc:`Database Utilities <database/utilities>` (rebuilds table indexes).
+	 - Added boolean data type support in ``escape()``.
+	 - Added ``update_batch()`` support.
+	 - Removed ``limit()`` and ``order_by()`` support for UPDATE and DELETE queries as PostgreSQL does not support those features.
+	 - Added a work-around for dead persistent connections to be re-created after a database restart.
    -  Added a constructor to the DB_result class and moved all driver-specific properties and logic out of the base DB_driver class to allow better abstraction.
    -  Removed protect_identifiers() and renamed internal method _protect_identifiers() to it instead - it was just an alias.
    -  Renamed internal method _escape_identifiers() to escape_identifiers().
@@ -137,74 +143,106 @@
    -  Added PDO support for create_database(), drop_database and drop_table() in :doc:`Database Forge <database/forge>`.
    -  Added unbuffered_row() method for getting a row without prefetching whole result (consume less memory).
    -  Added PDO support for ``list_fields()`` in :doc:`Database Results <database/results>`.
-   -  Added capability for packages to hold database.php config files 
+   -  Added capability for packages to hold database.php config files
    -  Added subdrivers support (currently only used by PDO).
+   -  Added client compression support for MySQL and MySQLi.
+   -  Removed :doc:`Loader Class <libraries/loader>` from Database error tracing to better find the likely culprit.
 
 -  Libraries
 
-   -  CI_Session now respects php.ini's session.gc_probability and session.gc_divisor
-   -  Added max_filename_increment config setting for Upload library.
-   -  CI_Loader::_ci_autoloader() is now a protected method.
-   -  Added custom filename to Email::attach() as $this->email->attach($filename, $disposition, $newname).
-   -  Added possibility to send attachment as buffer string in Email::attach() as $this->email->attach($buffer, $disposition, $newname, $mime).
+   -  :doc:`Session Library <libraries/sessions>` changes include:
+	 -  Library changed to :doc:`Driver <general/drivers>` with classic Cookie driver as default.
+	 -  Added Native PHP Session driver to work with $_SESSION.
+	 -  Custom session drivers can be added anywhere in package paths and loaded with Session library.
+	 -  Session drivers interchangeable on the fly.
+	 -  New tempdata feature allows setting user data items with an expiration time.
+	 -  Added default $config['sess_driver'] and $config['sess_valid_drivers'] items to config.php file.
+	 -  Cookie driver now respects php.ini's session.gc_probability and session.gc_divisor
+	 -  Changed the Cookie driver to select only one row when using database sessions.
+	 -  Cookie driver now only writes to database at end of request when using database.
+	 -  Cookie driver now uses PHP functions for faster array manipulation when using database.
+	 -  Added ``all_flashdata()`` method to session class. Returns an associative array of only flashdata.
+	 -  Added ``has_userdata()`` method to verify existence of userdata item.
+	 -  Added ``tempdata()``, ``set_tempdata()``, and ``unset_tempdata()`` methods for manipulating tempdata.
+   -  :doc:`File Uploading Library <libraries/file_uploading>` changes include:
+	 -  Added *max_filename_increment* config setting.
+	 -  Added an "index" parameter to the ``data()`` method.
    -  :doc:`Cart library <libraries/cart>` changes include:
 	 -  It now auto-increments quantity's instead of just resetting it, this is the default behaviour of large e-commerce sites.
 	 -  Product Name strictness can be disabled via the Cart Library by switching "$product_name_safe".
 	 -  Added function remove() to remove a cart item, updating with quantity of 0 seemed like a hack but has remained to retain compatibility.
    -  :doc:`Image Manipulation library <libraries/image_lib>` changes include:
 	 -  The initialize() method now only sets existing class properties.
-	 -  Added support for 3-length hex color values for wm_font_color and wm_shadow_color properties, as well as validation for them.
-	 -  Class properties wm_font_color, wm_shadow_color and wm_use_drop_shadow are now protected, to avoid breaking the text_watermark() method if they are set manually after initialization.
-	 -  If property maintain_ratio is set to TRUE, image_reproportion() now doesn't need both width and height to be specified.
-	 -  Property maintain_ratio is now taken into account when resizing images using ImageMagick library
-   -  Removed SHA1 function in the :doc:`Encryption Library <libraries/encryption>`.
-   -  Added $config['csrf_regeneration'] to the CSRF protection in the :doc:`Security library <libraries/security>`, which makes token regeneration optional.
-   -  Added $config['csrf_exclude_uris'] to the CSRF protection in the :doc:`Security library <libraries/security>`, which allows you list URIs which will not have the CSRF validation functions run.
+	 -  Added support for 3-length hex color values for *wm_font_color* and *wm_shadow_color* properties, as well as validation for them.
+	 -  Class properties *wm_font_color*, *wm_shadow_color* and *wm_use_drop_shadow* are now protected, to avoid breaking the ``text_watermark()`` method if they are set manually after initialization.
+	 -  If property *maintain_ratio* is set to TRUE, ``image_reproportion()`` now doesn't need both width and height to be specified.
+	 -  Property *maintain_ratio* is now taken into account when resizing images using ImageMagick library.
+	 -  Added support for maintaining transparency for PNG images in method ``text_watermark()``.
    -  :doc:`Form Validation library <libraries/form_validation>` changes include:
-	 -  Added method error_array() to return all error messages as an array.
-	 -  Added method set_data() to set an alternative data array to be validated instead of the default $_POST.
-	 -  Added method reset_validation(), which resets internal validation variables in case of multiple validation routines.
-	 -  Added support for setting error delimiters in the config file via $config['error_prefix'] and $config['error_suffix'].
-	 -  _execute() now considers input data to be invalid if a specified rule is not found.
-	 -  Removed method is_numeric() as it exists as a native PHP function and _execute() will find and use that (the 'is_numeric' rule itself is deprecated since 1.6.1).
+	 -  Added method ``error_array()`` to return all error messages as an array.
+	 -  Added method ``set_data()`` to set an alternative data array to be validated instead of the default ``$_POST``.
+	 -  Added method ``reset_validation()`` which resets internal validation variables in case of multiple validation routines.
+	 -  Added support for setting error delimiters in the config file via ``$config['error_prefix']`` and ``$config['error_suffix']``.
+	 -  ``_execute()`` now considers input data to be invalid if a specified rule is not found.
+	 -  Removed method ``is_numeric()`` as it exists as a native PHP function and ``_execute()`` will find and use that (the *is_numeric* rule itself is deprecated since 1.6.1).
 	 -  Native PHP functions used as rules can now accept an additional parameter, other than the data itself.
-	 -  Updated set_rules() to accept an array of rules as well as a string.
+	 -  Updated ``set_rules()`` to accept an array of rules as well as a string.
 	 -  Fields that have empty rules set no longer run through validation (and therefore are not considered erroneous).
-   -  Changed the :doc:`Session Library <libraries/sessions>` to select only one row when using database sessions.
-   -  Added all_flashdata() method to session class. Returns an associative array of only flashdata.
-   -  Allowed for setting table class defaults in a config file.
+	 -  Added rule *differs* to check if the value of a field differs from the value of another field.
+   -  Added support for setting :doc:`Table <libraries/table>` class defaults in a config file.
    -  Added a Wincache driver to the :doc:`Caching Library <libraries/caching>`.
    -  Added a Redis driver to the :doc:`Caching Library <libraries/caching>`.
-   -  Added dsn (delivery status notification) option to the :doc:`Email Library <libraries/email>`.
-   -  Renamed method _set_header() to set_header() and made it public to enable adding custom headers in the :doc:`Email Library <libraries/email>`.
-   -  Added an "index" parameter to the data() method in the :doc:`Upload Library <libraries/file_uploading>`.
+   -  :doc:`Email library <libraries/email>` changes include:
+	 -  Added custom filename to ``Email::attach()`` as ``$this->email->attach($filename, $disposition, $newname)``.
+	 -  Added possibility to send attachment as buffer string in ``Email::attach()`` as ``$this->email->attach($buffer, $disposition, $newname, $mime)``.
+	 -  Added dsn (delivery status notification) option.
+	 -  Renamed method _set_header() to set_header() and made it public to enable adding custom headers in the :doc:`Email Library <libraries/email>`.
+	 -  Successfully sent emails will automatically clear the parameters.
+	 -  Added a *return_path* parameter to the ``from()`` method.
+	 -  Removed the second parameter (character limit) from internal method ``_prep_quoted_printable()`` as it is never used.
+	 -  Internal method ``_prep_quoted_printable()`` will now utilize the native ``quoted_printable_encode()``, ``imap_8bit()`` functions (if available) when CRLF is set to "\r\n".
+	 -  Default charset now relies on the global ``$config['charset']`` setting.
    -  :doc:`Pagination Library <libraries/pagination>` changes include:
 	 -  Added support for the anchor "rel" attribute.
 	 -  Added support for setting custom attributes.
 	 -  Deprecated usage of the "anchor_class" setting (use the new "attributes" setting instead).
 	 -  Added $config['reuse_query_string'] to allow automatic repopulation of query string arguments, combined with normal URI segments.
+   -  Removed the default ``&nbsp;`` from a number of the configuration variables.
    -  Added the ability to use a proxy with the :doc:`XML-RPC Library <libraries/xmlrpc>`.
+   -  :doc:`Encryption Library <libraries/encryption>` changes include:
+	 -  Added support for hashing algorithms other than SHA1 and MD5.
+	 -  Removed previously deprecated ``sha1()`` method.
 
 -  Core
 
    -  Changed private methods in the :doc:`URI Library <libraries/uri>` to protected so MY_URI can override them.
-   -  Removed CI_CORE boolean constant from CodeIgniter.php (no longer Reactor and Core versions).
-   -  Added method get_vars() to the :doc:`Loader Library <libraries/loader>` to retrieve all variables loaded with $this->load->vars().
-   -  is_loaded() function from system/core/Commons.php now returns a reference.
-   -  $config['rewrite_short_tags'] now has no effect when using PHP 5.4 as *<?=* will always be available.
-   -  Added method() to the :doc:`Input Library <libraries/input>` to retrieve $_SERVER['REQUEST_METHOD'].
-   -  Modified valid_ip() to use PHP's filter_var() in the :doc:`Input Library <libraries/input>`.
-   -  Added support for HTTP-Only cookies with new config option ``cookie_httponly`` (default FALSE).
-   -  Renamed method _call_hook() to call_hook() in the :doc:`Hooks Library <general/hooks>`.
-   -  Added get_content_type() method to the :doc:`Output Library <libraries/output>`.
-   -  Added get_mimes() function to system/core/Commons.php to return the config/mimes.php array.
-   -  Added a second argument to set_content_type() in the :doc:`Output Library <libraries/output>` that allows setting the document charset as well.
-   -  $config['time_reference'] now supports all timezone strings supported by PHP.
-   -  Added support for HTTP code 303 ("See Other") in set_status_header().
-   -  Changed :doc:`Config Library <libraries/config>` method site_url() to accept an array as well.
-   -  Added method ``strip_image_tags()`` to the :doc:`Security Library <libraries/security>`.
-   -  Changed ``_exception_handler()`` to respect php.ini 'display_errors' setting.
-
+   -  Removed ``CI_CORE`` boolean constant from *CodeIgniter.php* (no longer Reactor and Core versions).
+   -  :doc:`Loader Library <libraries/loader>` changes include:
+	 -  Added method ``get_vars()`` to the Loader to retrieve all variables loaded with ``$this->load->vars()``.
+	 -  ``CI_Loader::_ci_autoloader()`` is now a protected method.
+	 -  Added autoloading of drivers with ``$autoload['drivers']``.
+	 -  ``CI_Loader::library()`` will now load drivers as well, for backward compatibility of converted libraries (like Session).
+   -  ``$config['rewrite_short_tags']`` now has no effect when using PHP 5.4 as *<?=* will always be available.
+   -  :doc:`Input Library <libraries/input>` changes include:
+	 -  Added ``method()`` to retrieve ``$_SERVER['REQUEST_METHOD']``.
+	 -  Modified ``valid_ip()`` to use PHP's ``filter_var()``.
+	 -  Added support for arrays and network addresses (e.g. 192.168.1.1/24) for use with the *proxy_ips* setting.
+   -  :doc:`Common functions <general/common_functions>` changes include:
+	 -  Added ``get_mimes()`` function to return the *config/mimes.php* array.
+	 -  Added support for HTTP code 303 ("See Other") in ``set_status_header()``.
+	 -  Removed redundant conditional to determine HTTP server protocol in ``set_status_header()``.
+	 -  Changed ``_exception_handler()`` to respect php.ini *display_errors* setting.
+   -  Added support for HTTP-Only cookies with new config option *cookie_httponly* (default FALSE).
+   -  Renamed method ``_call_hook()`` to ``call_hook()`` in the :doc:`Hooks Library <general/hooks>`.
+   -  :doc:`Output Library <libraries/output>` changes include:
+	 -  Added method ``get_content_type()``.
+	 -  Added a second argument to method ``set_content_type()`` that allows setting the document charset as well.
+   -  ``$config['time_reference']`` now supports all timezone strings supported by PHP.
+   -  Changed :doc:`Config Library <libraries/config>` method ``site_url()`` to accept an array as well.
+   -  :doc:`Security Library <libraries/security>` changes include:
+	 -  Added method ``strip_image_tags()``.
+	 -  Added ``$config['csrf_regeneration']``, which makes token regeneration optional.
+	 -  Added ``$config['csrf_exclude_uris']``, which allows you list URIs which will not have the CSRF validation methods run.
 
 Bug fixes for 3.0
 ------------------
@@ -239,23 +277,20 @@
 -  Fixed a bug (#129) - ODBC's num_rows() returned -1 in some cases, due to not all subdrivers supporting the odbc_num_rows() function.
 -  Fixed a bug (#153) - E_NOTICE being generated by getimagesize() in the :doc:`File Uploading Library <libraries/file_uploading>`.
 -  Fixed a bug (#611) - SQLSRV's error handling methods used to issue warnings when there's no actual error.
--  Fixed a bug (#1036) - is_write_type() method in the :doc:`Database Library <database/index>` didn't return TRUE for RENAME queries.
+-  Fixed a bug (#1036) - ``is_write_type()`` method in the :doc:`Database Library <database/index>` didn't return TRUE for RENAME queries.
 -  Fixed a bug in PDO's _version() method where it used to return the client version as opposed to the server one.
 -  Fixed a bug in PDO's insert_id() method where it could've failed if it's used with Postgre versions prior to 8.1.
 -  Fixed a bug in CUBRID's affected_rows() method where a connection resource was passed to cubrid_affected_rows() instead of a result.
 -  Fixed a bug (#638) - db_set_charset() ignored its arguments and always used the configured charset instead.
 -  Fixed a bug (#413) - Oracle's error handling methods used to only return connection-related errors.
--  Fixed a bug (#804) - Profiler library was trying to handle objects as strings in some cases, resulting in warnings being issued by htmlspecialchars().
 -  Fixed a bug (#1101) - MySQL/MySQLi result method field_data() was implemented as if it was handling a DESCRIBE result instead of the actual result set.
 -  Fixed a bug in Oracle's :doc:`Database Forge Class <database/forge>` method _create_table() where it failed with AUTO_INCREMENT as it's not supported.
 -  Fixed a bug (#1080) - When using the SMTP protocol, the :doc:`Email Library <libraries/email>` send() method was returning TRUE even if the connection/authentication against the server failed.
--  Fixed a bug (#499) - a CSRF cookie was created even with CSRF protection being disabled.
 -  Fixed a bug (#306) - ODBC's insert_id() method was calling non-existent function odbc_insert_id(), which resulted in a fatal error.
 -  Fixed a bug in Oracle's DB_result class where the cursor id passed to it was always NULL.
 -  Fixed a bug (#64) - Regular expression in DB_query_builder.php failed to handle queries containing SQL bracket delimiters in the join condition.
 -  Fixed a bug in the :doc:`Session Library <libraries/sessions>` where a PHP E_NOTICE error was triggered by _unserialize() due to results from databases such as MSSQL and Oracle being space-padded on the right.
 -  Fixed a bug (#501) - set_rules() to check if the request method is not 'POST' before aborting, instead of depending on count($_POST) in the :doc:`Form Validation Library <libraries/form_validation>`.
--  Fixed a bug (#940) - csrf_verify() used to set the CSRF cookie while processing a POST request with no actual POST data, which resulted in validating a request that should be considered invalid.
 -  Fixed a bug (#136) - PostgreSQL, MySQL and MySQLi's escape_str() method didn't properly escape LIKE wild characters.
 -  Fixed a bug in the library loader where some PHP versions wouldn't execute the class constructor.
 -  Fixed a bug (#88) - An unexisting property was used for configuration of the Memcache cache driver.
@@ -274,7 +309,6 @@
 -  Fixed a bug (#1265) - Database connections were always closed, regardless of the 'pconnect' option value.
 -  Fixed a bug (#128) - :doc:`Language Library <libraries/language>` did not correctly keep track of loaded language files.
 -  Fixed a bug (#1242) - Added Windows path compatibility to function read_dir of ZIP library.
--  Fixed a bug (#1314) - sess_destroy() did not destroy userdata.
 -  Fixed a bug (#1349) - get_extension() in the :doc:`File Uploading Library <libraries/file_uploading>` returned the original filename when it didn't have an actual extension.
 -  Fixed a bug (#1273) - E_NOTICE being generated by :doc:`Query Builder <database/query_builder>`'s set_update_batch() method.
 -  Fixed a bug (#44, #110) - :doc:`Upload library <libraries/file_uploading>`'s clean_file_name() method didn't clear '!' and '#' characters.
@@ -288,14 +322,13 @@
 -  Fixed a bug (#666) - :doc:`Output library <libraries/output>`'s set_content_type() method didn't set the document charset.
 -  Fixed a bug (#784, #861) - :doc:`Database Forge <database/forge>` method ``create_table()`` used to accept constraints for MSSQL/SQLSRV integer-type columns.
 -  Fixed a bug (#706) - SQLSRV/MSSSQL didn't escape field names.
--  Fixed a bug (#1452) - protect_identifiers() didn't properly detect identifiers with spaces in their names.
--  Fixed a bug where protect_identifiers() ignored it's extra arguments when the value passed to it is an array.
--  Fixed a bug where _has_operator() didn't detect BETWEEN.
--  Fixed a bug in :doc:`Query Builder <database/query_builder>`'s join() method where it failed with identifiers containing dashes.
+-  Fixed a bug (#1452) - ``protect_identifiers()`` didn't properly detect identifiers with spaces in their names.
+-  Fixed a bug where ``protect_identifiers()`` ignored it's extra arguments when the value passed to it is an array.
+-  Fixed a bug where ``_has_operator()`` didn't detect BETWEEN.
+-  Fixed a bug in :doc:`Query Builder <database/query_builder>`'s ``join()`` method where it failed with identifiers containing dashes.
 -  Fixed a bug (#1264) - :doc:`Database Forge <database/forge>` and :doc:`Database Utilities <database/utilities>` didn't update/reset the databases and tables list cache when a table or a database is created, dropped or renamed.
--  Fixed a bug (#7) - :doc:`Query Builder <database/query_builder>`'s join() method only escaped one set of conditions.
+-  Fixed a bug (#7) - :doc:`Query Builder <database/query_builder>`'s ``join()`` method only escaped one set of conditions.
 -  Fixed a bug (#1321) - Core Exceptions class couldn't find the errors/ folder in some cases.
--  Fixed a bug in the File-based :doc:`Cache Library <libraries/caching>` driver's get_metadata() method where a non-existent array key was accessed for the TTL value.
 -  Fixed a bug (#1202) - :doc:`Encryption Library <libraries/encryption>` encode_from_legacy() didn't set back the encrypt mode on failure.
 -  Fixed a bug (#145) - compile_binds() failed when the bind marker was present in a literal string within the query.
 -  Fixed a bug in protect_identifiers() where if passed along with the field names, operators got escaped as well.
@@ -316,6 +349,38 @@
 -  Fixed a bug (#135) - PHP Error logging was impossible without the errors being displayed.
 -  Fixed a bug (#1613) - :doc:`Form Helper <helpers/form_helper>` functions ``form_multiselect()``, ``form_dropdown()`` didn't properly handle empty array option groups.
 -  Fixed a bug (#1605) - :doc:`Pagination Library <libraries/pagination>` produced incorrect *previous* and *next* link values.
+-  Fixed a bug in SQLSRV's ``affected_rows()`` method where an erroneous function name was used.
+-  Fixed a bug (#1000) - Change syntax of ``$view_file`` to ``$_ci_view_file`` to prevent being overwritten by application.
+-  Fixed a bug (#1757) - :doc:`Directory Helper <helpers/directory_helper>` function ``directory_map()`` was skipping files and directories named *0*.
+-  Fixed a bug (#1789) - :doc:`Database Library <database/index>` method ``escape_str()`` escaped quote characters in LIKE conditions twice under MySQL.
+-  Fixed a bug (#395) - :doc:`Unit Testing Library <libraries/unit_testing>` method ``result()`` didn't properly check array result columns when called from ``report()``.
+-  Fixed a bug (#1692) - :doc:`Database Library <database/index>` method ``display_error()`` didn't properly trace the possible error source on Windows systems.
+-  Fixed a bug (#1745) - ``is_write_type()`` method in the :doc:`Database Library <database/index>` didn't return TRUE for LOAD queries.
+-  Fixed a bug (#1765) - :doc:`Database Library <database/index>` didn't properly detect connection errors for MySQLi.
+-  Fixed a bug (#1257) - :doc:`Query Builder <database/query_builder>` used to (unnecessarily) group FROM clause contents, which breaks certain queries and is invalid for some databases.
+-  Fixed a bug (#1709) - :doc:`Email <libraries/email>` headers were broken when using long email subjects and \r\n as CRLF.
+-  Fixed a bug where MB_ENABLED was only declared if UTF8_ENABLED was set to TRUE.
+
+Version 2.1.3
+=============
+
+Release Date: October 8, 2012
+
+-  Core
+   - :doc:`Common function <general/common_functions>` ``is_loaded()`` now returns a reference.
+
+Bug fixes for 2.1.3
+-------------------
+
+-  Fixed a bug (#1543) - File-based :doc:`Caching <libraries/caching>` method ``get_metadata()`` used a non-existent array key to look for the TTL value.
+-  Fixed a bug (#1314) - :doc:`Session Library <libraries/session>` method ``sess_destroy()`` didn't destroy the userdata array.
+-  Fixed a bug (#804) - Profiler library was trying to handle objects as strings in some cases, resulting in *E_WARNING* messages being issued by ``htmlspecialchars()``.
+-  Fixed a bug (#1699) - :doc:`Migration Library <libraries/migration>` ignored the ``$config['migration_path']`` setting.
+-  Fixed a bug (#227) - :doc:`Input Library <libraries/input>` allowed unconditional spoofing of HTTP clients' IP addresses through the *HTTP_CLIENT_IP* header.
+-  Fixed a bug (#907) - :doc:`Input Library <libraries/input>` ignored *HTTP_X_CLUSTER_CLIENT_IP* and *HTTP_X_CLIENT_IP* headers when checking for proxies.
+-  Fixed a bug (#940) - ``csrf_verify()`` used to set the CSRF cookie while processing a POST request with no actual POST data, which resulted in validating a request that should be considered invalid.
+-  Fixed a bug (#499) - :doc:`Security Library <libraries/security>` where a CSRF cookie was created even if ``$config['csrf_protection']`` is set tot FALSE.
+-  Fixed a bug (#1715) - :doc:`Input Library <libraries/input>` triggered ``csrf_verify()`` on CLI requests.
 
 Version 2.1.2
 =============
diff --git a/user_guide_src/source/conf.py b/user_guide_src/source/conf.py
index e972a38..f68405b 100644
--- a/user_guide_src/source/conf.py
+++ b/user_guide_src/source/conf.py
@@ -167,6 +167,7 @@
 # Output file base name for HTML help builder.
 htmlhelp_basename = 'CodeIgniterdoc'
 
+html_copy_source = False
 
 # -- Options for LaTeX output --------------------------------------------------
 
diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst
index c17de60..636b5b5 100644
--- a/user_guide_src/source/database/configuration.rst
+++ b/user_guide_src/source/database/configuration.rst
@@ -28,6 +28,7 @@
 		'dbcollat' => 'utf8_general_ci',
 		'swap_pre' => '',
 		'autoinit' => TRUE,
+		'compress' => TRUE,
 		'stricton' => FALSE,
 		'failover' => array()
 	);
@@ -69,6 +70,7 @@
 				'dbcollat' => 'utf8_general_ci',
 				'swap_pre' => '',
 				'autoinit' => TRUE,
+				'compress' => TRUE,
 				'stricton' => FALSE
 			),
 			array(
@@ -86,6 +88,7 @@
 				'dbcollat' => 'utf8_general_ci',
 				'swap_pre' => '',
 				'autoinit' => TRUE,
+				'compress' => TRUE,
 				'stricton' => FALSE
 			)
 		);
@@ -115,6 +118,7 @@
 		'dbcollat' => 'utf8_general_ci',
 		'swap_pre' => '',
 		'autoinit' => TRUE,
+		'compress' => TRUE,
 		'stricton' => FALSE,
 		'failover' => array()
 	);
@@ -174,11 +178,12 @@
 			customizable by the end user.
 **autoinit**		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.
+**compress**		Whether or not to use client compression for MySQL or MySQLi.
 **stricton**		TRUE/FALSE (boolean) - Whether to force "Strict Mode" connections, good for ensuring strict SQL
 			while developing an application.
 **port**		The database port number. To use this value you have to add a line to the database config array.
 			::
-			
+
 				$db['default']['port'] = 5432;
 ======================  ==================================================================================================
 
diff --git a/user_guide_src/source/general/autoloader.rst b/user_guide_src/source/general/autoloader.rst
index 259a498..8ecc13c 100644
--- a/user_guide_src/source/general/autoloader.rst
+++ b/user_guide_src/source/general/autoloader.rst
@@ -9,7 +9,7 @@
 
 The following items can be loaded automatically:
 
--  Core classes found in the "libraries" folder
+-  Classes found in the "libraries" folder
 -  Helper files found in the "helpers" folder
 -  Custom config files found in the "config" folder
 -  Language files found in the "system/language" folder
diff --git a/user_guide_src/source/general/cli.rst b/user_guide_src/source/general/cli.rst
index 7dc1ca3..649d5d5 100644
--- a/user_guide_src/source/general/cli.rst
+++ b/user_guide_src/source/general/cli.rst
@@ -52,7 +52,7 @@
 
 	example.com/index.php/tools/message/to
 
-Instead, we are going to open Terminal in Mac/Lunix or go to Run > "cmd"
+Instead, we are going to open Terminal in Mac/Linux or go to Run > "cmd"
 in Windows and navigate to our CodeIgniter project.
 
 .. code-block:: bash
diff --git a/user_guide_src/source/general/reserved_names.rst b/user_guide_src/source/general/reserved_names.rst
index 5ce7fc2..3354375 100644
--- a/user_guide_src/source/general/reserved_names.rst
+++ b/user_guide_src/source/general/reserved_names.rst
@@ -45,11 +45,11 @@
 ---------
 
 -  ENVIRONMENT
--  EXT
 -  FCPATH
 -  SELF
 -  BASEPATH
 -  APPPATH
+-  VIEWPATH
 -  CI_VERSION
 -  FILE_READ_MODE
 -  FILE_WRITE_MODE
diff --git a/user_guide_src/source/helpers/captcha_helper.rst b/user_guide_src/source/helpers/captcha_helper.rst
index 48095a1..9024473 100644
--- a/user_guide_src/source/helpers/captcha_helper.rst
+++ b/user_guide_src/source/helpers/captcha_helper.rst
@@ -102,7 +102,7 @@
 	CREATE TABLE captcha (  
 		captcha_id bigint(13) unsigned NOT NULL auto_increment,  
 		captcha_time int(10) unsigned NOT NULL,  
-		ip_address varchar(16) default '0' NOT NULL,  
+		ip_address varchar(45) NOT NULL,  
 		word varchar(20) NOT NULL,  
 		PRIMARY KEY `captcha_id` (`captcha_id`),  
 		KEY `word` (`word`)
diff --git a/user_guide_src/source/helpers/date_helper.rst b/user_guide_src/source/helpers/date_helper.rst
index e332a91..9de925b 100644
--- a/user_guide_src/source/helpers/date_helper.rst
+++ b/user_guide_src/source/helpers/date_helper.rst
@@ -306,6 +306,30 @@
 
 If the second parameter is empty, the current year will be used.
 
+date_range()
+============
+
+Returns a list of dates within a specified period.
+
+.. php:method:: date_range($unix_start = '', $mixed = '', $is_unix = TRUE, $format = 'Y-m-d')
+
+	:param integer	$unix_start: UNIX timestamp of the range start date
+	:param integer	$mixed: UNIX timestamp of the range end date or interval in days
+	:param boolean	$is_unix: set to FALSE if $mixed is not a timestamp
+	:param string	$format: output date format, same as in date()
+	:returns: array
+
+Example
+
+::
+
+	$range = date_range('2012-01-01', '2012-01-15');
+	echo "First 15 days of 2012:";
+	foreach ($range as $date)
+	{
+		echo $date."\n";
+	}
+
 timezones()
 ===========
 
diff --git a/user_guide_src/source/helpers/url_helper.rst b/user_guide_src/source/helpers/url_helper.rst
index 82db6a5..1987dfb 100644
--- a/user_guide_src/source/helpers/url_helper.rst
+++ b/user_guide_src/source/helpers/url_helper.rst
@@ -207,7 +207,12 @@
 	echo mailto('me@my-site.com', 'Click Here to Contact Me');
 
 As with the anchor() tab above, you can set attributes using the third
-parameter.
+parameter:
+
+::
+
+    $attributes = array('title' => 'Mail me');
+    echo mailto('me@my-site.com', 'Contact Me', $attributes);
 
 safe_mailto()
 =============
@@ -298,7 +303,7 @@
 
 The optional second parameter allows you to force a particular redirection
 method. The available methods are "location" or "refresh", with location
-being faster but less reliable on Windows servers. The default is "auto",
+being faster but less reliable on IIS servers. The default is "auto",
 which will attempt to intelligently choose the method based on the server
 environment.
 
@@ -320,3 +325,7 @@
 
 .. note:: For very fine grained control over headers, you should use the
 	`Output Library </libraries/output>` set_header() function.
+
+.. note:: To IIS users: if you hide the `Server` HTTP header, the "auto"
+	method won't detect IIS, in that case it is advised you explicitly
+	use the "refresh" method.
diff --git a/user_guide_src/source/images/codeigniter_1.7.1_library_reference.png b/user_guide_src/source/images/codeigniter_1.7.1_library_reference.png
index 554ae2e..7f054f9 100644
--- a/user_guide_src/source/images/codeigniter_1.7.1_library_reference.png
+++ b/user_guide_src/source/images/codeigniter_1.7.1_library_reference.png
Binary files differ
diff --git a/user_guide_src/source/index.rst b/user_guide_src/source/index.rst
index e42425b..3508970 100644
--- a/user_guide_src/source/index.rst
+++ b/user_guide_src/source/index.rst
@@ -80,6 +80,7 @@
 - :doc:`libraries/caching`
 - :doc:`database/index`
 - :doc:`libraries/javascript`
+- :doc:`libraries/sessions`
 
 ****************
 Helper Reference
@@ -119,4 +120,4 @@
 	documentation/index
 	tutorial/index
 	general/quick_reference
-	general/credits
\ No newline at end of file
+	general/credits
diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst
index f304a71..31a5c07 100644
--- a/user_guide_src/source/installation/upgrade_300.rst
+++ b/user_guide_src/source/installation/upgrade_300.rst
@@ -31,8 +31,24 @@
 Use of the ``$autoload['core']`` config array has been deprecated as of CodeIgniter 1.4.1 and is now removed.
 Move any entries that you might have listed there to ``$autoload['libraries']`` instead.
 
+**************************************************************
+Step 4: Add new session driver items to your config/config.php
+**************************************************************
+
+With the change from a single Session Library to the new Session Driver, two new config items have been added:
+
+   -  ``$config['sess_driver']`` selects which driver to initially load. Options are:
+       -  'cookie' (the default) for classic CodeIgniter cookie-based sessions
+       -  'native' for native PHP Session support
+       -  the name of a custom driver you have provided (see :doc:`Session Driver <../libraries/sessions>` for more info)
+   -  ``$config['sess_valid_drivers']`` provides an array of additional custom drivers to make available for loading
+
+As the new Session Driver library loads the classic Cookie driver by default and always makes 'cookie' and 'native'
+available as valid drivers, neither of these configuration items are required. However, it is recommended that you
+add them for clarity and ease of configuration in the future.
+
 ***************************************
-Step 4: Update your config/database.php
+Step 5: Update your config/database.php
 ***************************************
 
 Due to 3.0.0's renaming of Active Record to Query Builder, inside your `config/database.php`, you will
@@ -43,20 +59,20 @@
     $query_builder = TRUE;
 
 *******************************
-Step 5: Move your errors folder
+Step 6: Move your errors folder
 *******************************
 
 In version 3.0.0, the errors folder has been moved from _application/errors* to _application/views/errors*.
 
 ****************************************************************************
-Step 6: Check the calls to Array Helper's element() and elements() functions
+Step 7: Check the calls to Array Helper's element() and elements() functions
 ****************************************************************************
 
 The default return value of these functions, when the required elements
 don't exist, has been changed from FALSE to NULL.
 
 ***************************************************************
-Step 7: Remove usage of (previously) deprecated functionalities
+Step 8: Remove usage of (previously) deprecated functionalities
 ***************************************************************
 
 In addition to the ``$autoload['core']`` configuration setting, there's a number of other functionalities
@@ -138,4 +154,17 @@
 CodeIgniter 3.1+.
 
 .. note:: This setting is still available, but you're strongly encouraged to remove its' usage sooner
-	rather than later.
\ No newline at end of file
+	rather than later.
+
+Email library
+=============
+
+The :doc:`Email library <../libraries/email>` will automatically clear the set parameters after successfully sending
+emails. To override this behaviour, pass FALSE as the first parameter in the ``send()`` function:
+
+::
+
+	if ($this->email->send(FALSE))
+ 	{
+ 		// Parameters won't be cleared
+ 	}
diff --git a/user_guide_src/source/libraries/email.rst b/user_guide_src/source/libraries/email.rst
index c5fa680..da3bf26 100644
--- a/user_guide_src/source/libraries/email.rst
+++ b/user_guide_src/source/libraries/email.rst
@@ -97,7 +97,7 @@
 **mailtype**        text                   text or html                 Type of mail. If you send HTML email you must send it as a complete web
                                                                         page. Make sure you don't have any relative links or relative image
                                                                         paths otherwise they will not work.
-**charset**         utf-8                                               Character set (utf-8, iso-8859-1, etc.).
+**charset**         ``$config['charset']``                              Character set (utf-8, iso-8859-1, etc.).
 **validate**        FALSE                  TRUE or FALSE (boolean)      Whether to validate the email address.
 **priority**        3                      1, 2, 3, 4, 5                Email Priority. 1 = highest. 5 = lowest. 3 = normal.
 **crlf**            \\n                    "\\r\\n" or "\\n" or "\\r"   Newline character. (Use "\\r\\n" to comply with RFC 822).
@@ -117,6 +117,13 @@
 
 	$this->email->from('you@example.com', 'Your Name');
 
+You can also set a Return-Path, to help redirect undelivered mail::
+
+	$this->email->from('you@example.com', 'Your Name', 'returned_emails@example.com');
+	
+.. note:: Return-Path can't be used if you've configured
+	'smtp' as your protocol.
+
 $this->email->reply_to()
 -------------------------
 
@@ -226,6 +233,14 @@
 	    // Generate error
 	}
 
+This function will automatically clear all parameters if the request was
+successful. To stop this behaviour pass FALSE::
+
+ 	if ($this->email->send(FALSE))
+ 	{
+ 		// Parameters won't be cleared
+ 	}
+
 $this->email->attach()
 ----------------------
 
diff --git a/user_guide_src/source/libraries/file_uploading.rst b/user_guide_src/source/libraries/file_uploading.rst
index 65cd5c7..1698dcb 100644
--- a/user_guide_src/source/libraries/file_uploading.rst
+++ b/user_guide_src/source/libraries/file_uploading.rst
@@ -197,6 +197,7 @@
                                                                        Separate multiple types with a pipe.
 **file_name**                None              Desired file name       If set CodeIgniter will rename the uploaded file to this name. The
                                                                        extension provided in the file name must also be an allowed file type.
+                                                                       If no extension is provided in the original file_name will be used.
 **overwrite**                FALSE             TRUE/FALSE (boolean)    If set to true, if a file with the same name as the one you are
                                                                        uploading exists, it will be overwritten. If set to false, a number will
                                                                        be appended to the filename if another with the same name exists.
diff --git a/user_guide_src/source/libraries/form_validation.rst b/user_guide_src/source/libraries/form_validation.rst
index 3bcad7b..6c6743d 100644
--- a/user_guide_src/source/libraries/form_validation.rst
+++ b/user_guide_src/source/libraries/form_validation.rst
@@ -399,7 +399,7 @@
 functions. This permits you to extend the validation class to meet your
 needs. For example, if you need to run a database query to see if the
 user is choosing a unique username, you can create a callback function
-that does that. Let's create a example of this.
+that does that. Let's create an example of this.
 
 In your controller, change the "username" rule to this::
 
@@ -488,6 +488,17 @@
 
 	$this->form_validation->set_message('username_check')
 
+If you are using an error message that can accept two $s in your error string,
+such as:
+::
+
+	$this->form_validation->set_message('min_length', 'The $s field must contain at least $s characters.');
+
+Then you can also use %1$s and %2$s:
+::
+
+	$this->form_validation->set_message('min_length', 'This field must contain at least %2$s characters.');
+
 You can also override any error message found in the language file. For
 example, to change the message for the "required" rule you will do this::
 
@@ -861,8 +872,10 @@
 ========================= ========== ============================================================================================= =======================
 **required**              No         Returns FALSE if the form element is empty.                                                                          
 **matches**               Yes        Returns FALSE if the form element does not match the one in the parameter.                    matches[form_item]     
-**is_unique**             Yes        Returns FALSE if the form element is not unique to the                                        is_unique[table.field] 
-                                     table and field name in the parameter. is_unique[table.field]                                                        
+**differs**               Yes        Returns FALSE if the form element does not differ from the one in the parameter.              differs[form_item]     
+**is_unique**             Yes        Returns FALSE if the form element is not unique to the table and field name in the            is_unique[table.field] 
+                                     parameter. Note: This rule requires :doc:`Query Builder <../database/query_builder>` to be                             
+                                     enabled in order to work.
 **max_length**            Yes        Returns FALSE if the form element is longer then the parameter value.                         max_length[12]         
 **exact_length**          Yes        Returns FALSE if the form element is not exactly the parameter value.                         exact_length[8]        
 **greater_than**          Yes        Returns FALSE if the form element is less than or equal to the parameter value or not         greater_than[8]
@@ -884,7 +897,6 @@
                                      0, 1, 2, 3, etc.
 **is_natural_no_zero**    No         Returns FALSE if the form element contains anything other than a natural
                                      number, but not zero: 1, 2, 3, etc.
-**is_unique**             Yes        Returns FALSE if the form element is not unique in a database table.                          is_unique[table.field] 
 **valid_email**           No         Returns FALSE if the form element does not contain a valid email address.
 **valid_emails**          No         Returns FALSE if any value provided in a comma separated list is not a valid email.
 **valid_ip**              No         Returns FALSE if the supplied IP is not valid.
diff --git a/user_guide_src/source/libraries/image_lib.rst b/user_guide_src/source/libraries/image_lib.rst
index ed6575c..dcdccbd 100644
--- a/user_guide_src/source/libraries/image_lib.rst
+++ b/user_guide_src/source/libraries/image_lib.rst
@@ -91,9 +91,9 @@
 	    echo $this->image_lib->display_errors();
 	}
 
-Note: You can optionally specify the HTML formatting to be applied to
-the errors, by submitting the opening/closing tags in the function, like
-this::
+.. note:: You can optionally specify the HTML formatting to be applied to
+	the errors, by submitting the opening/closing tags in the function,
+	like this::
 
 	$this->image_lib->display_errors('<p>', '</p>');
 
@@ -225,8 +225,7 @@
 	$config['y_axis'] = '40';
 
 All preferences listed in the table above are available for this
-function except these: rotation_angle, width, height, create_thumb,
-new_image.
+function except these: rotation_angle, create_thumb, new_image.
 
 Here's an example showing how you might crop an image::
 
@@ -243,11 +242,11 @@
 	    echo $this->image_lib->display_errors();
 	}
 
-Note: Without a visual interface it is difficult to crop images, so this
-function is not very useful unless you intend to build such an
-interface. That's exactly what we did using for the photo gallery module
-in ExpressionEngine, the CMS we develop. We added a JavaScript UI that
-lets the cropping area be selected.
+.. note:: Without a visual interface it is difficult to crop images, so this
+	function is not very useful unless you intend to build such an
+	interface. That's exactly what we did using for the photo gallery module
+	in ExpressionEngine, the CMS we develop. We added a JavaScript UI that
+	lets the cropping area be selected.
 
 $this->image_lib->rotate()
 ===========================
@@ -338,8 +337,8 @@
 bottom/center of the image, 20 pixels from the bottom of the image.
 
 .. note:: In order for the image class to be allowed to do any
-	processing, the image file must have "write" file permissions. For
-	example, 777.
+	processing, the image file must have "write" file permissions
+	For example, 777.
 
 Watermarking Preferences
 ========================
diff --git a/user_guide_src/source/libraries/loader.rst b/user_guide_src/source/libraries/loader.rst
index aadf974..615aba1 100644
--- a/user_guide_src/source/libraries/loader.rst
+++ b/user_guide_src/source/libraries/loader.rst
@@ -4,6 +4,7 @@
 
 Loader, as the name suggests, is used to load elements. These elements
 can be libraries (classes) :doc:`View files <../general/views>`,
+:doc:`Drivers <../general/drivers>`,
 :doc:`Helpers <../general/helpers>`,
 :doc:`Models <../general/models>`, or your own files.
 
@@ -74,6 +75,70 @@
 
 If the third (optional) parameter is blank, the library will usually be
 assigned to an object with the same name as the library. For example, if
+the library is named Calendar, it will be assigned to a variable named
+$this->calendar.
+
+If you prefer to set your own class names you can pass its value to the
+third parameter::
+
+	$this->load->library('calendar', '', 'my_calendar');
+
+	// Calendar class is now accessed using:
+
+	$this->my_calendar
+
+Please take note, when multiple libraries are supplied in an array for
+the first parameter, this parameter is discarded.
+
+$this->load->driver('parent_name', $config, 'object name')
+===========================================================
+
+This function is used to load driver libraries. Where parent_name is the
+name of the parent class you want to load.
+
+As an example, if you would like to use sessions with CodeIgniter, the first
+step is to load the session driver within your controller::
+
+	$this->load->driver('session');
+
+Once loaded, the library will be ready for use, using
+$this->session->*some_function*().
+
+Driver files must be stored in a subdirectory within the main
+"libraries" folder, or within your personal application/libraries
+folder. The subdirectory must match the parent class name. Read the
+:doc:`Drivers <../general/drivers>` description for details.
+
+Additionally, multiple driver libraries can be loaded at the same time by
+passing an array of drivers to the load function.
+
+::
+
+	$this->load->driver(array('session', 'cache'));
+
+Setting options
+---------------
+
+The second (optional) parameter allows you to optionally pass
+configuration settings. You will typically pass these as an array::
+
+	$config = array (
+	                  'sess_driver' => 'cookie',
+	                  'sess_encrypt_cookie'  => true,
+	                  'encryption_key' => 'mysecretkey'
+	               );
+
+	$this->load->driver('session', $config);
+
+Config options can usually also be set via a config file. Each library
+is explained in detail in its own page, so please read the information
+regarding each one you would like to use.
+
+Assigning a Driver to a different object name
+----------------------------------------------
+
+If the third (optional) parameter is blank, the library will be assigned
+to an object with the same name as the parent class. For example, if
 the library is named Session, it will be assigned to a variable named
 $this->session.
 
@@ -86,8 +151,8 @@
 
 	$this->my_session
 
-Please take note, when multiple libraries are supplied in an array for
-the first parameter, this parameter is discarded.
+.. note:: Driver libraries may also be loaded with the library() method,
+	but it is faster to use driver()
 
 $this->load->view('file_name', $data, true/false)
 ==================================================
@@ -279,6 +344,6 @@
 	$this->load->remove_package_path(APPPATH.'my_app');
 
 	// Again without the second parameter:
-	$this->load->add_package_path(APPPATH.'my_app', TRUE);
+	$this->load->add_package_path(APPPATH.'my_app');
 	$this->load->view('my_app_index'); // Loads
-	$this->load->view('welcome_message'); // Loads
\ No newline at end of file
+	$this->load->view('welcome_message'); // Loads
diff --git a/user_guide_src/source/libraries/migration.rst b/user_guide_src/source/libraries/migration.rst
index 5192f1f..cb7d96a 100644
--- a/user_guide_src/source/libraries/migration.rst
+++ b/user_guide_src/source/libraries/migration.rst
@@ -2,4 +2,136 @@
 Migrations Class
 ################
 
-Coming soon.
\ No newline at end of file
+Migrations are a convenient way for you to alter your database in a 
+structured and organized manner. You could edit fragments of SQL by hand 
+but you would then be responsible for telling other developers that they 
+need to go and run them. You would also have to keep track of which changes 
+need to be run against the production machines next time you deploy.
+
+The database table **migration** tracks which migrations have already been 
+run so all you have to do is update your application files and 
+call **$this->migrate->current()** to work out which migrations should be run. 
+The current version is found in **config/migration.php**.
+
+******************
+Create a Migration
+******************
+
+.. note:: Each Migration is run in numerical order forward or backwards 
+	depending on the method taken. Use a prefix of 3 numbers followed by an 
+	underscore for the filename of your migration.
+	
+This will be the first migration for a new site which has a blog. All 
+migrations go in the folder **application/migrations/** and have names such 
+as: **001_add_blog.php**.::
+
+	defined('BASEPATH') OR exit('No direct script access allowed');
+
+	class Migration_Add_blog extends CI_Migration {
+
+		public function up()
+		{
+			$this->dbforge->add_field(array(
+				'blog_id' => array(
+					'type' => 'INT',
+					'constraint' => 5,
+					'unsigned' => TRUE,
+					'auto_increment' => TRUE
+				),
+				'blog_title' => array(
+					'type' => 'VARCHAR',
+					'constraint' => '100',
+				),
+				'blog_description' => array(
+					'type' => 'TEXT',
+					'null' => TRUE,
+				),
+			));
+			
+			$this->dbforge->create_table('blog');
+		}
+
+		public function down()
+		{
+			$this->dbforge->drop_table('blog');
+		}
+
+Then in **application/config/migration.php** set **$config['migration_version'] = 1;**.
+
+*************
+Usage Example
+*************
+
+In this example some simple code is placed in **application/controllers/migrate.php** 
+to update the schema.::
+
+	$this->load->library('migration');
+
+	if ( ! $this->migration->current())
+	{
+		show_error($this->migration->error_string());
+	}
+
+******************
+Function Reference
+******************
+
+There are five available methods for the Migration class:
+
+-  $this->migration->current();
+-  $this->migration->error_string();
+-  $this->migration->find_migrations();
+-  $this->migration->latest();
+-  $this->migration->version();
+
+$this->migration->current()
+============================
+
+The current migration is whatever is set for **$config['migration_version']** in 
+**application/config/migration.php**.
+
+$this->migration->error_string()
+=================================
+
+This returns a string of errors while performing a migration.
+
+$this->migration->find_migrations()
+====================================
+
+An array of migration filenames are returned that are found in the **migration_path** 
+property.
+
+$this->migration->latest()
+===========================
+
+This works much the same way as current() but instead of looking for 
+the **$config['migration_version']** the Migration class will use the very 
+newest migration found in the filesystem.
+
+$this->migration->version()
+============================
+
+Version can be used to roll back changes or step forwards programmatically to 
+specific versions. It works just like current but ignores **$config['migration_version']**.::
+
+	$this->load->library('migration');
+
+	$this->migration->version(5);
+
+*********************
+Migration Preferences
+*********************
+
+The following is a table of all the config options for migrations.
+
+========================== ====================== ============= =============================================
+Preference                 Default                Options       Description
+========================== ====================== ============= =============================================
+**migration_enabled**      FALSE                  TRUE / FALSE  Enable or disable migrations.
+**migration_path**         APPPATH.'migrations/'  None          The path to your migrations folder.
+**migration_version**      0                      None          The current version your database should use.
+**migration_table**        migrations             None          The table name for storing the shema
+                                                                version number.
+**migration_auto_latest**  FALSE                  TRUE / FALSE  Enable or disable automatically 
+                                                                running migrations.
+========================== ====================== ============= =============================================
diff --git a/user_guide_src/source/libraries/output.rst b/user_guide_src/source/libraries/output.rst
index 0472d14..3289a24 100644
--- a/user_guide_src/source/libraries/output.rst
+++ b/user_guide_src/source/libraries/output.rst
@@ -105,6 +105,9 @@
 `See here <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>`_ for
 a full list of headers.
 
+.. note:: This method is an alias for :doc:`Common function <../general/common_funtions.rst>`
+	``set_status_header()``.
+
 $this->output->enable_profiler();
 ==================================
 
diff --git a/user_guide_src/source/libraries/pagination.rst b/user_guide_src/source/libraries/pagination.rst
index 7d750bd..d9d3f50 100644
--- a/user_guide_src/source/libraries/pagination.rst
+++ b/user_guide_src/source/libraries/pagination.rst
@@ -80,8 +80,8 @@
 page number. For example, the number 2 will place two digits on either
 side, as in the example links at the very top of this page.
 
-$config['use_page_number'] = TRUE;
-==================================
+$config['use_page_numbers'] = TRUE;
+===================================
 
 By default, the URI segment will use the starting index for the items
 you are paginating. If you prefer to show the the actual page number,
diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst
index 5400524..dd9e8cb 100644
--- a/user_guide_src/source/libraries/sessions.rst
+++ b/user_guide_src/source/libraries/sessions.rst
@@ -1,29 +1,19 @@
-#############
-Session Class
-#############
+##############
+Session Driver
+##############
 
 The Session class permits you maintain a user's "state" and track their
-activity while they browse your site. The Session class stores session
-information for each user as serialized (and optionally encrypted) data
-in a cookie. It can also store the session data in a database table for
-added security, as this permits the session ID in the user's cookie to
-be matched against the stored session ID. By default only the cookie is
-saved. If you choose to use the database option you'll need to create
-the session table as indicated below.
-
-.. note:: The Session class does **not** utilize native PHP sessions. It
-	generates its own session data, offering more flexibility for
-	developers.
-
-.. note:: Even if you are not using encrypted sessions, you must set
-	an :doc:`encryption key <./encryption>` in your config file which is used
-	to aid in preventing session data manipulation.
+activity while they browse your site. CodeIgniter offers two default
+session drivers: the classic `Cookie Driver`_, and the `Native Driver`_,
+which supports usage of the native PHP Session mechanism. In addition,
+you may create your own `Custom Drivers`_ to store session data however
+you wish, while still taking advantage of the features of the Session class.
 
 Initializing a Session
 ======================
 
 Sessions will typically run globally with each page load, so the session
-class must either be :doc:`initialized <../general/libraries>` in your
+class must either be :doc:`initialized <../general/drivers>` in your
 :doc:`controller <../general/controllers>` constructors, or it can be
 :doc:`auto-loaded <../general/autoloader>` by the system. For the most
 part the session class will run unattended in the background, so simply
@@ -31,22 +21,25 @@
 sessions.
 
 To initialize the Session class manually in your controller constructor,
-use the $this->load->library function::
+use the $this->load->driver function::
 
-	$this->load->library('session');
+	$this->load->driver('session');
 
 Once loaded, the Sessions library object will be available using:
 $this->session
 
+.. note:: For backward compatibility, the Session class may stil be loaded
+	using the $this->load->library function, but converting your applications
+	to use $this->load->driver is strongly recommended.
+
 How do Sessions work?
 =====================
 
 When a page is loaded, the session class will check to see if valid
-session data exists in the user's session cookie. If sessions data does
-**not** exist (or if it has expired) a new session will be created and
-saved in the cookie. If a session does exist, its information will be
-updated and the cookie will be updated. With each update, the
-session_id will be regenerated.
+session data exists in the user's session. If sessions data does **not**
+exist (or if it has expired) a new session will be created and saved.
+If a session does exist, its information will be updated. With each update,
+the session_id will be regenerated.
 
 It's important for you to understand that once initialized, the Session
 class runs automatically. There is nothing you need to do to cause the
@@ -79,19 +72,12 @@
 	     'last_activity' => timestamp
 	)
 
-If you have the encryption option enabled, the serialized array will be
-encrypted before being stored in the cookie, making the data highly
-secure and impervious to being read or altered by someone. More info
-regarding encryption can be :doc:`found here <encryption>`, although
-the Session class will take care of initializing and encrypting the data
-automatically.
-
-Note: Session cookies are only updated every five minutes by default to
-reduce processor load. If you repeatedly reload a page you'll notice
-that the "last activity" time only updates if five minutes or more has
-passed since the last time the cookie was written. This time is
-configurable by changing the $config['sess_time_to_update'] line in
-your system/config/config.php file.
+.. note:: Sessions are only updated every five minutes by default to
+	reduce processor load. If you repeatedly reload a page you'll notice
+	that the "last activity" time only updates if five minutes or more has
+	passed since the last time the cookie was written. This time is
+	configurable by changing the $config['sess_time_to_update'] line in
+	your system/config/config.php file.
 
 Retrieving Session Data
 =======================
@@ -106,7 +92,7 @@
 
 	$session_id = $this->session->userdata('session_id');
 
-.. note:: The function returns FALSE (boolean) if the item you are
+.. note:: The function returns NULL if the item you are
 	trying to access does not exist.
 
 Adding Custom Session Data
@@ -117,7 +103,7 @@
 do this? Here's one example:
 
 Let's say a particular user logs into your site. Once authenticated, you
-could add their username and email address to the session cookie, making
+could add their username and email address to the session, making
 that data globally available to you without having to run a database
 query when you need it.
 
@@ -144,11 +130,11 @@
 
 	$this->session->set_userdata('some_name', 'some_value');
 
+If you want to verify that a userdata value exists, call has_userdata().
 
-.. note:: Cookies can only hold 4KB of data, so be careful not to exceed
-	the capacity. The encryption process in particular produces a longer
-	data string than the original so keep careful track of how much data you
-	are storing.
+::
+
+	$this->session->has_userdata('some_name');
 
 Retrieving All Session Data
 ===========================
@@ -195,8 +181,8 @@
 cleared. These can be very useful, and are typically used for
 informational or status messages (for example: "record 2 deleted").
 
-Note: Flash variables are prefaced with "flash\_" so avoid this prefix
-in your own session names.
+.. note:: Flash variables are prefaced with "flash\_" so avoid this prefix
+	in your own session names.
 
 To add flashdata::
 
@@ -222,9 +208,162 @@
 
 	$this->session->keep_flashdata('item');
 
+Tempdata
+========
+
+CodeIgniter also supports "tempdata", or session data with a specific
+expiration time. After the value expires, or the session expires or is
+deleted, the value is automatically removed.
+
+To add tempdata::
+
+	$expire = 300;	// Expire in 5 minutes
+
+	$this->session->set_tempdata('item', 'value', $expire);
+
+You can also pass an array to set_tempdata()::
+
+	$tempdata = array('newuser' => TRUE, 'message' => 'Thanks for joining!');
+
+	$this->session->set_tempdata($tempdata, '', $expire);
+
+.. note:: If the expiration is omitted or set to 0, the default expiration of
+	5 minutes will be used.
+
+To read a tempdata variable::
+
+	$this->session->tempdata('item');
+
+If you need to remove a tempdata value before it expires,
+use unset_tempdata()::
+
+	$this->session->unset_tempdata('item');
+
+Destroying a Session
+====================
+
+To clear the current session::
+
+	$this->session->sess_destroy();
+
+.. note:: This function should be the last one called, and even flash
+	variables will no longer be available. If you only want some items
+	destroyed and not all, use unset_userdata().
+
+Session Preferences
+===================
+
+You'll find the following Session related preferences in your
+application/config/config.php file:
+
+=========================== =============== =========================== ==========================================================================
+Preference                  Default         Options                     Description
+=========================== =============== =========================== ==========================================================================
+**sess_driver**             cookie          cookie/native/*custom*      The initial session driver to load.
+**sess_valid_drivers**      cookie, native  None                        Additional valid drivers which may be loaded.
+**sess_cookie_name**        ci_session      None                        The name you want the session cookie saved as (data for Cookie driver or
+                                                                        session ID for Native driver).
+**sess_expiration**         7200            None                        The number of seconds you would like the session to last. The default
+                                                                        value is 2 hours (7200 seconds). If you would like a non-expiring
+                                                                        session set the value to zero: 0
+**sess_expire_on_close**    FALSE           TRUE/FALSE (boolean)        Whether to cause the session to expire automatically when the browser
+                                                                        window is closed.
+**sess_encrypt_cookie**     FALSE           TRUE/FALSE (boolean)        Whether to encrypt the session data (Cookie driver only).
+**sess_use_database**       FALSE           TRUE/FALSE (boolean)        Whether to save the session data to a database. You must create the
+                                                                        table before enabling this option (Cookie driver only).
+**sess_table_name**         ci_sessions     Any valid SQL table name    The name of the session database table (Cookie driver only).
+**sess_time_to_update**     300             Time in seconds             This options controls how often the session class will regenerate itself
+                                                                        and create a new session id.
+**sess_match_ip**           FALSE           TRUE/FALSE (boolean)        Whether to match the user's IP address when reading the session data.
+                                                                        Note that some ISPs dynamically changes the IP, so if you want a
+                                                                        non-expiring session you will likely set this to FALSE.
+**sess_match_useragent**    TRUE            TRUE/FALSE (boolean)        Whether to match the User Agent when reading the session data.
+=========================== =============== =========================== ==========================================================================
+
+In addition to the values above, the cookie and native drivers apply the
+following configuration values shared by the :doc:`Input <input>` and
+:doc:`Security <security>` classes:
+
+=========================== =============== ==========================================================================
+Preference                  Default         Description
+=========================== =============== ==========================================================================
+**cookie_prefix**           ''              Set a cookie name prefix in order to avoid name collisions
+**cookie_domain**           ''              The domain for which the session is applicable
+**cookie_path**             /               The path to which the session is applicable
+=========================== =============== ==========================================================================
+
+Session Drivers
+===============
+
+By default, the `Cookie Driver`_ is loaded when a session is initialized.
+However, any valid driver may be selected with the $config['sess_driver']
+line in your config.php file.
+
+The session driver library comes with the cookie and native drivers
+installed, and `Custom Drivers`_ may also be installed by the user.
+
+Typically, only one driver will be used at a time, but CodeIgniter does
+support loading multiple drivers. If a specific valid driver is called, it
+will be automatically loaded. Or, an additional driver may be explicitly
+loaded by calling load_driver()::
+
+	$this->session->load_driver('native');
+
+The Session library keeps track of the most recently selected driver to call
+for driver methods. Normally, session class methods are called directly on
+the parent class, as illustrated above. However, any methods called through
+a specific driver will select that driver before invoking the parent method.
+
+So, alternation between multiple drivers can be achieved by specifying which
+driver to use for each call::
+
+	$this->session->native->set_userdata('foo', 'bar');
+
+	$this->session->cookie->userdata('foo');
+
+	$this->session->native->unset_userdata('foo');
+
+Notice in the previous example that the *native* userdata value 'foo'
+would be set to 'bar', which would NOT be returned by the call for
+the *cookie* userdata 'foo', nor would the *cookie* value be unset by
+the call to unset the *native* 'foo' value. The drivers maintain independent
+sets of values, regardless of key names.
+
+A specific driver may also be explicitly selected for use by pursuant
+methods with the select_driver() call::
+
+	$this->session->select_driver('native');
+
+	$this->session->userdata('item');	// Uses the native driver
+
+Cookie Driver
+-------------
+
+The Cookie driver stores session information for each user as serialized
+(and optionally encrypted) data in a cookie. It can also store the session
+data in a database table for added security, as this permits the session ID
+in the user's cookie to be matched against the stored session ID. By default
+only the cookie is saved. If you choose to use the database option you'll
+need to create the session table as indicated below.
+
+If you have the encryption option enabled, the serialized array will be
+encrypted before being stored in the cookie, making the data highly
+secure and impervious to being read or altered by someone. More info
+regarding encryption can be :doc:`found here <encryption>`, although
+the Session class will take care of initializing and encrypting the data
+automatically.
+
+.. note:: Even if you are not using encrypted sessions, you must set
+	an :doc:`encryption key <./encryption>` in your config file which is used
+	to aid in preventing session data manipulation.
+
+.. note:: Cookies can only hold 4KB of data, so be careful not to exceed
+	the capacity. The encryption process in particular produces a longer
+	data string than the original so keep careful track of how much data you
+	are storing.
 
 Saving Session Data to a Database
-=================================
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 While the session data array stored in the user's cookie contains a
 Session ID, unless you store session data in a database there is no way
@@ -267,44 +406,83 @@
 
 		$config['sess_table_name'] = 'ci_sessions';
 
-.. note:: The Session class has built-in garbage collection which clears
+.. note:: The Cookie driver has built-in garbage collection which clears
 	out expired sessions so you do not need to write your own routine to do
 	it.
 
-Destroying a Session
-====================
+Native Driver
+-------------
 
-To clear the current session::
+The Native driver relies on native PHP sessions to store data in the
+$_SESSION superglobal array. All stored values continue to be available
+through $_SESSION, but flash- and temp- data items carry special prefixes.
 
-	$this->session->sess_destroy();
+Custom Drivers
+--------------
 
-.. note:: This function should be the last one called, and even flash
-	variables will no longer be available. If you only want some items
-	destroyed and not all, use unset_userdata().
+You may also :doc:`create your own <../general/creating_drivers>` custom
+session drivers. A session driver basically manages an array of name/value
+pairs with some sort of storage mechanism.
 
-Session Preferences
-===================
+To make a new driver, extend CI_Session_driver. Overload the initialize()
+method and read or create session data. Then implement a save handler to
+write changed data to storage (sess_save), a destroy handler to remove
+deleted data (sess_destroy), a regenerate handler to make a new session ID
+(sess_regenerate), and an access handler to expose the data (get_userdata).
+Your initial class might look like::
 
-You'll find the following Session related preferences in your
-application/config/config.php file:
+	class CI_Session_custom extends CI_Session_driver {
+		protected function initialize()
+		{
+			// Read existing session data or create a new one
+		}
 
-=========================== =============== =========================== ==========================================================================
-Preference                  Default         Options                     Description
-=========================== =============== =========================== ==========================================================================
-**sess_cookie_name**        ci_session      None                        The name you want the session cookie saved as.
-**sess_expiration**         7200            None                        The number of seconds you would like the session to last. The default
-                                                                        value is 2 hours (7200 seconds). If you would like a non-expiring
-                                                                        session set the value to zero: 0
-**sess_expire_on_close**    FALSE           TRUE/FALSE (boolean)        Whether to cause the session to expire automatically when the browser
-                                                                        window is closed.
-**sess_encrypt_cookie**     FALSE           TRUE/FALSE (boolean)        Whether to encrypt the session data.
-**sess_use_database**       FALSE           TRUE/FALSE (boolean)        Whether to save the session data to a database. You must create the
-                                                                        table before enabling this option.
-**sess_table_name**         ci_sessions     Any valid SQL table name    The name of the session database table.
-**sess_time_to_update**     300             Time in seconds             This options controls how often the session class will regenerate itself
-                                                                        and create a new session id.
-**sess_match_ip**           FALSE           TRUE/FALSE (boolean)        Whether to match the user's IP address when reading the session data.
-                                                                        Note that some ISPs dynamically changes the IP, so if you want a
-                                                                        non-expiring session you will likely set this to FALSE.
-**sess_match_useragent**    TRUE            TRUE/FALSE (boolean)        Whether to match the User Agent when reading the session data.
-=========================== =============== =========================== ==========================================================================
\ No newline at end of file
+		public function sess_save()
+		{
+			// Save current data to storage
+		}
+
+		public function sess_destroy()
+		{
+			// Destroy the current session and clean up storage
+		}
+
+		public function sess_regenerate()
+		{
+			// Create new session ID
+		}
+
+		public function &get_userdata()
+		{
+			// Return a reference to your userdata array
+		}
+	}
+
+Notice that get_userdata() returns a reference so the parent library is
+accessing the same array the driver object is using. This saves memory
+and avoids synchronization issues during usage.
+
+Put your driver in the libraries/Session/drivers folder anywhere in your
+package paths. This includes the application directory, the system directory,
+or any path you add with $CI->load->add_package_path(). Your driver must be
+named CI_Session_<name>, and your filename must be Session_<name>.php,
+preferably also capitalized, such as::
+
+	CI_Session_foo in libraries/Session/drivers/Session_foo.php
+
+Then specify the driver by setting 'sess_driver' in your config.php file or as a
+parameter when loading the CI_Session object::
+
+	$config['sess_driver'] = 'foo';
+
+OR::
+
+	$CI->load->driver('session', array('sess_driver' => 'foo'));
+
+The driver specified by 'sess_driver' is automatically included as a valid
+driver. However, if you want to make a custom driver available as an option
+without making it the initially loaded driver, set 'sess_valid_drivers' in
+your config.php file to an array including your driver name::
+
+	$config['sess_valid_drivers'] = array('sess_driver');
+
diff --git a/user_guide_src/source/libraries/trackback.rst b/user_guide_src/source/libraries/trackback.rst
index 07b2b21..f9e0df8 100644
--- a/user_guide_src/source/libraries/trackback.rst
+++ b/user_guide_src/source/libraries/trackback.rst
@@ -114,7 +114,7 @@
 	 excerpt text NOT NULL,
 	 blog_name varchar(100) NOT NULL,
 	 tb_date int(10) NOT NULL,
-	 ip_address varchar(16) NOT NULL,
+	 ip_address varchar(45) NOT NULL,
 	 PRIMARY KEY `tb_id` (`tb_id`),
 	 KEY `entry_id` (`entry_id`)
 	);
diff --git a/user_guide_src/source/overview/cheatsheets.rst b/user_guide_src/source/overview/cheatsheets.rst
index 2e277aa..80c3112 100644
--- a/user_guide_src/source/overview/cheatsheets.rst
+++ b/user_guide_src/source/overview/cheatsheets.rst
@@ -5,12 +5,19 @@
 Library Reference
 =================
 
-`|CodeIgniter Library
-Reference| <../images/codeigniter_1.7.1_library_reference.pdf>`_
+|CodeIgniter Library Reference|
+
+-  :download:`Download PDF <../images/codeigniter_1.7.1_library_reference.pdf>`
+
 Helpers Reference
 =================
 
-`|image1| <../images/codeigniter_1.7.1_helper_reference.pdf>`_
+|CodeIgniter Helper Reference|
+
+-  :download:`Download PDF <../images/codeigniter_1.7.1_helper_reference.pdf>`
+
 
 .. |CodeIgniter Library Reference| image:: ../images/codeigniter_1.7.1_library_reference.png
-.. |image1| image:: ../images/codeigniter_1.7.1_helper_reference.png
+   :target: ../_downloads/codeigniter_1.7.1_library_reference.pdf
+.. |CodeIgniter Helper Reference| image:: ../images/codeigniter_1.7.1_helper_reference.png
+   :target: ../_downloads/codeigniter_1.7.1_helper_reference.pdf