Merge pull request #3715 from infinit89/patch-1

[ci skip] Fix pagination links with protocol-relative URLs
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..51fea41
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,22 @@
+# This file tells which files and directories should be ignored and
+# NOT downloaded when using composer to pull down a project with
+# the --prefer-dist option selected. Used to remove development
+# specific files so user has a clean download.
+
+# git files
+.gitattributes export-ignore
+# .gitignore
+
+# helper config files
+.travis.yml export-ignore
+phpdoc.dist.xml export-ignore
+
+# Misc other files
+readme.rst
+
+# They don't want all of our tests...
+tests/codeigniter/ export-ignore
+tests/travis/ export-ignore
+
+# User Guide Source Files
+user_guide_src
diff --git a/.gitignore b/.gitignore
index 9edcfca..5982f9b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,15 @@
 user_guide_src/cilexer/dist/*
 user_guide_src/cilexer/pycilexer.egg-info/*
 /vendor/
-/nbproject/
\ No newline at end of file
+
+# IDE Files
+#-------------------------
+/nbproject/
+.idea/*
+
+## Sublime Text cache files
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+*.sublime-workspace
+*.sublime-project
diff --git a/application/config/autoload.php b/application/config/autoload.php
index 72f855c..4bc6bf0 100644
--- a/application/config/autoload.php
+++ b/application/config/autoload.php
@@ -39,16 +39,15 @@
 |  $autoload['packages'] = array(APPPATH.'third_party', '/usr/local/shared');
 |
 */
-
 $autoload['packages'] = array();
 
-
 /*
 | -------------------------------------------------------------------
 |  Auto-load Libraries
 | -------------------------------------------------------------------
-| These are the classes located in the system/libraries folder
-| or in your application/libraries folder.
+| These are the classes located in system/libraries/ or your
+| application/libraries/ directory, with the addition of the
+| 'database' library, which is somewhat of a special case.
 |
 | Prototype:
 |
@@ -59,26 +58,23 @@
 |
 |	$autoload['libraries'] = array('user_agent' => 'ua');
 */
-
 $autoload['libraries'] = array();
 
-
 /*
 | -------------------------------------------------------------------
 |  Auto-load Drivers
 | -------------------------------------------------------------------
-| These classes are located in the system/libraries folder or in your
-| application/libraries folder within their own subdirectory. They
+| These classes are located in system/libraries/ or in your
+| application/libraries/ directory, but are also placed inside their
+| own subdirectory and they extend the CI_Driver_Library class. They
 | offer multiple interchangeable driver options.
 |
 | Prototype:
 |
 |	$autoload['drivers'] = array('cache');
 */
-
 $autoload['drivers'] = array();
 
-
 /*
 | -------------------------------------------------------------------
 |  Auto-load Helper Files
@@ -87,10 +83,8 @@
 |
 |	$autoload['helper'] = array('url', 'file');
 */
-
 $autoload['helper'] = array();
 
-
 /*
 | -------------------------------------------------------------------
 |  Auto-load Config files
@@ -103,10 +97,8 @@
 | config files.  Otherwise, leave it blank.
 |
 */
-
 $autoload['config'] = array();
 
-
 /*
 | -------------------------------------------------------------------
 |  Auto-load Language files
@@ -119,10 +111,8 @@
 | "codeigniter_lang.php" would be referenced as array('codeigniter');
 |
 */
-
 $autoload['language'] = array();
 
-
 /*
 | -------------------------------------------------------------------
 |  Auto-load Models
@@ -136,5 +126,4 @@
 |
 |	$autoload['model'] = array('first_model' => 'first');
 */
-
 $autoload['model'] = array();
diff --git a/application/config/config.php b/application/config/config.php
index f78371f..86ca312 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -58,7 +58,6 @@
 |
 | http://codeigniter.com/user_guide/general/urls.html
 */
-
 $config['url_suffix'] = '';
 
 /*
@@ -155,7 +154,6 @@
 */
 $config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
 
-
 /*
 |--------------------------------------------------------------------------
 | Enable Query Strings
@@ -192,8 +190,6 @@
 | Error Logging Threshold
 |--------------------------------------------------------------------------
 |
-| If you have enabled error logging, you can set an error threshold to
-| determine what gets logged. Threshold options are:
 | You can enable error logging by setting a threshold over zero. The
 | threshold determines what gets logged. Threshold options are:
 |
@@ -327,7 +323,7 @@
 |
 | 'sess_save_path'
 |
-|	The location to save sessions to, driver dependant.
+|	The location to save sessions to, driver dependent.
 |
 |	For the 'files' driver, it's a path to a writable directory.
 |	WARNING: Only absolute paths are supported!
@@ -390,7 +386,7 @@
 |--------------------------------------------------------------------------
 |
 | Determines whether to standardize newline characters in input data,
-| meaning to replace \r\n, \r, \n occurences with the PHP_EOL value.
+| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value.
 |
 | This is particularly useful for portability between UNIX-based OSes,
 | (usually \n) and Windows (\r\n).
@@ -480,7 +476,6 @@
 */
 $config['rewrite_short_tags'] = FALSE;
 
-
 /*
 |--------------------------------------------------------------------------
 | Reverse Proxy IPs
diff --git a/application/config/constants.php b/application/config/constants.php
index 01096c7..48283e2 100644
--- a/application/config/constants.php
+++ b/application/config/constants.php
@@ -27,7 +27,6 @@
 | These modes are used when working with fopen()/popen()
 |
 */
-
 define('FOPEN_READ', 'rb');
 define('FOPEN_READ_WRITE', 'r+b');
 define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care
diff --git a/application/config/database.php b/application/config/database.php
index 925b3e5..84aab91 100644
--- a/application/config/database.php
+++ b/application/config/database.php
@@ -58,7 +58,6 @@
 | The $query_builder variables lets you determine whether or not to load
 | the query builder class.
 */
-
 $active_group = 'default';
 $query_builder = TRUE;
 
@@ -71,7 +70,7 @@
 	'dbdriver' => 'mysqli',
 	'dbprefix' => '',
 	'pconnect' => FALSE,
-	'db_debug' => TRUE,
+	'db_debug' => (ENVIRONMENT !== 'production'),
 	'cache_on' => FALSE,
 	'cachedir' => '',
 	'char_set' => 'utf8',
diff --git a/application/config/foreign_chars.php b/application/config/foreign_chars.php
index d02dea9..ac406e3 100644
--- a/application/config/foreign_chars.php
+++ b/application/config/foreign_chars.php
@@ -56,6 +56,7 @@
 	'/ś|ŝ|ş|ș|š|ſ|σ|ς|с/' => 's',
 	'/Ț|Ţ|Ť|Ŧ|τ|Т/' => 'T',
 	'/ț|ţ|ť|ŧ|т/' => 't',
+	'/Þ|þ/' => 'th',
 	'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|Ũ|Ủ|Ụ|Ừ|Ứ|Ữ|Ử|Ự|У/' => 'U',
 	'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ|ủ|ụ|ừ|ứ|ữ|ử|ự|у/' => 'u',
 	'/Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ|Ỳ|Ỹ|Ỷ|Ỵ|Й/' => 'Y',
diff --git a/application/config/migration.php b/application/config/migration.php
index 083bf28..4b585a6 100644
--- a/application/config/migration.php
+++ b/application/config/migration.php
@@ -21,12 +21,12 @@
 | Migration file names may be based on a sequential identifier or on
 | a timestamp. Options are:
 |
-|   'sequential' = Default migration naming (001_add_blog.php)
+|   'sequential' = Sequential migration naming (001_add_blog.php)
 |   'timestamp'  = Timestamp migration naming (20121031104401_add_blog.php)
 |                  Use timestamp format YYYYMMDDHHIISS.
 |
-| If this configuration value is missing the Migration library defaults
-| to 'sequential' for backward compatibility.
+| Note: If this configuration value is missing the Migration library
+|       defaults to 'sequential' for backward compatibility with CI2.
 |
 */
 $config['migration_type'] = 'timestamp';
diff --git a/application/config/mimes.php b/application/config/mimes.php
index 8eff4d2..1f591ba 100644
--- a/application/config/mimes.php
+++ b/application/config/mimes.php
@@ -9,7 +9,6 @@
 | Upload class to help identify allowed file types.
 |
 */
-
 return array(
 	'hqx'	=>	array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'),
 	'cpt'	=>	'application/mac-compactpro',
@@ -127,7 +126,7 @@
 	'rsa'   =>	'application/x-pkcs7',
 	'cer'   =>	array('application/pkix-cert', 'application/x-x509-ca-cert'),
 	'3g2'   =>	'video/3gpp2',
-	'3gp'   =>	'video/3gp',
+	'3gp'   =>	array('video/3gp', 'video/3gpp'),
 	'mp4'   =>	'video/mp4',
 	'm4a'   =>	'audio/x-m4a',
 	'f4v'   =>	'video/mp4',
@@ -152,5 +151,8 @@
 	'wma'	=>	array('audio/x-ms-wma', 'video/x-ms-asf'),
 	'jar'	=>	array('application/java-archive', 'application/x-java-application', 'application/x-jar', 'application/x-compressed'),
 	'svg'	=>	array('image/svg+xml', 'application/xml', 'text/xml'),
-	'vcf'	=>	'text/x-vcard'
+	'vcf'	=>	'text/x-vcard',
+	'srt'	=>	array('text/srt', 'text/plain'),
+	'vtt'	=>	array('text/vtt', 'text/plain'),
+	'ico'	=>	array('image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon')
 );
diff --git a/application/config/smileys.php b/application/config/smileys.php
index 1428d68..1eeba47 100644
--- a/application/config/smileys.php
+++ b/application/config/smileys.php
@@ -13,7 +13,6 @@
 | http://codeigniter.com/user_guide/helpers/smiley_helper.html
 |
 */
-
 $smileys = array(
 
 //	smiley			image name						width	height	alt
diff --git a/application/config/user_agents.php b/application/config/user_agents.php
index 6f3295a..1129dba 100644
--- a/application/config/user_agents.php
+++ b/application/config/user_agents.php
@@ -10,7 +10,6 @@
 | mobile device data. The array keys are used to identify the device
 | and the array values are used to set the actual name of the item.
 */
-
 $platforms = array(
 	'windows nt 10.0'	=> 'Windows 10',
 	'windows nt 6.3'	=> 'Windows 8.1',
@@ -62,6 +61,7 @@
 $browsers = array(
 	'OPR'			=> 'Opera',
 	'Flock'			=> 'Flock',
+	'Edge'			=> 'Spartan',
 	'Chrome'		=> 'Chrome',
 	// Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string
 	'Opera.*?Version'	=> 'Opera',
@@ -198,7 +198,7 @@
 	'bingbot'		=> 'Bing',
 	'slurp'			=> 'Inktomi Slurp',
 	'yahoo'			=> 'Yahoo',
-	'askjeeves'		=> 'AskJeeves',
+	'ask jeeves'		=> 'Ask Jeeves',
 	'fastcrawler'		=> 'FastCrawler',
 	'infoseek'		=> 'InfoSeek Robot 1.0',
 	'lycos'			=> 'Lycos',
diff --git a/composer.json b/composer.json
index 4ff60c5..0653a78 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,15 @@
 {
-	"description" : "The CodeIgniter framework",
-	"name" : "codeigniter/framework",
+	"description": "The CodeIgniter framework",
+	"name": "codeigniter/framework",
+	"type": "project",
+	"homepage": "http://codeigniter.com",
 	"license": "MIT",
+	"support": {
+		"forum": "http://forum.codeigniter.com/",
+		"wiki": "https://github.com/bcit-ci/CodeIgniter/wiki",
+		"irc": "irc://irc.freenode.net/codeigniter",
+		"source": "https://github.com/bcit-ci/CodeIgniter"
+	},
 	"require": {
 		"php": ">=5.2.4"
 	},
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
index d830c18..ddf3227 100644
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -55,7 +55,7 @@
  * @var	string
  *
  */
-	define('CI_VERSION', '3.0-dev');
+	define('CI_VERSION', '3.0.1-dev');
 
 /*
  * ------------------------------------------------------
diff --git a/system/core/Common.php b/system/core/Common.php
index f28272b..b850fd3 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -181,7 +181,7 @@
 		// Did we find the class?
 		if ($name === FALSE)
 		{
-			// Note: We use exit() rather then show_error() in order to avoid a
+			// Note: We use exit() rather than show_error() in order to avoid a
 			// self-referencing loop with the Exceptions class
 			set_status_header(503);
 			echo 'Unable to locate the specified class: '.$class.'.php';
@@ -506,6 +506,9 @@
 		{
 			is_int($code) OR $code = (int) $code;
 			$stati = array(
+				100	=> 'Continue',
+				101	=> 'Switching Protocols',
+
 				200	=> 'OK',
 				201	=> 'Created',
 				202	=> 'Accepted',
@@ -524,6 +527,7 @@
 
 				400	=> 'Bad Request',
 				401	=> 'Unauthorized',
+				402	=> 'Payment Required',
 				403	=> 'Forbidden',
 				404	=> 'Not Found',
 				405	=> 'Method Not Allowed',
@@ -745,7 +749,7 @@
 		{
 			return $var;
 		}
-		
+
 		if (is_array($var))
 		{
 			return array_map('html_escape', $var, array_fill(0, count($var), $double_encode));
diff --git a/system/core/Hooks.php b/system/core/Hooks.php
index 08479b1..3b4fb22 100644
--- a/system/core/Hooks.php
+++ b/system/core/Hooks.php
@@ -46,7 +46,7 @@
  * @subpackage	Libraries
  * @category	Libraries
  * @author		EllisLab Dev Team
- * @link		http://codeigniter.com/user_guide/libraries/encryption.html
+ * @link		http://codeigniter.com/user_guide/general/hooks.html
  */
 class CI_Hooks {
 
diff --git a/system/core/Input.php b/system/core/Input.php
index 12332cf..b0bbb7b 100644
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -799,19 +799,27 @@
 	 */
 	public function get_request_header($index, $xss_clean = FALSE)
 	{
-		if (empty($this->headers))
+		static $headers;
+
+		if ( ! isset($headers))
 		{
-			$this->request_headers();
+			empty($this->headers) OR $this->request_headers();
+			foreach ($this->headers as $key => $value)
+			{
+				$headers[strtolower($key)] = $value;
+			}
 		}
 
-		if ( ! isset($this->headers[$index]))
+		$index = strtolower($index);
+
+		if ( ! isset($headers[$index]))
 		{
 			return NULL;
 		}
 
 		return ($xss_clean === TRUE)
-			? $this->security->xss_clean($this->headers[$index])
-			: $this->headers[$index];
+			? $this->security->xss_clean($headers[$index])
+			: $headers[$index];
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/core/Loader.php b/system/core/Loader.php
index 254ad0d..9205ad1 100644
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -1118,7 +1118,7 @@
 				}
 				else
 				{
-					log_message('debug', APPPATH.'libraries/'.$file_path.$subclass.'.php exists, but does not declare '.$subclass);
+					log_message('debug', $path.' exists, but does not declare '.$subclass);
 				}
 			}
 		}
@@ -1307,10 +1307,7 @@
 			}
 
 			// Load all other libraries
-			foreach ($autoload['libraries'] as $item)
-			{
-				$this->library($item);
-			}
+			$this->library($autoload['libraries']);
 		}
 
 		// Autoload models
diff --git a/system/core/Output.php b/system/core/Output.php
index 02f6693..e7d559a 100644
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -674,7 +674,7 @@
 		$cache_info = unserialize($match[1]);
 		$expire = $cache_info['expire'];
 
-		$last_modified = filemtime($cache_path);
+		$last_modified = filemtime($filepath);
 
 		// Has the file expired?
 		if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
diff --git a/system/core/Router.php b/system/core/Router.php
index eb3da22..0510005 100644
--- a/system/core/Router.php
+++ b/system/core/Router.php
@@ -105,7 +105,7 @@
 	/**
 	 * Enable query strings flag
 	 *
-	 * Determines wether to use GET parameters or segment URIs
+	 * Determines whether to use GET parameters or segment URIs
 	 *
 	 * @var	bool
 	 */
@@ -493,7 +493,7 @@
 	 * Set directory name
 	 *
 	 * @param	string	$dir	Directory name
-	 * @param	bool	$appent	Whether we're appending rather then setting the full value
+	 * @param	bool	$appent	Whether we're appending rather than setting the full value
 	 * @return	void
 	 */
 	public function set_directory($dir, $append = FALSE)
diff --git a/system/core/Security.php b/system/core/Security.php
index 9cef424..7c51992 100644
--- a/system/core/Security.php
+++ b/system/core/Security.php
@@ -275,7 +275,7 @@
 			$secure_cookie,
 			config_item('cookie_httponly')
 		);
