blob: 4f415cc0d6f7b917b06fe24fa3b60f4d070f74c1 [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 /**
161 * Reference to CodeIgniter instance
162 *
163 * @var object
164 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200165 public $CI;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200166
Timothy Warren68f09812012-04-27 10:38:32 -0400167 /**
168 * Current time
169 *
170 * @var int
171 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200172 public $now;
Darren Hillc4e266b2011-08-30 15:40:27 -0400173
174 /**
dchill423cecd822012-08-28 21:37:27 -0400175 * Default userdata keys
176 *
177 * @var array
178 */
179 protected $defaults = array(
dchill4288b636b2012-08-29 08:47:05 -0400180 'session_id' => NULL,
181 'ip_address' => NULL,
182 'user_agent' => NULL,
183 'last_activity' => NULL
dchill423cecd822012-08-28 21:37:27 -0400184 );
185
186 /**
187 * Data needs DB update flag
188 *
189 * @var bool
190 */
191 protected $data_dirty = FALSE;
192
193 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400194 * Initialize session driver object
195 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400196 * @return void
197 */
198 protected function initialize()
199 {
200 // Set the super object to a local variable for use throughout the class
201 $this->CI =& get_instance();
202
203 // Set all the session preferences, which can either be set
dchill4242b77a92012-07-23 11:28:42 -0400204 // manually via the $params array or via the config file
dchill4226429202012-07-31 10:55:07 -0400205 $prefs = array(
206 'sess_encrypt_cookie',
207 'sess_use_database',
208 'sess_table_name',
209 'sess_expiration',
210 'sess_expire_on_close',
211 'sess_match_ip',
212 'sess_match_useragent',
213 'sess_cookie_name',
214 'cookie_path',
215 'cookie_domain',
216 'cookie_secure',
217 'cookie_httponly',
218 'sess_time_to_update',
219 'time_reference',
220 'cookie_prefix',
221 'encryption_key'
222 );
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300223
dchill4226429202012-07-31 10:55:07 -0400224 foreach ($prefs as $key)
Darren Hillc4e266b2011-08-30 15:40:27 -0400225 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300226 $this->$key = isset($this->_parent->params[$key])
227 ? $this->_parent->params[$key]
228 : $this->CI->config->item($key);
Darren Hillc4e266b2011-08-30 15:40:27 -0400229 }
230
Alex Bilbied261b1e2012-06-02 11:12:16 +0100231 if ($this->encryption_key === '')
Darren Hillc4e266b2011-08-30 15:40:27 -0400232 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300233 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 -0400234 }
235
236 // Load the string helper so we can use the strip_slashes() function
237 $this->CI->load->helper('string');
238
239 // Do we need encryption? If so, load the encryption class
Alex Bilbied261b1e2012-06-02 11:12:16 +0100240 if ($this->sess_encrypt_cookie === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400241 {
242 $this->CI->load->library('encrypt');
243 }
244
dchill423cecd822012-08-28 21:37:27 -0400245 // Check for database
Alex Bilbied261b1e2012-06-02 11:12:16 +0100246 if ($this->sess_use_database === TRUE && $this->sess_table_name !== '')
Darren Hillc4e266b2011-08-30 15:40:27 -0400247 {
dchill423cecd822012-08-28 21:37:27 -0400248 // Load database driver
Darren Hillc4e266b2011-08-30 15:40:27 -0400249 $this->CI->load->database();
dchill423cecd822012-08-28 21:37:27 -0400250
251 // Register shutdown function
252 register_shutdown_function(array($this, '_update_db'));
Darren Hillc4e266b2011-08-30 15:40:27 -0400253 }
254
255 // Set the "now" time. Can either be GMT or server time, based on the config prefs.
256 // We use this to set the "last activity" time
257 $this->now = $this->_get_time();
258
259 // Set the session length. If the session expiration is
260 // set to zero we'll set the expiration two years from now.
Alex Bilbied261b1e2012-06-02 11:12:16 +0100261 if ($this->sess_expiration === 0)
Darren Hillc4e266b2011-08-30 15:40:27 -0400262 {
263 $this->sess_expiration = (60*60*24*365*2);
264 }
265
266 // Set the cookie name
267 $this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
268
269 // Run the Session routine. If a session doesn't exist we'll
270 // create a new one. If it does, we'll update it.
271 if ( ! $this->_sess_read())
272 {
273 $this->_sess_create();
274 }
275 else
276 {
277 $this->_sess_update();
278 }
279
280 // Delete expired sessions if necessary
281 $this->_sess_gc();
282 }
283
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300284 // ------------------------------------------------------------------------
285
Darren Hillc4e266b2011-08-30 15:40:27 -0400286 /**
287 * Write the session data
288 *
289 * @return void
290 */
291 public function sess_save()
292 {
dchill423cecd822012-08-28 21:37:27 -0400293 // Check for database
dchill4288b636b2012-08-29 08:47:05 -0400294 if ($this->sess_use_database === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400295 {
dchill423cecd822012-08-28 21:37:27 -0400296 // Mark custom data as dirty so we know to update the DB
297 $this->data_dirty = TRUE;
Darren Hillc4e266b2011-08-30 15:40:27 -0400298 }
299
dchill423cecd822012-08-28 21:37:27 -0400300 // Write the cookie
301 $this->_set_cookie();
Darren Hillc4e266b2011-08-30 15:40:27 -0400302 }
303
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300304 // ------------------------------------------------------------------------
305
Darren Hillc4e266b2011-08-30 15:40:27 -0400306 /**
307 * Destroy the current session
308 *
309 * @return void
310 */
311 public function sess_destroy()
312 {
313 // Kill the session DB row
dchill42aee92652012-08-26 21:45:35 -0400314 if ($this->sess_use_database === TRUE && isset($this->userdata['session_id']))
Darren Hillc4e266b2011-08-30 15:40:27 -0400315 {
dchill423cecd822012-08-28 21:37:27 -0400316 $this->CI->db->delete($this->sess_table_name, array('session_id' => $this->userdata['session_id']));
dchill4297b0d832012-09-04 10:09:00 -0400317 $this->data_dirty = FALSE;
Darren Hillc4e266b2011-08-30 15:40:27 -0400318 }
319
320 // Kill the cookie
dchill42c5872252012-07-30 14:53:11 -0400321 $this->_setcookie($this->sess_cookie_name, addslashes(serialize(array())), ($this->now - 31500000),
Darren Hillc4e266b2011-08-30 15:40:27 -0400322 $this->cookie_path, $this->cookie_domain, 0);
dchill42c5079de2012-07-23 10:53:47 -0400323
324 // Kill session data
325 $this->userdata = array();
Darren Hillc4e266b2011-08-30 15:40:27 -0400326 }
327
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300328 // ------------------------------------------------------------------------
329
Darren Hillc4e266b2011-08-30 15:40:27 -0400330 /**
331 * Regenerate the current session
332 *
333 * Regenerate the session id
334 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300335 * @param bool Destroy session data flag (default: false)
Darren Hillc4e266b2011-08-30 15:40:27 -0400336 * @return void
337 */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300338 public function sess_regenerate($destroy = FALSE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400339 {
340 // Check destroy flag
341 if ($destroy)
342 {
343 // Destroy old session and create new one
344 $this->sess_destroy();
345 $this->_sess_create();
346 }
347 else
348 {
349 // Just force an update to recreate the id
Andrey Andreev3f3f1352012-09-05 16:39:28 +0300350 $this->_sess_update(TRUE);
Darren Hillc4e266b2011-08-30 15:40:27 -0400351 }
352 }
353
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300354 // ------------------------------------------------------------------------
355
Darren Hillc4e266b2011-08-30 15:40:27 -0400356 /**
357 * Get a reference to user data array
358 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300359 * @return array Reference to userdata
Darren Hillc4e266b2011-08-30 15:40:27 -0400360 */
361 public function &get_userdata()
362 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400363 return $this->userdata;
364 }
365
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300366 // ------------------------------------------------------------------------
367
Darren Hillc4e266b2011-08-30 15:40:27 -0400368 /**
369 * Fetch the current session data if it exists
370 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400371 * @return bool
372 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400373 protected function _sess_read()
Darren Hillc4e266b2011-08-30 15:40:27 -0400374 {
375 // Fetch the cookie
376 $session = $this->CI->input->cookie($this->sess_cookie_name);
377
378 // No cookie? Goodbye cruel world!...
Phil Sturgeon55a6ddb2012-05-23 18:37:24 +0100379 if ($session === NULL)
Darren Hillc4e266b2011-08-30 15:40:27 -0400380 {
381 log_message('debug', 'A session cookie was not found.');
382 return FALSE;
383 }
384
dchill423cecd822012-08-28 21:37:27 -0400385 // Check for encryption
Alex Bilbied261b1e2012-06-02 11:12:16 +0100386 if ($this->sess_encrypt_cookie === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400387 {
dchill423cecd822012-08-28 21:37:27 -0400388 // Decrypt the cookie data
Darren Hillc4e266b2011-08-30 15:40:27 -0400389 $session = $this->CI->encrypt->decode($session);
390 }
391 else
392 {
dchill423cecd822012-08-28 21:37:27 -0400393 // Encryption was not used, so we need to check the md5 hash in the last 32 chars
394 $len = strlen($session)-32;
395 $hash = substr($session, $len);
396 $session = substr($session, 0, $len);
Darren Hillc4e266b2011-08-30 15:40:27 -0400397
398 // Does the md5 hash match? This is to prevent manipulation of session data in userspace
399 if ($hash !== md5($session.$this->encryption_key))
400 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300401 log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.');
Darren Hillc4e266b2011-08-30 15:40:27 -0400402 $this->sess_destroy();
403 return FALSE;
404 }
405 }
406
407 // Unserialize the session array
408 $session = $this->_unserialize($session);
409
410 // Is the session data we unserialized an array with the correct format?
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300411 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 -0400412 {
413 $this->sess_destroy();
414 return FALSE;
415 }
416
417 // Is the session current?
Andrey Andreev7e087f52012-01-20 11:46:27 +0200418 if (($session['last_activity'] + $this->sess_expiration) < $this->now)
Darren Hillc4e266b2011-08-30 15:40:27 -0400419 {
420 $this->sess_destroy();
421 return FALSE;
422 }
423
Andrey Andreev7e087f52012-01-20 11:46:27 +0200424 // Does the IP match?
Alex Bilbied261b1e2012-06-02 11:12:16 +0100425 if ($this->sess_match_ip === TRUE && $session['ip_address'] !== $this->CI->input->ip_address())
Darren Hillc4e266b2011-08-30 15:40:27 -0400426 {
427 $this->sess_destroy();
428 return FALSE;
429 }
430
431 // Does the User Agent Match?
dchill42c5079de2012-07-23 10:53:47 -0400432 if ($this->sess_match_useragent === TRUE &&
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300433 trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
Darren Hillc4e266b2011-08-30 15:40:27 -0400434 {
435 $this->sess_destroy();
436 return FALSE;
437 }
438
439 // Is there a corresponding session in the DB?
440 if ($this->sess_use_database === TRUE)
441 {
442 $this->CI->db->where('session_id', $session['session_id']);
443
Alex Bilbied261b1e2012-06-02 11:12:16 +0100444 if ($this->sess_match_ip === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400445 {
446 $this->CI->db->where('ip_address', $session['ip_address']);
447 }
448
Alex Bilbied261b1e2012-06-02 11:12:16 +0100449 if ($this->sess_match_useragent === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400450 {
451 $this->CI->db->where('user_agent', $session['user_agent']);
452 }
453
dchill42cd436e92012-09-04 10:15:14 -0400454 // Is caching in effect? Turn it off
455 $db_cache = $this->CI->db->cache_on;
456 $this->CI->db->cache_off();
457
Timothy Warrenf1421922012-03-02 11:51:42 -0500458 $query = $this->CI->db->limit(1)->get($this->sess_table_name);
Darren Hillc4e266b2011-08-30 15:40:27 -0400459
dchill42cd436e92012-09-04 10:15:14 -0400460 // Was caching in effect?
461 if ($db_cache)
462 {
463 // Turn it back on
464 $this->CI->db->cache_on();
465 }
466
Darren Hillc4e266b2011-08-30 15:40:27 -0400467 // No result? Kill it!
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200468 if ($query->num_rows() === 0)
Darren Hillc4e266b2011-08-30 15:40:27 -0400469 {
470 $this->sess_destroy();
471 return FALSE;
472 }
473
474 // Is there custom data? If so, add it to the main session array
475 $row = $query->row();
Andrey Andreev5036c9c2012-06-04 15:34:56 +0300476 if ( ! empty($row->user_data))
Darren Hillc4e266b2011-08-30 15:40:27 -0400477 {
478 $custom_data = $this->_unserialize($row->user_data);
479
480 if (is_array($custom_data))
481 {
dchill423cecd822012-08-28 21:37:27 -0400482 $session = $session + $custom_data;
Darren Hillc4e266b2011-08-30 15:40:27 -0400483 }
484 }
485 }
486
487 // Session is valid!
488 $this->userdata = $session;
Darren Hillc4e266b2011-08-30 15:40:27 -0400489 return TRUE;
490 }
491
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300492 // ------------------------------------------------------------------------
493
Darren Hillc4e266b2011-08-30 15:40:27 -0400494 /**
495 * Create a new session
496 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400497 * @return void
498 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400499 protected function _sess_create()
Darren Hillc4e266b2011-08-30 15:40:27 -0400500 {
dchill423cecd822012-08-28 21:37:27 -0400501 // Initialize userdata
Derek Allard2067d1a2008-11-13 22:59:24 +0000502 $this->userdata = array(
dchill423cecd822012-08-28 21:37:27 -0400503 'session_id' => $this->_make_sess_id(),
dchill42c5079de2012-07-23 10:53:47 -0400504 'ip_address' => $this->CI->input->ip_address(),
505 'user_agent' => substr($this->CI->input->user_agent(), 0, 120),
506 'last_activity' => $this->now,
dchill42c5079de2012-07-23 10:53:47 -0400507 );
Darren Hillc4e266b2011-08-30 15:40:27 -0400508
dchill423cecd822012-08-28 21:37:27 -0400509 // Check for database
Darren Hillc4e266b2011-08-30 15:40:27 -0400510 if ($this->sess_use_database === TRUE)
511 {
dchill423cecd822012-08-28 21:37:27 -0400512 // Add empty user_data field and save the data to the DB
513 $this->CI->db->set('user_data', '')->insert($this->sess_table_name, $this->userdata);
Darren Hillc4e266b2011-08-30 15:40:27 -0400514 }
515
516 // Write the cookie
517 $this->_set_cookie();
518 }
519
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300520 // ------------------------------------------------------------------------
521
Darren Hillc4e266b2011-08-30 15:40:27 -0400522 /**
523 * Update an existing session
524 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300525 * @param bool Force update flag (default: false)
Darren Hillc4e266b2011-08-30 15:40:27 -0400526 * @return void
527 */
dchill42c5079de2012-07-23 10:53:47 -0400528 protected function _sess_update($force = FALSE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400529 {
530 // We only update the session every five minutes by default (unless forced)
dchill4277ee3fd2012-07-24 11:50:01 -0400531 if ( ! $force && ($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
Darren Hillc4e266b2011-08-30 15:40:27 -0400532 {
533 return;
534 }
535
dchill423cecd822012-08-28 21:37:27 -0400536 // Update last activity to now
537 $this->userdata['last_activity'] = $this->now;
Andrey Andreev9c622f32012-01-19 14:12:54 +0200538
dchill423cecd822012-08-28 21:37:27 -0400539 // Save the old session id so we know which DB record to update
540 $old_sessid = $this->userdata['session_id'];
541
542 // Changing the session ID during an AJAX call causes problems
543 if ( ! $this->CI->input->is_ajax_request())
Andrey Andreev9c622f32012-01-19 14:12:54 +0200544 {
dchill423cecd822012-08-28 21:37:27 -0400545 // Get new id
546 $this->userdata['session_id'] = $this->_make_sess_id();
Andrey Andreev9c622f32012-01-19 14:12:54 +0200547 }
548
dchill423cecd822012-08-28 21:37:27 -0400549 // Check for database
550 if ($this->sess_use_database === TRUE)
551 {
552 // Update the session ID and last_activity field in the DB
553 $this->CI->db->update($this->sess_table_name, array(
554 'last_activity' => $this->now,
555 'session_id' => $this->userdata['session_id']
556 ), array('session_id' => $old_sessid));
557 }
558
559 // Write the cookie
560 $this->_set_cookie();
561 }
562
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300563 // ------------------------------------------------------------------------
564
dchill423cecd822012-08-28 21:37:27 -0400565 /**
566 * Update database with current data
567 *
568 * This gets called from the shutdown function and also
569 * registered with PHP to run at the end of the request
570 * so it's guaranteed to update even when a fatal error
571 * occurs. The first call makes the update and clears the
572 * dirty flag so it won't happen twice.
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300573 *
574 * @return void
dchill423cecd822012-08-28 21:37:27 -0400575 */
576 public function _update_db()
577 {
578 // Check for database and dirty flag and unsaved
579 if ($this->sess_use_database === TRUE && $this->data_dirty === TRUE)
580 {
581 // Set up activity and data fields to be set
582 // If we don't find custom data, user_data will remain an empty string
583 $set = array(
584 'last_activity' => $this->userdata['last_activity'],
585 'user_data' => ''
586 );
587
588 // Get the custom userdata, leaving out the defaults
589 // (which get stored in the cookie)
590 $userdata = array_diff_key($this->userdata, $this->defaults);
591
592 // Did we find any custom data?
593 if ( ! empty($userdata))
594 {
595 // Serialize the custom data array so we can store it
596 $set['user_data'] = $this->_serialize($userdata);
597 }
598
599 // Run the update query
600 // Any time we change the session id, it gets updated immediately,
601 // so our where clause below is always safe
602 $this->CI->db->update($this->sess_table_name, $set, array('session_id' => $this->userdata['session_id']));
603
604 // Clear dirty flag to prevent double updates
605 $this->data_dirty = FALSE;
606
607 log_message('debug', 'CI_Session Data Saved To DB');
608 }
609 }
610
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300611 // ------------------------------------------------------------------------
612
dchill423cecd822012-08-28 21:37:27 -0400613 /**
614 * Generate a new session id
615 *
616 * @return string Hashed session id
617 */
618 protected function _make_sess_id()
619 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400620 $new_sessid = '';
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200621 do
Darren Hillc4e266b2011-08-30 15:40:27 -0400622 {
623 $new_sessid .= mt_rand(0, mt_getrandmax());
624 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200625 while (strlen($new_sessid) < 32);
Darren Hillc4e266b2011-08-30 15:40:27 -0400626
627 // To make the session ID even more secure we'll combine it with the user's IP
628 $new_sessid .= $this->CI->input->ip_address();
629
dchill423cecd822012-08-28 21:37:27 -0400630 // Turn it into a hash and return
631 return md5(uniqid($new_sessid, TRUE));
Darren Hillc4e266b2011-08-30 15:40:27 -0400632 }
633
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300634 // ------------------------------------------------------------------------
635
Darren Hillc4e266b2011-08-30 15:40:27 -0400636 /**
637 * Get the "now" time
638 *
dchill42c5079de2012-07-23 10:53:47 -0400639 * @return int Time
Darren Hillc4e266b2011-08-30 15:40:27 -0400640 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400641 protected function _get_time()
Darren Hillc4e266b2011-08-30 15:40:27 -0400642 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300643 if ($this->time_reference === 'local' OR $this->time_reference === date_default_timezone_get())
Darren Hillc4e266b2011-08-30 15:40:27 -0400644 {
Iban Eguiafeb14da2012-06-12 16:09:36 +0200645 return time();
Darren Hillc4e266b2011-08-30 15:40:27 -0400646 }
647
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300648 $datetime = new DateTime('now', new DateTimeZone($this->time_reference));
Iban Eguiafeb14da2012-06-12 16:09:36 +0200649 sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
650
651 return mktime($hour, $minute, $second, $month, $day, $year);
Darren Hillc4e266b2011-08-30 15:40:27 -0400652 }
653
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300654 // ------------------------------------------------------------------------
655
Darren Hillc4e266b2011-08-30 15:40:27 -0400656 /**
657 * Write the session cookie
658 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400659 * @return void
660 */
dchill423cecd822012-08-28 21:37:27 -0400661 protected function _set_cookie()
Darren Hillc4e266b2011-08-30 15:40:27 -0400662 {
dchill423cecd822012-08-28 21:37:27 -0400663 // Get userdata (only defaults if database)
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300664 $cookie_data = ($this->sess_use_database === TRUE)
665 ? array_intersect_key($this->userdata, $this->defaults)
666 : $this->userdata;
Darren Hillc4e266b2011-08-30 15:40:27 -0400667
668 // Serialize the userdata for the cookie
669 $cookie_data = $this->_serialize($cookie_data);
670
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300671 $cookie_data = ($this->sess_encrypt_cookie === TRUE)
672 ? $this->CI->encrypt->encode($cookie_data)
Darren Hillc4e266b2011-08-30 15:40:27 -0400673 // if encryption is not used, we provide an md5 hash to prevent userside tampering
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300674 : $cookie_data.md5($cookie_data.$this->encryption_key);
Darren Hillc4e266b2011-08-30 15:40:27 -0400675
676 $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
677
678 // Set the cookie
dchill42c5872252012-07-30 14:53:11 -0400679 $this->_setcookie($this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain,
dchill42c5079de2012-07-23 10:53:47 -0400680 $this->cookie_secure, $this->cookie_httponly);
Darren Hillc4e266b2011-08-30 15:40:27 -0400681 }
682
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300683 // ------------------------------------------------------------------------
684
Darren Hillc4e266b2011-08-30 15:40:27 -0400685 /**
dchill42c5872252012-07-30 14:53:11 -0400686 * Set a cookie with the system
687 *
688 * This abstraction of the setcookie call allows overriding for unit testing
689 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300690 * @param string Cookie name
691 * @param string Cookie value
692 * @param int Expiration time
693 * @param string Cookie path
694 * @param string Cookie domain
695 * @param bool Secure connection flag
696 * @param bool HTTP protocol only flag
697 * @return void
dchill42c5872252012-07-30 14:53:11 -0400698 */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300699 protected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = FALSE, $httponly = FALSE)
dchill42c5872252012-07-30 14:53:11 -0400700 {
dchill42c5872252012-07-30 14:53:11 -0400701 setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
702 }
703
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300704 // ------------------------------------------------------------------------
705
dchill42c5872252012-07-30 14:53:11 -0400706 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400707 * Serialize an array
708 *
709 * This function first converts any slashes found in the array to a temporary
710 * marker, so when it gets unserialized the slashes will be preserved
711 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400712 * @param mixed Data to serialize
dchill42c5079de2012-07-23 10:53:47 -0400713 * @return string Serialized data
Darren Hillc4e266b2011-08-30 15:40:27 -0400714 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400715 protected function _serialize($data)
Darren Hillc4e266b2011-08-30 15:40:27 -0400716 {
717 if (is_array($data))
718 {
Chris Muench95933492011-10-16 14:14:04 -0400719 array_walk_recursive($data, array(&$this, '_escape_slashes'));
Darren Hillc4e266b2011-08-30 15:40:27 -0400720 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200721 elseif (is_string($data))
Darren Hillc4e266b2011-08-30 15:40:27 -0400722 {
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200723 $data = str_replace('\\', '{{slash}}', $data);
Darren Hillc4e266b2011-08-30 15:40:27 -0400724 }
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300725
Darren Hillc4e266b2011-08-30 15:40:27 -0400726 return serialize($data);
727 }
728
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300729 // ------------------------------------------------------------------------
730
Darren Hillc4e266b2011-08-30 15:40:27 -0400731 /**
Chris Muench95933492011-10-16 14:14:04 -0400732 * Escape slashes
733 *
734 * This function converts any slashes found into a temporary marker
735 *
dchill42c5079de2012-07-23 10:53:47 -0400736 * @param string Value
737 * @param string Key
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200738 * @return void
Chris Muench95933492011-10-16 14:14:04 -0400739 */
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200740 protected function _escape_slashes(&$val, $key)
Chris Muench95933492011-10-16 14:14:04 -0400741 {
742 if (is_string($val))
743 {
744 $val = str_replace('\\', '{{slash}}', $val);
745 }
746 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000747
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300748 // ------------------------------------------------------------------------
749
Derek Allard2067d1a2008-11-13 22:59:24 +0000750 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400751 * Unserialize
752 *
753 * This function unserializes a data string, then converts any
754 * temporary slash markers back to actual slashes
755 *
dchill42c5079de2012-07-23 10:53:47 -0400756 * @param mixed Data to unserialize
757 * @return mixed Unserialized data
Darren Hillc4e266b2011-08-30 15:40:27 -0400758 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400759 protected function _unserialize($data)
Darren Hillc4e266b2011-08-30 15:40:27 -0400760 {
Andrey Andreev6b831232012-03-06 11:16:57 +0200761 $data = @unserialize(strip_slashes(trim($data)));
Darren Hillc4e266b2011-08-30 15:40:27 -0400762
763 if (is_array($data))
764 {
Chris Muench95933492011-10-16 14:14:04 -0400765 array_walk_recursive($data, array(&$this, '_unescape_slashes'));
Darren Hillc4e266b2011-08-30 15:40:27 -0400766 return $data;
767 }
768
Andrey Andreev6b831232012-03-06 11:16:57 +0200769 return is_string($data) ? str_replace('{{slash}}', '\\', $data) : $data;
Derek Allard2067d1a2008-11-13 22:59:24 +0000770 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200771
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300772 // ------------------------------------------------------------------------
773
Chris Muench95933492011-10-16 14:14:04 -0400774 /**
775 * Unescape slashes
776 *
777 * This function converts any slash markers back into actual slashes
778 *
dchill42c5079de2012-07-23 10:53:47 -0400779 * @param string Value
780 * @param string Key
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200781 * @return void
Chris Muench95933492011-10-16 14:14:04 -0400782 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200783 protected function _unescape_slashes(&$val, $key)
Chris Muench95933492011-10-16 14:14:04 -0400784 {
Chris Muench3e414f92011-10-16 23:03:55 -0400785 if (is_string($val))
786 {
787 $val= str_replace('{{slash}}', '\\', $val);
788 }
Darren Hillc4e266b2011-08-30 15:40:27 -0400789 }
790
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300791 // ------------------------------------------------------------------------
792
Darren Hillc4e266b2011-08-30 15:40:27 -0400793 /**
794 * Garbage collection
795 *
796 * This deletes expired session rows from database
797 * if the probability percentage is met
798 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400799 * @return void
800 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400801 protected function _sess_gc()
Darren Hillc4e266b2011-08-30 15:40:27 -0400802 {
Alex Bilbied261b1e2012-06-02 11:12:16 +0100803 if ($this->sess_use_database !== TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400804 {
805 return;
806 }
807
Christopher Guiney7a142862012-06-29 20:34:28 -0700808 $probability = ini_get('session.gc_probability');
809 $divisor = ini_get('session.gc_divisor');
810
Darren Hillc4e266b2011-08-30 15:40:27 -0400811 srand(time());
Christopher Guiney7a142862012-06-29 20:34:28 -0700812 if ((mt_rand(0, $divisor) / $divisor) < $probability)
Darren Hillc4e266b2011-08-30 15:40:27 -0400813 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000814 $expire = $this->now - $this->sess_expiration;
dchill423cecd822012-08-28 21:37:27 -0400815 $this->CI->db->delete($this->sess_table_name, 'last_activity < '.$expire);
Darren Hillc4e266b2011-08-30 15:40:27 -0400816
817 log_message('debug', 'Session garbage collection performed.');
818 }
819 }
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300820
Darren Hillc4e266b2011-08-30 15:40:27 -0400821}
Darren Hillc4e266b2011-08-30 15:40:27 -0400822
823/* End of file Session_cookie.php */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300824/* Location: ./system/libraries/Session/drivers/Session_cookie.php */