blob: 21ded899aafe284182722cb40dc1fd6a827f33f5 [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 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +02007 * This content is released under the MIT License (MIT)
Andrey Andreev57ffbbb2011-12-25 04:48:47 +02008 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +02009 * Copyright (c) 2014, British Columbia Institute of Technology
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020010 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020011 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
Darren Hillc4e266b2011-08-30 15:40:27 -040017 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020018 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * THE SOFTWARE.
28 *
29 * @package CodeIgniter
30 * @author EllisLab Dev Team
darwinel871754a2014-02-11 17:34:57 +010031 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/)
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020032 * @copyright Copyright (c) 2014, British Columbia Institute of Technology (http://bcit.ca/)
33 * @license http://opensource.org/licenses/MIT MIT License
34 * @link http://codeigniter.com
35 * @since Version 1.0.0
Darren Hillc4e266b2011-08-30 15:40:27 -040036 * @filesource
37 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020038defined('BASEPATH') OR exit('No direct script access allowed');
Darren Hillc4e266b2011-08-30 15:40:27 -040039
Darren Hillc4e266b2011-08-30 15:40:27 -040040/**
41 * Cookie-based session management driver
42 *
dchill423cecd822012-08-28 21:37:27 -040043 * This is the classic CI_Session functionality, as written by EllisLab, abstracted out to a driver.
Darren Hillc4e266b2011-08-30 15:40:27 -040044 *
45 * @package CodeIgniter
46 * @subpackage Libraries
47 * @category Sessions
Derek Jonesf4a4bd82011-10-20 12:18:42 -050048 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000049 * @link http://codeigniter.com/user_guide/libraries/sessions.html
Darren Hillc4e266b2011-08-30 15:40:27 -040050 */
Darren Hill5073a372011-08-31 13:54:19 -040051class CI_Session_cookie extends CI_Session_driver {
Andrey Andreev9ffcee62012-09-05 16:25:16 +030052
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020053 /**
Timothy Warren68f09812012-04-27 10:38:32 -040054 * Whether to encrypt the session cookie
55 *
56 * @var bool
57 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020058 public $sess_encrypt_cookie = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020059
Timothy Warren68f09812012-04-27 10:38:32 -040060 /**
61 * Whether to use to the database for session storage
62 *
63 * @var bool
64 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020065 public $sess_use_database = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020066
Timothy Warren68f09812012-04-27 10:38:32 -040067 /**
68 * Name of the database table in which to store sessions
69 *
70 * @var string
71 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020072 public $sess_table_name = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020073
Timothy Warren68f09812012-04-27 10:38:32 -040074 /**
75 * Length of time (in seconds) for sessions to expire
76 *
77 * @var int
78 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020079 public $sess_expiration = 7200;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020080
Timothy Warren68f09812012-04-27 10:38:32 -040081 /**
82 * Whether to kill session on close of browser window
83 *
84 * @var bool
85 */
dchill4226429202012-07-31 10:55:07 -040086 public $sess_expire_on_close = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020087
Timothy Warren68f09812012-04-27 10:38:32 -040088 /**
89 * Whether to match session on ip address
90 *
91 * @var bool
92 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020093 public $sess_match_ip = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020094
Timothy Warren68f09812012-04-27 10:38:32 -040095 /**
96 * Whether to match session on user-agent
97 *
98 * @var bool
99 */
dchill4226429202012-07-31 10:55:07 -0400100 public $sess_match_useragent = TRUE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200101
Timothy Warren68f09812012-04-27 10:38:32 -0400102 /**
103 * Name of session cookie
104 *
105 * @var string
106 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200107 public $sess_cookie_name = 'ci_session';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200108
Timothy Warren68f09812012-04-27 10:38:32 -0400109 /**
110 * Session cookie prefix
111 *
112 * @var string
113 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200114 public $cookie_prefix = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200115
Timothy Warren68f09812012-04-27 10:38:32 -0400116 /**
117 * Session cookie path
118 *
119 * @var string
120 */
dchill4226429202012-07-31 10:55:07 -0400121 public $cookie_path = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200122
Timothy Warren68f09812012-04-27 10:38:32 -0400123 /**
124 * Session cookie domain
125 *
126 * @var string
127 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200128 public $cookie_domain = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200129
Timothy Warren68f09812012-04-27 10:38:32 -0400130 /**
131 * Whether to set the cookie only on HTTPS connections
132 *
133 * @var bool
134 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200135 public $cookie_secure = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200136
Timothy Warren68f09812012-04-27 10:38:32 -0400137 /**
138 * Whether cookie should be allowed only to be sent by the server
139 *
140 * @var bool
141 */
freewil4ad0fd82012-03-13 22:37:42 -0400142 public $cookie_httponly = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200143
Timothy Warren68f09812012-04-27 10:38:32 -0400144 /**
145 * Interval at which to update session
146 *
147 * @var int
148 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200149 public $sess_time_to_update = 300;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200150
Timothy Warren68f09812012-04-27 10:38:32 -0400151 /**
152 * Key with which to encrypt the session cookie
153 *
154 * @var string
155 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200156 public $encryption_key = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200157
Timothy Warren68f09812012-04-27 10:38:32 -0400158 /**
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300159 * Timezone to use for the current time
Timothy Warren68f09812012-04-27 10:38:32 -0400160 *
161 * @var string
162 */
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300163 public $time_reference = 'local';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200164
Timothy Warren68f09812012-04-27 10:38:32 -0400165 /**
166 * Session data
167 *
168 * @var array
169 */
dchill4226429202012-07-31 10:55:07 -0400170 public $userdata = array();
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200171
Timothy Warren68f09812012-04-27 10:38:32 -0400172 /**
Timothy Warren68f09812012-04-27 10:38:32 -0400173 * Current time
174 *
175 * @var int
176 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200177 public $now;
Darren Hillc4e266b2011-08-30 15:40:27 -0400178
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200179 // ------------------------------------------------------------------------
180
Darren Hillc4e266b2011-08-30 15:40:27 -0400181 /**
dchill423cecd822012-08-28 21:37:27 -0400182 * Default userdata keys
183 *
184 * @var array
185 */
186 protected $defaults = array(
dchill4288b636b2012-08-29 08:47:05 -0400187 'session_id' => NULL,
188 'ip_address' => NULL,
189 'user_agent' => NULL,
190 'last_activity' => NULL
dchill423cecd822012-08-28 21:37:27 -0400191 );
192
193 /**
194 * Data needs DB update flag
195 *
196 * @var bool
197 */
198 protected $data_dirty = FALSE;
199
200 /**
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200201 * Standardize newlines flag
202 *
203 * @var bool
204 */
205 protected $_standardize_newlines;
206
207 // ------------------------------------------------------------------------
208
209 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400210 * Initialize session driver object
211 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400212 * @return void
213 */
214 protected function initialize()
215 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400216 // Set all the session preferences, which can either be set
dchill4242b77a92012-07-23 11:28:42 -0400217 // manually via the $params array or via the config file
dchill4226429202012-07-31 10:55:07 -0400218 $prefs = array(
219 'sess_encrypt_cookie',
220 'sess_use_database',
221 'sess_table_name',
222 'sess_expiration',
223 'sess_expire_on_close',
224 'sess_match_ip',
225 'sess_match_useragent',
226 'sess_cookie_name',
227 'cookie_path',
228 'cookie_domain',
229 'cookie_secure',
230 'cookie_httponly',
231 'sess_time_to_update',
232 'time_reference',
233 'cookie_prefix',
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200234 'encryption_key',
dchill4226429202012-07-31 10:55:07 -0400235 );
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300236
Andrey Andreev4ea76cc2014-01-08 21:49:23 +0200237 $this->_standardize_newlines = (bool) config_item('standardize_newlines');
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200238
dchill4226429202012-07-31 10:55:07 -0400239 foreach ($prefs as $key)
Darren Hillc4e266b2011-08-30 15:40:27 -0400240 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300241 $this->$key = isset($this->_parent->params[$key])
242 ? $this->_parent->params[$key]
243 : $this->CI->config->item($key);
Darren Hillc4e266b2011-08-30 15:40:27 -0400244 }
245
Andrey Andreev4abd0942012-11-26 12:13:59 +0200246 if (empty($this->encryption_key))
Darren Hillc4e266b2011-08-30 15:40:27 -0400247 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300248 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 -0400249 }
250
Darren Hillc4e266b2011-08-30 15:40:27 -0400251 // Do we need encryption? If so, load the encryption class
Alex Bilbied261b1e2012-06-02 11:12:16 +0100252 if ($this->sess_encrypt_cookie === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400253 {
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200254 $this->CI->load->library('encryption');
Darren Hillc4e266b2011-08-30 15:40:27 -0400255 }
256
dchill423cecd822012-08-28 21:37:27 -0400257 // Check for database
Alex Bilbied261b1e2012-06-02 11:12:16 +0100258 if ($this->sess_use_database === TRUE && $this->sess_table_name !== '')
Darren Hillc4e266b2011-08-30 15:40:27 -0400259 {
dchill423cecd822012-08-28 21:37:27 -0400260 // Load database driver
Darren Hillc4e266b2011-08-30 15:40:27 -0400261 $this->CI->load->database();
dchill423cecd822012-08-28 21:37:27 -0400262
263 // Register shutdown function
264 register_shutdown_function(array($this, '_update_db'));
Darren Hillc4e266b2011-08-30 15:40:27 -0400265 }
266
267 // Set the "now" time. Can either be GMT or server time, based on the config prefs.
268 // We use this to set the "last activity" time
269 $this->now = $this->_get_time();
270
271 // Set the session length. If the session expiration is
272 // set to zero we'll set the expiration two years from now.
Alex Bilbied261b1e2012-06-02 11:12:16 +0100273 if ($this->sess_expiration === 0)
Darren Hillc4e266b2011-08-30 15:40:27 -0400274 {
275 $this->sess_expiration = (60*60*24*365*2);
276 }
277
278 // Set the cookie name
279 $this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
280
281 // Run the Session routine. If a session doesn't exist we'll
282 // create a new one. If it does, we'll update it.
283 if ( ! $this->_sess_read())
284 {
285 $this->_sess_create();
286 }
287 else
288 {
289 $this->_sess_update();
290 }
291
292 // Delete expired sessions if necessary
293 $this->_sess_gc();
294 }
295
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300296 // ------------------------------------------------------------------------
297
Darren Hillc4e266b2011-08-30 15:40:27 -0400298 /**
299 * Write the session data
300 *
301 * @return void
302 */
303 public function sess_save()
304 {
dchill423cecd822012-08-28 21:37:27 -0400305 // Check for database
dchill4288b636b2012-08-29 08:47:05 -0400306 if ($this->sess_use_database === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400307 {
dchill423cecd822012-08-28 21:37:27 -0400308 // Mark custom data as dirty so we know to update the DB
309 $this->data_dirty = TRUE;
Darren Hillc4e266b2011-08-30 15:40:27 -0400310 }
311
dchill423cecd822012-08-28 21:37:27 -0400312 // Write the cookie
313 $this->_set_cookie();
Darren Hillc4e266b2011-08-30 15:40:27 -0400314 }
315
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300316 // ------------------------------------------------------------------------
317
Darren Hillc4e266b2011-08-30 15:40:27 -0400318 /**
319 * Destroy the current session
320 *
321 * @return void
322 */
323 public function sess_destroy()
324 {
325 // Kill the session DB row
dchill42aee92652012-08-26 21:45:35 -0400326 if ($this->sess_use_database === TRUE && isset($this->userdata['session_id']))
Darren Hillc4e266b2011-08-30 15:40:27 -0400327 {
dchill423cecd822012-08-28 21:37:27 -0400328 $this->CI->db->delete($this->sess_table_name, array('session_id' => $this->userdata['session_id']));
dchill4297b0d832012-09-04 10:09:00 -0400329 $this->data_dirty = FALSE;
Darren Hillc4e266b2011-08-30 15:40:27 -0400330 }
331
332 // Kill the cookie
Andrey Andreevcf264e02012-10-18 16:14:51 +0300333 $this->_setcookie($this->sess_cookie_name, '', ($this->now - 31500000),
Darren Hillc4e266b2011-08-30 15:40:27 -0400334 $this->cookie_path, $this->cookie_domain, 0);
dchill42c5079de2012-07-23 10:53:47 -0400335
336 // Kill session data
337 $this->userdata = array();
Darren Hillc4e266b2011-08-30 15:40:27 -0400338 }
339
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300340 // ------------------------------------------------------------------------
341
Darren Hillc4e266b2011-08-30 15:40:27 -0400342 /**
343 * Regenerate the current session
344 *
345 * Regenerate the session id
346 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300347 * @param bool Destroy session data flag (default: false)
Darren Hillc4e266b2011-08-30 15:40:27 -0400348 * @return void
349 */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300350 public function sess_regenerate($destroy = FALSE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400351 {
352 // Check destroy flag
353 if ($destroy)
354 {
355 // Destroy old session and create new one
356 $this->sess_destroy();
357 $this->_sess_create();
358 }
359 else
360 {
361 // Just force an update to recreate the id
Andrey Andreev3f3f1352012-09-05 16:39:28 +0300362 $this->_sess_update(TRUE);
Darren Hillc4e266b2011-08-30 15:40:27 -0400363 }
364 }
365
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300366 // ------------------------------------------------------------------------
367
Darren Hillc4e266b2011-08-30 15:40:27 -0400368 /**
369 * Get a reference to user data array
370 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300371 * @return array Reference to userdata
Darren Hillc4e266b2011-08-30 15:40:27 -0400372 */
373 public function &get_userdata()
374 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400375 return $this->userdata;
376 }
377
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300378 // ------------------------------------------------------------------------
379
Darren Hillc4e266b2011-08-30 15:40:27 -0400380 /**
381 * Fetch the current session data if it exists
382 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400383 * @return bool
384 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400385 protected function _sess_read()
Darren Hillc4e266b2011-08-30 15:40:27 -0400386 {
387 // Fetch the cookie
388 $session = $this->CI->input->cookie($this->sess_cookie_name);
389
390 // No cookie? Goodbye cruel world!...
Phil Sturgeon55a6ddb2012-05-23 18:37:24 +0100391 if ($session === NULL)
Darren Hillc4e266b2011-08-30 15:40:27 -0400392 {
393 log_message('debug', 'A session cookie was not found.');
394 return FALSE;
395 }
396
Alex Bilbied261b1e2012-06-02 11:12:16 +0100397 if ($this->sess_encrypt_cookie === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400398 {
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200399 $session = $this->CI->encryption->decrypt($session);
400 if ($session === FALSE)
401 {
402 log_message('error', 'Session: Unable to decrypt the session cookie, possibly due to a HMAC mismatch.');
403 return FALSE;
404 }
405 }
406 else
407 {
408 if (($len = strlen($session) - 40) <= 0)
409 {
410 log_message('error', 'Session: The session cookie was not signed.');
411 return FALSE;
412 }
413
414 // Check cookie authentication
415 $hmac = substr($session, $len);
416 $session = substr($session, 0, $len);
417
Andrey Andreevdf7a6962014-02-06 05:36:47 +0200418 // Time-attack-safe comparison
419 $hmac_check = hash_hmac('sha1', $session, $this->encryption_key);
420 $diff = 0;
421 for ($i = 0; $i < 40; $i++)
422 {
423 $diff |= ord($hmac[$i]) ^ ord($hmac_check[$i]);
424 }
425
426 if ($diff !== 0)
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200427 {
428 log_message('error', 'Session: HMAC mismatch. The session cookie data did not match what was expected.');
429 $this->sess_destroy();
430 return FALSE;
431 }
Darren Hillc4e266b2011-08-30 15:40:27 -0400432 }
Darren Hillc4e266b2011-08-30 15:40:27 -0400433
434 // Unserialize the session array
Jordan Eldredge5306cad2013-12-23 11:10:51 -0800435 $session = @unserialize($session);
Darren Hillc4e266b2011-08-30 15:40:27 -0400436
437 // Is the session data we unserialized an array with the correct format?
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300438 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 -0400439 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300440 log_message('debug', 'Session: Wrong cookie data format');
Darren Hillc4e266b2011-08-30 15:40:27 -0400441 $this->sess_destroy();
442 return FALSE;
443 }
444
445 // Is the session current?
Andrey Andreev02117682012-10-15 11:12:37 +0300446 if (($session['last_activity'] + $this->sess_expiration) < $this->now OR $session['last_activity'] > $this->now)
Darren Hillc4e266b2011-08-30 15:40:27 -0400447 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300448 log_message('debug', 'Session: Expired');
Darren Hillc4e266b2011-08-30 15:40:27 -0400449 $this->sess_destroy();
450 return FALSE;
451 }
452
Andrey Andreev7e087f52012-01-20 11:46:27 +0200453 // Does the IP match?
Alex Bilbied261b1e2012-06-02 11:12:16 +0100454 if ($this->sess_match_ip === TRUE && $session['ip_address'] !== $this->CI->input->ip_address())
Darren Hillc4e266b2011-08-30 15:40:27 -0400455 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300456 log_message('debug', 'Session: IP address mismatch');
Darren Hillc4e266b2011-08-30 15:40:27 -0400457 $this->sess_destroy();
458 return FALSE;
459 }
460
461 // Does the User Agent Match?
dchill42c5079de2012-07-23 10:53:47 -0400462 if ($this->sess_match_useragent === TRUE &&
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300463 trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
Darren Hillc4e266b2011-08-30 15:40:27 -0400464 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300465 log_message('debug', 'Session: User Agent string mismatch');
Darren Hillc4e266b2011-08-30 15:40:27 -0400466 $this->sess_destroy();
467 return FALSE;
468 }
469
470 // Is there a corresponding session in the DB?
471 if ($this->sess_use_database === TRUE)
472 {
473 $this->CI->db->where('session_id', $session['session_id']);
474
Alex Bilbied261b1e2012-06-02 11:12:16 +0100475 if ($this->sess_match_ip === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400476 {
477 $this->CI->db->where('ip_address', $session['ip_address']);
478 }
479
Alex Bilbied261b1e2012-06-02 11:12:16 +0100480 if ($this->sess_match_useragent === TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400481 {
482 $this->CI->db->where('user_agent', $session['user_agent']);
483 }
484
dchill42cd436e92012-09-04 10:15:14 -0400485 // Is caching in effect? Turn it off
486 $db_cache = $this->CI->db->cache_on;
487 $this->CI->db->cache_off();
488
Andrey Andreeved3fc512014-11-18 11:12:35 +0200489 $query = $this->CI->db->get($this->sess_table_name);
Darren Hillc4e266b2011-08-30 15:40:27 -0400490
dchill42cd436e92012-09-04 10:15:14 -0400491 // Was caching in effect?
492 if ($db_cache)
493 {
494 // Turn it back on
495 $this->CI->db->cache_on();
496 }
497
Darren Hillc4e266b2011-08-30 15:40:27 -0400498 // No result? Kill it!
Andrey Andreeva8e34ac2012-12-17 10:39:32 +0200499 if (empty($query) OR $query->num_rows() === 0)
Darren Hillc4e266b2011-08-30 15:40:27 -0400500 {
Andrey Andreeve18de502013-07-17 19:59:20 +0300501 log_message('debug', 'Session: No match found in our database');
Darren Hillc4e266b2011-08-30 15:40:27 -0400502 $this->sess_destroy();
503 return FALSE;
504 }
505
506 // Is there custom data? If so, add it to the main session array
507 $row = $query->row();
Andrey Andreev5036c9c2012-06-04 15:34:56 +0300508 if ( ! empty($row->user_data))
Darren Hillc4e266b2011-08-30 15:40:27 -0400509 {
Jordan Eldredge5306cad2013-12-23 11:10:51 -0800510 $custom_data = unserialize(trim($row->user_data));
Darren Hillc4e266b2011-08-30 15:40:27 -0400511
512 if (is_array($custom_data))
513 {
dchill423cecd822012-08-28 21:37:27 -0400514 $session = $session + $custom_data;
Darren Hillc4e266b2011-08-30 15:40:27 -0400515 }
516 }
517 }
518
519 // Session is valid!
520 $this->userdata = $session;
Darren Hillc4e266b2011-08-30 15:40:27 -0400521 return TRUE;
522 }
523
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300524 // ------------------------------------------------------------------------
525
Darren Hillc4e266b2011-08-30 15:40:27 -0400526 /**
527 * Create a new session
528 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400529 * @return void
530 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400531 protected function _sess_create()
Darren Hillc4e266b2011-08-30 15:40:27 -0400532 {
dchill423cecd822012-08-28 21:37:27 -0400533 // Initialize userdata
Derek Allard2067d1a2008-11-13 22:59:24 +0000534 $this->userdata = array(
dchill423cecd822012-08-28 21:37:27 -0400535 'session_id' => $this->_make_sess_id(),
dchill42c5079de2012-07-23 10:53:47 -0400536 'ip_address' => $this->CI->input->ip_address(),
Daniel Robbins930d8ef2013-03-01 21:36:48 -0500537 'user_agent' => trim(substr($this->CI->input->user_agent(), 0, 120)),
dchill42c5079de2012-07-23 10:53:47 -0400538 'last_activity' => $this->now,
dchill42c5079de2012-07-23 10:53:47 -0400539 );
Darren Hillc4e266b2011-08-30 15:40:27 -0400540
Andrey Andreeve18de502013-07-17 19:59:20 +0300541 log_message('debug', 'Session: Creating new session ('.$this->userdata['session_id'].')');
542
dchill423cecd822012-08-28 21:37:27 -0400543 // Check for database
Darren Hillc4e266b2011-08-30 15:40:27 -0400544 if ($this->sess_use_database === TRUE)
545 {
dchill423cecd822012-08-28 21:37:27 -0400546 // Add empty user_data field and save the data to the DB
547 $this->CI->db->set('user_data', '')->insert($this->sess_table_name, $this->userdata);
Darren Hillc4e266b2011-08-30 15:40:27 -0400548 }
549
550 // Write the cookie
551 $this->_set_cookie();
552 }
553
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300554 // ------------------------------------------------------------------------
555
Darren Hillc4e266b2011-08-30 15:40:27 -0400556 /**
557 * Update an existing session
558 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300559 * @param bool Force update flag (default: false)
Darren Hillc4e266b2011-08-30 15:40:27 -0400560 * @return void
561 */
dchill42c5079de2012-07-23 10:53:47 -0400562 protected function _sess_update($force = FALSE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400563 {
564 // We only update the session every five minutes by default (unless forced)
dchill4277ee3fd2012-07-24 11:50:01 -0400565 if ( ! $force && ($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
Darren Hillc4e266b2011-08-30 15:40:27 -0400566 {
567 return;
568 }
569
dchill423cecd822012-08-28 21:37:27 -0400570 // Update last activity to now
571 $this->userdata['last_activity'] = $this->now;
Andrey Andreev9c622f32012-01-19 14:12:54 +0200572
dchill423cecd822012-08-28 21:37:27 -0400573 // Save the old session id so we know which DB record to update
574 $old_sessid = $this->userdata['session_id'];
575
576 // Changing the session ID during an AJAX call causes problems
577 if ( ! $this->CI->input->is_ajax_request())
Andrey Andreev9c622f32012-01-19 14:12:54 +0200578 {
dchill423cecd822012-08-28 21:37:27 -0400579 // Get new id
580 $this->userdata['session_id'] = $this->_make_sess_id();
Andrey Andreeve18de502013-07-17 19:59:20 +0300581
582 log_message('debug', 'Session: Regenerate ID');
Andrey Andreev9c622f32012-01-19 14:12:54 +0200583 }
584
dchill423cecd822012-08-28 21:37:27 -0400585 // Check for database
586 if ($this->sess_use_database === TRUE)
587 {
Andrey Andreeve2afc882012-11-01 01:35:34 +0200588 $this->CI->db->where('session_id', $old_sessid);
589
590 if ($this->sess_match_ip === TRUE)
591 {
592 $this->CI->db->where('ip_address', $this->CI->input->ip_address());
593 }
594
595 if ($this->sess_match_useragent === TRUE)
596 {
597 $this->CI->db->where('user_agent', trim(substr($this->CI->input->user_agent(), 0, 120)));
598 }
599
dchill423cecd822012-08-28 21:37:27 -0400600 // Update the session ID and last_activity field in the DB
Andrey Andreeve2afc882012-11-01 01:35:34 +0200601 $this->CI->db->update($this->sess_table_name,
602 array(
603 'last_activity' => $this->now,
604 'session_id' => $this->userdata['session_id']
605 )
606 );
dchill423cecd822012-08-28 21:37:27 -0400607 }
608
609 // Write the cookie
610 $this->_set_cookie();
611 }
612
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300613 // ------------------------------------------------------------------------
614
dchill423cecd822012-08-28 21:37:27 -0400615 /**
616 * Update database with current data
617 *
618 * This gets called from the shutdown function and also
619 * registered with PHP to run at the end of the request
620 * so it's guaranteed to update even when a fatal error
621 * occurs. The first call makes the update and clears the
622 * dirty flag so it won't happen twice.
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300623 *
624 * @return void
dchill423cecd822012-08-28 21:37:27 -0400625 */
626 public function _update_db()
627 {
628 // Check for database and dirty flag and unsaved
629 if ($this->sess_use_database === TRUE && $this->data_dirty === TRUE)
630 {
631 // Set up activity and data fields to be set
632 // If we don't find custom data, user_data will remain an empty string
633 $set = array(
634 'last_activity' => $this->userdata['last_activity'],
635 'user_data' => ''
636 );
637
638 // Get the custom userdata, leaving out the defaults
639 // (which get stored in the cookie)
640 $userdata = array_diff_key($this->userdata, $this->defaults);
641
642 // Did we find any custom data?
643 if ( ! empty($userdata))
644 {
645 // Serialize the custom data array so we can store it
Jordan Eldredge5306cad2013-12-23 11:10:51 -0800646 $set['user_data'] = serialize($userdata);
dchill423cecd822012-08-28 21:37:27 -0400647 }
648
Dionysis Arvanitisf8e2d0e2013-02-19 23:27:16 +0200649 // Reset query builder values.
650 $this->CI->db->reset_query();
651
dchill423cecd822012-08-28 21:37:27 -0400652 // Run the update query
653 // Any time we change the session id, it gets updated immediately,
654 // so our where clause below is always safe
Andrey Andreeve2afc882012-11-01 01:35:34 +0200655 $this->CI->db->where('session_id', $this->userdata['session_id']);
656
657 if ($this->sess_match_ip === TRUE)
658 {
659 $this->CI->db->where('ip_address', $this->CI->input->ip_address());
660 }
661
662 if ($this->sess_match_useragent === TRUE)
663 {
664 $this->CI->db->where('user_agent', trim(substr($this->CI->input->user_agent(), 0, 120)));
665 }
666
667 $this->CI->db->update($this->sess_table_name, $set);
dchill423cecd822012-08-28 21:37:27 -0400668
669 // Clear dirty flag to prevent double updates
670 $this->data_dirty = FALSE;
671
672 log_message('debug', 'CI_Session Data Saved To DB');
673 }
674 }
675
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300676 // ------------------------------------------------------------------------
677
dchill423cecd822012-08-28 21:37:27 -0400678 /**
679 * Generate a new session id
680 *
681 * @return string Hashed session id
682 */
683 protected function _make_sess_id()
684 {
Darren Hillc4e266b2011-08-30 15:40:27 -0400685 $new_sessid = '';
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200686 do
Darren Hillc4e266b2011-08-30 15:40:27 -0400687 {
vlakoff06127562013-03-30 00:06:39 +0100688 $new_sessid .= mt_rand();
Darren Hillc4e266b2011-08-30 15:40:27 -0400689 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200690 while (strlen($new_sessid) < 32);
Darren Hillc4e266b2011-08-30 15:40:27 -0400691
692 // To make the session ID even more secure we'll combine it with the user's IP
693 $new_sessid .= $this->CI->input->ip_address();
694
dchill423cecd822012-08-28 21:37:27 -0400695 // Turn it into a hash and return
696 return md5(uniqid($new_sessid, TRUE));
Darren Hillc4e266b2011-08-30 15:40:27 -0400697 }
698
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300699 // ------------------------------------------------------------------------
700
Darren Hillc4e266b2011-08-30 15:40:27 -0400701 /**
702 * Get the "now" time
703 *
dchill42c5079de2012-07-23 10:53:47 -0400704 * @return int Time
Darren Hillc4e266b2011-08-30 15:40:27 -0400705 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400706 protected function _get_time()
Darren Hillc4e266b2011-08-30 15:40:27 -0400707 {
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300708 if ($this->time_reference === 'local' OR $this->time_reference === date_default_timezone_get())
Darren Hillc4e266b2011-08-30 15:40:27 -0400709 {
Iban Eguiafeb14da2012-06-12 16:09:36 +0200710 return time();
Darren Hillc4e266b2011-08-30 15:40:27 -0400711 }
712
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300713 $datetime = new DateTime('now', new DateTimeZone($this->time_reference));
Iban Eguiafeb14da2012-06-12 16:09:36 +0200714 sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
715
716 return mktime($hour, $minute, $second, $month, $day, $year);
Darren Hillc4e266b2011-08-30 15:40:27 -0400717 }
718
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300719 // ------------------------------------------------------------------------
720
Darren Hillc4e266b2011-08-30 15:40:27 -0400721 /**
722 * Write the session cookie
723 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400724 * @return void
725 */
dchill423cecd822012-08-28 21:37:27 -0400726 protected function _set_cookie()
Darren Hillc4e266b2011-08-30 15:40:27 -0400727 {
dchill423cecd822012-08-28 21:37:27 -0400728 // Get userdata (only defaults if database)
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300729 $cookie_data = ($this->sess_use_database === TRUE)
730 ? array_intersect_key($this->userdata, $this->defaults)
731 : $this->userdata;
Darren Hillc4e266b2011-08-30 15:40:27 -0400732
Andrey Andreevbfb635b2014-01-08 18:32:05 +0200733 // The Input class will do this and since we use HMAC verification,
734 // unless we standardize here as well, the hash won't match.
735 if ($this->_standardize_newlines)
736 {
737 foreach (array_keys($this->userdata) as $key)
738 {
739 $this->userdata[$key] = preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $this->userdata[$key]);
740 }
741 }
742
Darren Hillc4e266b2011-08-30 15:40:27 -0400743 // Serialize the userdata for the cookie
Jordan Eldredge5306cad2013-12-23 11:10:51 -0800744 $cookie_data = serialize($cookie_data);
Darren Hillc4e266b2011-08-30 15:40:27 -0400745
Pascal Krietef69f0e82012-10-16 11:54:49 -0400746 if ($this->sess_encrypt_cookie === TRUE)
747 {
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200748 $cookie_data = $this->CI->encryption->encrypt($cookie_data);
Pascal Krietef69f0e82012-10-16 11:54:49 -0400749 }
Andrey Andreev4a2918a2014-02-05 01:03:46 +0200750 else
751 {
752 // Require message authentication
753 $cookie_data .= hash_hmac('sha1', $cookie_data, $this->encryption_key);
754 }
Darren Hillc4e266b2011-08-30 15:40:27 -0400755
756 $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
757
758 // Set the cookie
dchill42c5872252012-07-30 14:53:11 -0400759 $this->_setcookie($this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain,
dchill42c5079de2012-07-23 10:53:47 -0400760 $this->cookie_secure, $this->cookie_httponly);
Darren Hillc4e266b2011-08-30 15:40:27 -0400761 }
762
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300763 // ------------------------------------------------------------------------
764
Darren Hillc4e266b2011-08-30 15:40:27 -0400765 /**
dchill42c5872252012-07-30 14:53:11 -0400766 * Set a cookie with the system
767 *
768 * This abstraction of the setcookie call allows overriding for unit testing
769 *
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300770 * @param string Cookie name
771 * @param string Cookie value
772 * @param int Expiration time
773 * @param string Cookie path
774 * @param string Cookie domain
775 * @param bool Secure connection flag
776 * @param bool HTTP protocol only flag
777 * @return void
dchill42c5872252012-07-30 14:53:11 -0400778 */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300779 protected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = FALSE, $httponly = FALSE)
dchill42c5872252012-07-30 14:53:11 -0400780 {
dchill42c5872252012-07-30 14:53:11 -0400781 setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
782 }
783
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300784 // ------------------------------------------------------------------------
785
dchill42c5872252012-07-30 14:53:11 -0400786 /**
Darren Hillc4e266b2011-08-30 15:40:27 -0400787 * Garbage collection
788 *
789 * This deletes expired session rows from database
790 * if the probability percentage is met
791 *
Darren Hillc4e266b2011-08-30 15:40:27 -0400792 * @return void
793 */
Darren Hilla2ae6572011-09-01 07:36:26 -0400794 protected function _sess_gc()
Darren Hillc4e266b2011-08-30 15:40:27 -0400795 {
Alex Bilbied261b1e2012-06-02 11:12:16 +0100796 if ($this->sess_use_database !== TRUE)
Darren Hillc4e266b2011-08-30 15:40:27 -0400797 {
798 return;
799 }
800
Christopher Guiney7a142862012-06-29 20:34:28 -0700801 $probability = ini_get('session.gc_probability');
802 $divisor = ini_get('session.gc_divisor');
803
Tyler Brownell74c5f262013-12-13 00:23:12 -0500804 if (mt_rand(1, $divisor) <= $probability)
Darren Hillc4e266b2011-08-30 15:40:27 -0400805 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000806 $expire = $this->now - $this->sess_expiration;
dchill423cecd822012-08-28 21:37:27 -0400807 $this->CI->db->delete($this->sess_table_name, 'last_activity < '.$expire);
Darren Hillc4e266b2011-08-30 15:40:27 -0400808
809 log_message('debug', 'Session garbage collection performed.');
810 }
811 }
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300812
Darren Hillc4e266b2011-08-30 15:40:27 -0400813}
Darren Hillc4e266b2011-08-30 15:40:27 -0400814
815/* End of file Session_cookie.php */
Andrey Andreev9ffcee62012-09-05 16:25:16 +0300816/* Location: ./system/libraries/Session/drivers/Session_cookie.php */