-		log_message('info', 'CRSF cookie sent');
+		log_message('info', 'CSRF cookie sent');
 
 		return $this;
 	}
diff --git a/system/core/URI.php b/system/core/URI.php
index e967494..2211e36 100644
--- a/system/core/URI.php
+++ b/system/core/URI.php
@@ -205,13 +205,16 @@
 		$query = isset($uri['query']) ? $uri['query'] : '';
 		$uri = isset($uri['path']) ? $uri['path'] : '';
 
-		if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
+		if (isset($_SERVER['SCRIPT_NAME'][0]))
 		{
-			$uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME']));
-		}
-		elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
-		{
-			$uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
+			if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
+			{
+				$uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME']));
+			}
+			elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
+			{
+				$uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
+			}
 		}
 
 		// This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct
diff --git a/system/core/compat/hash.php b/system/core/compat/hash.php
index 477535d..1595455 100644
--- a/system/core/compat/hash.php
+++ b/system/core/compat/hash.php
@@ -174,9 +174,56 @@
 		}
 
 		$hash_length = strlen(hash($algo, NULL, TRUE));
-		if (empty($length))
+		empty($length) && $length = $hash_length;
+
+		// Pre-hash password inputs longer than the algorithm's block size
+		// (i.e. prepare HMAC key) to mitigate potential DoS attacks.
+		static $block_sizes;
+		empty($block_sizes) && $block_sizes = array(
+			'gost' => 32,
+			'haval128,3' => 128,
+			'haval160,3' => 128,
+			'haval192,3' => 128,
+			'haval224,3' => 128,
+			'haval256,3' => 128,
+			'haval128,4' => 128,
+			'haval160,4' => 128,
+			'haval192,4' => 128,
+			'haval224,4' => 128,
+			'haval256,4' => 128,
+			'haval128,5' => 128,
+			'haval160,5' => 128,
+			'haval192,5' => 128,
+			'haval224,5' => 128,
+			'haval256,5' => 128,
+			'md2' => 16,
+			'md4' => 64,
+			'md5' => 64,
+			'ripemd128' => 64,
+			'ripemd160' => 64,
+			'ripemd256' => 64,
+			'ripemd320' => 64,
+			'salsa10' => 64,
+			'salsa20' => 64,
+			'sha1' => 64,
+			'sha224' => 64,
+			'sha256' => 64,
+			'sha384' => 128,
+			'sha512' => 128,
+			'snefru' => 32,
+			'snefru256' => 32,
+			'tiger128,3' => 64,
+			'tiger160,3' => 64,
+			'tiger192,3' => 64,
+			'tiger128,4' => 64,
+			'tiger160,4' => 64,
+			'tiger192,4' => 64,
+			'whirlpool' => 64
+		);
+
+		if (isset($block_sizes[$algo]) && strlen($password) > $block_sizes[$algo])
 		{
-			$length = $hash_length;
+			$password = hash($algo, $password, TRUE);
 		}
 
 		$hash = '';
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index 3d35c2d..d2bce5c 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -1218,7 +1218,7 @@
 	/**
 	 * Fetch Field Names
 	 *
-	 * @param	string	the table name
+	 * @param	string	$table	Table name
 	 * @return	array
 	 */
 	public function list_fields($table)
@@ -1751,7 +1751,7 @@
 		//
 		// Added exception for single quotes as well, we don't want to alter
 		// literal strings. -- Narf
-		if (strpos($item, '(') !== FALSE OR strpos($item, "'") !== FALSE)
+		if (strcspn($item, "()'") !== strlen($item))
 		{
 			return $item;
 		}
diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php
index f6ee2a6..865498f 100644
--- a/system/database/DB_forge.php
+++ b/system/database/DB_forge.php
@@ -239,7 +239,7 @@
 	 */
 	public function add_key($key, $primary = FALSE)
 	{
-		if ($primary === TRUE && is_array($key))
+		if (is_array($key))
 		{
 			foreach ($key as $one)
 			{
@@ -453,12 +453,7 @@
 			return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
 		}
 
-		$query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists);
-		if ($query === FALSE)
-		{
-			return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
-		}
-		elseif ($query === TRUE)
+		if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE)
 		{
 			return TRUE;
 		}
diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php
index e5ffef2..a8b5b35 100644
--- a/system/database/DB_query_builder.php
+++ b/system/database/DB_query_builder.php
@@ -918,6 +918,8 @@
 		}
 
 		is_bool($escape) OR $escape = $this->_protect_identifiers;
+		// lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh)
+		$side = strtolower($side);
 
 		foreach ($field as $k => $v)
 		{
@@ -2090,10 +2092,13 @@
 		}
 		elseif (is_array($table))
 		{
+			empty($where) && $reset_data = FALSE;
+
 			foreach ($table as $single_table)
 			{
 				$this->delete($single_table, $where, $limit, $reset_data);
 			}
+
 			return;
 		}
 		else
@@ -2253,7 +2258,7 @@
 			else
 			{
 				// Cycle through the "select" portion of the query and prep each column name.
-				// The reason we protect identifiers here rather then in the select() function
+				// The reason we protect identifiers here rather than in the select() function
 				// is because until the user calls the from() function we don't know if there are aliases
 				foreach ($this->qb_select as $key => $val)
 				{
@@ -2324,7 +2329,7 @@
 
 				// Split multiple conditions
 				$conditions = preg_split(
-					'/(\s*AND\s+|\s*OR\s+)/i',
+					'/((^|\s+)AND\s+|(^|\s+)OR\s+)/i',
 					$this->{$qb_key}[$i]['condition'],
 					-1,
 					PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php
index 57356ac..78398ea 100644
--- a/system/database/DB_utility.php
+++ b/system/database/DB_utility.php
@@ -249,7 +249,7 @@
 			$out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim;
 		}
 
-		$out = substr(rtrim($out), 0, -strlen($delim)).$newline;
+		$out = substr($out, 0, -strlen($delim)).$newline;
 
 		// Next blast through the result array and build out the rows
 		while ($row = $query->unbuffered_row('array'))
@@ -258,7 +258,7 @@
 			{
 				$out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure.$delim;
 			}
-			$out = substr(rtrim($out), 0, -strlen($delim)).$newline;
+			$out = substr($out, 0, -strlen($delim)).$newline;
 		}
 
 		return $out;
diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php
index 4010995..b5cf265 100644
--- a/system/database/drivers/oci8/oci8_driver.php
+++ b/system/database/drivers/oci8/oci8_driver.php
@@ -573,7 +573,7 @@
 			{
 				$default = '';
 			}
-			$retval[$i]->default		= $query[$i]->COLUMN_DEFAULT;
+			$retval[$i]->default = $default;
 		}
 
 		return $retval;
diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
index 67dc5f5..206d835 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
@@ -157,6 +157,30 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Select the database
+	 *
+	 * @param	string	$database
+	 * @return	bool
+	 */
+	public function db_select($database = '')
+	{
+		if ($database === '')
+		{
+			$database = $this->database;
+		}
+
+		if (FALSE !== $this->simple_query('USE '.$this->escape_identifiers($database)))
+		{
+			$this->database = $database;
+			return TRUE;
+		}
+
+		return FALSE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Show table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
index f07f49f..409e650 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
@@ -121,17 +121,31 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Show column query
+	 * Fetch Field Names
 	 *
-	 * Generates a platform-specific query string so that the column names can be fetched
-	 *
-	 * @param	string	$table
-	 * @return	string
+	 * @param	string	$table	Table name
+	 * @return	array
 	 */
-	protected function _list_columns($table = '')
+	public function list_fields($table)
 	{
-		// Not supported
-		return FALSE;
+		// Is there a cached result?
+		if (isset($this->data_cache['field_names'][$table]))
+		{
+			return $this->data_cache['field_names'][$table];
+		}
+
+		if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)
+		{
+			return FALSE;
+		}
+
+		$this->data_cache['field_names'][$table] = array();
+		foreach ($result->result_array() as $row)
+		{
+			$this->data_cache['field_names'][$table][] = $row['name'];
+		}
+
+		return $this->data_cache['field_names'][$table];
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php
index 28faadd..15afbde 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php
@@ -89,6 +89,7 @@
 		if (version_compare($this->db->version(), '3.3', '<'))
 		{
 			$this->_create_table_if = FALSE;
+			$this->_drop_table_if   = FALSE;
 		}
 	}
 
diff --git a/system/database/drivers/sqlite3/sqlite3_driver.php b/system/database/drivers/sqlite3/sqlite3_driver.php
index fdbe949..31e37de 100644
--- a/system/database/drivers/sqlite3/sqlite3_driver.php
+++ b/system/database/drivers/sqlite3/sqlite3_driver.php
@@ -247,17 +247,31 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Show column query
+	 * Fetch Field Names
 	 *
-	 * Generates a platform-specific query string so that the column names can be fetched
-	 *
-	 * @param	string	$table
-	 * @return	string
+	 * @param	string	$table	Table name
+	 * @return	array
 	 */
-	protected function _list_columns($table = '')
+	public function list_fields($table)
 	{
-		// Not supported
-		return FALSE;
+		// Is there a cached result?
+		if (isset($this->data_cache['field_names'][$table]))
+		{
+			return $this->data_cache['field_names'][$table];
+		}
+
+		if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)
+		{
+			return FALSE;
+		}
+
+		$this->data_cache['field_names'][$table] = array();
+		foreach ($result->result_array() as $row)
+		{
+			$this->data_cache['field_names'][$table][] = $row['name'];
+		}
+
+		return $this->data_cache['field_names'][$table];
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/sqlite3/sqlite3_forge.php b/system/database/drivers/sqlite3/sqlite3_forge.php
index 69f65b6..24690ba 100644
--- a/system/database/drivers/sqlite3/sqlite3_forge.php
+++ b/system/database/drivers/sqlite3/sqlite3_forge.php
@@ -74,7 +74,8 @@
 
 		if (version_compare($this->db->version(), '3.3', '<'))
 		{
-			$this->create_table_if = FALSE;
+			$this->_create_table_if = FALSE;
+			$this->_drop_table_if   = FALSE;
 		}
 	}
 
diff --git a/system/helpers/array_helper.php b/system/helpers/array_helper.php
index e07b52b..2ce55b9 100644
--- a/system/helpers/array_helper.php
+++ b/system/helpers/array_helper.php
@@ -62,7 +62,7 @@
 	 * @param	mixed
 	 * @return	mixed	depends on what the array contains
 	 */
-	function element($item, $array, $default = NULL)
+	function element($item, array $array, $default = NULL)
 	{
 		return array_key_exists($item, $array) ? $array[$item] : $default;
 	}
@@ -99,7 +99,7 @@
 	 * @param	mixed
 	 * @return	mixed	depends on what the array contains
 	 */
-	function elements($items, $array, $default = NULL)
+	function elements($items, array $array, $default = NULL)
 	{
 		$return = array();
 
diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php
index 53ee8eb..fd80776 100644
--- a/system/helpers/form_helper.php
+++ b/system/helpers/form_helper.php
@@ -197,7 +197,7 @@
 	 *
 	 * @param	mixed
 	 * @param	string
-	 * @param	string
+	 * @param	mixed
 	 * @return	string
 	 */
 	function form_input($data = '', $value = '', $extra = '')
@@ -208,7 +208,7 @@
 			'value' => $value
 		);
 
-		return '<input '._parse_form_attributes($data, $defaults).$extra." />\n";
+		return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
 	}
 }
 
@@ -223,7 +223,7 @@
 	 *
 	 * @param	mixed
 	 * @param	string
-	 * @param	string
+	 * @param	mixed
 	 * @return	string
 	 */
 	function form_password($data = '', $value = '', $extra = '')
@@ -245,7 +245,7 @@
 	 *
 	 * @param	mixed
 	 * @param	string
-	 * @param	string
+	 * @param	mixed
 	 * @return	string
 	 */
 	function form_upload($data = '', $value = '', $extra = '')
@@ -253,7 +253,8 @@
 		$defaults = array('type' => 'file', 'name' => '');
 		is_array($data) OR $data = array('name' => $data);
 		$data['type'] = 'file';
-		return '<input '._parse_form_attributes($data, $defaults).$extra." />\n";
+
+		return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
 	}
 }
 
@@ -266,7 +267,7 @@
 	 *
 	 * @param	mixed	$data
 	 * @param	string	$value
-	 * @param	string	$extra
+	 * @param	mixed	$extra
 	 * @return	string
 	 */
 	function form_textarea($data = '', $value = '', $extra = '')
@@ -287,7 +288,9 @@
 			unset($data['value']); // textareas don't use the value attribute
 		}
 
-		return '<textarea '._parse_form_attributes($data, $defaults).$extra.'>'.html_escape($val)."</textarea>\n";
+		return '<textarea '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>'
+			.html_escape($val)
+			."</textarea>\n";
 	}
 }
 
@@ -301,12 +304,13 @@
 	 * @param	string
 	 * @param	array
 	 * @param	mixed
-	 * @param	string
+	 * @param	mixed
 	 * @return	string
 	 */
 	function form_multiselect($name = '', $options = array(), $selected = array(), $extra = '')
 	{
-		if ( ! strpos($extra, 'multiple'))
+		$extra = _attributes_to_string($extra);
+		if (stripos($extra, 'multiple') === FALSE)
 		{
 			$extra .= ' multiple="multiple"';
 		}
@@ -372,7 +376,7 @@
 
 		$extra = _attributes_to_string($extra);
 
-		$multiple = (count($selected) > 1 && strpos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : '';
+		$multiple = (count($selected) > 1 && stripos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : '';
 
 		$form = '<select '.rtrim(_parse_form_attributes($data, $defaults)).$extra.$multiple.">\n";
 
@@ -420,7 +424,7 @@
 	 * @param	mixed
 	 * @param	string
 	 * @param	bool
-	 * @param	string
+	 * @param	mixed
 	 * @return	string
 	 */
 	function form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '')
@@ -450,7 +454,7 @@
 			unset($defaults['checked']);
 		}
 
-		return '<input '._parse_form_attributes($data, $defaults).$extra." />\n";
+		return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
 	}
 }
 
@@ -464,13 +468,14 @@
 	 * @param	mixed
 	 * @param	string
 	 * @param	bool
