feature/session (#3073): Refactor configuration & fix cookie expiry times
diff --git a/application/config/config.php b/application/config/config.php
index e8d30b6..333e2e9 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -326,33 +326,41 @@
 | Session Variables
 |--------------------------------------------------------------------------
 |
-| 'sess_driver'				= the driver to load: cookie (Classic), native (PHP sessions),
-|	or your custom driver name
-| 'sess_valid_drivers'		= additional valid drivers which may be loaded
-| 'sess_cookie_name'		= the name you want for the cookie, must contain only [0-9a-z_-] characters
-| 'sess_expiration'			= the number of SECONDS you want the session to last.
-|   by default sessions last 7200 seconds (two hours).  Set to zero for no expiration.
-| 'sess_expire_on_close'	= Whether to cause the session to expire automatically
-|   when the browser window is closed
-| 'sess_encrypt_cookie'		= Whether to encrypt the cookie
-| 'sess_use_database'		= Whether to save the session data to a database
-| 'sess_table_name'			= The name of the session database table
-| 'sess_match_ip'			= Whether to match the user's IP address when reading the session data
-| 'sess_match_useragent'	= Whether to match the User Agent when reading the session data
-| 'sess_time_to_update'		= how many seconds between CI refreshing Session Information
+| 'sess_driver'
+|
+|	The storage driver to use: files, database, redis, memcache
+|
+| 'sess_cookie_name'
+|
+|	The session cookie name, must contain only [0-9a-z_-] characters
+|
+| 'sess_expiration'
+|
+|	The number of SECONDS you want the session to last.
+|	Setting to 0 (zero) means expire when the browser is closed.
+|
+| 'sess_save_path'
+|
+|	The location to save sessions to, driver dependant.
+|
+| 'sess_match_ip'
+|
+|	Whether to match the user's IP address when reading the session data.
+|
+| 'sess_time_to_update'
+|
+|	How many seconds between CI regenerating the session ID.
+|
+| Other session cookie settings are shared with the rest of the application,
+| except for 'cookie_prefix', which is ignored here.
 |
 */
-$config['sess_driver']			= 'cookie';
-$config['sess_valid_drivers']	= array();
-$config['sess_cookie_name']		= 'ci_session';
-$config['sess_expiration']		= 7200;
-$config['sess_expire_on_close']	= FALSE;
-$config['sess_encrypt_cookie']	= FALSE;
-$config['sess_use_database']	= FALSE;
-$config['sess_table_name']		= 'ci_sessions';
-$config['sess_match_ip']		= FALSE;
-$config['sess_match_useragent']	= TRUE;
-$config['sess_time_to_update']	= 300;
+$config['sess_driver'] = 'files';
+$config['sess_cookie_name'] = 'ci_session';
+$config['sess_expiration'] = 7200;
+$config['sess_save_path'] = NULL;
+$config['sess_match_ip'] = FALSE;
+$config['sess_time_to_update'] = 300;
 
 /*
 |--------------------------------------------------------------------------
diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php
index be9f5e3..47c4307 100644
--- a/system/libraries/Session/Session.php
+++ b/system/libraries/Session/Session.php
@@ -38,6 +38,7 @@
 class CI_Session {
 
 	protected $_driver = 'files';
+	protected $_config;
 
 	// ------------------------------------------------------------------------
 
@@ -65,8 +66,7 @@
 			$this->_driver = $params['driver'];
 			unset($params['driver']);
 		}
-		// Note: Make the autoloader pass sess_* params to this constructor
-		elseif (empty($params) && $driver = config_item('sess_driver'))
+		elseif ($driver = config_item('sess_driver'))
 		{
 			$this->_driver = $driver;
 		}
@@ -81,7 +81,10 @@
 			return;
 		}
 
-		$class = new $class($params);
+		// Configuration ...
+		$this->_configure($params);
+
+		$class = new $class($this->_config);
 		if ($class instanceof SessionHandlerInterface)
 		{
 			if (is_php('5.4'))
@@ -108,9 +111,50 @@
 			return;
 		}
 
+		// Work-around for PHP bug #66827 (https://bugs.php.net/bug.php?id=66827)
+		//
+		// The session ID sanitizer doesn't check for the value type and blindly does
+		// an implicit cast to string, which triggers an 'Array to string' E_NOTICE.
+		if (isset($_COOKIE[$this->_cookie_name]) && ! is_string($_COOKIE[$this->_cookie_name]))
+		{
+			unset($_COOKIE[$this->_cookie_name]);
+		}
+
 		session_start();
+
+		// Another work-around ... PHP doesn't seem to send the session cookie
+		// unless it is being currently created or regenerated
+		if (isset($_COOKIE[$this->_config['cookie_name']]) && $_COOKIE[$this->_config['cookie_name']] === session_id())
+		{
+			setcookie(
+				$this->_config['cookie_name'],
+				session_id(),
+				(empty($this->_config['cookie_lifetime']) ? 0 : time() + $this->_config['cookie_lifetime']),
+				$this->_config['cookie_path'],
+				$this->_config['cookie_domain'],
+				$this->_config['cookie_secure'],
+				TRUE
+			);
+		}
+
 		$this->_ci_init_vars();
 
+/*
+		Need to test if this is necessary for a custom driver or if it's only
+		relevant to PHP's own files handler.
+
+		https://bugs.php.net/bug.php?id=65475
+		do this after session is started:
+		if (is_php('5.5.2') && ! is_php('5.5.4'))
+		{
+			$session_id = session_id();
+			if ($_COOKIE[$this->_cookie_name] !== $session_id && file_exists(teh file))
+			{
+				unlink(<teh file>);
+			}
+		}
+*/
+
 		log_message('debug', "Session: Class initialized using '".$this->_driver."' driver.");
 	}
 
