Merge branch 'develop' of https://github.com/bcit-ci/CodeIgniter into fix/housekeeping
diff --git a/.travis.yml b/.travis.yml
index 4f56044..258ad76 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,7 @@
   - 5.4
   - 5.5
   - 5.6
+  - 7
   - hhvm
   
 env:
@@ -31,11 +32,14 @@
   allow_failures:
     - php: 5.2
     - php: hhvm
+    - php: 7
   exclude:
     - php: hhvm
       env: DB=pgsql
     - php: hhvm
       env: DB=pdo/pgsql
+    - php: 7
+      env: DB=mysql
     - php: 5.2
       env: DB=sqlite
     - php: 5.2
diff --git a/application/config/config.php b/application/config/config.php
index 7d5c24c..cc1307c 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -203,7 +203,7 @@
 |	3 = Informational Messages
 |	4 = All Messages
 |
-| You can also pass in a array with threshold levels to show individual error types
+| You can also pass an array with threshold levels to show individual error types
 |
 | 	array(2) = Debug Messages, without Error Messages
 |
@@ -404,6 +404,9 @@
 | Determines whether the XSS filter is always active when GET, POST or
 | COOKIE data is encountered
 |
+| WARNING: This feature is DEPRECATED and currently available only
+|          for backwards compatibility purposes!
+|
 */
 $config['global_xss_filtering'] = FALSE;
 
diff --git a/system/core/Common.php b/system/core/Common.php
index 7035c18..ee5a705 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -497,59 +497,58 @@
 			return;
 		}
 
-		$stati = array(
-			200	=> 'OK',
-			201	=> 'Created',
-			202	=> 'Accepted',
-			203	=> 'Non-Authoritative Information',
-			204	=> 'No Content',
-			205	=> 'Reset Content',
-			206	=> 'Partial Content',
-
-			300	=> 'Multiple Choices',
-			301	=> 'Moved Permanently',
-			302	=> 'Found',
-			303	=> 'See Other',
-			304	=> 'Not Modified',
-			305	=> 'Use Proxy',
-			307	=> 'Temporary Redirect',
-
-			400	=> 'Bad Request',
-			401	=> 'Unauthorized',
-			403	=> 'Forbidden',
-			404	=> 'Not Found',
-			405	=> 'Method Not Allowed',
-			406	=> 'Not Acceptable',
-			407	=> 'Proxy Authentication Required',
-			408	=> 'Request Timeout',
-			409	=> 'Conflict',
-			410	=> 'Gone',
-			411	=> 'Length Required',
-			412	=> 'Precondition Failed',
-			413	=> 'Request Entity Too Large',
-			414	=> 'Request-URI Too Long',
-			415	=> 'Unsupported Media Type',
-			416	=> 'Requested Range Not Satisfiable',
-			417	=> 'Expectation Failed',
-			422	=> 'Unprocessable Entity',
-
-			500	=> 'Internal Server Error',
-			501	=> 'Not Implemented',
-			502	=> 'Bad Gateway',
-			503	=> 'Service Unavailable',
-			504	=> 'Gateway Timeout',
-			505	=> 'HTTP Version Not Supported'
-		);
-
 		if (empty($code) OR ! is_numeric($code))
 		{
 			show_error('Status codes must be numeric', 500);
 		}
 