-	 * @param	string
+	 * @param	mixed
 	 * @return	string
 	 */
 	function form_radio($data = '', $value = '', $checked = FALSE, $extra = '')
 	{
 		is_array($data) OR $data = array('name' => $data);
 		$data['type'] = 'radio';
+
 		return form_checkbox($data, $value, $checked, $extra);
 	}
 }
@@ -484,7 +489,7 @@
 	 *
 	 * @param	mixed
 	 * @param	string
-	 * @param	string
+	 * @param	mixed
 	 * @return	string
 	 */
 	function form_submit($data = '', $value = '', $extra = '')
@@ -495,7 +500,7 @@
 			'value' => $value
 		);
 
-		return '<input '._parse_form_attributes($data, $defaults).$extra." />\n";
+		return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
 	}
 }
 
@@ -508,7 +513,7 @@
 	 *
 	 * @param	mixed
 	 * @param	string
-	 * @param	string
+	 * @param	mixed
 	 * @return	string
 	 */
 	function form_reset($data = '', $value = '', $extra = '')
@@ -519,7 +524,7 @@
 			'value' => $value
 		);
 
-		return '<input '._parse_form_attributes($data, $defaults).$extra." />\n";
+		return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
 	}
 }
 
@@ -532,7 +537,7 @@
 	 *
 	 * @param	mixed
 	 * @param	string
-	 * @param	string
+	 * @param	mixed
 	 * @return	string
 	 */
 	function form_button($data = '', $content = '', $extra = '')
@@ -548,7 +553,9 @@
 			unset($data['content']); // content is not an attribute
 		}
 
-		return '<button '._parse_form_attributes($data, $defaults).$extra.'>'.$content."</button>\n";
+		return '<button '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>'
+			.$content
+			."</button>\n";
 	}
 }
 
diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php
index d8ed45d..f289005 100644
--- a/system/helpers/inflector_helper.php
+++ b/system/helpers/inflector_helper.php
@@ -133,6 +133,7 @@
 		}
 
 		$plural_rules = array(
+			'/(quiz)$/'                => '\1zes',      // quizzes
 			'/^(ox)$/'                 => '\1\2en',     // ox
 			'/([m|l])ouse$/'           => '\1ice',      // mouse, louse
 			'/(matr|vert|ind)ix|ex$/'  => '\1ices',     // matrix, vertex, index
diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php
index bf623b0..d65f92f 100644
--- a/system/helpers/url_helper.php
+++ b/system/helpers/url_helper.php
@@ -161,7 +161,7 @@
 
 		$site_url = is_array($uri)
 			? site_url($uri)
-			: preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri);
+			: (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri));
 
 		if ($title === '')
 		{
@@ -474,7 +474,7 @@
 	 * @param	string	$str		Input string
 	 * @param	string	$separator	Word separator
 	 *			(usually '-' or '_')
-	 * @param	bool	$lowercase	Wether to transform the output string to lowercase
+	 * @param	bool	$lowercase	Whether to transform the output string to lowercase
 	 * @return	string
 	 */
 	function url_title($str, $separator = '-', $lowercase = FALSE)
@@ -492,7 +492,7 @@
 
 		$trans = array(
 			'&.+?;'			=> '',
-			'[^a-z0-9 _-]'		=> '',
+			'[^\w\d _-]'		=> '',
 			'\s+'			=> $separator,
 			'('.$q_separator.')+'	=> $separator
 		);
@@ -500,7 +500,7 @@
 		$str = strip_tags($str);
 		foreach ($trans as $key => $val)
 		{
-			$str = preg_replace('#'.$key.'#i', $val, $str);
+			$str = preg_replace('#'.$key.'#i'.(UTF8_ENABLED ? 'u' : ''), $val, $str);
 		}
 
 		if ($lowercase === TRUE)
diff --git a/system/libraries/Cache/Cache.php b/system/libraries/Cache/Cache.php
index 40ac701..0c87a56 100644
--- a/system/libraries/Cache/Cache.php
+++ b/system/libraries/Cache/Cache.php
@@ -100,28 +100,10 @@
 	 */
 	public function __construct($config = array())
 	{
-		$default_config = array(
-			'adapter',
-			'memcached'
-		);
-
-		foreach ($default_config as $key)
-		{
-			if (isset($config[$key]))
-			{
-				$param = '_'.$key;
-
-				$this->{$param} = $config[$key];
-			}
-		}
-
+		isset($config['adapter']) && $this->_adapter = $config['adapter'];
+		isset($config['backup']) && $this->_backup_driver = $config['backup'];
 		isset($config['key_prefix']) && $this->key_prefix = $config['key_prefix'];
 
-		if (isset($config['backup']) && in_array($config['backup'], $this->valid_drivers))
-		{
-			$this->_backup_driver = $config['backup'];
-		}
-
 		// If the specified adapter isn't available, check the backup.
 		if ( ! $this->is_supported($this->_adapter))
 		{
@@ -196,7 +178,7 @@
 	 */
 	public function increment($id, $offset = 1)
 	{
-		return $this->{$this->_adapter}->increment($id, $offset);
+		return $this->{$this->_adapter}->increment($this->key_prefix.$id, $offset);
 	}
 
 	// ------------------------------------------------------------------------
@@ -210,7 +192,7 @@
 	 */
 	public function decrement($id, $offset = 1)
 	{
-		return $this->{$this->_adapter}->decrement($id, $offset);
+		return $this->{$this->_adapter}->decrement($this->key_prefix.$id, $offset);
 	}
 
 	// ------------------------------------------------------------------------
@@ -261,14 +243,13 @@
 	 */
 	public function is_supported($driver)
 	{
-		static $support = array();
+		static $support;
 
-		if ( ! isset($support[$driver]))
+		if ( ! isset($support, $support[$driver]))
 		{
 			$support[$driver] = $this->{$driver}->is_supported();
 		}
 
 		return $support[$driver];
 	}
-
 }
diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php
index b90b561..111e210 100644
--- a/system/libraries/Cache/drivers/Cache_memcached.php
+++ b/system/libraries/Cache/drivers/Cache_memcached.php
@@ -68,6 +68,76 @@
 		)
 	);
 
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Class constructor
+	 *
+	 * Setup Memcache(d)
+	 *
+	 * @return	void
+	 */
+	public function __construct()
+	{
+		// Try to load memcached server info from the config file.
+		$CI =& get_instance();
+		$defaults = $this->_memcache_conf['default'];
+
+		if ($CI->config->load('memcached', TRUE, TRUE))
+		{
+			if (is_array($CI->config->config['memcached']))
+			{
+				$this->_memcache_conf = array();
+
+				foreach ($CI->config->config['memcached'] as $name => $conf)
+				{
+					$this->_memcache_conf[$name] = $conf;
+				}
+			}
+		}
+
+		if (class_exists('Memcached', FALSE))
+		{
+			$this->_memcached = new Memcached();
+		}
+		elseif (class_exists('Memcache', FALSE))
+		{
+			$this->_memcached = new Memcache();
+		}
+		else
+		{
+			throw new RuntimeException('Cache: Failed to create Memcache(d) object; extension not loaded?');
+		}
+
+		foreach ($this->_memcache_conf as $cache_server)
+		{
+			isset($cache_server['hostname']) OR $cache_server['hostname'] = $defaults['host'];
+			isset($cache_server['port']) OR $cache_server['port'] = $defaults['port'];
+			isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight'];
+
+			if (get_class($this->_memcached) === 'Memcache')
+			{
+				// Third parameter is persistance and defaults to TRUE.
+				$this->_memcached->addServer(
+					$cache_server['hostname'],
+					$cache_server['port'],
+					TRUE,
+					$cache_server['weight']
+				);
+			}
+			else
+			{
+				$this->_memcached->addServer(
+					$cache_server['hostname'],
+					$cache_server['port'],
+					$cache_server['weight']
+				);
+			}
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
 	/**
 	 * Fetch from cache
 	 *
@@ -205,75 +275,6 @@
 	// ------------------------------------------------------------------------
 
 	/**
-	 * Setup memcached.
-	 *
-	 * @return	bool
-	 */
-	protected function _setup_memcached()
-	{
-		// Try to load memcached server info from the config file.
-		$CI =& get_instance();
-		$defaults = $this->_memcache_conf['default'];
-
-		if ($CI->config->load('memcached', TRUE, TRUE))
-		{
-			if (is_array($CI->config->config['memcached']))
-			{
-				$this->_memcache_conf = array();
-
-				foreach ($CI->config->config['memcached'] as $name => $conf)
-				{
-					$this->_memcache_conf[$name] = $conf;
-				}
-			}
-		}
-
-		if (class_exists('Memcached', FALSE))
-		{
-			$this->_memcached = new Memcached();
-		}
-		elseif (class_exists('Memcache', FALSE))
-		{
-			$this->_memcached = new Memcache();
-		}
-		else
-		{
-			log_message('error', 'Failed to create object for Memcached Cache; extension not loaded?');
-			return FALSE;
-		}
-
-		foreach ($this->_memcache_conf as $cache_server)
-		{
-			isset($cache_server['hostname']) OR $cache_server['hostname'] = $defaults['host'];
-			isset($cache_server['port']) OR $cache_server['port'] = $defaults['port'];
-			isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight'];
-
-			if (get_class($this->_memcached) === 'Memcache')
-			{
-				// Third parameter is persistance and defaults to TRUE.
-				$this->_memcached->addServer(
-					$cache_server['hostname'],
-					$cache_server['port'],
-					TRUE,
-					$cache_server['weight']
-				);
-			}
-			else
-			{
-				$this->_memcached->addServer(
-					$cache_server['hostname'],
-					$cache_server['port'],
-					$cache_server['weight']
-				);
-			}
-		}
-
-		return TRUE;
-	}
-
-	// ------------------------------------------------------------------------
-
-	/**
 	 * Is supported
 	 *
 	 * Returns FALSE if memcached is not supported on the system.
@@ -289,7 +290,6 @@
 			return FALSE;
 		}
 
-		return $this->_setup_memcached();
+		return TRUE;
 	}
-
 }
diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php
index a35fbf6..d7dca19 100644
--- a/system/libraries/Cache/drivers/Cache_redis.php
+++ b/system/libraries/Cache/drivers/Cache_redis.php
@@ -79,6 +79,63 @@
 	// ------------------------------------------------------------------------
 
 	/**
+	 * Class constructor
+	 *
+	 * Setup Redis
+	 *
+	 * Loads Redis config file if present. Will halt execution
+	 * if a Redis connection can't be established.
+	 *
+	 * @return	void
+	 * @see		Redis::connect()
+	 */
+	public function __construct()
+	{
+		$config = array();
+		$CI =& get_instance();
+
+		if ($CI->config->load('redis', TRUE, TRUE))
+		{
+			$config = $CI->config->item('redis');
+		}
+
+		$config = array_merge(self::$_default_config, $config);
+		$this->_redis = new Redis();
+
+		try
+		{
+			if ($config['socket_type'] === 'unix')
+			{
+				$success = $this->_redis->connect($config['socket']);
+			}
+			else // tcp socket
+			{
+				$success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']);
+			}
+
+			if ( ! $success)
+			{
+				throw new RuntimeException('Cache: Redis connection failed. Check your configuration.');
+			}
+		}
+		catch (RedisException $e)
+		{
+			throw new RuntimeException('Cache: Redis connection refused ('.$e->getMessage().')');
+		}
+
+		if (isset($config['password']) && ! $this->_redis->auth($config['password']))
+		{
+			throw new RuntimeException('Cache: Redis authentication failed.');
+		}
+
+		// Initialize the index of serialized values.
+		$serialized = $this->_redis->sMembers('_ci_redis_serialized');
+		empty($serialized) OR $this->_serialized = array_flip($serialized);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
 	 * Get cache
 	 *
 	 * @param	string	Cache ID
@@ -125,9 +182,7 @@
 			$this->_redis->sRemove('_ci_redis_serialized', $id);
 		}
 
-		return ($ttl)
-			? $this->_redis->setex($id, $ttl, $data)
-			: $this->_redis->set($id, $data);
+		return $this->_redis->set($id, $data, $ttl);
 	}
 
 	// ------------------------------------------------------------------------
@@ -223,7 +278,7 @@
 	{
 		$value = $this->get($key);
 
-		if ($value)
+		if ($value !== FALSE)
 		{
 			return array(
 				'expire' => time() + $this->_redis->ttl($key),
@@ -249,69 +304,6 @@
 			return FALSE;
 		}
 
-		return $this->_setup_redis();
-	}
-
-	// ------------------------------------------------------------------------
-
-	/**
-	 * Setup Redis config and connection
-	 *
-	 * Loads Redis config file if present. Will halt execution
-	 * if a Redis connection can't be established.
-	 *
-	 * @return	bool
-	 * @see		Redis::connect()
-	 */
-	protected function _setup_redis()
-	{
-		$config = array();
-		$CI =& get_instance();
-
-		if ($CI->config->load('redis', TRUE, TRUE))
-		{
-			$config += $CI->config->item('redis');
-		}
-
-		$config = array_merge(self::$_default_config, $config);
-
-		$this->_redis = new Redis();
-
-		try
-		{
-			if ($config['socket_type'] === 'unix')
-			{
-				$success = $this->_redis->connect($config['socket']);
-			}
-			else // tcp socket
-			{
-				$success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']);
-			}
-
-			if ( ! $success)
-			{
-				log_message('debug', 'Cache: Redis connection refused. Check the config.');
-				return FALSE;
-			}
-		}
-		catch (RedisException $e)
-		{
-			log_message('debug', 'Cache: Redis connection refused ('.$e->getMessage().')');
-			return FALSE;
-		}
-
-		if (isset($config['password']))
-		{
-			$this->_redis->auth($config['password']);
-		}
-
-		// Initialize the index of serialized values.
-		$serialized = $this->_redis->sMembers('_ci_redis_serialized');
-		if ( ! empty($serialized))
-		{
-			$this->_serialized = array_flip($serialized);
-		}
-
 		return TRUE;
 	}
 
@@ -331,5 +323,4 @@
 			$this->_redis->close();
 		}
 	}
