blob: 8f527ace739bcaadcbc506deb2e85e4c761e52a9 [file] [log] [blame]
Andrey Andreev57ffbbb2011-12-25 04:48:47 +02001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
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
dchill42c5079de2012-07-23 10:53:47 -040012 * 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
Greg Aker0defe5d2012-01-01 18:46:41 -060021 * @copyright Copyright (c) 2008 - 2012, 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 */
27
Darren Hillc4e266b2011-08-30 15:40:27 -040028/**
29 * Cookie-based session management driver
30 *
dchill423cecd822012-08-28 21:37:27 -040031 * This is the classic CI_Session functionality, as written by EllisLab, abstracted out to a driver.
Darren Hillc4e266b2011-08-30 15:40:27 -040032 *
33 * @package CodeIgniter
34 * @subpackage Libraries
35 * @category Sessions
Derek Jonesf4a4bd82011-10-20 12:18:42 -050036 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000037 * @link http://codeigniter.com/user_guide/libraries/sessions.html
Darren Hillc4e266b2011-08-30 15:40:27 -040038 */
Darren Hill5073a372011-08-31 13:54:19 -040039class CI_Session_cookie extends CI_Session_driver {
Andrey Andreev9ffcee62012-09-05 16:25:16 +030040
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020041 /**
Timothy Warren68f09812012-04-27 10:38:32 -040042 * Whether to encrypt the session cookie
43 *
44 * @var bool
45 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020046 public $sess_encrypt_cookie = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020047
Timothy Warren68f09812012-04-27 10:38:32 -040048 /**
49 * Whether to use to the database for session storage
50 *
51 * @var bool
52 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020053 public $sess_use_database = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020054
Timothy Warren68f09812012-04-27 10:38:32 -040055 /**
56 * Name of the database table in which to store sessions
57 *
58 * @var string
59 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020060 public $sess_table_name = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020061
Timothy Warren68f09812012-04-27 10:38:32 -040062 /**
63 * Length of time (in seconds) for sessions to expire
64 *
65 * @var int
66 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020067 public $sess_expiration = 7200;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020068
Timothy Warren68f09812012-04-27 10:38:32 -040069 /**
70 * Whether to kill session on close of browser window
71 *
72 * @var bool
73 */
dchill4226429202012-07-31 10:55:07 -040074 public $sess_expire_on_close = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020075
Timothy Warren68f09812012-04-27 10:38:32 -040076 /**
77 * Whether to match session on ip address
78 *
79 * @var bool
80 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020081 public $sess_match_ip = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020082
Timothy Warren68f09812012-04-27 10:38:32 -040083 /**
84 * Whether to match session on user-agent
85 *
86 * @var bool
87 */
dchill4226429202012-07-31 10:55:07 -040088 public $sess_match_useragent = TRUE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020089
Timothy Warren68f09812012-04-27 10:38:32 -040090 /**
91 * Name of session cookie
92 *
93 * @var string
94 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020095 public $sess_cookie_name = 'ci_session';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020096
Timothy Warren68f09812012-04-27 10:38:32 -040097 /**
98 * Session cookie prefix
99 *
100 * @var string
101 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200102 public $cookie_prefix = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200103
Timothy Warren68f09812012-04-27 10:38:32 -0400104 /**
105 * Session cookie path
106 *
107 * @var string
108 */
dchill4226429202012-07-31 10:55:07 -0400109 public $cookie_path = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200110
Timothy Warren68f09812012-04-27 10:38:32 -0400111 /**
112 * Session cookie domain
113 *
114 * @var string
115 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200116 public $cookie_domain = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200117
Timothy Warren68f09812012-04-27 10:38:32 -0400118 /**
119 * Whether to set the cookie only on HTTPS connections
120 *
121 * @var bool
122 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200123 public $cookie_secure = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200124
Timothy Warren68f09812012-04-27 10:38:32 -0400125 /**
126 * Whether cookie should be allowed only to be sent by the server
127 *
128 * @var bool
129 */
freewil4ad0fd82012-03-13 22:37:42 -0400130 public $cookie_httponly = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200131
Timothy Warren68f09812012-04-27 10:38:32 -0400132 /**
133 * Interval at which to update session
134 *
135 * @var int
136 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200137 public $sess_time_to_update = 300;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200138
Timothy Warren68f09812012-04-27 10:38:32 -0400139 /**
140 * Key with which to encrypt the session cookie
141 *
142 * @var string
143 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200144 public $encryption_key = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200145
Timothy Warren68f09812012-04-27 10:38:32 -0400146 /**
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300147 * Timezone to use for the current time
Timothy Warren68f09812012-04-27 10:38:32 -0400148 *
149 * @var string
150 */
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300151 public $time_reference = 'local';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200152
Timothy Warren68f09812012-04-27 10:38:32 -0400153 /**
154 * Session data
155 *
156 * @var array
157 */
dchill4226429202012-07-31 10:55:07 -0400158 public $userdata = array();
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200159
Timothy Warren68f09812012-04-27 10:38:32 -0400160 /**
Timothy Warren68f09812012-04-27 10:38:32 -0400161 * Current time
162 *
163 * @var int
164 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200165 public $now;
Darren Hillc4e266b2011-08-30 15:40:27 -0400166
167 /**
dchill423cecd822012-08-28 21:37:27 -0400168 * Default userdata keys
169 *
170 * @var array
171 */
172 protected $defaults = array(
dchill4288b636b2012-08-29 08:47:05 -0400173 'session_id' => NULL,
174 'ip_address' => NULL,
175 'user_agent' => NULL,
176 'last_activity' => NULL
dchill423cecd822012-08-28 21:37:27 -0400177 );
178
179 /**
180 * Data needs DB update flag
181 *
182 * @var bool
183 */
184 protected $data_dirty = FALSE;
185
186 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400187 * Initialize session driver object
188 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400189 * @return void
190 */
191 protected function initialize()
192 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400193 // Set all the session preferences, which can either be set
dchill4242b77a92012-07-23 11:28:42 -0400194 // manually via the $params array or via the config file
dchill4226429202012-07-31 10:55:07 -0400195 $prefs = array(
196 'sess_encrypt_cookie',
197 'sess_use_database',
198 'sess_table_name',
199 'sess_expiration',
200 'sess_expire_on_close',
201 'sess_match_ip',
202 'sess_match_useragent',
203 'sess_cookie_name',
204 'cookie_path',
205 'cookie_domain',
206 'cookie_secure',
207 'cookie_httponly',
208 'sess_time_to_update',
209 'time_reference',
210 'cookie_prefix',
211 'encryption_key'
212 );
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300213
dchill4226429202012-07-31 10:55:07 -0400214 foreach ($prefs as $key)
Darren Hillc4e266b2011-08-30 15:40:27 -0400215 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300216 $this->$key = isset($this->_parent->params[$key])
217 ? $this->_parent->params[$key]
218 : $this->CI->config->item($key);
Darren Hillc4e266b2011-08-30 15:40:27 -0400219 }
220
Alex Bilbied261b1e2012-06-02 11:12:16 +0100221 if ($this->encryption_key === '')
Darren Hillc4e266b2011-08-30 15:40:27 -0400222 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300223 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 -0400224 }
225
Darren Hillc4e266b2011-08-30 15:40:27 -0400226 // Do we need encryption? If so, load the encryption class
Alex Bilbied261b1e2012-06-02 11:12:16 +0100227 if ($this->sess_encrypt_cookie === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400228 {
229 $this->CI->load->library('encrypt');
230 }
231
dchill423cecd822012-08-28 21:37:27 -0400232 // Check for database
Alex Bilbied261b1e2012-06-02 11:12:16 +0100233 if ($this->sess_use_database === TRUE && $this->sess_table_name !== '')
Darren Hillc4e266b2011-08-30 15:40:27 -0400234 {
dchill423cecd822012-08-28 21:37:27 -0400235 // Load database driver
Darren Hillc4e266b2011-08-30 15:40:27 -0400236 $this->CI->load->database();
dchill423cecd822012-08-28 21:37:27 -0400237
238 // Register shutdown function
239 register_shutdown_function(array($this, '_update_db'));
Darren Hillc4e266b2011-08-30 15:40:27 -0400240 }
241
242 // Set the "now" time. Can either be GMT or server time, based on the config prefs.
243 // We use this to set the "last activity" time
244 $this->now = $this->_get_time();
245
246 // Set the session length. If the session expiration is
247 // set to zero we'll set the expiration two years from now.
Alex Bilbied261b1e2012-06-02 11:12:16 +0100248 if ($this->sess_expiration === 0)
Darren Hillc4e266b2011-08-30 15:40:27 -0400249 {
250 $this->sess_expiration = (60*60*24*365*2);
251 }
252
253 // Set the cookie name
254 $this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
255
256 // Run the Session routine. If a session doesn't exist we'll
257 // create a new one. If it does, we'll update it.
258 if ( ! $this->_sess_read())
259 {
260 $this->_sess_create();
261 }
262 else
263 {
264 $this->_sess_update();
265 }
266
267 // Delete expired sessions if necessary
268 $this->_sess_gc();
269 }
270
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300271 // ------------------------------------------------------------------------
272
Darren Hillc4e266b2011-08-30 15:40:27 -0400273 /**
274 * Write the session data
275 *
276 * @return void
277 */
278 public function sess_save()
279 {
dchill423cecd822012-08-28 21:37:27 -0400280 // Check for database
dchill4288b636b2012-08-29 08:47:05 -0400281 if ($this->sess_use_database === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400282 {
dchill423cecd822012-08-28 21:37:27 -0400283 // Mark custom data as dirty so we know to update the DB
284 $this->data_dirty = TRUE;
Darren Hillc4e266b2011-08-30 15:40:27 -0400285 }
286
dchill423cecd822012-08-28 21:37:27 -0400287 // Write the cookie
288 $this->_set_cookie();
Darren Hillc4e266b2011-08-30 15:40:27 -0400289 }
290
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300291 // ------------------------------------------------------------------------
292
Darren Hillc4e266b2011-08-30 15:40:27 -0400293 /**
294 * Destroy the current session
295 *
296 * @return void
297 */
298 public function sess_destroy()
299 {
300 // Kill the session DB row
dchill42aee92652012-08-26 21:45:35 -0400301 if ($this->sess_use_database === TRUE && isset($this->userdata['session_id']))
Darren Hillc4e266b2011-08-30 15:40:27 -0400302 {
dchill423cecd822012-08-28 21:37:27 -0400303 $this->CI->db->delete($this->sess_table_name, array('session_id' => $this->userdata['session_id']));
dchill4297b0d832012-09-04 10:09:00 -0400304 $this->data_dirty = FALSE;
Darren Hillc4e266b2011-08-30 15:40:27 -0400305 }
306
307 // Kill the cookie
Andrey Andreevcf264e02012-10-18 16:14:51 +0300308 $this->_setcookie($this->sess_cookie_name, '', ($this->now - 31500000),
Darren Hillc4e266b2011-08-30 15:40:27 -0400309 $this->cookie_path, $this->cookie_domain, 0);
dchill42c5079de2012-07-23 10:53:47 -0400310
311 // Kill session data
312 $this->userdata = array();
Darren Hillc4e266b2011-08-30 15:40:27 -0400313 }
314
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300315 // ------------------------------------------------------------------------
316
Darren Hillc4e266b2011-08-30 15:40:27 -0400317 /**
318 * Regenerate the current session
319 *
320 * Regenerate the session id
321 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300322 * @param bool Destroy session data flag (default: false)
Darren Hillc4e266b2011-08-30 15:40:27 -0400323 * @return void
324 */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300325 public function sess_regenerate($destroy = FALSE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400326 {
327 // Check destroy flag
328 if ($destroy)
329 {
330 // Destroy old session and create new one
331 $this->sess_destroy();
332 $this->_sess_create();
333 }
334 else
335 {
336 // Just force an update to recreate the id
Andrey Andreev3f3f1352012-09-05 16:39:28 +0300337 $this->_sess_update(TRUE);
Darren Hillc4e266b2011-08-30 15:40:27 -0400338 }
339 }
340
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300341 // ------------------------------------------------------------------------
342
Darren Hillc4e266b2011-08-30 15:40:27 -0400343 /**
344 * Get a reference to user data array
345 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300346 * @return array Reference to userdata
Darren Hillc4e266b2011-08-30 15:40:27 -0400347 */
348 public function &get_userdata()
349 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400350 return $this->userdata;
351 }
352
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300353 // ------------------------------------------------------------------------
354
Darren Hillc4e266b2011-08-30 15:40:27 -0400355 /**
356 * Fetch the current session data if it exists
357 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400358 * @return bool
359 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400360 protected function _sess_read()
Darren Hillc4e266b2011-08-30 15:40:27 -0400361 {
362 // Fetch the cookie
363 $session = $this->CI->input->cookie($this->sess_cookie_name);
364
365 // No cookie? Goodbye cruel world!...
Phil Sturgeon55a6ddb2012-05-23 18:37:24 +0100366 if ($session === NULL)
Darren Hillc4e266b2011-08-30 15:40:27 -0400367 {
368 log_message('debug', 'A session cookie was not found.');
369 return FALSE;
370 }
371
Pascal Krietef69f0e82012-10-16 11:54:49 -0400372 $len = strlen($session) - 40;
373
374 if ($len < 0)
375 {
376 log_message('debug', 'The session cookie was not signed.');
377 return FALSE;
378 }
379
380 // Check cookie authentication
381 $hmac = substr($session, $len);
382 $session = substr($session, 0, $len);
383
384 if ($hmac !== hash_hmac('sha1', $session, $this->encryption_key))
385 {
Pascal Kriete28dc2022012-10-17 11:27:29 -0400386 log_message('error', 'The session cookie data did not match what was expected.');
Pascal Krietef69f0e82012-10-16 11:54:49 -0400387 $this->sess_destroy();
388 return FALSE;
389 }
390
dchill423cecd822012-08-28 21:37:27 -0400391 // Check for encryption
Alex Bilbied261b1e2012-06-02 11:12:16 +0100392 if ($this->sess_encrypt_cookie === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400393 {
dchill423cecd822012-08-28 21:37:27 -0400394 // Decrypt the cookie data
Darren Hillc4e266b2011-08-30 15:40:27 -0400395 $session = $this->CI->encrypt->decode($session);
396 }
Darren Hillc4e266b2011-08-30 15:40:27 -0400397
398 // Unserialize the session array
399 $session = $this->_unserialize($session);
400
401 // Is the session data we unserialized an array with the correct format?
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300402 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 -0400403 {
404 $this->sess_destroy();
405 return FALSE;
406 }
407
408 // Is the session current?
Andrey Andreev02117682012-10-15 11:12:37 +0300409 if (($session['last_activity'] + $this->sess_expiration) < $this->now OR $session['last_activity'] > $this->now)
Darren Hillc4e266b2011-08-30 15:40:27 -0400410 {
411 $this->sess_destroy();
412 return FALSE;
413 }
414
Andrey Andreev7e087f52012-01-20 11:46:27 +0200415 // Does the IP match?
Alex Bilbied261b1e2012-06-02 11:12:16 +0100416 if ($this->sess_match_ip === TRUE && $session['ip_address'] !== $this->CI->input->ip_address())
Darren Hillc4e266b2011-08-30 15:40:27 -0400417 {
418 $this->sess_destroy();
419 return FALSE;
420 }
421
422 // Does the User Agent Match?
dchill42c5079de2012-07-23 10:53:47 -0400423 if ($this->sess_match_useragent === TRUE &&
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300424 trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
Darren Hillc4e266b2011-08-30 15:40:27 -0400425 {
426 $this->sess_destroy();
427 return FALSE;
428 }
429
430 // Is there a corresponding session in the DB?
431 if ($this->sess_use_database === TRUE)
432 {
433 $this->CI->db->where('session_id', $session['session_id']);
434
Alex Bilbied261b1e2012-06-02 11:12:16 +0100435 if ($this->sess_match_ip === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400436 {
437 $this->CI->db->where('ip_address', $session['ip_address']);
438 }
439
Alex Bilbied261b1e2012-06-02 11:12:16 +0100440 if ($this->sess_match_useragent === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400441 {
442 $this->CI->db->where('user_agent', $session['user_agent']);
443 }
444
dchill42cd436e92012-09-04 10:15:14 -0400445 // Is caching in effect? Turn it off
446 $db_cache = $this->CI->db->cache_on;
447 $this->CI->db->cache_off();
448
Timothy Warrenf1421922012-03-02 11:51:42 -0500449 $query = $this->CI->db->limit(1)->get($this->sess_table_name);
Darren Hillc4e266b2011-08-30 15:40:27 -0400450
dchill42cd436e92012-09-04 10:15:14 -0400451 // Was caching in effect?
452 if ($db_cache)
453 {
454 // Turn it back on
455 $this->CI->db->cache_on();
456 }
457
Darren Hillc4e266b2011-08-30 15:40:27 -0400458 // No result? Kill it!
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200459 if ($query->num_rows() === 0)
Darren Hillc4e266b2011-08-30 15:40:27 -0400460 {
461 $this->sess_destroy();
462 return FALSE;
463 }
464
465 // Is there custom data? If so, add it to the main session array
466 $row = $query->row();
Andrey Andreev5036c9c2012-06-04 15:34:56 +0300467 if ( ! empty($row->user_data))
Darren Hillc4e266b2011-08-30 15:40:27 -0400468 {
469 $custom_data = $this->_unserialize($row->user_data);
470
471 if (is_array($custom_data))
472 {
dchill423cecd822012-08-28 21:37:27 -0400473 $session = $session + $custom_data;
Darren Hillc4e266b2011-08-30 15:40:27 -0400474 }
475 }
476 }
477
478 // Session is valid!
479 $this->userdata = $session;
Darren Hillc4e266b2011-08-30 15:40:27 -0400480 return TRUE;
481 }
482
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300483 // ------------------------------------------------------------------------
484
Darren Hillc4e266b2011-08-30 15:40:27 -0400485 /**
486 * Create a new session
487 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400488 * @return void
489 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400490 protected function _sess_create()
Darren Hillc4e266b2011-08-30 15:40:27 -0400491 {
dchill423cecd822012-08-28 21:37:27 -0400492 // Initialize userdata
Derek Allard2067d1a2008-11-13 22:59:24 +0000493 $this->userdata = array(
dchill423cecd822012-08-28 21:37:27 -0400494 'session_id' => $this->_make_sess_id(),
dchill42c5079de2012-07-23 10:53:47 -0400495 'ip_address' => $this->CI->input->ip_address(),
496 'user_agent' => substr($this->CI->input->user_agent(), 0, 120),
497 'last_activity' => $this->now,
dchill42c5079de2012-07-23 10:53:47 -0400498 );
Darren Hillc4e266b2011-08-30 15:40:27 -0400499
dchill423cecd822012-08-28 21:37:27 -0400500 // Check for database
Darren Hillc4e266b2011-08-30 15:40:27 -0400501 if ($this->sess_use_database === TRUE)
502 {
dchill423cecd822012-08-28 21:37:27 -0400503 // Add empty user_data field and save the data to the DB
504 $this->CI->db->set('user_data', '')->insert($this->sess_table_name, $this->userdata);
Darren Hillc4e266b2011-08-30 15:40:27 -0400505 }
506
507 // Write the cookie
508 $this->_set_cookie();
509 }
510
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300511 // ------------------------------------------------------------------------
512
Darren Hillc4e266b2011-08-30 15:40:27 -0400513 /**
514 * Update an existing session
515 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300516 * @param bool Force update flag (default: false)
Darren Hillc4e266b2011-08-30 15:40:27 -0400517 * @return void
518 */
dchill42c5079de2012-07-23 10:53:47 -0400519 protected function _sess_update($force = FALSE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400520 {
521 // We only update the session every five minutes by default (unless forced)
dchill4277ee3fd2012-07-24 11:50:01 -0400522 if ( ! $force && ($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
Darren Hillc4e266b2011-08-30 15:40:27 -0400523 {
524 return;
525 }
526
dchill423cecd822012-08-28 21:37:27 -0400527 // Update last activity to now
528 $this->userdata['last_activity'] = $this->now;
Andrey Andreev9c622f32012-01-19 14:12:54 +0200529
dchill423cecd822012-08-28 21:37:27 -0400530 // Save the old session id so we know which DB record to update
531 $old_sessid = $this->userdata['session_id'];
532
533 // Changing the session ID during an AJAX call causes problems
534 if ( ! $this->CI->input->is_ajax_request())
Andrey Andreev9c622f32012-01-19 14:12:54 +0200535 {
dchill423cecd822012-08-28 21:37:27 -0400536 // Get new id
537 $this->userdata['session_id'] = $this->_make_sess_id();
Andrey Andreev9c622f32012-01-19 14:12:54 +0200538 }
539
dchill423cecd822012-08-28 21:37:27 -0400540 // Check for database
541 if ($this->sess_use_database === TRUE)
542 {
Andrey Andreeve2afc882012-11-01 01:35:34 +0200543 $this->CI->db->where('session_id', $old_sessid);
544
545 if ($this->sess_match_ip === TRUE)
546 {
547 $this->CI->db->where('ip_address', $this->CI->input->ip_address());
548 }
549
550 if ($this->sess_match_useragent === TRUE)
551 {
552 $this->CI->db->where('user_agent', trim(substr($this->CI->input->user_agent(), 0, 120)));
553 }
554
dchill423cecd822012-08-28 21:37:27 -0400555 // Update the session ID and last_activity field in the DB
Andrey Andreeve2afc882012-11-01 01:35:34 +0200556 $this->CI->db->update($this->sess_table_name,
557 array(
558 'last_activity' => $this->now,
559 'session_id' => $this->userdata['session_id']
560 )
561 );
dchill423cecd822012-08-28 21:37:27 -0400562 }
563
564 // Write the cookie
565 $this->_set_cookie();
566 }
567
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300568 // ------------------------------------------------------------------------
569
dchill423cecd822012-08-28 21:37:27 -0400570 /**
571 * Update database with current data
572 *
573 * This gets called from the shutdown function and also
574 * registered with PHP to run at the end of the request
575 * so it's guaranteed to update even when a fatal error
576 * occurs. The first call makes the update and clears the
577 * dirty flag so it won't happen twice.
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300578 *
579 * @return void
dchill423cecd822012-08-28 21:37:27 -0400580 */
581 public function _update_db()
582 {
583 // Check for database and dirty flag and unsaved
584 if ($this->sess_use_database === TRUE && $this->data_dirty === TRUE)
585 {
586 // Set up activity and data fields to be set
587 // If we don't find custom data, user_data will remain an empty string
588 $set = array(
589 'last_activity' => $this->userdata['last_activity'],
590 'user_data' => ''
591 );
592
593 // Get the custom userdata, leaving out the defaults
594 // (which get stored in the cookie)
595 $userdata = array_diff_key($this->userdata, $this->defaults);
596
597 // Did we find any custom data?
598 if ( ! empty($userdata))
599 {
600 // Serialize the custom data array so we can store it
601 $set['user_data'] = $this->_serialize($userdata);
602 }
603
604 // Run the update query
605 // Any time we change the session id, it gets updated immediately,
606 // so our where clause below is always safe
Andrey Andreeve2afc882012-11-01 01:35:34 +0200607 $this->CI->db->where('session_id', $this->userdata['session_id']);
608
609 if ($this->sess_match_ip === TRUE)
610 {
611 $this->CI->db->where('ip_address', $this->CI->input->ip_address());
612 }
613
614 if ($this->sess_match_useragent === TRUE)
615 {
616 $this->CI->db->where('user_agent', trim(substr($this->CI->input->user_agent(), 0, 120)));
617 }
618
619 $this->CI->db->update($this->sess_table_name, $set);
dchill423cecd822012-08-28 21:37:27 -0400620
621 // Clear dirty flag to prevent double updates
622 $this->data_dirty = FALSE;
623
624 log_message('debug', 'CI_Session Data Saved To DB');
625 }
626 }
627
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300628 // ------------------------------------------------------------------------
629
dchill423cecd822012-08-28 21:37:27 -0400630 /**
631 * Generate a new session id
632 *
633 * @return string Hashed session id
634 */
635 protected function _make_sess_id()
636 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400637 $new_sessid = '';
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200638 do
Darren Hillc4e266b2011-08-30 15:40:27 -0400639 {
640 $new_sessid .= mt_rand(0, mt_getrandmax());
641 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200642 while (strlen($new_sessid) < 32);
Darren Hillc4e266b2011-08-30 15:40:27 -0400643
644 // To make the session ID even more secure we'll combine it with the user's IP
645 $new_sessid .= $this->CI->input->ip_address();
646
dchill423cecd822012-08-28 21:37:27 -0400647 // Turn it into a hash and return
648 return md5(uniqid($new_sessid, TRUE));
Darren Hillc4e266b2011-08-30 15:40:27 -0400649 }
650
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300651 // ------------------------------------------------------------------------
652
Darren Hillc4e266b2011-08-30 15:40:27 -0400653 /**
654 * Get the "now" time
655 *
dchill42c5079de2012-07-23 10:53:47 -0400656 * @return int Time
Darren Hillc4e266b2011-08-30 15:40:27 -0400657 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400658 protected function _get_time()
Darren Hillc4e266b2011-08-30 15:40:27 -0400659 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300660 if ($this->time_reference === 'local' OR $this->time_reference === date_default_timezone_get())
Darren Hillc4e266b2011-08-30 15:40:27 -0400661 {
Iban Eguiafeb14da2012-06-12 16:09:36 +0200662 return time();
Darren Hillc4e266b2011-08-30 15:40:27 -0400663 }
664
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300665 $datetime = new DateTime('now', new DateTimeZone($this->time_reference));
Iban Eguiafeb14da2012-06-12 16:09:36 +0200666 sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
667
668 return mktime($hour, $minute, $second, $month, $day, $year);
Darren Hillc4e266b2011-08-30 15:40:27 -0400669 }
670
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300671 // ------------------------------------------------------------------------
672
Darren Hillc4e266b2011-08-30 15:40:27 -0400673 /**
674 * Write the session cookie
675 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400676 * @return void
677 */
dchill423cecd822012-08-28 21:37:27 -0400678 protected function _set_cookie()
Darren Hillc4e266b2011-08-30 15:40:27 -0400679 {
dchill423cecd822012-08-28 21:37:27 -0400680 // Get userdata (only defaults if database)
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300681 $cookie_data = ($this->sess_use_database === TRUE)
682 ? array_intersect_key($this->userdata, $this->defaults)
683 : $this->userdata;
Darren Hillc4e266b2011-08-30 15:40:27 -0400684
685 // Serialize the userdata for the cookie
686 $cookie_data = $this->_serialize($cookie_data);
687
Pascal Krietef69f0e82012-10-16 11:54:49 -0400688 if ($this->sess_encrypt_cookie === TRUE)
689 {
Andrey Andreevcf264e02012-10-18 16:14:51 +0300690 $cookie_data = $this->CI->encrypt->encode($cookie_data);
Pascal Krietef69f0e82012-10-16 11:54:49 -0400691 }
692
693 // Require message authentication
694 $cookie_data .= hash_hmac('sha1', $cookie_data, $this->encryption_key);
Darren Hillc4e266b2011-08-30 15:40:27 -0400695
696 $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
697
698 // Set the cookie
dchill42c5872252012-07-30 14:53:11 -0400699 $this->_setcookie($this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain,
dchill42c5079de2012-07-23 10:53:47 -0400700 $this->cookie_secure, $this->cookie_httponly);
Darren Hillc4e266b2011-08-30 15:40:27 -0400701 }
702
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300703 // ------------------------------------------------------------------------
704
Darren Hillc4e266b2011-08-30 15:40:27 -0400705 /**
dchill42c5872252012-07-30 14:53:11 -0400706 * Set a cookie with the system
707 *
708 * This abstraction of the setcookie call allows overriding for unit testing
709 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300710 * @param string Cookie name
711 * @param string Cookie value
712 * @param int Expiration time
713 * @param string Cookie path
714 * @param string Cookie domain
715 * @param bool Secure connection flag
716 * @param bool HTTP protocol only flag
717 * @return void
dchill42c5872252012-07-30 14:53:11 -0400718 */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300719 protected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = FALSE, $httponly = FALSE)
dchill42c5872252012-07-30 14:53:11 -0400720 {
dchill42c5872252012-07-30 14:53:11 -0400721 setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
722 }
723
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300724 // ------------------------------------------------------------------------
725
dchill42c5872252012-07-30 14:53:11 -0400726 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400727 * Serialize an array
728 *
729 * This function first converts any slashes found in the array to a temporary
730 * marker, so when it gets unserialized the slashes will be preserved
731 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400732 * @param mixed Data to serialize
dchill42c5079de2012-07-23 10:53:47 -0400733 * @return string Serialized data
Darren Hillc4e266b2011-08-30 15:40:27 -0400734 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400735 protected function _serialize($data)
Darren Hillc4e266b2011-08-30 15:40:27 -0400736 {
737 if (is_array($data))
738 {
Chris Muench95933492011-10-16 14:14:04 -0400739 array_walk_recursive($data, array(&$this, '_escape_slashes'));
Darren Hillc4e266b2011-08-30 15:40:27 -0400740 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200741 elseif (is_string($data))
Darren Hillc4e266b2011-08-30 15:40:27 -0400742 {
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200743 $data = str_replace('\\', '{{slash}}', $data);
Darren Hillc4e266b2011-08-30 15:40:27 -0400744 }
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300745
Darren Hillc4e266b2011-08-30 15:40:27 -0400746 return serialize($data);
747 }
748
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300749 // ------------------------------------------------------------------------
750
Darren Hillc4e266b2011-08-30 15:40:27 -0400751 /**
Chris Muench95933492011-10-16 14:14:04 -0400752 * Escape slashes
753 *
754 * This function converts any slashes found into a temporary marker
755 *
dchill42c5079de2012-07-23 10:53:47 -0400756 * @param string Value
757 * @param string Key
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200758 * @return void
Chris Muench95933492011-10-16 14:14:04 -0400759 */
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200760 protected function _escape_slashes(&$val, $key)
Chris Muench95933492011-10-16 14:14:04 -0400761 {
762 if (is_string($val))
763 {
764 $val = str_replace('\\', '{{slash}}', $val);
765 }
766 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000767
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300768 // ------------------------------------------------------------------------
769
Derek Allard2067d1a2008-11-13 22:59:24 +0000770 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400771 * Unserialize
772 *
773 * This function unserializes a data string, then converts any
774 * temporary slash markers back to actual slashes
775 *
dchill42c5079de2012-07-23 10:53:47 -0400776 * @param mixed Data to unserialize
777 * @return mixed Unserialized data
Darren Hillc4e266b2011-08-30 15:40:27 -0400778 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400779 protected function _unserialize($data)
Darren Hillc4e266b2011-08-30 15:40:27 -0400780 {
Andrey Andreevca20d842012-10-27 03:02:38 +0300781 $data = @unserialize(trim($data));
Darren Hillc4e266b2011-08-30 15:40:27 -0400782
783 if (is_array($data))
784 {
Chris Muench95933492011-10-16 14:14:04 -0400785 array_walk_recursive($data, array(&$this, '_unescape_slashes'));
Darren Hillc4e266b2011-08-30 15:40:27 -0400786 return $data;
787 }
788
Andrey Andreev6b831232012-03-06 11:16:57 +0200789 return is_string($data) ? str_replace('{{slash}}', '\\', $data) : $data;
Derek Allard2067d1a2008-11-13 22:59:24 +0000790 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200791
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300792 // ------------------------------------------------------------------------
793
Chris Muench95933492011-10-16 14:14:04 -0400794 /**
795 * Unescape slashes
796 *
797 * This function converts any slash markers back into actual slashes
798 *
dchill42c5079de2012-07-23 10:53:47 -0400799 * @param string Value
800 * @param string Key
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200801 * @return void
Chris Muench95933492011-10-16 14:14:04 -0400802 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200803 protected function _unescape_slashes(&$val, $key)
Chris Muench95933492011-10-16 14:14:04 -0400804 {
Chris Muench3e414f92011-10-16 23:03:55 -0400805 if (is_string($val))
806 {
807 $val= str_replace('{{slash}}', '\\', $val);
808 }
Darren Hillc4e266b2011-08-30 15:40:27 -0400809 }
810
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300811 // ------------------------------------------------------------------------
812
Darren Hillc4e266b2011-08-30 15:40:27 -0400813 /**
814 * Garbage collection
815 *
816 * This deletes expired session rows from database
817 * if the probability percentage is met
818 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400819 * @return void
820 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400821 protected function _sess_gc()
Darren Hillc4e266b2011-08-30 15:40:27 -0400822 {
Alex Bilbied261b1e2012-06-02 11:12:16 +0100823 if ($this->sess_use_database !== TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400824 {
825 return;
826 }
827
Christopher Guiney7a142862012-06-29 20:34:28 -0700828 $probability = ini_get('session.gc_probability');
829 $divisor = ini_get('session.gc_divisor');
830
Darren Hillc4e266b2011-08-30 15:40:27 -0400831 srand(time());
Christopher Guiney7a142862012-06-29 20:34:28 -0700832 if ((mt_rand(0, $divisor) / $divisor) < $probability)
Darren Hillc4e266b2011-08-30 15:40:27 -0400833 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000834 $expire = $this->now - $this->sess_expiration;
dchill423cecd822012-08-28 21:37:27 -0400835 $this->CI->db->delete($this->sess_table_name, 'last_activity < '.$expire);
Darren Hillc4e266b2011-08-30 15:40:27 -0400836
837 log_message('debug', 'Session garbage collection performed.');
838 }
839 }
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300840
Darren Hillc4e266b2011-08-30 15:40:27 -0400841}
Darren Hillc4e266b2011-08-30 15:40:27 -0400842
843/* End of file Session_cookie.php */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300844/* Location: ./system/libraries/Session/drivers/Session_cookie.php */