-		is_int($code) OR $code = (int) $code;
-
 		if (empty($text))
 		{
+			is_int($code) OR $code = (int) $code;
+			$stati = array(
+				200	=> 'OK',
+				201	=> 'Created',
+				202	=> 'Accepted',
+				203	=> 'Non-Authoritative Information',
+				204	=> 'No Content',
+				205	=> 'Reset Content',
+				206	=> 'Partial Content',
+
+				300	=> 'Multiple Choices',
+				301	=> 'Moved Permanently',
+				302	=> 'Found',
+				303	=> 'See Other',
+				304	=> 'Not Modified',
+				305	=> 'Use Proxy',
+				307	=> 'Temporary Redirect',
+
+				400	=> 'Bad Request',
+				401	=> 'Unauthorized',
+				403	=> 'Forbidden',
+				404	=> 'Not Found',
+				405	=> 'Method Not Allowed',
+				406	=> 'Not Acceptable',
+				407	=> 'Proxy Authentication Required',
+				408	=> 'Request Timeout',
+				409	=> 'Conflict',
+				410	=> 'Gone',
+				411	=> 'Length Required',
+				412	=> 'Precondition Failed',
+				413	=> 'Request Entity Too Large',
+				414	=> 'Request-URI Too Long',
+				415	=> 'Unsupported Media Type',
+				416	=> 'Requested Range Not Satisfiable',
+				417	=> 'Expectation Failed',
+				422	=> 'Unprocessable Entity',
+
+				500	=> 'Internal Server Error',
+				501	=> 'Not Implemented',
+				502	=> 'Bad Gateway',
+				503	=> 'Service Unavailable',
+				504	=> 'Gateway Timeout',
+				505	=> 'HTTP Version Not Supported'
+			);
+
 			if (isset($stati[$code]))
 			{
 				$text = $stati[$code];
diff --git a/system/core/Config.php b/system/core/Config.php
index a191a77..b9af8e3 100644
--- a/system/core/Config.php
+++ b/system/core/Config.php
@@ -126,7 +126,6 @@
 			foreach (array($file, ENVIRONMENT.'/'.$file) as $location)
 			{
 				$file_path = $path.'config/'.$location.'.php';
-
 				if (in_array($file_path, $this->is_loaded, TRUE))
 				{
 					return TRUE;
@@ -165,14 +164,13 @@
 				$loaded = TRUE;
 				log_message('debug', 'Config file loaded: '.$file_path);
 			}
-
-			if ($loaded === TRUE)
-			{
-				return TRUE;
-			}
 		}
 
-		if ($fail_gracefully === TRUE)
+		if ($loaded === TRUE)
+		{
+			return TRUE;
+		}
+		elseif ($fail_gracefully === TRUE)
 		{
 			return FALSE;
 		}
diff --git a/system/core/Input.php b/system/core/Input.php
index fae3b6c..6be4b9a 100644
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -55,7 +55,7 @@
 	 *
 	 * @var	string
 	 */
-	public $ip_address = FALSE;
+	protected $ip_address = FALSE;
 
 	/**
 	 * Allow GET array flag
@@ -104,14 +104,28 @@
 	protected $headers = array();
 
 	/**
-	 * Input stream data
+	 * Raw input stream data
+	 *
+	 * Holds a cache of php://input contents
+	 *
+	 * @var	string
+	 */
+	protected $_raw_input_stream;
+
+	/**
+	 * Parsed input stream data
 	 *
 	 * Parsed from php://input at runtime
 	 *
 	 * @see	CI_Input::input_stream()
 	 * @var	array
 	 */
-	protected $_input_stream = NULL;
+	protected $_input_stream;
+
+	protected $security;
+	protected $uni;
+
+	// --------------------------------------------------------------------
 
 	/**
 	 * Class constructor
@@ -313,7 +327,8 @@
 		// so we'll need to check if we have already done that first.
 		if ( ! is_array($this->_input_stream))
 		{
-			parse_str(file_get_contents('php://input'), $this->_input_stream);
+			// $this->raw_input_stream will trigger __get().
+			parse_str($this->raw_input_stream, $this->_input_stream);
 			is_array($this->_input_stream) OR $this->_input_stream = array();
 		}
 
@@ -846,4 +861,27 @@
 			: strtolower($this->server('REQUEST_METHOD'));
 	}
 
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Magic __get()
+	 *
+	 * Allows read access to protected properties
+	 *
+	 * @param	string	$name
+	 * @return	mixed
+	 */
+	public function __get($name)
+	{
+		if ($name === 'raw_input_stream')
+		{
+			isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input');
+			return $this->_raw_input_stream;
+		}
+		elseif ($name === 'ip_address')
+		{
+			return $this->ip_address;
+		}
+	}
+
 }
diff --git a/system/core/Log.php b/system/core/Log.php
index 8333162..e8cb401 100644
--- a/system/core/Log.php
+++ b/system/core/Log.php
@@ -70,13 +70,6 @@
 	protected $_threshold = 1;
 
 	/**
-	 * Highest level of logging
-	 *
-	 * @var int
-	 */
-	protected $_threshold_max = 0;
-
-	/**
 	 * Array of threshold levels to log
 	 *
 	 * @var array
@@ -139,7 +132,7 @@
 		}
 		elseif (is_array($config['log_threshold']))
 		{
-			$this->_threshold = $this->_threshold_max;
+			$this->_threshold = 0;
 			$this->_threshold_array = array_flip($config['log_threshold']);
 		}
 
diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php
index f2a41cc..5236556 100644
--- a/system/libraries/Cache/drivers/Cache_redis.php
+++ b/system/libraries/Cache/drivers/Cache_redis.php
@@ -243,15 +243,13 @@
 	 */
 	public function is_supported()
 	{
-		if (extension_loaded('redis'))
-		{
-			return $this->_setup_redis();
-		}
-		else
+		if ( ! extension_loaded('redis'))
 		{
 			log_message('debug', 'The Redis extension must be loaded to use Redis cache.');
 			return FALSE;
 		}
+
+		return $this->_setup_redis();
 	}
 
 	// ------------------------------------------------------------------------
diff --git a/system/libraries/Cache/drivers/Cache_wincache.php b/system/libraries/Cache/drivers/Cache_wincache.php
index 528b2b9..9cc6ff0 100644
--- a/system/libraries/Cache/drivers/Cache_wincache.php
+++ b/system/libraries/Cache/drivers/Cache_wincache.php
@@ -194,7 +194,7 @@
 	 */
 	public function is_supported()
 	{
-		if ( ! extension_loaded('wincache'))
+		if ( ! extension_loaded('wincache') OR ! ini_get('wincache.ucenabled'))
 		{
 			log_message('debug', 'The Wincache PHP extension must be loaded to use Wincache Cache.');
 			return FALSE;
diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php
index f496b4f..76c1cf3 100644
--- a/system/libraries/Session/drivers/Session_database_driver.php
+++ b/system/libraries/Session/drivers/Session_database_driver.php
@@ -319,7 +319,7 @@
 		if ($this->_platform === 'mysql')
 		{
 			$arg = $session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : '');
-			if ($this->_db->query("SELECT GET_LOCK('".$arg."', 10) AS ci_session_lock")->row()->ci_session_lock)
+			if ($this->_db->query("SELECT GET_LOCK('".$arg."', 300) AS ci_session_lock")->row()->ci_session_lock)
 			{
 				$this->_lock = $arg;
 				return TRUE;
diff --git a/system/libraries/Session/drivers/Session_files_driver.php b/system/libraries/Session/drivers/Session_files_driver.php
index 5852277..74528e9 100644
--- a/system/libraries/Session/drivers/Session_files_driver.php
+++ b/system/libraries/Session/drivers/Session_files_driver.php
@@ -299,7 +299,9 @@
 	{
 		if ($this->close())
 		{
-			return unlink($this->_file_path.$session_id) && $this->_cookie_destroy();
+			return file_exists($this->_file_path.$session_id)
+				? (unlink($this->_file_path.$session_id) && $this->_cookie_destroy())
+				: TRUE;
 		}
 		elseif ($this->_file_path !== NULL)
 		{
diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php
index f1a6e24..938a612 100644
--- a/system/libraries/Session/drivers/Session_memcached_driver.php
+++ b/system/libraries/Session/drivers/Session_memcached_driver.php
@@ -204,7 +204,7 @@
 
 		if (isset($this->_lock_key))
 		{
-			$this->_memcached->replace($this->_lock_key, time(), 5);
+			$this->_memcached->replace($this->_lock_key, time(), 300);
 			if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
 			{
 				if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
@@ -299,34 +299,21 @@
 	{
 		if (isset($this->_lock_key))
 		{
-			return $this->_memcached->replace($this->_lock_key, time(), 5);
+			return $this->_memcached->replace($this->_lock_key, time(), 300);
 		}
 
+		// 30 attempts to obtain a lock, in case another request already has it
 		$lock_key = $this->_key_prefix.$session_id.':lock';
-		if ( ! ($ts = $this->_memcached->get($lock_key)))
-		{
-			if ( ! $this->_memcached->set($lock_key, TRUE, 5))
-			{
-				log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
-				return FALSE;
-			}
-
-			$this->_lock_key = $lock_key;
-			$this->_lock = TRUE;
-			return TRUE;
-		}
-
-		// Another process has the lock, we'll try to wait for it to free itself ...
 		$attempt = 0;
-		while ($attempt++ < 5)
+		do
 		{
-			usleep(((time() - $ts) * 1000000) - 20000);
-			if (($ts = $this->_memcached->get($lock_key)) < time())
+			if ($this->_memcached->get($lock_key))
 			{
+				sleep(1);
 				continue;
 			}
 
-			if ( ! $this->_memcached->set($lock_key, time(), 5))
+			if ( ! $this->_memcached->set($lock_key, time(), 300))
 			{
 				log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
 				return FALSE;
@@ -335,8 +322,9 @@
 			$this->_lock_key = $lock_key;
 			break;
 		}
+		while ($attempt++ < 30);
 
-		if ($attempt === 5)
+		if ($attempt === 30)
 		{
 			log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.');
 			return FALSE;
diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php
index 1cc4d75..1ce101d 100644
--- a/system/libraries/Session/drivers/Session_redis_driver.php
+++ b/system/libraries/Session/drivers/Session_redis_driver.php
@@ -205,7 +205,7 @@
 
 		if (isset($this->_lock_key))
 		{
-			$this->_redis->setTimeout($this->_lock_key, 5);
+			$this->_redis->setTimeout($this->_lock_key, 300);
 			if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
 			{
 				if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
@@ -272,7 +272,7 @@
 	{
 		if (isset($this->_redis, $this->_lock_key))
 		{
-			if ($this->_redis->delete($this->_key_prefix.$session_id) !== 1)
+			if (($result = $this->_redis->delete($this->_key_prefix.$session_id)) !== 1)
 			{
 				log_message('debug', 'Session: Redis::delete() expected to return 1, got '.var_export($result, TRUE).' instead.');
 			}
@@ -313,40 +313,21 @@
 	{
 		if (isset($this->_lock_key))
 		{
-			return $this->_redis->setTimeout($this->_lock_key, 5);
+			return $this->_redis->setTimeout($this->_lock_key, 300);
 		}
 
+		// 30 attempts to obtain a lock, in case another request already has it
 		$lock_key = $this->_key_prefix.$session_id.':lock';
-		if (($ttl = $this->_redis->ttl($lock_key)) < 1)
-		{
-			if ( ! $this->_redis->setex($lock_key, 5, time()))
-			{
-				log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
-				return FALSE;
-			}
-
-			$this->_lock_key = $lock_key;
-
-			if ($ttl === -1)
-			{
-				log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.');
-			}
-
-			$this->_lock = TRUE;
-			return TRUE;
-		}
-
-		// Another process has the lock, we'll try to wait for it to free itself ...
 		$attempt = 0;
-		while ($attempt++ < 5)
+		do
 		{
-			usleep(($ttl * 1000000) - 20000);
 			if (($ttl = $this->_redis->ttl($lock_key)) > 0)
 			{
+				sleep(1);
 				continue;
 			}
 
-			if ( ! $this->_redis->setex($lock_key, 5, time()))
+			if ( ! $this->_redis->setex($lock_key, 300, time()))
 			{
 				log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
 				return FALSE;
@@ -355,12 +336,17 @@
 			$this->_lock_key = $lock_key;
 			break;
 		}
+		while ($attempt++ < 30);
 
-		if ($attempt === 5)
+		if ($attempt === 30)
 		{
-			log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.');
+			log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.');
 			return FALSE;
 		}
+		elseif ($ttl === -1)
+		{
+			log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.');
+		}
 
 		$this->_lock = TRUE;
 		return TRUE;
diff --git a/tests/codeigniter/core/Security_test.php b/tests/codeigniter/core/Security_test.php
index d967613..c96eecf 100644
--- a/tests/codeigniter/core/Security_test.php
+++ b/tests/codeigniter/core/Security_test.php
@@ -126,5 +126,36 @@
 
 		$this->assertEquals('foo', $safe_filename);
 	}
+        
+        // --------------------------------------------------------------------
 
+	public function test_strip_image_tags()
+	{
+                $imgtags = Array(
+                    '<img src="smiley.gif" alt="Smiley face" height="42" width="42">',
+                    '<img alt="Smiley face" height="42" width="42" src="smiley.gif">',
+                    '<img src="http://www.w3schools.com/images/w3schools_green.jpg">',
+                    '<img src="/img/sunset.gif" height="100%" width="100%">',
+                    '<img src="mdn-logo-sm.png" alt="MD Logo" srcset="mdn-logo-HD.png 2x, mdn-logo-small.png 15w, mdn-banner-HD.png 100w 2x" />',
+                    '<img sqrc="/img/sunset.gif" height="100%" width="100%">',
+                    '<img srqc="/img/sunset.gif" height="100%" width="100%">',
+                    '<img srcq="/img/sunset.gif" height="100%" width="100%">'
+                );
+                
+                $urls = Array(
+                    'smiley.gif',
+                    'smiley.gif',
+                    'http://www.w3schools.com/images/w3schools_green.jpg',
+                    '/img/sunset.gif',
+                    'mdn-logo-sm.png',
+                    '<img sqrc="/img/sunset.gif" height="100%" width="100%">',
+                    '<img srqc="/img/sunset.gif" height="100%" width="100%">',
+                    '<img srcq="/img/sunset.gif" height="100%" width="100%">'
+                );
+                
+                for($i = 0; $i < count($imgtags); $i++) 
+                {
+                    $this->assertEquals($urls[$i], $this->security->strip_image_tags($imgtags[$i]));
+                }
+	}
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/DB_driver_test.php b/tests/codeigniter/database/DB_driver_test.php
index c04c42b..26416d3 100644
--- a/tests/codeigniter/database/DB_driver_test.php
+++ b/tests/codeigniter/database/DB_driver_test.php
@@ -6,7 +6,7 @@
 	{
 		$config = Mock_Database_DB::config(DB_DRIVER);
 		sscanf(DB_DRIVER, '%[^/]/', $driver_name);
-		$driver = $this->$driver_name($config[DB_DRIVER]);
+		$driver = $this->{$driver_name}($config[DB_DRIVER]);
 
 		$this->assertTrue($driver->initialize());
 	}
diff --git a/tests/codeigniter/database/DB_test.php b/tests/codeigniter/database/DB_test.php
index d5a9369..dc4fae9 100644
--- a/tests/codeigniter/database/DB_test.php
+++ b/tests/codeigniter/database/DB_test.php
@@ -26,6 +26,14 @@
 	{
 		$config = Mock_Database_DB::config(DB_DRIVER);
 		$connection = new Mock_Database_DB($config);
+
+		// E_DEPRECATED notices thrown by mysql_connect(), mysql_pconnect()
+		// on PHP 5.5+ cause the tests to fail
+		if (DB_DRIVER === 'mysql' && version_compare(PHP_VERSION, '5.5', '>='))
+		{
+			error_reporting(E_ALL & ~E_DEPRECATED);
+		}
+
 		$db = Mock_Database_DB::DB($connection->set_dsn(DB_DRIVER), TRUE);
 
 		$this->assertTrue($db instanceof CI_DB);
diff --git a/tests/mocks/core/input.php b/tests/mocks/core/input.php
index 0d18738..40e2744 100644
--- a/tests/mocks/core/input.php
+++ b/tests/mocks/core/input.php
@@ -38,4 +38,12 @@
 		return FALSE;
 	}
 
+	public function __set($name, $value)
+	{
+		if ($name === 'ip_address')
+		{
+			$this->ip_address = $value;
+		}
+	}
+
 }
\ No newline at end of file
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 8f77f36..ef3d2af 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -480,6 +480,7 @@
 
    -  :doc:`Input Library <libraries/input>` changes include:
 
+      -  Deprecated the ``$config['global_xss_filtering']`` setting.
       -  Added ``method()`` to retrieve ``$_SERVER['REQUEST_METHOD']``.
       -  Added support for arrays and network addresses (e.g. 192.168.1.1/24) for use with the *proxy_ips* setting.
       -  Added method ``input_stream()`` to aid in using **php://input** stream data such as one passed via PUT, DELETE and PATCH requests.
@@ -493,6 +494,7 @@
       -  Added an option for ``_clean_input_keys()`` to return FALSE instead of terminating the whole script.
       -  Deprecated the ``is_cli_request()`` method, it is now an alias for the new :php:func:`is_cli()` common function.
       -  Added an ``$xss_clean`` parameter to method ``user_agent()`` and removed the ``$user_agent`` property.
+      -  Added property ``$raw_input_stream`` to access **php://input** data.
 
    -  :doc:`Common functions <general/common_functions>` changes include:
 
@@ -768,7 +770,7 @@
 -  Fixed a bug (#3161) - :doc:`Cache Library <libraries/caching>` methods `increment()`, `decrement()` didn't auto-create non-existent items when using redis and/or file storage.
 -  Fixed a bug (#3189) - :doc:`Parser Library <libraries/parser>` used double replacement on ``key->value`` pairs, exposing a potential template injection vulnerability.
 -  Fixed a bug (#3573) - :doc:`Email Library <libraries/email>` violated `RFC5321 <https://tools.ietf.org/rfc/rfc5321.txt>`_ by sending 'localhost.localdomain' as a hostname.
--  Fixed a bug (#3572) - :doc:`CI_Security::_remove_evil_attributes()` failed for large-sized inputs due to *pcre.backtrack_limit* and didn't properly match HTML tags.
+-  Fixed a bug (#3572) - ``CI_Security::_remove_evil_attributes()`` failed for large-sized inputs due to *pcre.backtrack_limit* and didn't properly match HTML tags.
 
 Version 2.2.1
 =============
diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst
index fa1e903..9b46947 100644
--- a/user_guide_src/source/database/query_builder.rst
+++ b/user_guide_src/source/database/query_builder.rst
@@ -1221,7 +1221,7 @@
 
 		:param	string	$key: The field to search
 		:param	array	$values: The values searched on
-		:param	boolean	$escape: Whether to escape values and identifiers
+		:param	boolean	$escape: Whether to escape identifiers
 		:returns:	DB_query_builder instance
 		:rtype:	object
 
@@ -1232,7 +1232,7 @@
 
 		:param	string	$key: The field to search
 		:param	array	$values: The values searched on
-		:param	boolean	$escape: Whether to escape values and identifiers
+		:param	boolean	$escape: Whether to escape identifiers
 		:returns:	DB_query_builder instance
 		:rtype:	object
 
@@ -1243,7 +1243,7 @@
 
 		:param	string	$key: Name of field to examine
 		:param	array	$values: Array of target values
-		:param	boolean	$escape: Whether to escape values and identifiers
+		:param	boolean	$escape: Whether to escape identifiers
 		:returns:	DB_query_builder instance
 		:rtype:	object
 
@@ -1254,7 +1254,7 @@
 
 		:param	string	$key: Name of field to examine
 		:param	array	$values: Array of target values
-		:param	boolean	$escape: Whether to escape values and identifiers
+		:param	boolean	$escape: Whether to escape identifiers
 		:returns:	DB_query_builder instance
 		:rtype:	object
 
diff --git a/user_guide_src/source/database/results.rst b/user_guide_src/source/database/results.rst
index a22c2e8..ac44566 100644
--- a/user_guide_src/source/database/results.rst
+++ b/user_guide_src/source/database/results.rst
@@ -102,7 +102,7 @@
 to instantiate the row with::
 
 	$query = $this->db->query("SELECT * FROM users LIMIT 1;");
-	$query->row(0, 'User');
+	$row = $query->row(0, 'User');
 	
 	echo $row->name; // access attributes
 	echo $row->reverse_name(); // or methods defined on the 'User' class
@@ -431,4 +431,4 @@
 		:rtype:	array
 
 		Returns an array containing the field names in the
-		result set.
\ No newline at end of file
+		result set.
diff --git a/user_guide_src/source/general/security.rst b/user_guide_src/source/general/security.rst
index 0c58f96..efc821f 100644
--- a/user_guide_src/source/general/security.rst
+++ b/user_guide_src/source/general/security.rst
@@ -133,6 +133,10 @@
    provides them for you as long as you're running at least PHP version
    5.3.7 (and if you don't meet that requirement - please, upgrade).
 
+   If you're one of the really unlucky people who can't even upgrade to a
+   more recent PHP version, use `hash_pbkdf() <http://php.net/hash_pbkdf2>`,
+   which we also provide in our compatibility layer.
+
 -  DO NOT ever display or send a password in plain-text format!
 
    Even to the password's owner, if you need a "Forgotten password"
diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst
index 73ed0f4..2f806cc 100644
--- a/user_guide_src/source/installation/upgrade_300.rst
+++ b/user_guide_src/source/installation/upgrade_300.rst
@@ -1,5 +1,5 @@
 #############################
-Upgrading from 2.2.1 to 3.0.0
+Upgrading from 2.2.x to 3.0.0
 #############################
 
 .. note:: These upgrade notes are for a version that is yet to be released.
@@ -551,6 +551,22 @@
 .. note:: This function is still available, but you're strongly encouraged to remove its usage sooner
 	rather than later.
 
+The $config['global_xss_filtering'] setting
+===========================================
+
+As already explained above, XSS filtering should not be done on input data,
+but on output instead. Therefore, the ``$config['global_xss_filtering']``,
+which automatically filters *input* data, is considered a bad practice and
+is now deprecated.
+
+Instead, you should manually escape any user-provided data via the
+:php:func:`xss_clean()` function when you need to output it, or use a
+library like `HTML Purifier <http://htmlpurifier.org/>`_ that does that
+for you.
+
+.. note:: The setting is still available, but you're strongly encouraged to
+	remove its usage sooner rather than later.
+
 File helper read_file()
 =======================
 
@@ -795,7 +811,7 @@
 	sooner rather than later.
 
 ***********************************************************
-Step 18: Check your usage of Text helper highlight_phrase()
+Step 20: Check your usage of Text helper highlight_phrase()
 ***********************************************************
 
 The default HTML tag used by :doc:`Text Helper <../helpers/text_helper>` function
diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst
index ab36e9b..89e90e7 100644
--- a/user_guide_src/source/installation/upgrading.rst
+++ b/user_guide_src/source/installation/upgrading.rst
@@ -8,7 +8,7 @@
 .. toctree::
 	:titlesonly:
 
-	Upgrading from 2.2.1 to 3.0.0 <upgrade_300>
+	Upgrading from 2.2.x to 3.0.0 <upgrade_300>
 	Upgrading from 2.2.0 to 2.2.1 <upgrade_221>
 	Upgrading from 2.1.4 to 2.2.0 <upgrade_220>
 	Upgrading from 2.1.3 to 2.1.4 <upgrade_214>
diff --git a/user_guide_src/source/libraries/input.rst b/user_guide_src/source/libraries/input.rst
index 967f69d..d9c6c2d 100644
--- a/user_guide_src/source/libraries/input.rst
+++ b/user_guide_src/source/libraries/input.rst
@@ -53,6 +53,10 @@
 Please refer to the :doc:`Security class <security>` documentation for
 information on using XSS Filtering in your application.
 
+.. important:: The 'global_xss_filtering' setting is DEPRECATED and kept
+	solely for backwards-compatibility purposes. XSS escaping should
+	be performed on *output*, not *input*!
+
 *******************
 Accessing form data
 *******************
@@ -91,8 +95,14 @@
 and access multiple variables without caring that you might only have
 one shot at all of the POST data.
 
-CodeIgniter will take care of that for you, and you can access data
-from the **php://input** stream at any time, just by calling the
+CodeIgniter will take care of that for you, and you can read the data
+from the **php://input** stream at any time, just by using the
+``$raw_input_stream`` property::
+
+	$this->input->raw_input_stream;
+
+Additionally if the input stream is form-encoded like $_POST you can 
+access its values by calling the
 ``input_stream()`` method::
 
 	$this->input->input_stream('key');
@@ -114,6 +124,12 @@
 
 .. php:class:: CI_Input
 
+	.. attribute:: $raw_input_stream
+		
+		Read only property that will return php://input data as is.
+		
+		The property can be read multiple times.
+
 	.. php:method:: post([$index = NULL[, $xss_clean = NULL]])
 
 		:param	mixed	$index: POST parameter name
diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst
index 5a1b905..51ecc03 100644
--- a/user_guide_src/source/libraries/sessions.rst
+++ b/user_guide_src/source/libraries/sessions.rst
@@ -632,8 +632,7 @@
 
 .. note:: Since Redis doesn't have a locking mechanism exposed, locks for
 	this driver are emulated by a separate value that is kept for up
-	to 5 seconds. You may experience issues if your page loads take
-	longer than that!
+	to 300 seconds.
 
 Redis is a storage engine typically used for caching and popular because
 of its high performance, which is also probably your reason to use the
@@ -670,8 +669,7 @@
 
 .. note:: Since Memcache doesn't have a locking mechanism exposed, locks
 	for this driver are emulated by a separate value that is kept for
-	up to 5 seconds. You may experience issues if your page loads take
-	longer than that!
+	up to 300 seconds.
 
 The 'memcached' driver is very similar to the 'redis' one in all of its
 properties, except perhaps for availability, because PHP's `Memcached
diff --git a/user_guide_src/source/tutorial/news_section.rst b/user_guide_src/source/tutorial/news_section.rst
index 80938de..f436b25 100644
--- a/user_guide_src/source/tutorial/news_section.rst
+++ b/user_guide_src/source/tutorial/news_section.rst
@@ -151,7 +151,7 @@
 		<div class="main">
 			<?php echo $news_item['text'] ?>
 		</div>
-		<p><a href="news/<?php echo $news_item['slug'] ?>">View article</a></p>
+		<p><a href="<?php echo $news_item['slug'] ?>">View article</a></p>
 
 	<?php endforeach ?>
 
diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst
index 8ba0486..53f2864 100644
--- a/user_guide_src/source/tutorial/static_pages.rst
+++ b/user_guide_src/source/tutorial/static_pages.rst
@@ -11,12 +11,16 @@
 It is the glue of your web application.
 
 For example, when a call is made to:
-``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://example.com/[controller-class]/[controller-method]/[arguments]``
+
+	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://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.
 
@@ -25,15 +29,13 @@
 
 ::
 
-    <?php 
-    class Pages extends CI_Controller { 
+	<?php 
+	class Pages extends CI_Controller { 
 
-        public function view($page = 'home') 
-        {
-	
-        }
-		 
-    }
+		public function view($page = 'home') 
+		{
+	        }
+	}
 
 You have created a class named "pages", with a view method that accepts
 one argument named $page. The pages class is extending the
@@ -56,13 +58,13 @@
 
 ::
 
-    <html>
-        <head>
-            <title>CodeIgniter Tutorial</title>
-        </head>
-        <body>
+	<html>
+		<head>
+			<title>CodeIgniter Tutorial</title>
+		</head>
+		<body>
 
-            <h1>CodeIgniter Tutorial</h1>
+			<h1>CodeIgniter Tutorial</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
@@ -72,16 +74,16 @@
 
 ::
 
-            <em>&copy; 2014</em>
-        </body>
-    </html>
+			<em>&copy; 2014</em>
+		</body>
+	</html>
 
 Adding logic to the controller
 ------------------------------
 
-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/
+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/
 directory.
 
 In that directory, create two files named home.php and about.php. Within
@@ -93,43 +95,40 @@
 
 ::
 
-    <?php 
-    public function view($page = 'home')
-    {
-                
-        if ( ! file_exists(APPPATH.'/views/pages/'.$page.'.php'))
-        {
-            // Whoops, we don't have a page for that!
-            show_404();
-        }
-        
-        $data['title'] = ucfirst($page); // Capitalize the first letter
-        
-        $this->load->view('templates/header', $data);
-        $this->load->view('pages/'.$page, $data);
-        $this->load->view('templates/footer', $data);
+	public function view($page = 'home')
+	{
+	        if ( ! file_exists(APPPATH.'/views/pages/'.$page.'.php'))
+		{
+			// Whoops, we don't have a page for that!
+			show_404();
+		}
 
-    }
+		$data['title'] = ucfirst($page); // Capitalize the first letter
+
+		$this->load->view('templates/header', $data);
+		$this->load->view('pages/'.$page, $data);
+		$this->load->view('templates/footer', $data);
+	}
 
 Now, when the page does exist, it is loaded, including the header and
 footer, and displayed to the user. If the page doesn't exist, a "404
 Page not found" error is shown.
 
 The first line in this method checks whether the page actually exists.
-PHP's native file\_exists() function is used to check whether the file
-is where it's expected to be. show\_404() is a built-in CodeIgniter
+PHP's native ``file_exists()`` function is used to check whether the file
+is where it's expected to be. ``show_404()`` is a built-in CodeIgniter
 function that renders the default error page.
 
-In the header template, the $title variable was used to customize the
+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.
 
 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
+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
 -------
@@ -149,8 +148,8 @@
 
 ::
 
-    $route['default_controller'] = 'pages/view';
-    $route['(:any)'] = 'pages/view/$1';
+	$route['default_controller'] = 'pages/view';
+	$route['(:any)'] = 'pages/view/$1';
 
 CodeIgniter reads its routing rules from top to bottom and routes the
 request to the first matching rule. Each rule is a regular expression
@@ -163,8 +162,8 @@
 `documentation <../general/routing.html>`_.
 
 Here, the second rule in the $routes array matches **any** request using
-the wildcard string (:any). and passes the parameter to the view()
+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!