-
 }
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index 66b5803..57693e1 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -2126,12 +2126,32 @@
 	protected function _send_data($data)
 	{
 		$data .= $this->newline;
-		for ($written = 0, $length = strlen($data); $written < $length; $written += $result)
+		for ($written = $timestamp = 0, $length = strlen($data); $written < $length; $written += $result)
 		{
 			if (($result = fwrite($this->_smtp_connect, substr($data, $written))) === FALSE)
 			{
 				break;
 			}
+			// See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951
+			elseif ($result === 0)
+			{
+				if ($timestamp === 0)
+				{
+					$timestamp = time();
+				}
+				elseif ($timestamp < (time() - $this->smtp_timeout))
+				{
+					$result = FALSE;
+					break;
+				}
+
+				usleep(250000);
+				continue;
+			}
+			else
+			{
+				$timestamp = 0;
+			}
 		}
 
 		if ($result === FALSE)
diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php
index e3e6813..f3e0398 100644
--- a/system/libraries/Encryption.php
+++ b/system/libraries/Encryption.php
@@ -121,7 +121,7 @@
 	);
 
 	/**
-	 * List of supported HMAC algorightms
+	 * List of supported HMAC algorithms
 	 *
 	 * name => digest size pairs
 	 *
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index 05de596..d9ecc45 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -198,22 +198,20 @@
 			return $this;
 		}
 
-		// No fields? Nothing to do...
-		if ( ! is_string($field) OR $field === '')
+		// No fields or no rules? Nothing to do...
+		if ( ! is_string($field) OR $field === '' OR empty($rules))
 		{
 			return $this;
 		}
 		elseif ( ! is_array($rules))
 		{
 			// BC: Convert pipe-separated rules string to an array
-			if (is_string($rules))
-			{
-				$rules = explode('|', $rules);
-			}
-			else
+			if ( ! is_string($rules))
 			{
 				return $this;
 			}
+
+			$rules = explode('|', $rules);
 		}
 
 		// If the field label wasn't passed we use the field name
@@ -463,7 +461,7 @@
 			{
 				$this->_field_data[$field]['postdata'] = $this->_reduce_array($validation_array, $row['keys']);
 			}
-			elseif (isset($validation_array[$field]) && $validation_array[$field] !== '')
+			elseif (isset($validation_array[$field]))
 			{
 				$this->_field_data[$field]['postdata'] = $validation_array[$field];
 			}
@@ -620,6 +618,12 @@
 					$rules = array(1 => $rule);
 					break;
 				}
+				elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1]))
+				{
+					$callback = TRUE;
+					$rules = array(array($rule[0], $rule[1]));
+					break;
+				}
 			}
 
 			if ( ! $callback)
@@ -817,11 +821,10 @@
 				// Callable rules might not have named error messages
 				if ( ! is_string($rule))
 				{
-					return;
+					$line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)';
 				}
-
 				// Check if a custom message is defined
-				if (isset($this->_field_data[$row['field']]['errors'][$rule]))
+				elseif (isset($this->_field_data[$row['field']]['errors'][$rule]))
 				{
 					$line = $this->_field_data[$row['field']]['errors'][$rule];
 				}
@@ -872,17 +875,11 @@
 	 */
 	protected function _translate_fieldname($fieldname)
 	{
-		// Do we need to translate the field name?
-		// We look for the prefix lang: to determine this
-		if (sscanf($fieldname, 'lang:%s', $line) === 1)
+		// Do we need to translate the field name? We look for the prefix 'lang:' to determine this
+		// If we find one, but there's no translation for the string - just return it
+		if (sscanf($fieldname, 'lang:%s', $line) === 1 && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE)))
 		{
-			// Were we able to translate the field name?  If not we use $line
-			if (FALSE === ($fieldname = $this->CI->lang->line('form_validation_'.$line))
-				// DEPRECATED support for non-prefixed keys
-				&& FALSE === ($fieldname = $this->CI->lang->line($line, FALSE)))
-			{
-				return $line;
-			}
+			return $line;
 		}
 
 		return $fieldname;
diff --git a/system/libraries/Ftp.php b/system/libraries/Ftp.php
index af45bb5..b532075 100644
--- a/system/libraries/Ftp.php
+++ b/system/libraries/Ftp.php
@@ -490,7 +490,7 @@
 				// so we'll recursively call delete_dir()
 				if ( ! preg_match('#/\.\.?$#', $list[$i]) && ! @ftp_delete($this->conn_id, $list[$i]))
 				{
-					$this->delete_dir($list[$i]);
+					$this->delete_dir($filepath.$list[$i]);
 				}
 			}
 		}
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index e056654..c69283a 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -845,7 +845,7 @@
 	 */
 	public function image_process_imagemagick($action = 'resize')
 	{
-		//  Do we have a vaild library path?
+		// Do we have a vaild library path?
 		if ($this->library_path === '')
 		{
 			$this->set_error('imglib_libpath_invalid');
@@ -1010,7 +1010,7 @@
 		// going to have to figure out how to determine the color
 		// of the alpha channel in a future release.
 
-		$white	= imagecolorallocate($src_img, 255, 255, 255);
+		$white = imagecolorallocate($src_img, 255, 255, 255);
 
 		// Rotate it!
 		$dst_img = imagerotate($src_img, $this->rotation_angle, $white);
@@ -1055,8 +1055,11 @@
 
 		if ($this->rotation_angle === 'hor')
 		{
-			for ($i = 0; $i < $height; $i++, $left = 0, $right = $width-1)
+			for ($i = 0; $i < $height; $i++)
 			{
+				$left = 0;
+				$right = $width - 1;
+
 				while ($left < $right)
 				{
 					$cl = imagecolorat($src_img, $left, $i);
@@ -1072,18 +1075,21 @@
 		}
 		else
 		{
-			for ($i = 0; $i < $width; $i++, $top = 0, $bot = $height-1)
+			for ($i = 0; $i < $width; $i++)
 			{
-				while ($top < $bot)
+				$top = 0;
+				$bottom = $height - 1;
+
+				while ($top < $bottom)
 				{
 					$ct = imagecolorat($src_img, $i, $top);
-					$cb = imagecolorat($src_img, $i, $bot);
+					$cb = imagecolorat($src_img, $i, $bottom);
 
 					imagesetpixel($src_img, $i, $top, $cb);
-					imagesetpixel($src_img, $i, $bot, $ct);
+					imagesetpixel($src_img, $i, $bottom, $ct);
 
 					$top++;
-					$bot--;
+					$bottom--;
 				}
 			}
 		}
@@ -1327,7 +1333,7 @@
 		{
 			$y_axis += $this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight / 2);
 		}
-		
+
 		// Set horizontal alignment
 		if ($this->wm_hor_alignment === 'R')
 		{
@@ -1337,13 +1343,13 @@
 		{
 			$x_axis += floor(($this->orig_width - ($fontwidth * strlen($this->wm_text))) / 2);
 		}
-		
+
 		if ($this->wm_use_drop_shadow)
 		{
 			// Offset from text
 			$x_shad = $x_axis + $this->wm_shadow_distance;
 			$y_shad = $y_axis + $this->wm_shadow_distance;
-				
+
 			/* Set RGB values for shadow
 			 *
 			 * First character is #, so we don't really need it.
@@ -1352,7 +1358,7 @@
 			 */
 			$drp_color = str_split(substr($this->wm_shadow_color, 1, 6), 2);
 			$drp_color = imagecolorclosest($src_img, hexdec($drp_color[0]), hexdec($drp_color[1]), hexdec($drp_color[2]));
-			
+
 			// Add the shadow to the source image
 			if ($this->wm_use_truetype)
 			{
@@ -1363,7 +1369,7 @@
 				imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);
 			}
 		}
-		
+
 		/* Set RGB values for text
 		 *
 		 * First character is #, so we don't really need it.
@@ -1382,7 +1388,7 @@
 		{
 			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)
 		{
@@ -1431,7 +1437,7 @@
 
 		switch ($image_type)
 		{
-			case 1 :
+			case 1:
 				if ( ! function_exists('imagecreatefromgif'))
 				{
 					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
@@ -1439,7 +1445,7 @@
 				}
 
 				return imagecreatefromgif($path);
-			case 2 :
+			case 2:
 				if ( ! function_exists('imagecreatefromjpeg'))
 				{
 					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
@@ -1447,7 +1453,7 @@
 				}
 
 				return imagecreatefromjpeg($path);
-			case 3 :
+			case 3:
 				if ( ! function_exists('imagecreatefrompng'))
 				{
 					$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php
index ae36a3b..45a3cbb 100644
--- a/system/libraries/Migration.php
+++ b/system/libraries/Migration.php
@@ -191,7 +191,7 @@
 	 * choice
 	 *
 	 * @param	string	$target_version	Target schema version
-	 * @return	mixed	TRUE if already latest, FALSE if failed, string if upgraded
+	 * @return	mixed	TRUE if no migrations are found, current version string on success, FALSE on failure
 	 */
 	public function version($target_version)
 	{
@@ -294,7 +294,7 @@
 	/**
 	 * Sets the schema to the latest migration
 	 *
-	 * @return	mixed	TRUE if already latest, FALSE if failed, string if upgraded
+	 * @return	mixed	Current version string on success, FALSE on failure
 	 */
 	public function latest()
 	{
@@ -318,7 +318,7 @@
 	/**
 	 * Sets the schema to the migration version set in config
 	 *
-	 * @return	mixed	TRUE if already current, FALSE if failed, string if upgraded
+	 * @return	mixed	TRUE if no migrations are found, current version string on success, FALSE on failure
 	 */
 	public function current()
 	{
diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php
index bb457c6..0549fef 100644
--- a/system/libraries/Session/Session.php
+++ b/system/libraries/Session/Session.php
@@ -869,7 +869,7 @@
 	public function set_tempdata($data, $value = NULL, $ttl = 300)
 	{
 		$this->set_userdata($data, $value);
-		$this->mark_as_temp($data, $ttl);
+		$this->mark_as_temp(is_array($data) ? array_keys($data) : $data, $ttl);
 	}
 
 	// ------------------------------------------------------------------------
diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php
index c7185ee..97b8605 100644
--- a/system/libraries/Session/drivers/Session_memcached_driver.php
+++ b/system/libraries/Session/drivers/Session_memcached_driver.php
@@ -322,7 +322,7 @@
 			$this->_lock_key = $lock_key;
 			break;
 		}
-		while ($attempt++ < 30);
+		while (++$attempt < 30);
 
 		if ($attempt === 30)
 		{
diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php
index 1ce101d..b098cc4 100644
--- a/system/libraries/Session/drivers/Session_redis_driver.php
+++ b/system/libraries/Session/drivers/Session_redis_driver.php
@@ -336,7 +336,7 @@
 			$this->_lock_key = $lock_key;
 			break;
 		}
-		while ($attempt++ < 30);
+		while (++$attempt < 30);
 
 		if ($attempt === 30)
 		{
diff --git a/system/libraries/Unit_test.php b/system/libraries/Unit_test.php
index 7b744ad..3f986f3 100644
--- a/system/libraries/Unit_test.php
+++ b/system/libraries/Unit_test.php
@@ -55,14 +55,14 @@
 	 *
 	 * @var	bool
 	 */
-	public $active			= TRUE;
+	public $active = TRUE;
 
 	/**
 	 * Test results
 	 *
 	 * @var	array
 	 */
-	public $results			= array();
+	public $results = array();
 
 	/**
 	 * Strict comparison flag
@@ -71,21 +71,21 @@
 	 *
 	 * @var	bool
 	 */
-	public $strict			= FALSE;
+	public $strict = FALSE;
 
 	/**
 	 * Template
 	 *
 	 * @var	string
 	 */
-	protected $_template		= NULL;
+	protected $_template = NULL;
 
 	/**
 	 * Template rows
 	 *
 	 * @var	string
 	 */
-	protected $_template_rows	= NULL;
+	protected $_template_rows = NULL;
 
 	/**
 	 * List of visible test items
@@ -93,13 +93,13 @@
 	 * @var	array
 	 */
 	protected $_test_items_visible	= array(
-			'test_name',
-			'test_datatype',
-			'res_datatype',
-			'result',
-			'file',
-			'line',
-			'notes'
+		'test_name',
+		'test_datatype',
+		'res_datatype',
+		'result',
+		'file',
+		'line',
+		'notes'
 	);
 
 	// --------------------------------------------------------------------
@@ -152,7 +152,7 @@
 			return FALSE;
 		}
 
-		if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null'), TRUE))
+		if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null', 'is_resource'), TRUE))
 		{
 			$expected = str_replace('is_double', 'is_float', $expected);
 			$result = $expected($test);
@@ -167,14 +167,14 @@
 		$back = $this->_backtrace();
 
 		$report = array (
-							'test_name'			=> $test_name,
-							'test_datatype'		=> gettype($test),
-							'res_datatype'		=> $extype,
-							'result'			=> ($result === TRUE) ? 'passed' : 'failed',
-							'file'				=> $back['file'],
-							'line'				=> $back['line'],
-							'notes'				=> $notes
-						);
+			'test_name'     => $test_name,
+			'test_datatype' => gettype($test),
+			'res_datatype'  => $extype,
+			'result'        => ($result === TRUE) ? 'passed' : 'failed',
+			'file'          => $back['file'],
+			'line'          => $back['line'],
+			'notes'         => $notes
+		);
 
 		$this->results[] = $report;
 
@@ -291,10 +291,12 @@
 				{
 					continue;
 				}
-
-				if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE)))
+				elseif (in_array($key, array('test_name', 'test_datatype', 'test_res_datatype', 'result'), TRUE))
 				{
-					$val = $line;
+					if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE)))
+					{
+						$val = $line;
+					}
 				}
 
 				$temp[$CI->lang->line('ut_'.$key, FALSE)] = $val;
@@ -334,9 +336,9 @@
 	{
 		$back = debug_backtrace();
 		return array(
-				'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''),
-				'line' => (isset($back[1]['line']) ? $back[1]['line'] : '')
-			);
+			'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''),
+			'line' => (isset($back[1]['line']) ? $back[1]['line'] : '')
+		);
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index f5534a7..51232f8 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -397,7 +397,7 @@
 
 		if ( ! isset($_file))
 		{
-			$this->set_error('upload_no_file_selected');
+			$this->set_error('upload_no_file_selected', 'debug');
 			return FALSE;
 		}
 
@@ -416,28 +416,28 @@
 			switch ($error)
 			{
 				case UPLOAD_ERR_INI_SIZE:
-					$this->set_error('upload_file_exceeds_limit');
+					$this->set_error('upload_file_exceeds_limit', 'info');
 					break;
 				case UPLOAD_ERR_FORM_SIZE:
-					$this->set_error('upload_file_exceeds_form_limit');
+					$this->set_error('upload_file_exceeds_form_limit', 'info');
 					break;
 				case UPLOAD_ERR_PARTIAL:
-					$this->set_error('upload_file_partial');
+					$this->set_error('upload_file_partial', 'debug');
 					break;
 				case UPLOAD_ERR_NO_FILE:
-					$this->set_error('upload_no_file_selected');
+					$this->set_error('upload_no_file_selected', 'debug');
 					break;
 				case UPLOAD_ERR_NO_TMP_DIR:
-					$this->set_error('upload_no_temp_directory');
+					$this->set_error('upload_no_temp_directory', 'error');
 					break;
 				case UPLOAD_ERR_CANT_WRITE:
-					$this->set_error('upload_unable_to_write_file');
+					$this->set_error('upload_unable_to_write_file', 'error');
 					break;
 				case UPLOAD_ERR_EXTENSION:
-					$this->set_error('upload_stopped_by_extension');
+					$this->set_error('upload_stopped_by_extension', 'debug');
 					break;
 				default:
-					$this->set_error('upload_no_file_selected');
+					$this->set_error('upload_no_file_selected', 'debug');
 					break;
 			}
 
@@ -463,7 +463,7 @@
 		// Is the file type allowed to be uploaded?
 		if ( ! $this->is_allowed_filetype())
 		{
-			$this->set_error('upload_invalid_filetype');
+			$this->set_error('upload_invalid_filetype', 'debug');
 			return FALSE;
 		}
 
@@ -485,7 +485,7 @@
 
 			if ( ! $this->is_allowed_filetype(TRUE))
 			{
-				$this->set_error('upload_invalid_filetype');
+				$this->set_error('upload_invalid_filetype', 'debug');
 				return FALSE;
 			}
 		}
@@ -499,7 +499,7 @@
 		// Is the file size within the allowed maximum?
 		if ( ! $this->is_allowed_filesize())
 		{
-			$this->set_error('upload_invalid_filesize');
+			$this->set_error('upload_invalid_filesize', 'info');
 			return FALSE;
 		}
 
@@ -507,7 +507,7 @@
 		// Note: This can fail if the server has an open_basedir restriction.
 		if ( ! $this->is_allowed_dimensions())
 		{
-			$this->set_error('upload_invalid_dimensions');
+			$this->set_error('upload_invalid_dimensions', 'info');
 			return FALSE;
 		}
 
@@ -533,15 +533,9 @@
 		 * If it returns false there was a problem.
 		 */
 		$this->orig_name = $this->file_name;