@@ -171,6 +215,77 @@
 	// ------------------------------------------------------------------------
 
 	/**
+	 * Configuration
+	 *
+	 * Handle input parameters and configuration defaults
+	 *
+	 * @param	array	&$params	Input parameters
+	 * @return	void
+	 */
+	protected function _configure(&$params)
+	{
+		$expiration = config_item('sess_expiration');
+
+		if (isset($params['cookie_lifetime']))
+		{
+			$params['cookie_lifetime'] = (int) $params['cookie_lifetime'];
+		}
+		else
+		{
+			$params['cookie_lifetime'] = ( ! isset($expiration) && config_item('sess_expire_on_close'))
+				? 0 : (int) $expiration;
+		}
+
+		isset($params['cookie_name']) OR $params['cookie_name'] = config_item('sess_cookie_name');
+		if (empty($params['cookie_name']))
+		{
+			$params['cookie_name'] = ini_get('session.name');
+		}
+		else
+		{
+			ini_set('session.name', $params['cookie_name']);
+		}
+
+		isset($params['cookie_path']) OR $params['cookie_path'] = config_item('cookie_path');
+		isset($params['cookie_domain']) OR $parrams['cookie_domain'] = config_item('cookie_domain');
+		isset($params['cookie_secure']) OR $params['cookie_secure'] = (bool) config_item('cookie_secure');
+
+		session_set_cookie_params(
+			$params['cookie_lifetime'],
+			$params['cookie_path'],
+			$params['cookie_domain'],
+			$params['cookie_secure'],
+			TRUE // HttpOnly; Yes, this is intentional and not configurable for security reasons
+		);
+
+		if (empty($expiration))
+		{
+			$params['expiration'] = (int) ini_get('session.gc_maxlifetime');
+		}
+		else
+		{
+			$params['expiration'] = (int) $expiration;
+			ini_set('session.gc_maxlifetime', $expiration);
+		}
+
+		$params['match_ip'] = (bool) (isset($params['match_ip']) ? $params['match_ip'] : config_item('sess_match_ip'));
+
+		isset($params['save_path']) OR $params['save_path'] = config_item('sess_save_path');
+
+		$this->_config = $params;
+
+		// Security is king
+		ini_set('session.use_trans_id', 0);
+		ini_set('session.use_strict_mode', 1);
+		ini_set('session.use_cookies', 1);
+		ini_set('session.use_only_cookies', 1);
+		ini_set('session.hash_function', 1);
+		ini_set('session.hash_bits_per_character', 4);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
 	 * Handle temporary variables
 	 *
 	 * Clears old "flash" data, marks the new one for deletion and handles
diff --git a/system/libraries/Session/Session_driver.php b/system/libraries/Session/Session_driver.php
index a3bc392..fb695da 100644
--- a/system/libraries/Session/Session_driver.php
+++ b/system/libraries/Session/Session_driver.php
@@ -37,57 +37,7 @@
  */
 abstract class CI_Session_driver implements SessionHandlerInterface {
 
-	// WARNING! Setting default values to properties will
-	// prevent using the configuration file values.
-
-	/**
-	 * Expiration time
-	 *
-	 * @var	int
-	 */
-	protected $_expiration;
-
-	/**
-	 * Cookie name
-	 *
-	 * @var	string
-	 */
-	protected $_cookie_name;
-
-	/**
-	 * Cookie domain
-	 *
-	 * @var	string
-	 */
-	protected $_cookie_domain;
-
-	/**
-	 * Cookie path
-	 *
-	 * @var	string
-	 */
-	protected $_cookie_path;
-
-	/**
-	 * Cookie secure flag
-	 *
-	 * @var	bool
-	 */
-	protected $_cookie_secure;
-
-	/**
-	 * Cookie HTTP-only flag
-	 *
-	 * @var	bool
-	 */
-	protected $_cookie_httponly;
-
-	/**
-	 * Match IP addresses flag
-	 *
-	 * @var	bool
-	 */
-	protected $_match_ip;
+	protected $_config;
 
 	/**
 	 * Data fingerprint
@@ -111,87 +61,9 @@
 	 * @param	array	$params	Configuration parameters
 	 * @return	void
 	 */
-	public function __construct($params)
+	public function __construct(&$params)
 	{
-		foreach ($params as $key => &$value)
-		{
-			$key = (strncmp($key, 'sess_', 5) === 0)
-				? substr($key, 4)
-				: '_'.$key;
-
-			property_exists($this, $key) && $this->$key = $value;
-		}
-
-		isset($this->_expiration) OR $this->_expiration = (int) config_item('sess_expiration');
-		isset($this->_cookie_name) OR $this->_cookie_name = config_item('sess_cookie_name');
-		isset($this->_cookie_domain) OR $this->_cookie_domain = config_item('cookie_domain');
-		isset($this->_cookie_path) OR $this->_cookie_path = config_item('cookie_path');
-		isset($this->_cookie_secure) OR $this->_cookie_secure = config_item('cookie_secure');
-		isset($this->_cookie_httponly) OR $this->_cookie_httponly = config_item('cookie_httponly');
-		isset($this->_match_ip) OR $this->_match_ip = config_item('sess_match_ip');
-
-		// Pass our configuration to php.ini, when appropriate
-		ini_set('session.name', $this->_cookie_name);
-		isset($this->_cookie_domain) && ini_set('session.cookie_domain', $this->_cookie_domain);
-		isset($this->_cookie_path) && ini_set('session.cookie_path', $this->_cookie_path);
-		isset($this->_cookie_secure) && ini_set('session.cookie_secure', $this->_cookie_secure);
-		isset($this->_cookie_httponly) && ini_set('session.cookie_httponly', $this->_cookie_httponly);
-
-		if ($this->_expiration)
-		{
-			ini_set('session.gc_maxlifetime', $this->_expiration);
-			ini_set('session.cookie_lifetime', $this->_expiration);
-		}
-		// BC workaround for setting cookie lifetime
-		elseif (config_item('sess_expire_on_close'))
-		{
-			ini_set('session.cookie_lifetime', 0);
-		}
-
-		// Security is king
-		ini_set('session.use_trans_id', 0);
-		ini_set('session.use_strict_mode', 1);
-		ini_set('session.use_cookies', 1);
-		ini_set('session.use_only_cookies', 1);
-		ini_set('session.hash_function', 1);
-		ini_set('session.hash_bits_per_character', 4);
-
-		// Work-around for PHP bug #66827 (https://bugs.php.net/bug.php?id=66827)
-		//
-		// The session ID sanitizer doesn't check for the value type and blindly does
-		// an implicit cast to string, which triggers an 'Array to string' E_NOTICE.
-		if (isset($_COOKIE[$this->_cookie_name]) && ! is_string($_COOKIE[$this->_cookie_name]))
-		{
-			unset($_COOKIE[$this->_cookie_name]);
-		}
-
-/*
-		Need to test if this is necessary for a custom driver or if it's only
-		relevant to PHP's own files handler.
-
-		https://bugs.php.net/bug.php?id=65475
-		do this after session is started:
-		if (is_php('5.5.2') && ! is_php('5.5.4'))
-		{
-			$session_id = session_id();
-			if ($_COOKIE[$this->_cookie_name] !== $session_id && file_exists(teh file))
-			{
-				unlink(<teh file>);
-			}
-
-			setcookie(
-				$this->_cookie_name,
-				$session_id,
-				$this->_expiration
-					? time() + $this->_expiration
-					: 0,
-				$this->_cookie_path,
-				$this->_cookie_domain,
-				$this->_cookie_secure,
-				$this->_cookie_httponly
-			);
-		}
-*/
+		$this->_config =& $params;
 	}
 
 	// ------------------------------------------------------------------------
@@ -199,13 +71,13 @@
 	protected function _cookie_destroy()
 	{
 		return setcookie(
-			$this->_cookie_name,
+			$this->_config['cookie_name'],
 			NULL,
 			1,
-			$this->_cookie_path,
-			$this->_cookie_domain,
-			$this->_cookie_secure,
-			$this->_cookie_httponly
+			$this->_config['cookie_path'],
+			$this->_config['cookie_domain'],
+			$this->_config['cookie_secure'],
+			TRUE
 		);
 	}
 
@@ -230,7 +102,7 @@
 			return TRUE;
 		}
 
-		if (($this->_lock = sem_get($session_id.($this->_match_ip ? '_'.$_SERVER['REMOTE_ADDR'] : ''), 1, 0644)) === FALSE)
+		if (($this->_lock = sem_get($session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : ''), 1, 0644)) === FALSE)
 		{
 			return FALSE;
 		}
diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php
index 563d1fd..e3a3c50 100644
--- a/system/libraries/Session/drivers/Session_database_driver.php
+++ b/system/libraries/Session/drivers/Session_database_driver.php
@@ -45,13 +45,6 @@
 	protected $_db;
 
 	/**
-	 * DB table
-	 *
-	 * @var	string
-	 */
-	protected $_table;
-
-	/**
 	 * Row exists flag
 	 *
 	 * @var	bool
@@ -100,7 +93,7 @@
 			$this->_lock_driver = 'postgre';
 		}
 
-		isset($this->_table) OR $this->_table = config_item('sess_table_name');
+		isset($this->_config['save_path']) OR $this->_config['save_path'] = config_item('sess_table_name');
 	}
 
 	// ------------------------------------------------------------------------
@@ -120,10 +113,10 @@
 		{
 			$this->_db
 				->select('data')
-				->from($this->_table)
+				->from($this->_config['save_path'])
 				->where('id', $session_id);
 
-			if ($this->_match_ip)
+			if ($this->_config['match_ip'])
 			{
 				$this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);
 			}
@@ -152,7 +145,7 @@
 
 		if ($this->_row_exists === FALSE)
 		{
-			if ($this->_db->insert($this->_table, array('id' => $session_id, 'ip_address' => $_SERVER['REMOTE_ADDR'], 'timestamp' => time(), 'data' => $session_data)))
+			if ($this->_db->insert($this->_config['save_path'], array('id' => $session_id, 'ip_address' => $_SERVER['REMOTE_ADDR'], 'timestamp' => time(), 'data' => $session_data)))
 			{
 				$this->_fingerprint = md5($session_data);
 				return $this->_row_exists = TRUE;
@@ -162,7 +155,7 @@
 		}
 
 		$this->_db->where('id', $session_id);
-		if ($this->_match_ip)
+		if ($this->_config['match_ip'])
 		{
 			$this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);
 		}
@@ -171,7 +164,7 @@
 			? array('timestamp' => time())
 			: array('timestamp' => time(), 'data' => $session_data);
 
-		if ($this->_db->update($this->_table, $update_data))
+		if ($this->_db->update($this->_config['save_path'], $update_data))
 		{
 			$this->_fingerprint = md5($session_data);
 			return TRUE;
@@ -196,12 +189,12 @@
 		if ($this->_lock)
 		{
 			$this->_db->where('id', $session_id);
-			if ($this->_match_ip)
+			if ($this->_config['match_ip'])
 			{
 				$this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);
 			}
 
-			return $this->_db->delete($this->_table)
+			return $this->_db->delete($this->_config['save_path'])
 				? ($this->close() && $this->_cookie_destroy())
 				: FALSE;
 		}
@@ -213,7 +206,7 @@
 
 	public function gc($maxlifetime)
 	{
-		return $this->_db->delete($this->_table, 'timestamp < '.(time() - $maxlifetime));
+		return $this->_db->delete($this->_config['save_path'], 'timestamp < '.(time() - $maxlifetime));
 	}
 
 	// ------------------------------------------------------------------------
@@ -222,7 +215,7 @@
 	{
 		if ($this->_lock_driver === 'mysql')
 		{
-			$arg = $session_id.($this->_match_ip ? '_'.$_SERVER['REMOTE_ADDR'] : '');
+			$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)
 			{
 				$this->_lock = $arg;
@@ -233,7 +226,7 @@
 		}
 		elseif ($this->_lock_driver === 'postgre')
 		{
-			$arg = "hashtext('".$session_id."')".($this->_match_ip ? ", hashtext('".$_SERVER['REMOTE_ADDR']."')" : '');
+			$arg = "hashtext('".$session_id."')".($this->_config['match_ip'] ? ", hashtext('".$_SERVER['REMOTE_ADDR']."')" : '');
 			if ($this->_db->simple_query('SELECT pg_advisory_lock('.$arg.')'))
 			{
 				$this->_lock = $arg;
diff --git a/system/libraries/Session/drivers/Session_files_driver.php b/system/libraries/Session/drivers/Session_files_driver.php
index 7779e9b..a4f1b9f 100644
--- a/system/libraries/Session/drivers/Session_files_driver.php
+++ b/system/libraries/Session/drivers/Session_files_driver.php
@@ -77,14 +77,14 @@
 	{
 		parent::__construct($params);
 
-		if (isset($this->_save_path))
+		if (isset($this->_config['save_path']))
 		{
-			$this->_save_path = rtrim($this->_save_path, '/\\');
-			ini_set('session.save_path', $this->_save_path);
+			$this->_config['save_path'] = rtrim($this->_config['save_path'], '/\\');
+			ini_set('session.save_path', $this->_config['save_path']);
 		}
 		else
 		{
-			$this->_save_path = rtrim(ini_get('session.save_path'), '/\\');
+			$this->_config['save_path'] = rtrim(ini_get('session.save_path'), '/\\');
 		}
 	}
 
@@ -94,14 +94,14 @@
 	{
 		if ( ! is_dir($save_path) && ! mkdir($save_path, 0700, TRUE))
 		{
-			log_message('error', "Session: Configured save path '".$this->_save_path."' is not a directory, doesn't exist or cannot be created.");
+			log_message('error', "Session: Configured save path '".$this->_config['save_path']."' is not a directory, doesn't exist or cannot be created.");
 			return FALSE;
 		}
 
-		$this->_save_path = $save_path;
-		$this->_file_path = $this->_save_path.DIRECTORY_SEPARATOR
+		$this->_config['save_path'] = $save_path;
+		$this->_file_path = $this->_config['save_path'].DIRECTORY_SEPARATOR
 			.$name // we'll use the session cookie name as a prefix to avoid collisions
-			.($this->_match_ip ? md5($_SERVER['REMOTE_ADDR']) : '');
+			.($this->_config['match_ip'] ? md5($_SERVER['REMOTE_ADDR']) : '');
 
 		return TRUE;
 	}
@@ -248,9 +248,9 @@
 
 	public function gc($maxlifetime)
 	{
-		if ( ! is_dir($this->_save_path) OR ($files = scandir($this->_save_path)) === FALSE)
+		if ( ! is_dir($this->_config['save_path']) OR ($files = scandir($this->_config['save_path'])) === FALSE)
 		{
-			log_message('debug', "Session: Garbage collector couldn't list files under directory '".$this->_save_path."'.");
+			log_message('debug', "Session: Garbage collector couldn't list files under directory '".$this->_config['save_path']."'.");
 			return FALSE;
 		}
 
@@ -260,14 +260,14 @@
 		{
 			// If the filename doesn't match this pattern, it's either not a session file or is not ours
 			if ( ! preg_match('/(?:[0-9a-f]{32})?[0-9a-f]{40}$/i', $file)
-				OR ! is_file($this->_save_path.DIRECTORY_SEPARATOR.$file)
-				OR ($mtime = filemtime($this->_save_path.DIRECTORY_SEPARATOR.$file)) === FALSE
+				OR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)
+				OR ($mtime = filemtime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE
 				OR $mtime > $ts)
 			{
 				continue;
 			}
 
-			unlink($this->_save_path.DIRECTORY_SEPARATOR.$file);
+			unlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file);
 		}
 
 		return TRUE;
diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php
index c6ad565..318c11a 100644
--- a/system/libraries/Session/drivers/Session_memcached_driver.php
+++ b/system/libraries/Session/drivers/Session_memcached_driver.php
@@ -38,13 +38,6 @@
 class CI_Session_memcached_driver extends CI_Session_driver implements SessionHandlerInterface {
 
 	/**
-	 * Save path
-	 *
-	 * @var	string
-	 */
-	protected $_save_path;
-
-	/**
 	 * Memcached instance
 	 *
 	 * @var	Memcached
@@ -77,12 +70,12 @@
 	{
 		parent::__construct($params);
 
-		if (empty($this->_save_path))
+		if (empty($this->_config['save_path']))
 		{
 			log_message('error', 'Session: No Memcached save path configured.');
 		}
 
-		if ($this->_match_ip === TRUE)
+		if ($this->_config['match_ip'] === TRUE)
 		{
 			$this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':';
 		}
@@ -99,10 +92,10 @@
 			$server_list[] = $server['host'].':'.$server['port'];
 		}
 
-		if ( ! preg_match_all('#,?([^,:]+)\:(\d{1,5})(?:\:(\d+))?#', $this->_save_path, $matches, PREG_SET_ORDER))
+		if ( ! preg_match_all('#,?([^,:]+)\:(\d{1,5})(?:\:(\d+))?#', $this->_config['save_path'], $matches, PREG_SET_ORDER))
 		{
 			$this->_memcached = NULL;
-			log_message('error', 'Session: Invalid Memcached save path format: '.$this->_save_path);
+			log_message('error', 'Session: Invalid Memcached save path format: '.$this->_config['save_path']);
 			return FALSE;
 		}
 
@@ -155,7 +148,7 @@
 			$this->_memcached->replace($this->_lock_key, time(), 5);
 			if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
 			{
-				if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_expiration))
+				if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
 				{
 					$this->_fingerprint = $fingerprint;
 					return TRUE;
@@ -164,7 +157,7 @@
 				return FALSE;
 			}
 
-			return $this->_memcached->touch($this->_key_prefix.$session_id, $this->_expiration);
+			return $this->_memcached->touch($this->_key_prefix.$session_id, $this->_config['expiration']);
 		}
 
 		return FALSE;
diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php
index 6d8044d..ef18def 100644
--- a/system/libraries/Session/drivers/Session_redis_driver.php
+++ b/system/libraries/Session/drivers/Session_redis_driver.php
@@ -38,13 +38,6 @@
 class CI_Session_redis_driver extends CI_Session_driver implements SessionHandlerInterface {
 
 	/**
-	 * Save path
-	 *
-	 * @var	string
-	 */
-	protected $_save_path;
-
-	/**
 	 * phpRedis instance
 	 *
 	 * @var	resource
@@ -77,14 +70,14 @@
 	{
 		parent::__construct($params);
 
-		if (empty($this->_save_path))
+		if (empty($this->_config['save_path']))
 		{
 			log_message('error', 'Session: No Redis save path configured.');
 		}
-		elseif (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(\?.+)?#', $this->_save_path, $matches))
+		elseif (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(\?.+)?#', $this->_config['save_path'], $matches))
 		{
 			isset($matches[3]) OR $matches[3] = ''; // Just to avoid undefined index notices below
-			$this->_save_path = array(
+			$this->_config['save_path'] = array(
 				'host' => $matches[1],
 				'port' => empty($matches[2]) ? NULL : $matches[2],
 				'password' => preg_match('#auth=([^\s&]+)#', $matches[3], $match) ? $match[1] : NULL,
@@ -96,10 +89,10 @@
 		}
 		else
 		{
-			log_message('error', 'Session: Invalid Redis save path format: '.$this->_save_path);
+			log_message('error', 'Session: Invalid Redis save path format: '.$this->_config['save_path']);
 		}
 
-		if ($this->_match_ip === TRUE)
+		if ($this->_config['match_ip'] === TRUE)
 		{
 			$this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':';
 		}
@@ -109,23 +102,23 @@
 
 	public function open($save_path, $name)
 	{
-		if (empty($this->_save_path))
+		if (empty($this->_config['save_path']))
 		{
 			return FALSE;
 		}
 
 		$redis = new Redis();
-		if ( ! $redis->connect($this->_save_path['host'], $this->_save_path['port'], $this->_save_path['timeout']))
+		if ( ! $redis->connect($this->_config['save_path']['host'], $this->_config['save_path']['port'], $this->_config['save_path']['timeout']))
 		{
 			log_message('error', 'Session: Unable to connect to Redis with the configured settings.');
 		}
-		elseif (isset($this->_save_path['password']) && ! $redis->auth($this->_save_path['password']))
+		elseif (isset($this->_config['save_path']['password']) && ! $redis->auth($this->_config['save_path']['password']))
 		{
 			log_message('error', 'Session: Unable to authenticate to Redis instance.');
 		}
-		elseif (isset($this->_save_path['database']) && ! $redis->select($this->_save_path['database']))
+		elseif (isset($this->_config['save_path']['database']) && ! $redis->select($this->_config['save_path']['database']))
 		{
-			log_message('error', 'Session: Unable to select Redis database with index '.$this->_save_path['database']);
+			log_message('error', 'Session: Unable to select Redis database with index '.$this->_config['save_path']['database']);
 		}
 		else
 		{
@@ -157,7 +150,7 @@
 			$this->_redis->setTimeout($this->_lock_key, 5);
 			if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
 			{
-				if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_expiration))
+				if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
 				{
 					$this->_fingerprint = $fingerprint;
 					return TRUE;
@@ -166,7 +159,7 @@
 				return FALSE;
 			}
 
-			return $this->_redis->setTimeout($this->_key_prefix.$session_id, $this->_expiration);
+			return $this->_redis->setTimeout($this->_key_prefix.$session_id, $this->_config['expiration']);
 		}
 
 		return FALSE;