blob: 566c40bd8b2970180a5234685723d84d1142e04f [file] [log] [blame]
Andrey Andreevc5536aa2012-11-01 17:33:58 +02001<?php
Darren Hillc4e266b2011-08-30 15:40:27 -04002/**
3 * CodeIgniter
4 *
Phil Sturgeon07c1ac82012-03-09 17:03:37 +00005 * An open source application development framework for PHP 5.2.4 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05007 * NOTICE OF LICENSE
Andrey Andreev57ffbbb2011-12-25 04:48:47 +02008 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05009 * Licensed under the Open Software License version 3.0
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020010 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -050011 * This source file is subject to the Open Software License (OSL 3.0) that is
Andrey Andreevc5536aa2012-11-01 17:33:58 +020012 * bundled with this package in the files license.txt / license.rst. It is
Derek Jonesf4a4bd82011-10-20 12:18:42 -050013 * also available through the world wide web at this URL:
14 * http://opensource.org/licenses/OSL-3.0
15 * If you did not receive a copy of the license and are unable to obtain it
16 * through the world wide web, please send an email to
17 * licensing@ellislab.com so we can send you a copy immediately.
Darren Hillc4e266b2011-08-30 15:40:27 -040018 *
19 * @package CodeIgniter
Derek Jonesf4a4bd82011-10-20 12:18:42 -050020 * @author EllisLab Dev Team
darwinel871754a2014-02-11 17:34:57 +010021 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/)
Derek Jonesf4a4bd82011-10-20 12:18:42 -050022 * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
Darren Hillc4e266b2011-08-30 15:40:27 -040023 * @link http://codeigniter.com
Derek Allard2067d1a2008-11-13 22:59:24 +000024 * @since Version 1.0
Darren Hillc4e266b2011-08-30 15:40:27 -040025 * @filesource
26 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020027defined('BASEPATH') OR exit('No direct script access allowed');
Darren Hillc4e266b2011-08-30 15:40:27 -040028
Darren Hillc4e266b2011-08-30 15:40:27 -040029/**
30 * Cookie-based session management driver
31 *
dchill423cecd822012-08-28 21:37:27 -040032 * This is the classic CI_Session functionality, as written by EllisLab, abstracted out to a driver.
Darren Hillc4e266b2011-08-30 15:40:27 -040033 *
34 * @package CodeIgniter
35 * @subpackage Libraries
36 * @category Sessions
Derek Jonesf4a4bd82011-10-20 12:18:42 -050037 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000038 * @link http://codeigniter.com/user_guide/libraries/sessions.html
Darren Hillc4e266b2011-08-30 15:40:27 -040039 */
Darren Hill5073a372011-08-31 13:54:19 -040040class CI_Session_cookie extends CI_Session_driver {
Andrey Andreev9ffcee62012-09-05 16:25:16 +030041
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020042 /**
Timothy Warren68f09812012-04-27 10:38:32 -040043 * Whether to encrypt the session cookie
44 *
45 * @var bool
46 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020047 public $sess_encrypt_cookie = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020048
Timothy Warren68f09812012-04-27 10:38:32 -040049 /**
50 * Whether to use to the database for session storage
51 *
52 * @var bool
53 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020054 public $sess_use_database = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020055
Timothy Warren68f09812012-04-27 10:38:32 -040056 /**
57 * Name of the database table in which to store sessions
58 *
59 * @var string
60 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020061 public $sess_table_name = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020062
Timothy Warren68f09812012-04-27 10:38:32 -040063 /**
64 * Length of time (in seconds) for sessions to expire
65 *
66 * @var int
67 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020068 public $sess_expiration = 7200;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020069
Timothy Warren68f09812012-04-27 10:38:32 -040070 /**
71 * Whether to kill session on close of browser window
72 *
73 * @var bool
74 */
dchill4226429202012-07-31 10:55:07 -040075 public $sess_expire_on_close = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020076
Timothy Warren68f09812012-04-27 10:38:32 -040077 /**
78 * Whether to match session on ip address
79 *
80 * @var bool
81 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020082 public $sess_match_ip = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020083
Timothy Warren68f09812012-04-27 10:38:32 -040084 /**
85 * Whether to match session on user-agent
86 *
87 * @var bool
88 */
dchill4226429202012-07-31 10:55:07 -040089 public $sess_match_useragent = TRUE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020090
Timothy Warren68f09812012-04-27 10:38:32 -040091 /**
92 * Name of session cookie
93 *
94 * @var string
95 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020096 public $sess_cookie_name = 'ci_session';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020097
Timothy Warren68f09812012-04-27 10:38:32 -040098 /**
99 * Session cookie prefix
100 *
101 * @var string
102 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200103 public $cookie_prefix = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200104
Timothy Warren68f09812012-04-27 10:38:32 -0400105 /**
106 * Session cookie path
107 *
108 * @var string
109 */
dchill4226429202012-07-31 10:55:07 -0400110 public $cookie_path = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200111
Timothy Warren68f09812012-04-27 10:38:32 -0400112 /**
113 * Session cookie domain
114 *
115 * @var string
116 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200117 public $cookie_domain = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200118
Timothy Warren68f09812012-04-27 10:38:32 -0400119 /**
120 * Whether to set the cookie only on HTTPS connections
121 *
122 * @var bool
123 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200124 public $cookie_secure = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200125
Timothy Warren68f09812012-04-27 10:38:32 -0400126 /**
127 * Whether cookie should be allowed only to be sent by the server
128 *
129 * @var bool
130 */
freewil4ad0fd82012-03-13 22:37:42 -0400131 public $cookie_httponly = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200132
Timothy Warren68f09812012-04-27 10:38:32 -0400133 /**
134 * Interval at which to update session
135 *
136 * @var int
137 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200138 public $sess_time_to_update = 300;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200139
Timothy Warren68f09812012-04-27 10:38:32 -0400140 /**
141 * Key with which to encrypt the session cookie
142 *
143 * @var string
144 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200145 public $encryption_key = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200146
Timothy Warren68f09812012-04-27 10:38:32 -0400147 /**
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300148 * Timezone to use for the current time
Timothy Warren68f09812012-04-27 10:38:32 -0400149 *
150 * @var string
151 */
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300152 public $time_reference = 'local';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200153
Timothy Warren68f09812012-04-27 10:38:32 -0400154 /**
155 * Session data
156 *
157 * @var array
158 */
dchill4226429202012-07-31 10:55:07 -0400159 public $userdata = array();
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200160
Timothy Warren68f09812012-04-27 10:38:32 -0400161 /**
Timothy Warren68f09812012-04-27 10:38:32 -0400162 * Current time
163 *
164 * @var int
165 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200166 public $now;
Darren Hillc4e266b2011-08-30 15:40:27 -0400167
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200168 // ------------------------------------------------------------------------
169
Darren Hillc4e266b2011-08-30 15:40:27 -0400170 /**
dchill423cecd822012-08-28 21:37:27 -0400171 * Default userdata keys
172 *
173 * @var array
174 */
175 protected $defaults = array(
dchill4288b636b2012-08-29 08:47:05 -0400176 'session_id' => NULL,
177 'ip_address' => NULL,
178 'user_agent' => NULL,
179 'last_activity' => NULL
dchill423cecd822012-08-28 21:37:27 -0400180 );
181
182 /**
183 * Data needs DB update flag
184 *
185 * @var bool
186 */
187 protected $data_dirty = FALSE;
188
189 /**
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200190 * Standardize newlines flag
191 *
192 * @var bool
193 */
194 protected $_standardize_newlines;
195
196 // ------------------------------------------------------------------------
197
198 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400199 * Initialize session driver object
200 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400201 * @return void
202 */
203 protected function initialize()
204 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400205 // Set all the session preferences, which can either be set
dchill4242b77a92012-07-23 11:28:42 -0400206 // manually via the $params array or via the config file
dchill4226429202012-07-31 10:55:07 -0400207 $prefs = array(
208 'sess_encrypt_cookie',
209 'sess_use_database',
210 'sess_table_name',
211 'sess_expiration',
212 'sess_expire_on_close',
213 'sess_match_ip',
214 'sess_match_useragent',
215 'sess_cookie_name',
216 'cookie_path',
217 'cookie_domain',
218 'cookie_secure',
219 'cookie_httponly',
220 'sess_time_to_update',
221 'time_reference',
222 'cookie_prefix',
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200223 'encryption_key',
dchill4226429202012-07-31 10:55:07 -0400224 );
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300225
Andrey Andreev4ea76cc2014-01-08 21:49:23 +0200226 $this->_standardize_newlines = (bool) config_item('standardize_newlines');
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200227
dchill4226429202012-07-31 10:55:07 -0400228 foreach ($prefs as $key)
Darren Hillc4e266b2011-08-30 15:40:27 -0400229 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300230 $this->$key = isset($this->_parent->params[$key])
231 ? $this->_parent->params[$key]
232 : $this->CI->config->item($key);
Darren Hillc4e266b2011-08-30 15:40:27 -0400233 }
234
Andrey Andreev4abd0942012-11-26 12:13:59 +0200235 if (empty($this->encryption_key))
Darren Hillc4e266b2011-08-30 15:40:27 -0400236 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300237 show_error('In order to use the Cookie Session driver you are required to set an encryption key in your config file.');
Darren Hillc4e266b2011-08-30 15:40:27 -0400238 }
239
Darren Hillc4e266b2011-08-30 15:40:27 -0400240 // Do we need encryption? If so, load the encryption class
Alex Bilbied261b1e2012-06-02 11:12:16 +0100241 if ($this->sess_encrypt_cookie === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400242 {
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200243 $this->CI->load->library('encryption');
Darren Hillc4e266b2011-08-30 15:40:27 -0400244 }
245
dchill423cecd822012-08-28 21:37:27 -0400246 // Check for database
Alex Bilbied261b1e2012-06-02 11:12:16 +0100247 if ($this->sess_use_database === TRUE && $this->sess_table_name !== '')
Darren Hillc4e266b2011-08-30 15:40:27 -0400248 {
dchill423cecd822012-08-28 21:37:27 -0400249 // Load database driver
Darren Hillc4e266b2011-08-30 15:40:27 -0400250 $this->CI->load->database();
dchill423cecd822012-08-28 21:37:27 -0400251
252 // Register shutdown function
253 register_shutdown_function(array($this, '_update_db'));
Darren Hillc4e266b2011-08-30 15:40:27 -0400254 }
255
256 // Set the "now" time. Can either be GMT or server time, based on the config prefs.
257 // We use this to set the "last activity" time
258 $this->now = $this->_get_time();
259
260 // Set the session length. If the session expiration is
261 // set to zero we'll set the expiration two years from now.
Alex Bilbied261b1e2012-06-02 11:12:16 +0100262 if ($this->sess_expiration === 0)
Darren Hillc4e266b2011-08-30 15:40:27 -0400263 {
264 $this->sess_expiration = (60*60*24*365*2);
265 }
266
267 // Set the cookie name
268 $this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
269
270 // Run the Session routine. If a session doesn't exist we'll
271 // create a new one. If it does, we'll update it.
272 if ( ! $this->_sess_read())
273 {
274 $this->_sess_create();
275 }
276 else
277 {
278 $this->_sess_update();
279 }
280
281 // Delete expired sessions if necessary
282 $this->_sess_gc();
283 }
284
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300285 // ------------------------------------------------------------------------
286
Darren Hillc4e266b2011-08-30 15:40:27 -0400287 /**
288 * Write the session data
289 *
290 * @return void
291 */
292 public function sess_save()
293 {
dchill423cecd822012-08-28 21:37:27 -0400294 // Check for database
dchill4288b636b2012-08-29 08:47:05 -0400295 if ($this->sess_use_database === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400296 {
dchill423cecd822012-08-28 21:37:27 -0400297 // Mark custom data as dirty so we know to update the DB
298 $this->data_dirty = TRUE;
Darren Hillc4e266b2011-08-30 15:40:27 -0400299 }
300
dchill423cecd822012-08-28 21:37:27 -0400301 // Write the cookie
302 $this->_set_cookie();
Darren Hillc4e266b2011-08-30 15:40:27 -0400303 }
304
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300305 // ------------------------------------------------------------------------
306
Darren Hillc4e266b2011-08-30 15:40:27 -0400307 /**
308 * Destroy the current session
309 *
310 * @return void
311 */
312 public function sess_destroy()
313 {
314 // Kill the session DB row
dchill42aee92652012-08-26 21:45:35 -0400315 if ($this->sess_use_database === TRUE && isset($this->userdata['session_id']))
Darren Hillc4e266b2011-08-30 15:40:27 -0400316 {
dchill423cecd822012-08-28 21:37:27 -0400317 $this->CI->db->delete($this->sess_table_name, array('session_id' => $this->userdata['session_id']));
dchill4297b0d832012-09-04 10:09:00 -0400318 $this->data_dirty = FALSE;
Darren Hillc4e266b2011-08-30 15:40:27 -0400319 }
320
321 // Kill the cookie
Andrey Andreevcf264e02012-10-18 16:14:51 +0300322 $this->_setcookie($this->sess_cookie_name, '', ($this->now - 31500000),
Darren Hillc4e266b2011-08-30 15:40:27 -0400323 $this->cookie_path, $this->cookie_domain, 0);
dchill42c5079de2012-07-23 10:53:47 -0400324
325 // Kill session data
326 $this->userdata = array();
Darren Hillc4e266b2011-08-30 15:40:27 -0400327 }
328
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300329 // ------------------------------------------------------------------------
330
Darren Hillc4e266b2011-08-30 15:40:27 -0400331 /**
332 * Regenerate the current session
333 *
334 * Regenerate the session id
335 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300336 * @param bool Destroy session data flag (default: false)
Darren Hillc4e266b2011-08-30 15:40:27 -0400337 * @return void
338 */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300339 public function sess_regenerate($destroy = FALSE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400340 {
341 // Check destroy flag
342 if ($destroy)
343 {
344 // Destroy old session and create new one
345 $this->sess_destroy();
346 $this->_sess_create();
347 }
348 else
349 {
350 // Just force an update to recreate the id
Andrey Andreev3f3f1352012-09-05 16:39:28 +0300351 $this->_sess_update(TRUE);
Darren Hillc4e266b2011-08-30 15:40:27 -0400352 }
353 }
354
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300355 // ------------------------------------------------------------------------
356
Darren Hillc4e266b2011-08-30 15:40:27 -0400357 /**
358 * Get a reference to user data array
359 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300360 * @return array Reference to userdata
Darren Hillc4e266b2011-08-30 15:40:27 -0400361 */
362 public function &get_userdata()
363 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400364 return $this->userdata;
365 }
366
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300367 // ------------------------------------------------------------------------
368
Darren Hillc4e266b2011-08-30 15:40:27 -0400369 /**
370 * Fetch the current session data if it exists
371 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400372 * @return bool
373 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400374 protected function _sess_read()
Darren Hillc4e266b2011-08-30 15:40:27 -0400375 {
376 // Fetch the cookie
377 $session = $this->CI->input->cookie($this->sess_cookie_name);
378
379 // No cookie? Goodbye cruel world!...
Phil Sturgeon55a6ddb2012-05-23 18:37:24 +0100380 if ($session === NULL)
Darren Hillc4e266b2011-08-30 15:40:27 -0400381 {
382 log_message('debug', 'A session cookie was not found.');
383 return FALSE;
384 }
385
Alex Bilbied261b1e2012-06-02 11:12:16 +0100386 if ($this->sess_encrypt_cookie === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400387 {
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200388 $session = $this->CI->encryption->decrypt($session);
389 if ($session === FALSE)
390 {
391 log_message('error', 'Session: Unable to decrypt the session cookie, possibly due to a HMAC mismatch.');
392 return FALSE;
393 }
394 }
395 else
396 {
397 if (($len = strlen($session) - 40) <= 0)
398 {
399 log_message('error', 'Session: The session cookie was not signed.');
400 return FALSE;
401 }
402
403 // Check cookie authentication
404 $hmac = substr($session, $len);
405 $session = substr($session, 0, $len);
406
Andrey Andreevdf7a6962014-02-06 05:36:47 +0200407 // Time-attack-safe comparison
408 $hmac_check = hash_hmac('sha1', $session, $this->encryption_key);
409 $diff = 0;
410 for ($i = 0; $i < 40; $i++)
411 {
412 $diff |= ord($hmac[$i]) ^ ord($hmac_check[$i]);
413 }
414
415 if ($diff !== 0)
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200416 {
417 log_message('error', 'Session: HMAC mismatch. The session cookie data did not match what was expected.');
418 $this->sess_destroy();
419 return FALSE;
420 }
Darren Hillc4e266b2011-08-30 15:40:27 -0400421 }
Darren Hillc4e266b2011-08-30 15:40:27 -0400422
423 // Unserialize the session array
Jordan Eldredge5306cad2013-12-23 11:10:51 -0800424 $session = @unserialize($session);
Darren Hillc4e266b2011-08-30 15:40:27 -0400425
426 // Is the session data we unserialized an array with the correct format?
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300427 if ( ! is_array($session) OR ! isset($session['session_id'], $session['ip_address'], $session['user_agent'], $session['last_activity']))
Darren Hillc4e266b2011-08-30 15:40:27 -0400428 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300429 log_message('debug', 'Session: Wrong cookie data format');
Darren Hillc4e266b2011-08-30 15:40:27 -0400430 $this->sess_destroy();
431 return FALSE;
432 }
433
434 // Is the session current?
Andrey Andreev02117682012-10-15 11:12:37 +0300435 if (($session['last_activity'] + $this->sess_expiration) < $this->now OR $session['last_activity'] > $this->now)
Darren Hillc4e266b2011-08-30 15:40:27 -0400436 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300437 log_message('debug', 'Session: Expired');
Darren Hillc4e266b2011-08-30 15:40:27 -0400438 $this->sess_destroy();
439 return FALSE;
440 }
441
Andrey Andreev7e087f52012-01-20 11:46:27 +0200442 // Does the IP match?
Alex Bilbied261b1e2012-06-02 11:12:16 +0100443 if ($this->sess_match_ip === TRUE && $session['ip_address'] !== $this->CI->input->ip_address())
Darren Hillc4e266b2011-08-30 15:40:27 -0400444 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300445 log_message('debug', 'Session: IP address mismatch');
Darren Hillc4e266b2011-08-30 15:40:27 -0400446 $this->sess_destroy();
447 return FALSE;
448 }
449
450 // Does the User Agent Match?
dchill42c5079de2012-07-23 10:53:47 -0400451 if ($this->sess_match_useragent === TRUE &&
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300452 trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
Darren Hillc4e266b2011-08-30 15:40:27 -0400453 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300454 log_message('debug', 'Session: User Agent string mismatch');
Darren Hillc4e266b2011-08-30 15:40:27 -0400455 $this->sess_destroy();
456 return FALSE;
457 }
458
459 // Is there a corresponding session in the DB?
460 if ($this->sess_use_database === TRUE)
461 {
462 $this->CI->db->where('session_id', $session['session_id']);
463
Alex Bilbied261b1e2012-06-02 11:12:16 +0100464 if ($this->sess_match_ip === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400465 {
466 $this->CI->db->where('ip_address', $session['ip_address']);
467 }
468
Alex Bilbied261b1e2012-06-02 11:12:16 +0100469 if ($this->sess_match_useragent === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400470 {
471 $this->CI->db->where('user_agent', $session['user_agent']);
472 }
473
dchill42cd436e92012-09-04 10:15:14 -0400474 // Is caching in effect? Turn it off
475 $db_cache = $this->CI->db->cache_on;
476 $this->CI->db->cache_off();
477
Timothy Warrenf1421922012-03-02 11:51:42 -0500478 $query = $this->CI->db->limit(1)->get($this->sess_table_name);
Darren Hillc4e266b2011-08-30 15:40:27 -0400479
dchill42cd436e92012-09-04 10:15:14 -0400480 // Was caching in effect?
481 if ($db_cache)
482 {
483 // Turn it back on
484 $this->CI->db->cache_on();
485 }
486
Darren Hillc4e266b2011-08-30 15:40:27 -0400487 // No result? Kill it!
Andrey Andreeva8e34ac2012-12-17 10:39:32 +0200488 if (empty($query) OR $query->num_rows() === 0)
Darren Hillc4e266b2011-08-30 15:40:27 -0400489 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300490 log_message('debug', 'Session: No match found in our database');
Darren Hillc4e266b2011-08-30 15:40:27 -0400491 $this->sess_destroy();
492 return FALSE;
493 }
494
495 // Is there custom data? If so, add it to the main session array
496 $row = $query->row();
Andrey Andreev5036c9c2012-06-04 15:34:56 +0300497 if ( ! empty($row->user_data))
Darren Hillc4e266b2011-08-30 15:40:27 -0400498 {
Jordan Eldredge5306cad2013-12-23 11:10:51 -0800499 $custom_data = unserialize(trim($row->user_data));
Darren Hillc4e266b2011-08-30 15:40:27 -0400500
501 if (is_array($custom_data))
502 {
dchill423cecd822012-08-28 21:37:27 -0400503 $session = $session + $custom_data;
Darren Hillc4e266b2011-08-30 15:40:27 -0400504 }
505 }
506 }
507
508 // Session is valid!
509 $this->userdata = $session;
Darren Hillc4e266b2011-08-30 15:40:27 -0400510 return TRUE;
511 }
512
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300513 // ------------------------------------------------------------------------
514
Darren Hillc4e266b2011-08-30 15:40:27 -0400515 /**
516 * Create a new session
517 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400518 * @return void
519 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400520 protected function _sess_create()
Darren Hillc4e266b2011-08-30 15:40:27 -0400521 {
dchill423cecd822012-08-28 21:37:27 -0400522 // Initialize userdata
Derek Allard2067d1a2008-11-13 22:59:24 +0000523 $this->userdata = array(
dchill423cecd822012-08-28 21:37:27 -0400524 'session_id' => $this->_make_sess_id(),
dchill42c5079de2012-07-23 10:53:47 -0400525 'ip_address' => $this->CI->input->ip_address(),
Daniel Robbins930d8ef2013-03-01 21:36:48 -0500526 'user_agent' => trim(substr($this->CI->input->user_agent(), 0, 120)),
dchill42c5079de2012-07-23 10:53:47 -0400527 'last_activity' => $this->now,
dchill42c5079de2012-07-23 10:53:47 -0400528 );
Darren Hillc4e266b2011-08-30 15:40:27 -0400529
Andrey Andreeve18de502013-07-17 19:59:20 +0300530 log_message('debug', 'Session: Creating new session ('.$this->userdata['session_id'].')');
531
dchill423cecd822012-08-28 21:37:27 -0400532 // Check for database
Darren Hillc4e266b2011-08-30 15:40:27 -0400533 if ($this->sess_use_database === TRUE)
534 {
dchill423cecd822012-08-28 21:37:27 -0400535 // Add empty user_data field and save the data to the DB
536 $this->CI->db->set('user_data', '')->insert($this->sess_table_name, $this->userdata);
Darren Hillc4e266b2011-08-30 15:40:27 -0400537 }
538
539 // Write the cookie
540 $this->_set_cookie();
541 }
542
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300543 // ------------------------------------------------------------------------
544
Darren Hillc4e266b2011-08-30 15:40:27 -0400545 /**
546 * Update an existing session
547 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300548 * @param bool Force update flag (default: false)
Darren Hillc4e266b2011-08-30 15:40:27 -0400549 * @return void
550 */
dchill42c5079de2012-07-23 10:53:47 -0400551 protected function _sess_update($force = FALSE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400552 {
553 // We only update the session every five minutes by default (unless forced)
dchill4277ee3fd2012-07-24 11:50:01 -0400554 if ( ! $force && ($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
Darren Hillc4e266b2011-08-30 15:40:27 -0400555 {
556 return;
557 }
558
dchill423cecd822012-08-28 21:37:27 -0400559 // Update last activity to now
560 $this->userdata['last_activity'] = $this->now;
Andrey Andreev9c622f32012-01-19 14:12:54 +0200561
dchill423cecd822012-08-28 21:37:27 -0400562 // Save the old session id so we know which DB record to update
563 $old_sessid = $this->userdata['session_id'];
564
565 // Changing the session ID during an AJAX call causes problems
566 if ( ! $this->CI->input->is_ajax_request())
Andrey Andreev9c622f32012-01-19 14:12:54 +0200567 {
dchill423cecd822012-08-28 21:37:27 -0400568 // Get new id
569 $this->userdata['session_id'] = $this->_make_sess_id();
Andrey Andreeve18de502013-07-17 19:59:20 +0300570
571 log_message('debug', 'Session: Regenerate ID');
Andrey Andreev9c622f32012-01-19 14:12:54 +0200572 }
573
dchill423cecd822012-08-28 21:37:27 -0400574 // Check for database
575 if ($this->sess_use_database === TRUE)
576 {
Andrey Andreeve2afc882012-11-01 01:35:34 +0200577 $this->CI->db->where('session_id', $old_sessid);
578
579 if ($this->sess_match_ip === TRUE)
580 {
581 $this->CI->db->where('ip_address', $this->CI->input->ip_address());
582 }
583
584 if ($this->sess_match_useragent === TRUE)
585 {
586 $this->CI->db->where('user_agent', trim(substr($this->CI->input->user_agent(), 0, 120)));
587 }
588
dchill423cecd822012-08-28 21:37:27 -0400589 // Update the session ID and last_activity field in the DB
Andrey Andreeve2afc882012-11-01 01:35:34 +0200590 $this->CI->db->update($this->sess_table_name,
591 array(
592 'last_activity' => $this->now,
593 'session_id' => $this->userdata['session_id']
594 )
595 );
dchill423cecd822012-08-28 21:37:27 -0400596 }
597
598 // Write the cookie
599 $this->_set_cookie();
600 }
601
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300602 // ------------------------------------------------------------------------
603
dchill423cecd822012-08-28 21:37:27 -0400604 /**
605 * Update database with current data
606 *
607 * This gets called from the shutdown function and also
608 * registered with PHP to run at the end of the request
609 * so it's guaranteed to update even when a fatal error
610 * occurs. The first call makes the update and clears the
611 * dirty flag so it won't happen twice.
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300612 *
613 * @return void
dchill423cecd822012-08-28 21:37:27 -0400614 */
615 public function _update_db()
616 {
617 // Check for database and dirty flag and unsaved
618 if ($this->sess_use_database === TRUE && $this->data_dirty === TRUE)
619 {
620 // Set up activity and data fields to be set
621 // If we don't find custom data, user_data will remain an empty string
622 $set = array(
623 'last_activity' => $this->userdata['last_activity'],
624 'user_data' => ''
625 );
626
627 // Get the custom userdata, leaving out the defaults
628 // (which get stored in the cookie)
629 $userdata = array_diff_key($this->userdata, $this->defaults);
630
631 // Did we find any custom data?
632 if ( ! empty($userdata))
633 {
634 // Serialize the custom data array so we can store it
Jordan Eldredge5306cad2013-12-23 11:10:51 -0800635 $set['user_data'] = serialize($userdata);
dchill423cecd822012-08-28 21:37:27 -0400636 }
637
Dionysis Arvanitisf8e2d0e2013-02-19 23:27:16 +0200638 // Reset query builder values.
639 $this->CI->db->reset_query();
640
dchill423cecd822012-08-28 21:37:27 -0400641 // Run the update query
642 // Any time we change the session id, it gets updated immediately,
643 // so our where clause below is always safe
Andrey Andreeve2afc882012-11-01 01:35:34 +0200644 $this->CI->db->where('session_id', $this->userdata['session_id']);
645
646 if ($this->sess_match_ip === TRUE)
647 {
648 $this->CI->db->where('ip_address', $this->CI->input->ip_address());
649 }
650
651 if ($this->sess_match_useragent === TRUE)
652 {
653 $this->CI->db->where('user_agent', trim(substr($this->CI->input->user_agent(), 0, 120)));
654 }
655
656 $this->CI->db->update($this->sess_table_name, $set);
dchill423cecd822012-08-28 21:37:27 -0400657
658 // Clear dirty flag to prevent double updates
659 $this->data_dirty = FALSE;
660
661 log_message('debug', 'CI_Session Data Saved To DB');
662 }
663 }
664
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300665 // ------------------------------------------------------------------------
666
dchill423cecd822012-08-28 21:37:27 -0400667 /**
668 * Generate a new session id
669 *
670 * @return string Hashed session id
671 */
672 protected function _make_sess_id()
673 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400674 $new_sessid = '';
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200675 do
Darren Hillc4e266b2011-08-30 15:40:27 -0400676 {
vlakoff06127562013-03-30 00:06:39 +0100677 $new_sessid .= mt_rand();
Darren Hillc4e266b2011-08-30 15:40:27 -0400678 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200679 while (strlen($new_sessid) < 32);
Darren Hillc4e266b2011-08-30 15:40:27 -0400680
681 // To make the session ID even more secure we'll combine it with the user's IP
682 $new_sessid .= $this->CI->input->ip_address();
683
dchill423cecd822012-08-28 21:37:27 -0400684 // Turn it into a hash and return
685 return md5(uniqid($new_sessid, TRUE));
Darren Hillc4e266b2011-08-30 15:40:27 -0400686 }
687
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300688 // ------------------------------------------------------------------------
689
Darren Hillc4e266b2011-08-30 15:40:27 -0400690 /**
691 * Get the "now" time
692 *
dchill42c5079de2012-07-23 10:53:47 -0400693 * @return int Time
Darren Hillc4e266b2011-08-30 15:40:27 -0400694 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400695 protected function _get_time()
Darren Hillc4e266b2011-08-30 15:40:27 -0400696 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300697 if ($this->time_reference === 'local' OR $this->time_reference === date_default_timezone_get())
Darren Hillc4e266b2011-08-30 15:40:27 -0400698 {
Iban Eguiafeb14da2012-06-12 16:09:36 +0200699 return time();
Darren Hillc4e266b2011-08-30 15:40:27 -0400700 }
701
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300702 $datetime = new DateTime('now', new DateTimeZone($this->time_reference));
Iban Eguiafeb14da2012-06-12 16:09:36 +0200703 sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
704
705 return mktime($hour, $minute, $second, $month, $day, $year);
Darren Hillc4e266b2011-08-30 15:40:27 -0400706 }
707
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300708 // ------------------------------------------------------------------------
709
Darren Hillc4e266b2011-08-30 15:40:27 -0400710 /**
711 * Write the session cookie
712 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400713 * @return void
714 */
dchill423cecd822012-08-28 21:37:27 -0400715 protected function _set_cookie()
Darren Hillc4e266b2011-08-30 15:40:27 -0400716 {
dchill423cecd822012-08-28 21:37:27 -0400717 // Get userdata (only defaults if database)
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300718 $cookie_data = ($this->sess_use_database === TRUE)
719 ? array_intersect_key($this->userdata, $this->defaults)
720 : $this->userdata;
Darren Hillc4e266b2011-08-30 15:40:27 -0400721
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200722 // The Input class will do this and since we use HMAC verification,
723 // unless we standardize here as well, the hash won't match.
724 if ($this->_standardize_newlines)
725 {
726 foreach (array_keys($this->userdata) as $key)
727 {
728 $this->userdata[$key] = preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $this->userdata[$key]);
729 }
730 }
731
Darren Hillc4e266b2011-08-30 15:40:27 -0400732 // Serialize the userdata for the cookie
Jordan Eldredge5306cad2013-12-23 11:10:51 -0800733 $cookie_data = serialize($cookie_data);
Darren Hillc4e266b2011-08-30 15:40:27 -0400734
Pascal Krietef69f0e82012-10-16 11:54:49 -0400735 if ($this->sess_encrypt_cookie === TRUE)
736 {
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200737 $cookie_data = $this->CI->encryption->encrypt($cookie_data);
Pascal Krietef69f0e82012-10-16 11:54:49 -0400738 }
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200739 else
740 {
741 // Require message authentication
742 $cookie_data .= hash_hmac('sha1', $cookie_data, $this->encryption_key);
743 }
Darren Hillc4e266b2011-08-30 15:40:27 -0400744
745 $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
746
747 // Set the cookie
dchill42c5872252012-07-30 14:53:11 -0400748 $this->_setcookie($this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain,
dchill42c5079de2012-07-23 10:53:47 -0400749 $this->cookie_secure, $this->cookie_httponly);
Darren Hillc4e266b2011-08-30 15:40:27 -0400750 }
751
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300752 // ------------------------------------------------------------------------
753
Darren Hillc4e266b2011-08-30 15:40:27 -0400754 /**
dchill42c5872252012-07-30 14:53:11 -0400755 * Set a cookie with the system
756 *
757 * This abstraction of the setcookie call allows overriding for unit testing
758 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300759 * @param string Cookie name
760 * @param string Cookie value
761 * @param int Expiration time
762 * @param string Cookie path
763 * @param string Cookie domain
764 * @param bool Secure connection flag
765 * @param bool HTTP protocol only flag
766 * @return void
dchill42c5872252012-07-30 14:53:11 -0400767 */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300768 protected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = FALSE, $httponly = FALSE)
dchill42c5872252012-07-30 14:53:11 -0400769 {
dchill42c5872252012-07-30 14:53:11 -0400770 setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
771 }
772
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300773 // ------------------------------------------------------------------------
774
dchill42c5872252012-07-30 14:53:11 -0400775 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400776 * Garbage collection
777 *
778 * This deletes expired session rows from database
779 * if the probability percentage is met
780 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400781 * @return void
782 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400783 protected function _sess_gc()
Darren Hillc4e266b2011-08-30 15:40:27 -0400784 {
Alex Bilbied261b1e2012-06-02 11:12:16 +0100785 if ($this->sess_use_database !== TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400786 {
787 return;
788 }
789
Christopher Guiney7a142862012-06-29 20:34:28 -0700790 $probability = ini_get('session.gc_probability');
791 $divisor = ini_get('session.gc_divisor');
792
Tyler Brownell74c5f262013-12-13 00:23:12 -0500793 if (mt_rand(1, $divisor) <= $probability)
Darren Hillc4e266b2011-08-30 15:40:27 -0400794 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000795 $expire = $this->now - $this->sess_expiration;
dchill423cecd822012-08-28 21:37:27 -0400796 $this->CI->db->delete($this->sess_table_name, 'last_activity < '.$expire);
Darren Hillc4e266b2011-08-30 15:40:27 -0400797
798 log_message('debug', 'Session garbage collection performed.');
799 }
800 }
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300801
Darren Hillc4e266b2011-08-30 15:40:27 -0400802}
Darren Hillc4e266b2011-08-30 15:40:27 -0400803
804/* End of file Session_cookie.php */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300805/* Location: ./system/libraries/Session/drivers/Session_cookie.php */