-
-		if ($this->overwrite === FALSE)
+		if (FALSE === ($this->file_name = $this->set_filename($this->upload_path, $this->file_name)))
 		{
-			$this->file_name = $this->set_filename($this->upload_path, $this->file_name);
-
-			if ($this->file_name === FALSE)
-			{
-				return FALSE;
-			}
+			return FALSE;
 		}
 
 		/*
@@ -552,7 +546,7 @@
 		 */
 		if ($this->xss_clean && $this->do_xss_clean() === FALSE)
 		{
-			$this->set_error('upload_unable_to_write_file');
+			$this->set_error('upload_unable_to_write_file', 'error');
 			return FALSE;
 		}
 
@@ -567,7 +561,7 @@
 		{
 			if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
 			{
-				$this->set_error('upload_destination_error');
+				$this->set_error('upload_destination_error', 'error');
 				return FALSE;
 			}
 		}
@@ -656,7 +650,7 @@
 			$filename = md5(uniqid(mt_rand())).$this->file_ext;
 		}
 
-		if ( ! file_exists($path.$filename))
+		if ($this->overwrite === TRUE OR ! file_exists($path.$filename))
 		{
 			return $filename;
 		}
@@ -675,7 +669,7 @@
 
 		if ($new_filename === '')
 		{
-			$this->set_error('upload_bad_filename');
+			$this->set_error('upload_bad_filename', 'debug');
 			return FALSE;
 		}
 		else
@@ -875,7 +869,7 @@
 
 		if (empty($this->allowed_types) OR ! is_array($this->allowed_types))
 		{
-			$this->set_error('upload_no_file_types');
+			$this->set_error('upload_no_file_types', 'debug');
 			return FALSE;
 		}
 
@@ -974,7 +968,7 @@
 	{
 		if ($this->upload_path === '')
 		{
-			$this->set_error('upload_no_filepath');
+			$this->set_error('upload_no_filepath', 'error');
 			return FALSE;
 		}
 
@@ -985,13 +979,13 @@
 
 		if ( ! is_dir($this->upload_path))
 		{
-			$this->set_error('upload_no_filepath');
+			$this->set_error('upload_no_filepath', 'error');
 			return FALSE;
 		}
 
 		if ( ! is_really_writable($this->upload_path))
 		{
-			$this->set_error('upload_not_writable');
+			$this->set_error('upload_not_writable', 'error');
 			return FALSE;
 		}
 
@@ -1121,17 +1115,16 @@
 	 * @param	string	$msg
 	 * @return	CI_Upload
 	 */
-	public function set_error($msg)
+	public function set_error($msg, $log_level = 'error')
 	{
 		$this->_CI->lang->load('upload');
 
 		is_array($msg) OR $msg = array($msg);
-
 		foreach ($msg as $val)
 		{
 			$msg = ($this->_CI->lang->line($val) === FALSE) ? $val : $this->_CI->lang->line($val);
 			$this->error_msg[] = $msg;
-			log_message('error', $msg);
+			log_message($log_level, $msg);
 		}
 
 		return $this;
diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php
index 8fbc18f..55555f5 100644
--- a/system/libraries/Xmlrpc.php
+++ b/system/libraries/Xmlrpc.php
@@ -735,12 +735,32 @@
 			.'Content-Length: '.strlen($msg->payload).$r.$r
 			.$msg->payload;
 
-		for ($written = 0, $length = strlen($op); $written < $length; $written += $result)
+		for ($written = $timestamp = 0, $length = strlen($op); $written < $length; $written += $result)
 		{
 			if (($result = fwrite($fp, substr($op, $written))) === FALSE)
 			{
 				break;
 			}
+			// See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951
+			elseif ($result === 0)
+			{
+				if ($timestamp === 0)
+				{
+					$timestamp = time();
+				}
+				elseif ($timestamp < (time() - $this->timeout))
+				{
+					$result = FALSE;
+					break;
+				}
+
+				usleep(250000);
+				continue;
+			}
+			else
+			{
+				$timestamp = 0;
+			}
 		}
 
 		if ($result === FALSE)
diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php
index f2f1714..3e98ac5 100644
--- a/system/libraries/Zip.php
+++ b/system/libraries/Zip.php
@@ -352,7 +352,7 @@
 		// Set the original directory root for child dir's to use as relative
 		if ($root_path === NULL)
 		{
-			$root_path = dirname($path).DIRECTORY_SEPARATOR;
+			$root_path = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, dirname($path)).DIRECTORY_SEPARATOR;
 		}
 
 		while (FALSE !== ($file = readdir($fp)))
diff --git a/tests/codeigniter/core/Lang_test.php b/tests/codeigniter/core/Lang_test.php
index 929bc2f..d2dd759 100644
--- a/tests/codeigniter/core/Lang_test.php
+++ b/tests/codeigniter/core/Lang_test.php
@@ -49,6 +49,32 @@
 
 	// --------------------------------------------------------------------
 
+	public function test_multiple_file_load()
+	{
+		// Multiple files
+		$this->ci_vfs_clone('system/language/english/profiler_lang.php');
+		$files = array(
+			0 => 'profiler',
+			1 => 'nonexistent'
+		);
+		$this->setExpectedException(
+			'RuntimeException',
+			'CI Error: Unable to load the requested language file: language/english/nonexistent_lang.php'
+		);
+		$this->lang->load($files, 'english');
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_alternative_path_load()
+	{
+		// Alternative Path
+		$this->ci_vfs_clone('system/language/english/profiler_lang.php');
+		$this->assertTrue($this->lang->load('profiler', 'english', FALSE, TRUE, 'vfs://system/'));
+	}
+
+	// --------------------------------------------------------------------
+
 	/**
 	 * @depends	test_load
 	 */
diff --git a/tests/codeigniter/core/Loader_test.php b/tests/codeigniter/core/Loader_test.php
index 9e2092e..cfaf6c7 100644
--- a/tests/codeigniter/core/Loader_test.php
+++ b/tests/codeigniter/core/Loader_test.php
@@ -22,6 +22,9 @@
 
 	public function test_library()
 	{
+		// Test getting CI_Loader object
+		$this->assertInstanceOf('CI_Loader', $this->load->library(NULL));
+
 		// Create library in VFS
 		$lib = 'unit_test_lib';
 		$class = 'CI_'.ucfirst($lib);
@@ -35,6 +38,13 @@
 		$this->assertTrue(class_exists($class), $class.' does not exist');
 		$this->assertAttributeInstanceOf($class, $lib, $this->ci_obj);
 
+		// Create library in VFS
+		$lib = array('unit_test_lib' => 'unit_test_lib');
+
+		// Test loading as an array (int).
+		$this->assertInstanceOf('CI_Loader', $this->load->library($lib));
+		$this->assertTrue(class_exists($class), $class.' does not exist');
+
 		// Test a string given to params
 		$this->assertInstanceOf('CI_Loader', $this->load->library($lib, ' '));
 
@@ -319,6 +329,24 @@
 
 	// --------------------------------------------------------------------
 
+	public function test_clear_vars()
+	{
+		$key1 = 'foo';
+		$val1 = 'bar';
+		$key2 = 'boo';
+		$val2 = 'hoo';
+		$this->assertInstanceOf('CI_Loader', $this->load->vars(array($key1 => $val1)));
+		$this->assertInstanceOf('CI_Loader', $this->load->vars($key2, $val2));
+		$this->assertEquals($val1, $this->load->get_var($key1));
+		$this->assertEquals(array($key1 => $val1, $key2 => $val2), $this->load->get_vars());
+
+		$this->assertInstanceOf('CI_Loader', $this->load->clear_vars());
+		$this->assertEquals('', $this->load->get_var($key1));
+		$this->assertEquals('', $this->load->get_var($key2));
+	}
+
+	// --------------------------------------------------------------------
+
 	public function test_helper()
 	{
 		// Create helper in VFS
@@ -443,6 +471,24 @@
 
 	// --------------------------------------------------------------------
 
+	public function test_remove_package_path()
+	{
+		$dir = 'third-party';
+		$path = APPPATH.$dir.'/';
+		$path2 = APPPATH.'another/';
+		$paths = $this->load->get_package_paths(TRUE);
+
+		$this->assertInstanceOf('CI_Loader', $this->load->add_package_path($path));
+		$this->assertInstanceOf('CI_Loader', $this->load->remove_package_path($path));
+		$this->assertEquals($paths, $this->load->get_package_paths(TRUE));
+
+		$this->assertInstanceOf('CI_Loader', $this->load->add_package_path($path2));
+		$this->assertInstanceOf('CI_Loader', $this->load->remove_package_path());
+		$this->assertNotContains($path2, $this->load->get_package_paths(TRUE));
+	}
+
+	// --------------------------------------------------------------------
+
 	public function test_load_config()
 	{
 		$cfg = 'someconfig';
@@ -511,5 +557,4 @@
 		// Verify config calls
 		$this->assertEquals($cfg['config'], $this->ci_obj->config->loaded);
 	}
-
-}
\ No newline at end of file
+}
diff --git a/tests/codeigniter/helpers/email_helper_test.php b/tests/codeigniter/helpers/email_helper_test.php
index 53a2068..529e969 100644
--- a/tests/codeigniter/helpers/email_helper_test.php
+++ b/tests/codeigniter/helpers/email_helper_test.php
@@ -13,6 +13,7 @@
 		$this->assertEquals(FALSE, valid_email('test@test@test.com'));
 		$this->assertEquals(TRUE, valid_email('test@test.com'));
 		$this->assertEquals(TRUE, valid_email('my.test@test.com'));
+		$this->assertEquals(TRUE, valid_email('my.test@subdomain.test.com'));
 	}
 
 	public function test_send_mail()
diff --git a/tests/codeigniter/libraries/Form_validation_test.php b/tests/codeigniter/libraries/Form_validation_test.php
index 1bbd175..26d82ec 100644
--- a/tests/codeigniter/libraries/Form_validation_test.php
+++ b/tests/codeigniter/libraries/Form_validation_test.php
@@ -248,7 +248,7 @@
 		$this->assertTrue($this->form_validation->valid_emails('1@sample.com,2@sample.com'));
 		$this->assertTrue($this->form_validation->valid_emails('email@sample.com'));
 
-		$this->assertFalse($this->form_validation->valid_emails('valid_email', '@sample.com'));	
+		$this->assertFalse($this->form_validation->valid_emails('valid_email', '@sample.com'));
 		$this->assertFalse($this->form_validation->valid_emails('@sample.com,2@sample.com,validemail@email.ca'));
 	}
 
@@ -323,6 +323,254 @@
 		$this->assertEquals('', $this->form_validation->error('req_field'));
 	}
 
+	public function test_set_error_delimiters()
+	{
+		$this->form_validation->reset_validation();
+		$prefix = '<div class="error">';
+		$suffix = '</div>';
+		$this->form_validation->set_error_delimiters($prefix, $suffix);
+		$this->form_validation->set_rules('foo', 'label', 'required');
+		$_POST = array('foo' => '');
+		$this->form_validation->run();
+		$error_msg = $this->form_validation->error('foo');
+
+		$this->assertTrue(strrpos($error_msg, $prefix) === 0);
+		$this->assertTrue(strrpos($error_msg, $suffix, -strlen($suffix)) === (strlen($error_msg) - strlen($suffix)));
+	}
+
+	public function test_error_array()
+	{
+		$this->form_validation->reset_validation();
+		$error_message = 'What a terrible error!';
+		$this->form_validation->set_message('required', $error_message);
+		$this->form_validation->set_rules('foo', 'label', 'required');
+		$_POST = array('foo' => '');
+		$this->form_validation->run();
+		$error_array = $this->form_validation->error_array();
+		$this->assertEquals($error_message, $error_array['foo']);
+	}
+
+	public function test_error_string()
+	{
+		$this->form_validation->reset_validation();
+		$error_message = 'What a terrible error!';
+		$prefix_default = '<foo>';
+		$suffix_default = '</foo>';
+		$prefix_test = '<bar>';
+		$suffix_test = '</bar>';
+		$this->form_validation->set_error_delimiters($prefix_default, $suffix_default);
+		$this->form_validation->set_message('required', $error_message);
+		$this->form_validation->set_rules('foo', 'label', 'required');
+		$_POST = array('foo' => '');
+		$this->form_validation->run();
+
+		$this->assertEquals($prefix_default.$error_message.$suffix_default."\n", $this->form_validation->error_string());
+		$this->assertEquals($prefix_test.$error_message.$suffix_default."\n", $this->form_validation->error_string($prefix_test, ''));
+		$this->assertEquals($prefix_default.$error_message.$suffix_test."\n", $this->form_validation->error_string('', $suffix_test));
+		$this->assertEquals($prefix_test.$error_message.$suffix_test."\n", $this->form_validation->error_string($prefix_test, $suffix_test));
+
+		$this->form_validation->reset_validation();
+		$this->form_validation->set_rules('foo', 'label', 'required');
+		$_POST = array('foo' => 'bar');
+		$this->form_validation->run();
+		$this->assertEquals('', $this->form_validation->error_string());
+	}
+
+	public function test_run()
+	{
+		// form_validation->run() is tested in many of the other unit tests
+		// This test will only test run(group='') when group is not empty
+		$config = array(
+			'pass' => array(
+				array(
+					'field' => 'username',
+					'label' => 'user',
+					'rules' => 'alpha_numeric'
+				)
+			),
+			'fail' => array(
+				array(
+					'field' => 'username',
+					'label' => 'user',
+					'rules' => 'alpha'
+				)
+			)
+		);
+		$_POST = array('username' => 'foo42');
+		$form_validation = new CI_Form_validation($config);
+		$this->assertTrue($form_validation->run('pass'));
+
+		$form_validation = new CI_Form_validation($config);
+		$this->assertFalse($form_validation->run('fail'));
+	}
+
+	public function test_has_rule()
+	{
+		$this->form_validation->reset_validation();
+		$this->form_validation->set_rules('foo', 'label', 'required');
+
+		$this->assertTrue($this->form_validation->has_rule('foo'));
+		$this->assertFalse($this->form_validation->has_rule('bar'));
+	}
+
+	public function test_set_value()
+	{
+		$this->form_validation->reset_validation();
+		$default = 'default';
+		$this->form_validation->set_rules('foo', 'label', 'required');
+		$this->form_validation->set_rules('bar[]', 'label', 'required');
+
+		// No post data yet: should return the default value provided
+		$this->assertEquals($default, $this->form_validation->set_value('foo', $default));
+		$_POST = array('foo' => 'foo', 'bar' => array('bar1', 'bar2'));
+		$this->form_validation->run();
+		$this->assertEquals('foo', $this->form_validation->set_value('foo', $default));
+		$this->assertEquals('bar1', $this->form_validation->set_value('bar[]', $default));
+		$this->assertEquals('bar2', $this->form_validation->set_value('bar[]', $default));
+	}
+
+	public function test_set_select()
+	{
+		// Test 1: No options selected
+		$this->form_validation->reset_validation();
+		$_POST = array();
+		$this->form_validation->run();
+
+		$this->assertEquals('', $this->form_validation->set_select('select', 'foo'));
+		$this->assertEquals(' selected="selected"', $this->form_validation->set_select('select', 'bar', TRUE));
+
+		// Test 2: 1 option selected
+		$this->form_validation->reset_validation();
+		$this->form_validation->set_rules('select', 'label', 'alpha_numeric');
+		$_POST = array('select' => 'foo');
+		$this->form_validation->run();
+
+		$this->assertEquals(' selected="selected"', $this->form_validation->set_select('select', 'foo'));
+		$this->assertEquals(' selected="selected"', $this->form_validation->set_select('select', 'foo', TRUE));
+		$this->assertEquals('', $this->form_validation->set_select('select', 'bar'));
+		$this->assertEquals('', $this->form_validation->set_select('select', 'bar', TRUE));
+
+		// Test 3: Multiple options selected
+		$this->form_validation->reset_validation();
+		$this->form_validation->set_rules('select[]', 'label', 'alpha_numeric');
+		$_POST = array('select' => array('foo', 'bar'));
+		$this->form_validation->run();
+
+		$this->assertEquals(' selected="selected"', $this->form_validation->set_select('select[]', 'foo'));
+		$this->assertEquals(' selected="selected"', $this->form_validation->set_select('select[]', 'foo', TRUE));
+		$this->assertEquals(' selected="selected"', $this->form_validation->set_select('select[]', 'bar'));
+		$this->assertEquals(' selected="selected"', $this->form_validation->set_select('select[]', 'bar', TRUE));
+		$this->assertEquals('', $this->form_validation->set_select('select[]', 'foobar'));
+		$this->assertEquals('', $this->form_validation->set_select('select[]', 'foobar', TRUE));
+	}
+
+	public function test_set_radio()
+	{
+		// Test 1: No options selected
+		$this->form_validation->reset_validation();
+		$_POST = array();
+		$this->form_validation->run();
+
+		$this->assertEquals('', $this->form_validation->set_radio('select', 'foo'));
+		// Default should only work when no rules are set
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select', 'bar', TRUE));
+
+		// Test 2: 1 option selected
+		$this->form_validation->reset_validation();
+		$this->form_validation->set_rules('select', 'label', 'alpha_numeric');
+		$_POST = array('select' => 'foo');
+		$this->form_validation->run();
+
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select', 'foo'));
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select', 'foo', TRUE));
+		$this->assertEquals('', $this->form_validation->set_radio('select', 'bar'));
+		$this->assertEquals('', $this->form_validation->set_radio('select', 'bar', TRUE));
+
+		// Test 3: Multiple options checked
+		$this->form_validation->reset_validation();
+		$this->form_validation->set_rules('select[]', 'label', 'alpha_numeric');
+		$_POST = array('select' => array('foo', 'bar'));
+		$this->form_validation->run();
+
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select[]', 'foo'));
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select[]', 'foo', TRUE));
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select[]', 'bar'));
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select[]', 'bar', TRUE));
+		$this->assertEquals('', $this->form_validation->set_radio('select[]', 'foobar'));
+		$this->assertEquals('', $this->form_validation->set_radio('select[]', 'foobar', TRUE));
+	}
+
+	public function test_set_checkbox()
+	{
+		// Test 1: No options selected
+		$this->form_validation->reset_validation();
+		$_POST = array();
+		$this->form_validation->run();
+
+		$this->assertEquals('', $this->form_validation->set_checkbox('select', 'foo'));
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select', 'bar', TRUE));
+
+		// Test 2: 1 option selected
+		$this->form_validation->reset_validation();
+		$this->form_validation->set_rules('select', 'label', 'alpha_numeric');
+		$_POST = array('select' => 'foo');
+		$this->form_validation->run();
+
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select', 'foo'));
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select', 'foo', TRUE));
+		$this->assertEquals('', $this->form_validation->set_checkbox('select', 'bar'));
+		$this->assertEquals('', $this->form_validation->set_checkbox('select', 'bar', TRUE));
+
+		// Test 3: Multiple options selected
+		$this->form_validation->reset_validation();
+		$this->form_validation->set_rules('select[]', 'label', 'alpha_numeric');
+		$_POST = array('select' => array('foo', 'bar'));
+		$this->form_validation->run();
+
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select[]', 'foo'));
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select[]', 'foo', TRUE));
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select[]', 'bar'));
+		$this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select[]', 'bar', TRUE));
+		$this->assertEquals('', $this->form_validation->set_checkbox('select[]', 'foobar'));
+		$this->assertEquals('', $this->form_validation->set_checkbox('select[]', 'foobar', TRUE));
+	}
+
+	public function test_regex_match()
+	{
+		$regex = '/f[a-zA-Z]+/';
+		$this->assertTrue($this->form_validation->regex_match('foo', $regex));
+		$this->assertFalse($this->form_validation->regex_match('bar', $regex));
+	}
+
+	public function test_prep_for_form()
+	{
+		$this->form_validation->reset_validation();
+		$error_msg_unprepped = '<error =\'foobar\'">';
+		$error_msg_prepped = '&lt;error =&#39;foobar&#39;&quot;&gt;';
+		$this->form_validation->set_rules('foo', 'label', 'required', array('required' => $error_msg_unprepped));
+		$_POST = array('foo' => '');
+		$this->form_validation->run();
+		$error_arr = $this->form_validation->error_array();
+
+		$this->assertEquals('', $this->form_validation->prep_for_form(''));
+		$this->assertEquals(array('foo' => $error_msg_prepped), $this->form_validation->prep_for_form($error_arr));
+	}
+
+	public function test_prep_url()
+	{
+		$this->assertEquals('', $this->form_validation->prep_url(''));
+		$this->assertEquals('http://codeigniter.com', $this->form_validation->prep_url('codeigniter.com'));
+		$this->assertEquals('https://codeigniter.com', $this->form_validation->prep_url('https://codeigniter.com'));
+		$this->assertEquals('http://codeigniter.com', $this->form_validation->prep_url('http://codeigniter.com'));
+		$this->assertEquals('http://www.codeigniter.com', $this->form_validation->prep_url('www.codeigniter.com'));
+	}
+
+	public function test_encode_php_tags()
+	{
+		$this->assertEquals("&lt;?php", $this->form_validation->encode_php_tags('<?php'));
+		$this->assertEquals('?&gt;', $this->form_validation->encode_php_tags('?>'));
+	}
+
 	/**
 	 * Run rules
 	 *
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index a1b1510..55463ed 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -2,11 +2,67 @@
 Change Log
 ##########
 
-Version 3.0 (planned)
-=======================
+Version 3.0.1
+=============
 
 Release Date: Not Released
 
+-  Core
+
+   -  Added DoS mitigation to :php:func:`hash_pbkdf2()` :doc:`compatibility function <general/compatibility_functions>`.
+
+-  Database
+
+   -  Added ``list_fields()`` support for SQLite ('sqlite3' and 'pdo_sqlite' drivers).
+
+-  Libraries
+
+   -  :doc:`File Uploading Library <libraries/file_uploading>` changes:
+
+      - Changed method ``set_error()`` to accept a custom log level (defaults to 'error').
+      - Errors "no_file_selected", "file_partial", "stopped_by_extension", "no_file_types", "invalid_filetype", "bad_filename" are now logged at the 'debug' level.
+      - Errors "file_exceeds_limit", "file_exceeds_form_limit", "invalid_filesize", "invalid_dimensions" are now logged at the 'info' level.
+
+   -  Added 'is_resource' to the available expectations in :doc:`Unit Testing Library <libraries/unit_testing>`.
+
+-  Helpers
+
+   -  Added Unicode support to :doc:`URL Helper <helpers/url_helper>` function :php:func:`url_title()`.
+   -  Added support for passing the "extra" parameter as an array to all :doc:`Form Helper <helpers/form_helper>` functions that use it.
+
+Bug fixes for 3.0.1
+-------------------
+
+-  Fixed a bug (#3733) - Autoloading of libraries with aliases didn't work, although it was advertised to.
+-  Fixed a bug (#3744) - Redis :doc:`Caching <libraries/caching>` driver didn't handle authentication failures properly.
+-  Fixed a bug (#3761) - :doc:`URL Helper <helpers/url_helper>` function :php:func:`anchor()` didn't work with array inputs.
+-  Fixed a bug (#3773) - ``db_select()`` didn't work for MySQL with the PDO :doc:`Database <database/index>` driver.
+-  Fixed a bug (#3771) - :doc:`Form Validation Library <libraries/form_validation>` was looking for a 'form_validation\_' prefix when trying to translate field name labels.
+-  Fixed a bug (#3787) - :doc:`FTP Library <libraries/ftp>` method ``delete_dir()`` failed when the target has subdirectories.
+-  Fixed a bug (#3801) - :doc:`Output Library <libraries/output>` method ``_display_cache()`` incorrectly looked for the last modified time of a directory instead of the cache file.
+-  Fixed a bug (#3816) - :doc:`Form Validation Library <libraries/form_validation>` treated empty string values as non-existing ones.
+-  Fixed a bug (#3823) - :doc:`Session Library <libraries/sessions>` drivers Redis and Memcached didn't properly handle locks that are blocking the request for more than 30 seconds.
+-  Fixed a bug (#3846) - :doc:`Image Manipulation Library <libraries/image_lib>` method `image_mirror_gd()` didn't properly initialize its variables.
+-  Fixed a bug (#3854) - `field_data()` didn't work properly with the Oracle (OCI8) database driver.
+-  Fixed a bug in the :doc:`Database Utility Class <database/utilities>` method ``csv_from_result()`` didn't work with a whitespace CSV delimiter.
+-  Fixed a bug (#3890) - :doc:`Input Library <libraries/input>` method ``get_request_header()`` treated header names as case-sensitive.
+-  Fixed a bug (#3903) - :doc:`Form Validation Library <libraries/form_validation>` ignored "unnamed" closure validation rules.
+-  Fixed a bug (#3904) - :doc:`Form Validation Library <libraries/form_validation>` ignored "named" callback rules when the field is empty and there's no 'required' rule.
+-  Fixed a bug (#3922) - :doc:`Email <libraries/email>` and :doc:`XML-RPC <libraries/xmlrpc>` libraries could enter an infinite loop due to `PHP bug #39598 <https://bugs.php.net/bug.php?id=39598>`_.
+-  Fixed a bug (#3913) - :doc:`Cache Library <libraries/caching>` didn't work with the direct ``$this->cache->$driver_name->method()`` syntax with Redis and Memcache(d).
+-  Fixed a bug (#3932) - :doc:`Query Builder <database/query_builder>` didn't properly compile WHERE and HAVING conditions for field names that end with "and", "or".
+-  Fixed a bug in :doc:`Query Builder <database/query_builder>` where ``delete()`` didn't properly work on multiple tables with a WHERE condition previously set via ``where()``.
+-  Fixed a bug (#3952) - :doc:`Database <database/index>` method ``list_fields()`` didn't work with SQLite3.
+-  Fixed a bug (#3955) - :doc:`Cache Library <libraries/caching>` methods ``increment()`` and ``decrement()`` ignored the 'key_prefix' setting.
+-  Fixed a bug (#3963) - :doc:`Unit Testing Library <libraries/unit_testing>` wrongly tried to translate filenames, line numbers and notes values in test results.
+-  Fixed a bug (#3965) - :doc:`File Uploading Library <libraries/file_uploading>` ignored the "encrypt_name" setting when "overwrite" is enabled.
+-  Fixed a bug (#3968) - :doc:`Database Forge <database/forge>` method ``add_key()`` didn't treat array inputs as composite keys unless it's a PRIMARY KEY.
+
+Version 3.0.0
+=============
+
+Release Date: March 30, 2015
+
 -  License
 
    -  CodeIgniter has been relicensed with the `MIT License <http://opensource.org/licenses/MIT>`_, eliminating its old proprietary licensing.
@@ -565,7 +621,7 @@
       -  Changed the library constructor to try to create the **log_path** directory if it doesn't exist.
       -  Added support for microseconds ("u" date format character) in ``$config['log_date_format']``.
 
-   -  Added `compatibility layers <general/compatibility_functions>` for:
+   -  Added :doc:`compatibility layers <general/compatibility_functions>` for:
 
       - `Multibyte String <http://php.net/mbstring>`_ (limited support).
       - `Hash <http://php.net/hash>`_ (``hash_equals()``, ``hash_pbkdf2()``).
@@ -579,7 +635,7 @@
 
 
 Bug fixes for 3.0
-------------------
+-----------------
 
 -  Fixed a bug where ``unlink()`` raised an error if cache file did not exist when you try to delete it.
 -  Fixed a bug (#181) - a typo in the form validation language file.
diff --git a/user_guide_src/source/conf.py b/user_guide_src/source/conf.py
index d65fe0d..1704654 100644
--- a/user_guide_src/source/conf.py
+++ b/user_guide_src/source/conf.py
@@ -48,9 +48,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '3.0'
+version = '3.0.1'
 # The full version, including alpha/beta/rc tags.
-release = '3.0-dev'
+release = '3.0.0-dev'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/user_guide_src/source/database/forge.rst b/user_guide_src/source/database/forge.rst
index 89fac02..646e3a5 100644
--- a/user_guide_src/source/database/forge.rst
+++ b/user_guide_src/source/database/forge.rst
@@ -143,13 +143,15 @@
 	$this->dbforge->add_field("label varchar(100) NOT NULL DEFAULT 'default label'");
 
 
+.. note:: Passing raw strings as fields cannot be followed by ``add_key()`` calls on those fields.
+
 .. note:: Multiple calls to add_field() are cumulative.
 
 Creating an id field
 --------------------
 
 There is a special exception for creating id fields. A field with type
-id will automatically be assinged as an INT(9) auto_incrementing
+id will automatically be assigned as an INT(9) auto_incrementing
 Primary Key.
 
 ::
@@ -227,7 +229,7 @@
 	$this->dbforge->drop_table('table_name');
 
 	// Produces: DROP TABLE IF EXISTS table_name
-	$this->dbforge->drop_table('table_name');
+	$this->dbforge->drop_table('table_name',TRUE);
 
 
 Renaming a table
@@ -405,4 +407,4 @@
 		:returns:	TRUE on success, FALSE on failure
 		:rtype:	bool
 
-		Renames a table. Usage:  See `Renaming a table`_.
\ No newline at end of file
+		Renames a table. Usage:  See `Renaming a table`_.
diff --git a/user_guide_src/source/database/utilities.rst b/user_guide_src/source/database/utilities.rst
index cc4aeb0..2fa5239 100644
--- a/user_guide_src/source/database/utilities.rst
+++ b/user_guide_src/source/database/utilities.rst
@@ -18,7 +18,7 @@
 
 Load the Utility Class as follows::
 
-	$this->load->dbutil()
+	$this->load->dbutil();
 
 You can also pass another database object to the DB Utility loader, in case
 the database you want to manage isn't the default one::
@@ -35,7 +35,7 @@
 Once initialized you will access the methods using the ``$this->dbutil``
 object::
 
-	$this->dbutil->some_method()
+	$this->dbutil->some_method();
 
 ****************************
 Using the Database Utilities
@@ -180,7 +180,7 @@
 
 .. note:: For Interbase/Firebird databases, the backup file name is the only parameter.
 
-		Eg. $this->dbutil->backup('db_backup_filename');
+		$this->dbutil->backup('db_backup_filename');
 
 .. note:: Due to the limited execution time and memory available to PHP,
 	backing up very large databases may not be possible. If your database is
@@ -197,7 +197,7 @@
 	$this->load->dbutil();
 
 	// Backup your entire database and assign it to a variable
-	$backup =& $this->dbutil->backup();
+	$backup = $this->dbutil->backup();
 
 	// Load the file helper and write the file to your server
 	$this->load->helper('file');
diff --git a/user_guide_src/source/general/controllers.rst b/user_guide_src/source/general/controllers.rst
index bc8319d..7ab5a7f 100644
--- a/user_guide_src/source/general/controllers.rst
+++ b/user_guide_src/source/general/controllers.rst
@@ -138,7 +138,7 @@
 To specify a default controller, open your **application/config/routes.php**
 file and set this variable::
 
-	$route['default_controller'] = 'Blog';
+	$route['default_controller'] = 'blog';
 
 Where Blog is the name of the controller class you want used. If you now
 load your main index.php file without specifying any URI segments you'll
diff --git a/user_guide_src/source/general/environments.rst b/user_guide_src/source/general/environments.rst
index f5a4f61..7f030b6 100644
--- a/user_guide_src/source/general/environments.rst
+++ b/user_guide_src/source/general/environments.rst
@@ -48,5 +48,5 @@
 Optionally, you can have CodeIgniter load environment-specific
 configuration files. This may be useful for managing things like
 differing API keys across multiple environments. This is described in
-more detail in the environment section of the :doc:`Config
-Class <../libraries/config#environments>`_ documentation.
\ No newline at end of file
+more detail in the environment section of the :doc:`Config Class
+<../libraries/config>`_ documentation.
\ No newline at end of file
diff --git a/user_guide_src/source/general/reserved_names.rst b/user_guide_src/source/general/reserved_names.rst
index a7b0c34..5d745cb 100644
--- a/user_guide_src/source/general/reserved_names.rst
+++ b/user_guide_src/source/general/reserved_names.rst
@@ -75,6 +75,7 @@
 -  FOPEN_READ_WRITE_CREATE
 -  FOPEN_WRITE_CREATE_STRICT
 -  FOPEN_READ_WRITE_CREATE_STRICT
+-  SHOW_DEBUG_BACKTRACE
 -  EXIT_SUCCESS
 -  EXIT_ERROR
 -  EXIT_CONFIG
diff --git a/user_guide_src/source/general/security.rst b/user_guide_src/source/general/security.rst
index efc821f..d4120d1 100644
--- a/user_guide_src/source/general/security.rst
+++ b/user_guide_src/source/general/security.rst
@@ -61,7 +61,7 @@
 step approach:
 
 #. Validate the data to ensure it conforms to the correct type, length,
-   size, etc. (sometimes this step can replace step one)
+   size, etc.
 #. Filter the data as if it were tainted.
 #. Escape the data before submitting it into your database or outputting
    it to a browser.
@@ -143,11 +143,15 @@
    feature, just randomly generate a new, one-time (this is also important)
    password and send that instead.
 
--  DO NOT put artificial limits on your users' passwords.
+-  DO NOT put unnecessary limits on your users' passwords.
 
-   There's no point in forcing a rule that a password can only be up to
-   a number of characters, or that it can't contain a certain set of
-   special characters.
+   If you're using a hashing algorithm other than BCrypt (which has a limit
+   of 72 characters), you should set a relatively high limit on password
+   lengths in order to mitigate DoS attacks - say, 1024 characters.
+
+   Other than that however, there's no point in forcing a rule that a
+   password can only be up to a number of characters, or that it can't
+   contain a certain set of special characters.
 
    Not only does this **reduce** security instead of improving it, but
    there's literally no reason to do it. No technical limitations and
@@ -195,4 +199,4 @@
 CodeIgniter will have an index.html file in all of its
 directories in an attempt to hide some of this data, but have
 it in mind that this is not enough to prevent a serious
-attacker.
\ No newline at end of file
+attacker.
diff --git a/user_guide_src/source/helpers/date_helper.rst b/user_guide_src/source/helpers/date_helper.rst
index e0f9f00..a85da26 100644
--- a/user_guide_src/source/helpers/date_helper.rst
+++ b/user_guide_src/source/helpers/date_helper.rst
@@ -400,7 +400,7 @@
 UM95            (UTC - 09:30) Marquesas Islands
 UM9             (UTC - 09:00) Alaska Standard Time, Gambier Islands
 UM8             (UTC - 08:00) Pacific Standard Time, Clipperton Island
-UM7             (UTC - 11:00) Mountain Standard Time
+UM7             (UTC - 07:00) Mountain Standard Time
 UM6             (UTC - 06:00) Central Standard Time
 UM5             (UTC - 05:00) Eastern Standard Time, Western Caribbean
 UM45            (UTC - 04:30) Venezuelan Standard Time
diff --git a/user_guide_src/source/helpers/form_helper.rst b/user_guide_src/source/helpers/form_helper.rst
index 9ddca89..6317f08 100644
--- a/user_guide_src/source/helpers/form_helper.rst
+++ b/user_guide_src/source/helpers/form_helper.rst
@@ -191,7 +191,7 @@
 
 	:param	array	$data: Field attributes data
 	:param	string	$value: Field value
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML text input field tag
 	:rtype:	string
 
@@ -226,11 +226,16 @@
 		$js = 'onClick="some_function()"';
 		echo form_input('username', 'johndoe', $js);
 
+	Or you can pass it as an array::
+
+		$js = array('onClick' => 'some_function();');
+		echo form_input('username', 'johndoe', $js);
+
 .. php:function:: form_password([$data = ''[, $value = ''[, $extra = '']]])
 
 	:param	array	$data: Field attributes data
 	:param	string	$value: Field value
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML password input field tag
 	:rtype:	string
 
@@ -242,7 +247,7 @@
 
 	:param	array	$data: Field attributes data
 	:param	string	$value: Field value
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML file upload input field tag
 	:rtype:	string
 
@@ -255,7 +260,7 @@
 
 	:param	array	$data: Field attributes data
 	:param	string	$value: Field value
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML textarea tag
 	:rtype:	string
 
@@ -270,7 +275,7 @@
 	:param	string	$name: Field name
 	:param	array	$options: An associative array of options to be listed
 	:param	array	$selected: List of fields to mark with the *selected* attribute
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML dropdown select field tag
 	:rtype:	string
 
@@ -324,6 +329,14 @@
 		$js = 'id="shirts" onChange="some_function();"';
 		echo form_dropdown('shirts', $options, 'large', $js);
 
+	Or you can pass it as an array::
+
+		$js = array(
+			'id'       => 'shirts',
+			'onChange' => 'some_function();'
+		);
+		echo form_dropdown('shirts', $options, 'large', $js);
+
 	If the array passed as ``$options`` is a multidimensional array, then
 	``form_dropdown()`` will produce an <optgroup> with the array key as the
 	label.
@@ -334,7 +347,7 @@
 	:param	string	$name: Field name
 	:param	array	$options: An associative array of options to be listed
 	:param	array	$selected: List of fields to mark with the *selected* attribute
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML dropdown multiselect field tag
 	:rtype:	string
 
@@ -417,7 +430,7 @@
 	:param	array	$data: Field attributes data
 	:param	string	$value: Field value
 	:param	bool	$checked: Whether to mark the checkbox as being *checked*
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML checkbox input tag
 	:rtype:	string
 
@@ -450,13 +463,18 @@
 		$js = 'onClick="some_function()"';
 		echo form_checkbox('newsletter', 'accept', TRUE, $js)
 
+	Or you can pass it as an array::
+
+		$js = array('onClick' => 'some_function();');
+		echo form_checkbox('newsletter', 'accept', TRUE, $js)
+
 
 .. php:function:: form_radio([$data = ''[, $value = ''[, $checked = FALSE[, $extra = '']]]])
 
 	:param	array	$data: Field attributes data
 	:param	string	$value: Field value
 	:param	bool	$checked: Whether to mark the radio button as being *checked*
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML radio input tag
 	:rtype:	string
 
@@ -495,7 +513,7 @@
 
 	:param	string	$data: Button name
 	:param	string	$value: Button value
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML input submit tag
 	:rtype:	string
 
@@ -513,7 +531,7 @@
 
 	:param	string	$data: Button name
 	:param	string	$value: Button value
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML input reset button tag
 	:rtype:	string
 
@@ -525,7 +543,7 @@
 
 	:param	string	$data: Button name
 	:param	string	$content: Button label
-	:param	string	$extra: Extra attributes to be added to the tag *as is*
+	:param	mixed	$extra: Extra attributes to be added to the tag either as an array or a literal string
 	:returns:	An HTML button tag
 	:rtype:	string
 
diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst
index 7e34797..de816b6 100644
--- a/user_guide_src/source/installation/upgrade_300.rst
+++ b/user_guide_src/source/installation/upgrade_300.rst
@@ -2,17 +2,19 @@
 Upgrading from 2.2.x to 3.0.0
 #############################
 
-.. note:: These upgrade notes are for a version that is yet to be released.
-
 Before performing an update you should take your site offline by replacing the index.php file with a static one.
 
 *************************************
 Step 1: Update your CodeIgniter files
 *************************************
 
-Replace all files and directories in your "system" folder and replace
-your index.php file. If any modifications were made to your index.php
-they will need to be made fresh in this new one.
+**Replace** all files and directories in your *system/* directory and
+replace your index.php file. If any modifications were made to your
+index.php they will need to be made fresh in this new one.
+
+.. important:: You have to delete the old *system/* directory first and
+	then put the new one in its place. A simple copy-paste may cause
+	issues.
 
 .. note:: If you have any custom developed files in these folders please
 	make copies of them first.
@@ -36,12 +38,12 @@
 The same goes for driver libraries and extensions and/or overrides of CodeIgniter's
 own libraries and core classes.
 
-	application/libraries/MY_email.php
+	application/libraries/MY_email.php  
 	application/core/MY_log.php
 
 The above files should respectively be renamed to the following:
 
-	application/libraries/MY_Email.php
+	application/libraries/MY_Email.php  
 	application/core/MY_Log.php
 
 Controllers:
@@ -78,9 +80,9 @@
 
 The Log Class is considered as a "core" class and is now located in the
 **system/core/** directory. Therefore, in order for your Log class overrides
-or extensions to work, you need to move them to **application/core/**::
+or extensions to work, you need to move them to **application/core/**:
 
-	application/libraries/Log.php -> application/core/Log.php
+	application/libraries/Log.php -> application/core/Log.php  
 	application/libraries/MY_Log.php -> application/core/MY_Log.php
 
 *****************************************
diff --git a/user_guide_src/source/libraries/caching.rst b/user_guide_src/source/libraries/caching.rst
index f54de5f..a7081ec 100644
--- a/user_guide_src/source/libraries/caching.rst
+++ b/user_guide_src/source/libraries/caching.rst
@@ -18,7 +18,7 @@
 Example Usage
 *************
 
-The following example will load the cache driver, specify `APC <#apc>`_
+The following example will load the cache driver, specify `APC <#alternative-php-cache-apc-caching>`_
 as the driver to use, and fall back to file-based caching if APC is not
 available in the hosting environment.
 
@@ -66,7 +66,7 @@
 		hosting environment.
 		::
 
-			if ($this->cache->apc->is_supported()
+			if ($this->cache->apc->is_supported())
 			{
 				if ($data = $this->cache->apc->get('my_cache'))
 				{
diff --git a/user_guide_src/source/libraries/config.rst b/user_guide_src/source/libraries/config.rst
index 3138e34..a45cacd 100644
--- a/user_guide_src/source/libraries/config.rst
+++ b/user_guide_src/source/libraries/config.rst
@@ -92,9 +92,9 @@
 
 To retrieve an item from your config file, use the following function::
 
-	$this->config->item('item name');
+	$this->config->item('item_name');
 
-Where item name is the $config array index you want to retrieve. For
+Where item_name is the $config array index you want to retrieve. For
 example, to fetch your language choice you'll do this::
 
 	$lang = $this->config->item('language');
diff --git a/user_guide_src/source/libraries/email.rst b/user_guide_src/source/libraries/email.rst
index 54fb53f..eadfcfd 100644
--- a/user_guide_src/source/libraries/email.rst
+++ b/user_guide_src/source/libraries/email.rst
@@ -373,7 +373,7 @@
 			foreach ($list as $address)
 			{
 				$this->email->to($address);
-				$cid = $this->email->attach_cid($filename);
+				$cid = $this->email->attachment_cid($filename);
 				$this->email->message('<img src='cid:". $cid ."' alt="photo1" />');
 				$this->email->send();
 			}
diff --git a/user_guide_src/source/libraries/file_uploading.rst b/user_guide_src/source/libraries/file_uploading.rst
index ea2fef7..6d2106b 100644
--- a/user_guide_src/source/libraries/file_uploading.rst
+++ b/user_guide_src/source/libraries/file_uploading.rst
@@ -118,7 +118,7 @@
 
 			$this->load->library('upload', $config);
 
-			if ( ! $this->upload->do_upload())
+			if ( ! $this->upload->do_upload('userfile'))
 			{
 				$error = array('error' => $this->upload->display_errors());
 
@@ -352,4 +352,4 @@
 		image_height     Image height
 		image_type       Image type (usually the file name extension without the period)
 		image_size_str   A string containing the width and height (useful to put into an image tag)
-		================ ====================================================================================================
\ No newline at end of file
+		================ ====================================================================================================
diff --git a/user_guide_src/source/libraries/form_validation.rst b/user_guide_src/source/libraries/form_validation.rst
index da43a4b..fa50c6d 100644
--- a/user_guide_src/source/libraries/form_validation.rst
+++ b/user_guide_src/source/libraries/form_validation.rst
@@ -327,15 +327,15 @@
 rules like this::
 
 	$this->form_validation->set_rules('username', 'Username', 'trim|required|min_length[5]|max_length[12]');
-	$this->form_validation->set_rules('password', 'Password', 'trim|required|md5');
+	$this->form_validation->set_rules('password', 'Password', 'trim|required|min_length[8]');
 	$this->form_validation->set_rules('passconf', 'Password Confirmation', 'trim|required|matches[password]');
 	$this->form_validation->set_rules('email', 'Email', 'trim|required|valid_email');
 
 In the above example, we are "trimming" the fields, checking for length
-where necessary and converting the password to MD5.
+where necessary and making sure that both password fields match.
 
 **Any native PHP function that accepts one parameter can be used as a
-rule, like htmlspecialchars, trim, md5, etc.**
+rule, like ``htmlspecialchars()``, ``trim()``, etc.**
 
 .. note:: You will generally want to use the prepping functions
 	**after** the validation rules so if there is an error, the
@@ -946,6 +946,7 @@
 ========================= ========== ============================================================================================= =======================
 **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]
+**regex_match**           Yes        Returns FALSE if the form element does not match the regular expression.                      regex_match[/regex/]
 **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
diff --git a/user_guide_src/source/libraries/image_lib.rst b/user_guide_src/source/libraries/image_lib.rst
index ae2c847..e5f7c00 100644
--- a/user_guide_src/source/libraries/image_lib.rst
+++ b/user_guide_src/source/libraries/image_lib.rst
@@ -89,7 +89,7 @@
 
 	echo $this->image_lib->display_errors();
 
-A good practice is use the processing function conditionally, showing an
+A good practice is to use the processing function conditionally, showing an
 error upon failure, like this::
 
 	if ( ! $this->image_lib->resize())
@@ -187,7 +187,7 @@
 
 There are two types of watermarking that you can use:
 
--  **Text**: The watermark message will be generating using text, either
+-  **Text**: The watermark message will be generated using text, either
    with a True Type font that you specify, or using the native text
    output that the GD library supports. If you use the True Type version
    your GD installation must be compiled with True Type support (most
@@ -231,7 +231,7 @@
 Watermarking Preferences
 ========================
 
-This table shown the preferences that are available for both types of
+This table shows the preferences that are available for both types of
 watermarking (text or overlay)
 
 ======================= =================== ======================= ==========================================================================
@@ -264,7 +264,7 @@
 Text Preferences
 ----------------
 
-This table shown the preferences that are available for the text type of
+This table shows the preferences that are available for the text type of
 watermarking.
 
 ======================= =================== =================== ==========================================================================
@@ -289,7 +289,7 @@
 Overlay Preferences
 -------------------
 
-This table shown the preferences that are available for the overlay type
+This table shows the preferences that are available for the overlay type
 of watermarking.
 
 ======================= =================== =================== ==========================================================================
diff --git a/user_guide_src/source/libraries/migration.rst b/user_guide_src/source/libraries/migration.rst
index 25be0c9..97c7230 100644
--- a/user_guide_src/source/libraries/migration.rst
+++ b/user_guide_src/source/libraries/migration.rst
@@ -88,7 +88,7 @@
 		}
 	}
 
-Then in **application/config/migration.php** set ``$config['migration_version'] = 1;``.
+Then in **application/config/migration.php** set ``$config['migration_version'] = 20121031100537;``.
 
 *************
 Usage Example
@@ -164,7 +164,7 @@
 
 	.. php:method:: latest()
 
-		:returns:	TRUE if no migrations are found, current version string on success, FALSE on failure
+		:returns:	Current version string on success, FALSE on failure
 		:rtype:	mixed
 
 		This works much the same way as ``current()`` but instead of looking for 
@@ -181,4 +181,4 @@
 		specific versions. It works just like ``current()`` but ignores ``$config['migration_version']``.
 		::
 
-			$this->migration->version(5);
\ No newline at end of file
+			$this->migration->version(5);
diff --git a/user_guide_src/source/libraries/security.rst b/user_guide_src/source/libraries/security.rst
index ac56fc5..305a8e5 100644
--- a/user_guide_src/source/libraries/security.rst
+++ b/user_guide_src/source/libraries/security.rst
@@ -35,12 +35,6 @@
 
 	$data = $this->security->xss_clean($data);
 
-If you want the filter to run automatically every time it encounters
-POST or COOKIE data you can enable it by opening your
-application/config/config.php file and setting this::
-
-	$config['global_xss_filtering'] = TRUE;
-
 An optional second parameter, *is_image*, allows this function to be used
 to test images for potential XSS attacks, useful for file upload
 security. When this second parameter is set to TRUE, instead of
@@ -88,7 +82,7 @@
 
 ::
 
-	$config['csrf_regeneration'] = TRUE;
+	$config['csrf_regenerate'] = TRUE;
 
 Select URIs can be whitelisted from csrf protection (for example API
 endpoints expecting externally POSTed content). You can add these URIs
diff --git a/user_guide_src/source/libraries/unit_testing.rst b/user_guide_src/source/libraries/unit_testing.rst
index 026781c..57934cb 100644
--- a/user_guide_src/source/libraries/unit_testing.rst
+++ b/user_guide_src/source/libraries/unit_testing.rst
@@ -76,6 +76,7 @@
 -  is_double
 -  is_array
 -  is_null
+-  is_resource
 
 Generating Reports
 ==================
diff --git a/user_guide_src/source/overview/at_a_glance.rst b/user_guide_src/source/overview/at_a_glance.rst
index facbeda..ce195c2 100644
--- a/user_guide_src/source/overview/at_a_glance.rst
+++ b/user_guide_src/source/overview/at_a_glance.rst
@@ -16,8 +16,8 @@
 CodeIgniter is Free
 ===================
 
-CodeIgniter is licensed under an Apache/BSD-style open source license so
-you can use it however you please. For more information please read the
+CodeIgniter is licensed under the MIT license so you can use it however
+you please. For more information please read the
 :doc:`license agreement <../license>`.
 
 CodeIgniter is Light Weight
diff --git a/user_guide_src/source/overview/features.rst b/user_guide_src/source/overview/features.rst
index b230be9..f62546b 100644
--- a/user_guide_src/source/overview/features.rst
+++ b/user_guide_src/source/overview/features.rst
@@ -8,8 +8,8 @@
 anything about the quality of the code, or the performance, or the
 attention to detail, or security practices. The only way to really judge
 an app is to try it and get to know the code.
-:doc:`Installing <../installation/>`_ CodeIgniter is child's play so we
-encourage you to do just that. In the mean time here's a list of
+:doc:`Installing <../installation/index>` CodeIgniter is child's play so
+we encourage you to do just that. In the mean time here's a list of
 CodeIgniter's main features.
 
 -  Model-View-Controller Based System
diff --git a/user_guide_src/source/tutorial/create_news_items.rst b/user_guide_src/source/tutorial/create_news_items.rst
index 71d2080..5c52704 100644
--- a/user_guide_src/source/tutorial/create_news_items.rst
+++ b/user_guide_src/source/tutorial/create_news_items.rst
@@ -18,11 +18,11 @@
 
 ::
 
-    <h2><?php echo $title ?></h2>
+    <h2><?php echo $title; ?></h2>
 
     <?php echo validation_errors(); ?>
 
-    <?php echo form_open('news/create') ?>
+    <?php echo form_open('news/create'); ?>
 
         <label for="title">Title</label> 
         <input type="input" name="title" /><br />
diff --git a/user_guide_src/source/tutorial/news_section.rst b/user_guide_src/source/tutorial/news_section.rst
index d8ebac4..286d620 100644
--- a/user_guide_src/source/tutorial/news_section.rst
+++ b/user_guide_src/source/tutorial/news_section.rst
@@ -15,10 +15,9 @@
 are the place where you retrieve, insert, and update information in your
 database or other data stores. They represent your data.
 
-Open up the application/models directory and create a new file called
-News_model.php and add the following code. Make sure you've configured
-your database properly as described
-:doc:`here <../database/configuration>`.
+Open up the *application/models/* directory and create a new file called
+*News_model.php* and add the following code. Make sure you've configured
+your database properly as described :doc:`here <../database/configuration>`.
 
 ::
 
@@ -37,8 +36,8 @@
 ``$this->db`` object.
 
 Before querying the database, a database schema has to be created.
-Connect to your database and run the SQL command below. Also add some
-seed records.
+Connect to your database and run the SQL command below (MySQL).
+Also add some seed records.
 
 ::
 
@@ -75,7 +74,7 @@
 
 With this code you can perform two different queries. You can get all
 news records, or get a news item by its `slug <#>`_. You might have
-noticed that the $slug variable wasn't sanitized before running the
+noticed that the ``$slug`` variable wasn't sanitized before running the
 query; :doc:`Query Builder <../database/query_builder>` does this for you.
 
 Display the news
@@ -83,9 +82,9 @@
 
 Now that the queries are written, the model should be tied to the views
 that are going to display the news items to the user. This could be done
-in our pages controller created earlier, but for the sake of clarity, a
-new "news" controller is defined. Create the new controller at
-application/controllers/News.php.
+in our ``Pages`` controller created earlier, but for the sake of clarity,
+a new ``News`` controller is defined. Create the new controller at
+*application/controllers/News.php*.
 
 ::
 
@@ -96,6 +95,7 @@
 		{
 			parent::__construct();
 			$this->load->model('news_model');
+			$this->load->helper('url_helper');
 		}
 
 		public function index()
@@ -113,11 +113,13 @@
 created earlier. First, the ``__construct()`` method: it calls the
 constructor of its parent class (``CI_Controller``) and loads the model,
 so it can be used in all other methods in this controller.
+It also loads a collection of :doc:`URL Helper <../helpers/url_helper>`
+functions, because we'll use one of them in a view later.
 
-Next, there are two methods to view all news items and one for a
-specific news item. You can see that the $slug variable is passed to the
-model's method in the second method. The model is using this slug to
-identify the news item to be returned.
+Next, there are two methods to view all news items and one for a specific
+news item. You can see that the ``$slug`` variable is passed to the model's
+method in the second method. The model is using this slug to identify the
+news item to be returned.
 
 Now the data is retrieved by the controller through our model, but
 nothing is displayed yet. The next thing to do is passing this data to
@@ -136,35 +138,35 @@
 	}
 
 The code above gets all news records from the model and assigns it to a
-variable. The value for the title is also assigned to the $data['title']
+variable. The value for the title is also assigned to the ``$data['title']``
 element and all data is passed to the views. You now need to create a
-view to render the news items. Create application/views/news/index.php
+view to render the news items. Create *application/views/news/index.php*
 and add the next piece of code.
 
 ::
 
-	<h2><?php echo $title ?></h2>
+	<h2><?php echo $title; ?></h2>
 	
 	<?php foreach ($news as $news_item): ?>
 
-		<h3><?php echo $news_item['title'] ?></h3>
+		<h3><?php echo $news_item['title']; ?></h3>
 		<div class="main">
-			<?php echo $news_item['text'] ?>
+			<?php echo $news_item['text']; ?>
 		</div>
-		<p><a href="<?php echo $news_item['slug'] ?>">View article</a></p>
+		<p><a href="<?php echo site_url('news/'.$news_item['slug']); ?>">View article</a></p>
 
-	<?php endforeach ?>
+	<?php endforeach; ?>
 
 Here, each news item is looped and displayed to the user. You can see we
-wrote our template in PHP mixed with HTML. If you prefer to use a
-template language, you can use CodeIgniter's :doc:`Template
+wrote our template in PHP mixed with HTML. If you prefer to use a template
+language, you can use CodeIgniter's :doc:`Template
 Parser <../libraries/parser>` class or a third party parser.
 
 The news overview page is now done, but a page to display individual
 news items is still absent. The model created earlier is made in such
 way that it can easily be used for this functionality. You only need to
 add some code to the controller and create a new view. Go back to the
-news controller and update ``view()`` with the following:
+``News`` controller and update ``view()`` with the following:
 
 ::
 
@@ -198,12 +200,12 @@
 Routing
 -------
 
-Because of the wildcard routing rule created earlier, you need an
-extra route to view the controller that you just made. Modify your
-routing file (application/config/routes.php) so it looks as follows.
-This makes sure the requests reaches the news controller instead of
-going directly to the pages controller. The first line routes URI's with
-a slug to the view method in the news controller.
+Because of the wildcard routing rule created earlier, you need an extra
+route to view the controller that you just made. Modify your routing file
+(*application/config/routes.php*) so it looks as follows.
+This makes sure the requests reaches the ``News`` controller instead of
+going directly to the ``Pages`` controller. The first line routes URI's
+with a slug to the ``view()`` method in the ``News`` controller.
 
 ::
 
diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst
index 0c75d5a..6662147 100644
--- a/user_guide_src/source/tutorial/static_pages.rst
+++ b/user_guide_src/source/tutorial/static_pages.rst
@@ -12,14 +12,14 @@
 
 For example, when a call is made to:
 
-	http&#58;//example.com/news/latest/10
+	http://example.com/news/latest/10
 
 We might imagine that there is a controller named "news". The method
 being called on news would be "latest". The news method's job could be to
 grab 10 news items, and render them on the page. Very often in MVC,
 you'll see URL patterns that match:
 
-	http&#58;//example.com/[controller-class]/[controller-method]/[arguments]
+	http://example.com/[controller-class]/[controller-method]/[arguments]
 
 As URL schemes become more complex, this may change. But for now, this
 is all we will need to know.
@@ -37,24 +37,24 @@
 	        }
 	}
 
-You have created a class named "pages", with a view method that accepts
-one argument named $page. The pages class is extending the
-CI_Controller class. This means that the new pages class can access the
-methods and variables defined in the CI_Controller class
-(system/core/Controller.php).
+You have created a class named ``Pages``, with a view method that accepts
+one argument named ``$page``. The ``Pages`` class is extending the
+``CI_Controller`` class. This means that the new pages class can access the
+methods and variables defined in the ``CI_Controller`` class
+(*system/core/Controller.php*).
 
 The **controller is what will become the center of every request** to
 your web application. In very technical CodeIgniter discussions, it may
 be referred to as the *super object*. Like any php class, you refer to
-it within your controllers as $this. Referring to $this is how you will
-load libraries, views, and generally command the framework.
+it within your controllers as ``$this``. Referring to ``$this`` is how
+you will load libraries, views, and generally command the framework.
 
 Now you've created your first method, it's time to make some basic page
 templates. We will be creating two "views" (page templates) that act as
 our page footer and header.
 
-Create the header at application/views/templates/header.php and add the
-following code.
+Create the header at *application/views/templates/header.php* and add
+the following code:
 
 ::
 
@@ -64,17 +64,17 @@
 		</head>
 		<body>
 
-			<h1><?php echo $title ?></h1>
+			<h1><?php echo $title; ?></h1>
 
 The header contains the basic HTML code that you'll want to display
 before loading the main view, together with a heading. It will also
-output the $title variable, which we'll define later in the controller.
-Now create a footer at application/views/templates/footer.php that
+output the ``$title`` variable, which we'll define later in the controller.
+Now, create a footer at *application/views/templates/footer.php* that
 includes the following code:
 
 ::
 
-			<em>&copy; 2014</em>
+			<em>&copy; 2015</em>
 		</body>
 	</html>
 
@@ -83,12 +83,12 @@
 
 Earlier you set up a controller with a ``view()`` method. The method
 accepts one parameter, which is the name of the page to be loaded. The
-static page templates will be located in the application/views/pages/
+static page templates will be located in the *application/views/pages/*
 directory.
 
-In that directory, create two files named home.php and about.php. Within
-those files, type some text − anything you'd like − and save them. If
-you like to be particularly un-original, try "Hello World!".
+In that directory, create two files named *home.php* and *about.php*.
+Within those files, type some text − anything you'd like − and save them.
+If you like to be particularly un-original, try "Hello World!".
 
 In order to load those pages, you'll have to check whether the requested
 page actually exists:
@@ -122,20 +122,21 @@
 In the header template, the ``$title`` variable was used to customize the
 page title. The value of title is defined in this method, but instead of
 assigning the value to a variable, it is assigned to the title element
-in the $data array.
+in the ``$data`` array.
 
 The last thing that has to be done is loading the views in the order
 they should be displayed. The second parameter in the ``view()`` method is
 used to pass values to the view. Each value in the ``$data`` array is
 assigned to a variable with the name of its key. So the value of
-``$data['title']`` in the controller is equivalent to $title in the view.
+``$data['title']`` in the controller is equivalent to ``$title`` in the
+view.
 
 Routing
 -------
 
 The controller is now functioning! Point your browser to
-[your-site-url]index.php/pages/view to see your page. When you visit
-index.php/pages/view/about you'll see the about page, again including
+``[your-site-url]index.php/pages/view`` to see your page. When you visit
+``index.php/pages/view/about`` you'll see the about page, again including
 the header and footer.
 
 Using custom routing rules, you have the power to map any URI to any
@@ -143,8 +144,8 @@
 ``http://example.com/[controller-class]/[controller-method]/[arguments]``
 
 Let's do that. Open the routing file located at
-application/config/routes.php and add the following two lines. Remove
-all other code that sets any element in the $route array.
+*application/config/routes.php* and add the following two lines.
+Remove all other code that sets any element in the ``$route`` array.
 
 ::
 
@@ -161,9 +162,9 @@
 More information about routing can be found in the URI Routing
 :doc:`documentation <../general/routing>`.
 
-Here, the second rule in the $routes array matches **any** request using
-the wildcard string (:any). and passes the parameter to the ``view()``
-method of the pages class.
+Here, the second rule in the ``$routes`` array matches **any** request
+using the wildcard string ``(:any)``. and passes the parameter to the
+``view()`` method of the ``Pages`` class.
 
-Now visit index.php/about. Did it get routed correctly to the ``view()``
+Now visit ``index.php/about``. Did it get routed correctly to the ``view()``
 method in the pages controller? Awesome!