blob: af38dc366ceb163ff7424e83f4341c0f1c14714d [file] [log] [blame]
Andrey Andreev57ffbbb2011-12-25 04:48:47 +02001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +00002/**
3 * CodeIgniter
4 *
Phil Sturgeon07c1ac82012-03-09 17:03:37 +00005 * An open source application development framework for PHP 5.2.4 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05007 * NOTICE OF LICENSE
Andrey Andreev57ffbbb2011-12-25 04:48:47 +02008 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05009 * Licensed under the Open Software License version 3.0
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020010 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -050011 * This source file is subject to the Open Software License (OSL 3.0) that is
Andrey Andreevf863a022012-01-24 15:31:54 +020012 * bundled with this package in the files license.txt / license.rst. It is
Derek Jonesf4a4bd82011-10-20 12:18:42 -050013 * also available through the world wide web at this URL:
14 * http://opensource.org/licenses/OSL-3.0
15 * If you did not receive a copy of the license and are unable to obtain it
16 * through the world wide web, please send an email to
17 * licensing@ellislab.com so we can send you a copy immediately.
18 *
Derek Allard2067d1a2008-11-13 22:59:24 +000019 * @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)
Derek Allard2067d1a2008-11-13 22:59:24 +000023 * @link http://codeigniter.com
24 * @since Version 1.0
25 * @filesource
26 */
27
Derek Allard2067d1a2008-11-13 22:59:24 +000028/**
29 * Session Class
30 *
31 * @package CodeIgniter
32 * @subpackage Libraries
33 * @category Sessions
Derek Jonesf4a4bd82011-10-20 12:18:42 -050034 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000035 * @link http://codeigniter.com/user_guide/libraries/sessions.html
36 */
37class CI_Session {
38
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020039 /**
Timothy Warren68f09812012-04-27 10:38:32 -040040 * Whether to encrypt the session cookie
41 *
42 * @var bool
43 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020044 public $sess_encrypt_cookie = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020045
Timothy Warren68f09812012-04-27 10:38:32 -040046 /**
47 * Whether to use to the database for session storage
48 *
49 * @var bool
50 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020051 public $sess_use_database = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020052
Timothy Warren68f09812012-04-27 10:38:32 -040053 /**
54 * Name of the database table in which to store sessions
55 *
56 * @var string
57 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020058 public $sess_table_name = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020059
Timothy Warren68f09812012-04-27 10:38:32 -040060 /**
61 * Length of time (in seconds) for sessions to expire
62 *
63 * @var int
64 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020065 public $sess_expiration = 7200;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020066
Timothy Warren68f09812012-04-27 10:38:32 -040067 /**
68 * Whether to kill session on close of browser window
69 *
70 * @var bool
71 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020072 public $sess_expire_on_close = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020073
Timothy Warren68f09812012-04-27 10:38:32 -040074 /**
75 * Whether to match session on ip address
76 *
77 * @var bool
78 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020079 public $sess_match_ip = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020080
Timothy Warren68f09812012-04-27 10:38:32 -040081 /**
82 * Whether to match session on user-agent
83 *
84 * @var bool
85 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020086 public $sess_match_useragent = TRUE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020087
Timothy Warren68f09812012-04-27 10:38:32 -040088 /**
89 * Name of session cookie
90 *
91 * @var string
92 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +020093 public $sess_cookie_name = 'ci_session';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +020094
Timothy Warren68f09812012-04-27 10:38:32 -040095 /**
96 * Session cookie prefix
97 *
98 * @var string
99 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200100 public $cookie_prefix = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200101
Timothy Warren68f09812012-04-27 10:38:32 -0400102 /**
103 * Session cookie path
104 *
105 * @var string
106 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200107 public $cookie_path = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200108
Timothy Warren68f09812012-04-27 10:38:32 -0400109 /**
110 * Session cookie domain
111 *
112 * @var string
113 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200114 public $cookie_domain = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200115
Timothy Warren68f09812012-04-27 10:38:32 -0400116 /**
117 * Whether to set the cookie only on HTTPS connections
118 *
119 * @var bool
120 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200121 public $cookie_secure = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200122
Timothy Warren68f09812012-04-27 10:38:32 -0400123 /**
124 * Whether cookie should be allowed only to be sent by the server
125 *
126 * @var bool
127 */
freewil4ad0fd82012-03-13 22:37:42 -0400128 public $cookie_httponly = FALSE;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200129
Timothy Warren68f09812012-04-27 10:38:32 -0400130 /**
131 * Interval at which to update session
132 *
133 * @var int
134 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200135 public $sess_time_to_update = 300;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200136
Timothy Warren68f09812012-04-27 10:38:32 -0400137 /**
138 * Key with which to encrypt the session cookie
139 *
140 * @var string
141 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200142 public $encryption_key = '';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200143
Timothy Warren68f09812012-04-27 10:38:32 -0400144 /**
145 * String to indicate flash data cookies
146 *
147 * @var string
148 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200149 public $flashdata_key = 'flash';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200150
Timothy Warren68f09812012-04-27 10:38:32 -0400151 /**
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300152 * Timezone to use for the current time
Timothy Warren68f09812012-04-27 10:38:32 -0400153 *
154 * @var string
155 */
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300156 public $time_reference = 'local';
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200157
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200158
Timothy Warren68f09812012-04-27 10:38:32 -0400159 /**
160 * Session data
161 *
162 * @var array
163 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200164 public $userdata = array();
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200165
Timothy Warren68f09812012-04-27 10:38:32 -0400166 /**
167 * Reference to CodeIgniter instance
168 *
169 * @var object
170 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200171 public $CI;
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200172
Timothy Warren68f09812012-04-27 10:38:32 -0400173 /**
174 * Current time
175 *
176 * @var int
177 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200178 public $now;
Derek Allard2067d1a2008-11-13 22:59:24 +0000179
180 /**
181 * Session Constructor
182 *
183 * The constructor runs the session routines automatically
184 * whenever the class is instantiated.
Timothy Warren68f09812012-04-27 10:38:32 -0400185 *
186 * @param array
Andrey Andreev56454792012-05-17 14:32:19 +0300187 * @return void
Derek Allard2067d1a2008-11-13 22:59:24 +0000188 */
Greg Akera9263282010-11-10 15:26:43 -0600189 public function __construct($params = array())
Derek Allard2067d1a2008-11-13 22:59:24 +0000190 {
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200191 log_message('debug', 'Session Class Initialized');
Derek Allard2067d1a2008-11-13 22:59:24 +0000192
193 // Set the super object to a local variable for use throughout the class
194 $this->CI =& get_instance();
195
196 // Set all the session preferences, which can either be set
197 // manually via the $params array above or via the config file
Christopher Guiney7a142862012-06-29 20:34:28 -0700198 foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration', 'sess_expire_on_close', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key)
Derek Allard2067d1a2008-11-13 22:59:24 +0000199 {
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300200 $this->$key = isset($params[$key]) ? $params[$key] : $this->CI->config->item($key);
Derek Allard2067d1a2008-11-13 22:59:24 +0000201 }
202
Alex Bilbied261b1e2012-06-02 11:12:16 +0100203 if ($this->encryption_key === '')
Derek Jones5485db52010-08-30 21:31:08 -0500204 {
205 show_error('In order to use the Session class you are required to set an encryption key in your config file.');
206 }
207
Derek Allard2067d1a2008-11-13 22:59:24 +0000208 // Load the string helper so we can use the strip_slashes() function
209 $this->CI->load->helper('string');
210
211 // Do we need encryption? If so, load the encryption class
Alex Bilbied261b1e2012-06-02 11:12:16 +0100212 if ($this->sess_encrypt_cookie === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000213 {
214 $this->CI->load->library('encrypt');
215 }
216
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200217 // Are we using a database? If so, load it
Alex Bilbied261b1e2012-06-02 11:12:16 +0100218 if ($this->sess_use_database === TRUE && $this->sess_table_name !== '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000219 {
220 $this->CI->load->database();
221 }
222
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200223 // Set the "now" time. Can either be GMT or server time, based on the
224 // config prefs. We use this to set the "last activity" time
Derek Allard2067d1a2008-11-13 22:59:24 +0000225 $this->now = $this->_get_time();
226
227 // Set the session length. If the session expiration is
228 // set to zero we'll set the expiration two years from now.
Alex Bilbied261b1e2012-06-02 11:12:16 +0100229 if ($this->sess_expiration === 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000230 {
231 $this->sess_expiration = (60*60*24*365*2);
232 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200233
Derek Allard2067d1a2008-11-13 22:59:24 +0000234 // Set the cookie name
235 $this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
236
237 // Run the Session routine. If a session doesn't exist we'll
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200238 // create a new one. If it does, we'll update it.
Derek Allard2067d1a2008-11-13 22:59:24 +0000239 if ( ! $this->sess_read())
240 {
241 $this->sess_create();
242 }
243 else
244 {
245 $this->sess_update();
246 }
247
248 // Delete 'old' flashdata (from last request)
Barry Mienydd671972010-10-04 16:33:58 +0200249 $this->_flashdata_sweep();
Derek Allard2067d1a2008-11-13 22:59:24 +0000250
251 // Mark all new flashdata as old (data will be deleted before next request)
Barry Mienydd671972010-10-04 16:33:58 +0200252 $this->_flashdata_mark();
Derek Allard2067d1a2008-11-13 22:59:24 +0000253
254 // Delete expired sessions if necessary
255 $this->_sess_gc();
256
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200257 log_message('debug', 'Session routines successfully run');
Derek Allard2067d1a2008-11-13 22:59:24 +0000258 }
259
260 // --------------------------------------------------------------------
261
262 /**
263 * Fetch the current session data if it exists
264 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000265 * @return bool
266 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200267 public function sess_read()
Derek Allard2067d1a2008-11-13 22:59:24 +0000268 {
269 // Fetch the cookie
270 $session = $this->CI->input->cookie($this->sess_cookie_name);
271
Derek Jones4b9c6292011-07-01 17:40:48 -0500272 // No cookie? Goodbye cruel world!...
Phil Sturgeon55a6ddb2012-05-23 18:37:24 +0100273 if ($session === NULL)
Derek Allard2067d1a2008-11-13 22:59:24 +0000274 {
275 log_message('debug', 'A session cookie was not found.');
276 return FALSE;
277 }
278
279 // Decrypt the cookie data
Alex Bilbied261b1e2012-06-02 11:12:16 +0100280 if ($this->sess_encrypt_cookie === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000281 {
282 $session = $this->CI->encrypt->decode($session);
283 }
284 else
285 {
286 // encryption was not used, so we need to check the md5 hash
287 $hash = substr($session, strlen($session)-32); // get last 32 chars
288 $session = substr($session, 0, strlen($session)-32);
289
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200290 // Does the md5 hash match? This is to prevent manipulation of session data in userspace
Derek Jones4b9c6292011-07-01 17:40:48 -0500291 if ($hash !== md5($session.$this->encryption_key))
Derek Allard2067d1a2008-11-13 22:59:24 +0000292 {
293 log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.');
294 $this->sess_destroy();
295 return FALSE;
296 }
297 }
298
299 // Unserialize the session array
300 $session = $this->_unserialize($session);
301
302 // Is the session data we unserialized an array with the correct format?
Andrey Andreev7e087f52012-01-20 11:46:27 +0200303 if ( ! is_array($session) OR ! isset($session['session_id'], $session['ip_address'], $session['user_agent'], $session['last_activity']))
304 {
305 $this->sess_destroy();
306 return FALSE;
307 }
308
309 // Is the session current?
310 if (($session['last_activity'] + $this->sess_expiration) < $this->now)
311 {
312 $this->sess_destroy();
313 return FALSE;
314 }
315
316 // Does the IP match?
Alex Bilbied261b1e2012-06-02 11:12:16 +0100317 if ($this->sess_match_ip === TRUE && $session['ip_address'] !== $this->CI->input->ip_address())
Andrey Andreev7e087f52012-01-20 11:46:27 +0200318 {
319 $this->sess_destroy();
320 return FALSE;
321 }
322
323 // Does the User Agent Match?
Alex Bilbied261b1e2012-06-02 11:12:16 +0100324 if ($this->sess_match_useragent === TRUE && trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
Derek Allard2067d1a2008-11-13 22:59:24 +0000325 {
326 $this->sess_destroy();
327 return FALSE;
328 }
329
330 // Is there a corresponding session in the DB?
331 if ($this->sess_use_database === TRUE)
332 {
333 $this->CI->db->where('session_id', $session['session_id']);
334
Alex Bilbied261b1e2012-06-02 11:12:16 +0100335 if ($this->sess_match_ip === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000336 {
337 $this->CI->db->where('ip_address', $session['ip_address']);
338 }
339
Alex Bilbied261b1e2012-06-02 11:12:16 +0100340 if ($this->sess_match_useragent === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000341 {
342 $this->CI->db->where('user_agent', $session['user_agent']);
343 }
344
Timothy Warrenf1421922012-03-02 11:51:42 -0500345 $query = $this->CI->db->limit(1)->get($this->sess_table_name);
Derek Allard2067d1a2008-11-13 22:59:24 +0000346
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200347 // No result? Kill it!
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200348 if ($query->num_rows() === 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000349 {
350 $this->sess_destroy();
351 return FALSE;
352 }
353
Derek Jones4b9c6292011-07-01 17:40:48 -0500354 // Is there custom data? If so, add it to the main session array
Derek Allard2067d1a2008-11-13 22:59:24 +0000355 $row = $query->row();
Andrey Andreev5036c9c2012-06-04 15:34:56 +0300356 if ( ! empty($row->user_data))
Derek Allard2067d1a2008-11-13 22:59:24 +0000357 {
358 $custom_data = $this->_unserialize($row->user_data);
359
360 if (is_array($custom_data))
361 {
362 foreach ($custom_data as $key => $val)
363 {
364 $session[$key] = $val;
365 }
366 }
367 }
368 }
369
370 // Session is valid!
371 $this->userdata = $session;
372 unset($session);
373
374 return TRUE;
375 }
376
377 // --------------------------------------------------------------------
378
379 /**
380 * Write the session data
381 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000382 * @return void
383 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200384 public function sess_write()
Derek Allard2067d1a2008-11-13 22:59:24 +0000385 {
Derek Jones4b9c6292011-07-01 17:40:48 -0500386 // Are we saving custom data to the DB? If not, all we do is update the cookie
Derek Allard2067d1a2008-11-13 22:59:24 +0000387 if ($this->sess_use_database === FALSE)
388 {
389 $this->_set_cookie();
390 return;
391 }
392
393 // set the custom userdata, the session data we will set in a second
394 $custom_userdata = $this->userdata;
395 $cookie_userdata = array();
396
397 // Before continuing, we need to determine if there is any custom data to deal with.
398 // Let's determine this by removing the default indexes to see if there's anything left in the array
399 // and set the session data while we're at it
400 foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
401 {
402 unset($custom_userdata[$val]);
403 $cookie_userdata[$val] = $this->userdata[$val];
404 }
405
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200406 // Did we find any custom data? If not, we turn the empty array into a string
Derek Allard2067d1a2008-11-13 22:59:24 +0000407 // since there's no reason to serialize and store an empty array in the DB
408 if (count($custom_userdata) === 0)
409 {
410 $custom_userdata = '';
411 }
412 else
413 {
414 // Serialize the custom data array so we can store it
415 $custom_userdata = $this->_serialize($custom_userdata);
416 }
417
418 // Run the update query
419 $this->CI->db->where('session_id', $this->userdata['session_id']);
420 $this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata));
421
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200422 // Write the cookie. Notice that we manually pass the cookie data array to the
Derek Allard2067d1a2008-11-13 22:59:24 +0000423 // _set_cookie() function. Normally that function will store $this->userdata, but
424 // in this case that array contains custom data, which we do not want in the cookie.
425 $this->_set_cookie($cookie_userdata);
426 }
427
428 // --------------------------------------------------------------------
429
430 /**
431 * Create a new session
432 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000433 * @return void
434 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200435 public function sess_create()
Derek Allard2067d1a2008-11-13 22:59:24 +0000436 {
437 $sessid = '';
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200438 do
Derek Allard2067d1a2008-11-13 22:59:24 +0000439 {
440 $sessid .= mt_rand(0, mt_getrandmax());
441 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200442 while (strlen($sessid) < 32);
Derek Allard2067d1a2008-11-13 22:59:24 +0000443
444 // To make the session ID even more secure we'll combine it with the user's IP
445 $sessid .= $this->CI->input->ip_address();
446
447 $this->userdata = array(
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200448 'session_id' => md5(uniqid($sessid, TRUE)),
449 'ip_address' => $this->CI->input->ip_address(),
450 'user_agent' => substr($this->CI->input->user_agent(), 0, 120),
451 'last_activity' => $this->now,
452 'user_data' => ''
453 );
Derek Allard2067d1a2008-11-13 22:59:24 +0000454
455 // Save the data to the DB if needed
456 if ($this->sess_use_database === TRUE)
457 {
458 $this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->userdata));
459 }
460
461 // Write the cookie
462 $this->_set_cookie();
463 }
464
465 // --------------------------------------------------------------------
466
467 /**
468 * Update an existing session
469 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000470 * @return void
471 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200472 public function sess_update()
Derek Allard2067d1a2008-11-13 22:59:24 +0000473 {
474 // We only update the session every five minutes by default
Andrey Andreev9c622f32012-01-19 14:12:54 +0200475 if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
Derek Allard2067d1a2008-11-13 22:59:24 +0000476 {
477 return;
478 }
479
Andrey Andreev9c622f32012-01-19 14:12:54 +0200480 // _set_cookie() will handle this for us if we aren't using database sessions
481 // by pushing all userdata to the cookie.
482 $cookie_data = NULL;
483
484 /* Changing the session ID during an AJAX call causes problems,
485 * so we'll only update our last_activity
486 */
487 if ($this->CI->input->is_ajax_request())
488 {
489 $this->userdata['last_activity'] = $this->now;
490
491 // Update the session ID and last_activity field in the DB if needed
492 if ($this->sess_use_database === TRUE)
493 {
494 // set cookie explicitly to only have our session data
495 $cookie_data = array();
496 foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
497 {
498 $cookie_data[$val] = $this->userdata[$val];
499 }
500
501 $this->CI->db->query($this->CI->db->update_string($this->sess_table_name,
502 array('last_activity' => $this->userdata['last_activity']),
503 array('session_id' => $this->userdata['session_id'])));
504 }
505
506 return $this->_set_cookie($cookie_data);
507 }
508
Derek Allard2067d1a2008-11-13 22:59:24 +0000509 // Save the old session id so we know which record to
510 // update in the database if we need it
511 $old_sessid = $this->userdata['session_id'];
512 $new_sessid = '';
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200513 do
Derek Allard2067d1a2008-11-13 22:59:24 +0000514 {
515 $new_sessid .= mt_rand(0, mt_getrandmax());
516 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200517 while (strlen($new_sessid) < 32);
Derek Allard2067d1a2008-11-13 22:59:24 +0000518
519 // To make the session ID even more secure we'll combine it with the user's IP
520 $new_sessid .= $this->CI->input->ip_address();
521
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200522 // Turn it into a hash and update the session data array
523 $this->userdata['session_id'] = $new_sessid = md5(uniqid($new_sessid, TRUE));
Derek Allard2067d1a2008-11-13 22:59:24 +0000524 $this->userdata['last_activity'] = $this->now;
525
Derek Allard2067d1a2008-11-13 22:59:24 +0000526 // Update the session ID and last_activity field in the DB if needed
527 if ($this->sess_use_database === TRUE)
528 {
529 // set cookie explicitly to only have our session data
530 $cookie_data = array();
531 foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
532 {
533 $cookie_data[$val] = $this->userdata[$val];
534 }
535
536 $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
537 }
538
539 // Write the cookie
540 $this->_set_cookie($cookie_data);
541 }
542
543 // --------------------------------------------------------------------
544
545 /**
546 * Destroy the current session
547 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000548 * @return void
549 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200550 public function sess_destroy()
Derek Allard2067d1a2008-11-13 22:59:24 +0000551 {
552 // Kill the session DB row
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200553 if ($this->sess_use_database === TRUE && isset($this->userdata['session_id']))
Derek Allard2067d1a2008-11-13 22:59:24 +0000554 {
555 $this->CI->db->where('session_id', $this->userdata['session_id']);
556 $this->CI->db->delete($this->sess_table_name);
557 }
558
559 // Kill the cookie
560 setcookie(
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200561 $this->sess_cookie_name,
562 addslashes(serialize(array())),
563 ($this->now - 31500000),
564 $this->cookie_path,
565 $this->cookie_domain,
566 0
567 );
Michiel Vugteveen4c316b62012-05-04 11:32:48 +0200568
569 // Kill session data
570 $this->userdata = array();
Derek Allard2067d1a2008-11-13 22:59:24 +0000571 }
572
573 // --------------------------------------------------------------------
574
575 /**
576 * Fetch a specific item from the session array
577 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000578 * @param string
579 * @return string
580 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200581 public function userdata($item)
Derek Allard2067d1a2008-11-13 22:59:24 +0000582 {
Phil Sturgeon55a6ddb2012-05-23 18:37:24 +0100583 return isset($this->userdata[$item]) ? $this->userdata[$item] : NULL;
Derek Allard2067d1a2008-11-13 22:59:24 +0000584 }
585
586 // --------------------------------------------------------------------
587
588 /**
589 * Fetch all session data
590 *
Greg Aker34033662011-04-18 11:18:09 -0500591 * @return array
Derek Allard2067d1a2008-11-13 22:59:24 +0000592 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200593 public function all_userdata()
Derek Allard2067d1a2008-11-13 22:59:24 +0000594 {
Greg Aker34033662011-04-18 11:18:09 -0500595 return $this->userdata;
Derek Allard2067d1a2008-11-13 22:59:24 +0000596 }
Andrey Andreev38d0e932012-04-03 19:27:45 +0300597
Mike Funkc15e17c2012-02-23 14:56:18 -0500598 // --------------------------------------------------------------------------
Andrey Andreev38d0e932012-04-03 19:27:45 +0300599
Mike Funkc15e17c2012-02-23 14:56:18 -0500600 /**
601 * Fetch all flashdata
Andrey Andreev38d0e932012-04-03 19:27:45 +0300602 *
Mike Funkc91a66c2012-02-28 13:46:00 -0500603 * @return array
Mike Funkc15e17c2012-02-23 14:56:18 -0500604 */
605 public function all_flashdata()
606 {
607 $out = array();
Andrey Andreev38d0e932012-04-03 19:27:45 +0300608
Mike Funkc15e17c2012-02-23 14:56:18 -0500609 // loop through all userdata
610 foreach ($this->all_userdata() as $key => $val)
Andrey Andreev38d0e932012-04-03 19:27:45 +0300611 {
Mike Funkc15e17c2012-02-23 14:56:18 -0500612 // if it contains flashdata, add it
613 if (strpos($key, 'flash:old:') !== FALSE)
614 {
615 $out[$key] = $val;
616 }
617 }
618 return $out;
619 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000620
621 // --------------------------------------------------------------------
622
623 /**
624 * Add or change data in the "userdata" array
625 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000626 * @param mixed
627 * @param string
628 * @return void
629 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200630 public function set_userdata($newdata = array(), $newval = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000631 {
632 if (is_string($newdata))
633 {
634 $newdata = array($newdata => $newval);
635 }
636
637 if (count($newdata) > 0)
638 {
639 foreach ($newdata as $key => $val)
640 {
641 $this->userdata[$key] = $val;
642 }
643 }
644
645 $this->sess_write();
646 }
647
648 // --------------------------------------------------------------------
649
650 /**
651 * Delete a session variable from the "userdata" array
652 *
Timothy Warren68f09812012-04-27 10:38:32 -0400653 * @param array
Derek Allard2067d1a2008-11-13 22:59:24 +0000654 * @return void
655 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200656 public function unset_userdata($newdata = array())
Derek Allard2067d1a2008-11-13 22:59:24 +0000657 {
658 if (is_string($newdata))
659 {
660 $newdata = array($newdata => '');
661 }
662
663 if (count($newdata) > 0)
664 {
665 foreach ($newdata as $key => $val)
666 {
667 unset($this->userdata[$key]);
668 }
669 }
670
671 $this->sess_write();
672 }
673
674 // ------------------------------------------------------------------------
675
676 /**
677 * Add or change flashdata, only available
678 * until the next request
679 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000680 * @param mixed
681 * @param string
682 * @return void
683 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200684 public function set_flashdata($newdata = array(), $newval = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000685 {
686 if (is_string($newdata))
687 {
688 $newdata = array($newdata => $newval);
689 }
690
691 if (count($newdata) > 0)
692 {
693 foreach ($newdata as $key => $val)
694 {
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200695 $this->set_userdata($this->flashdata_key.':new:'.$key, $val);
Derek Allard2067d1a2008-11-13 22:59:24 +0000696 }
697 }
698 }
699
700 // ------------------------------------------------------------------------
701
702 /**
703 * Keeps existing flashdata available to next request.
704 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000705 * @param string
706 * @return void
707 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200708 public function keep_flashdata($key)
Derek Allard2067d1a2008-11-13 22:59:24 +0000709 {
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200710 // 'old' flashdata gets removed. Here we mark all
Derek Allard2067d1a2008-11-13 22:59:24 +0000711 // flashdata as 'new' to preserve it from _flashdata_sweep()
Phil Sturgeon55a6ddb2012-05-23 18:37:24 +0100712 // Note the function will return NULL if the $key
Derek Allard2067d1a2008-11-13 22:59:24 +0000713 // provided cannot be found
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200714 $value = $this->userdata($this->flashdata_key.':old:'.$key);
Derek Allard2067d1a2008-11-13 22:59:24 +0000715
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200716 $this->set_userdata($this->flashdata_key.':new:'.$key, $value);
Derek Allard2067d1a2008-11-13 22:59:24 +0000717 }
718
719 // ------------------------------------------------------------------------
720
721 /**
722 * Fetch a specific flashdata item from the session array
723 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000724 * @param string
725 * @return string
726 */
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200727 public function flashdata($key)
Derek Allard2067d1a2008-11-13 22:59:24 +0000728 {
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200729 return $this->userdata($this->flashdata_key.':old:'.$key);
Derek Allard2067d1a2008-11-13 22:59:24 +0000730 }
731
732 // ------------------------------------------------------------------------
733
734 /**
735 * Identifies flashdata as 'old' for removal
736 * when _flashdata_sweep() runs.
737 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000738 * @return void
739 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200740 protected function _flashdata_mark()
Derek Allard2067d1a2008-11-13 22:59:24 +0000741 {
742 $userdata = $this->all_userdata();
743 foreach ($userdata as $name => $value)
744 {
745 $parts = explode(':new:', $name);
746 if (is_array($parts) && count($parts) === 2)
747 {
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200748 $this->set_userdata($this->flashdata_key.':old:'.$parts[1], $value);
Derek Allard2067d1a2008-11-13 22:59:24 +0000749 $this->unset_userdata($name);
750 }
751 }
752 }
753
754 // ------------------------------------------------------------------------
755
756 /**
757 * Removes all flashdata marked as 'old'
758 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000759 * @return void
760 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200761 protected function _flashdata_sweep()
Derek Allard2067d1a2008-11-13 22:59:24 +0000762 {
763 $userdata = $this->all_userdata();
764 foreach ($userdata as $key => $value)
765 {
766 if (strpos($key, ':old:'))
767 {
768 $this->unset_userdata($key);
769 }
770 }
771
772 }
773
774 // --------------------------------------------------------------------
775
776 /**
777 * Get the "now" time
778 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000779 * @return string
780 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200781 protected function _get_time()
Derek Allard2067d1a2008-11-13 22:59:24 +0000782 {
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300783 if ($this->time_reference === 'local' OR $this->time_reference === date_default_timezone_get())
Iban Eguiafeb14da2012-06-12 16:09:36 +0200784 {
785 return time();
786 }
787
Andrey Andreevd163e0b2012-06-14 03:09:53 +0300788 $datetime = new DateTime('now', new DateTimeZone($this->time_reference));
Iban Eguiafeb14da2012-06-12 16:09:36 +0200789 sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
790
791 return mktime($hour, $minute, $second, $month, $day, $year);
Derek Allard2067d1a2008-11-13 22:59:24 +0000792 }
793
794 // --------------------------------------------------------------------
795
796 /**
797 * Write the session cookie
798 *
Timothy Warren68f09812012-04-27 10:38:32 -0400799 * @param mixed
Derek Allard2067d1a2008-11-13 22:59:24 +0000800 * @return void
801 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200802 protected function _set_cookie($cookie_data = NULL)
Derek Allard2067d1a2008-11-13 22:59:24 +0000803 {
804 if (is_null($cookie_data))
805 {
806 $cookie_data = $this->userdata;
807 }
808
809 // Serialize the userdata for the cookie
810 $cookie_data = $this->_serialize($cookie_data);
811
Alex Bilbied261b1e2012-06-02 11:12:16 +0100812 if ($this->sess_encrypt_cookie === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000813 {
814 $cookie_data = $this->CI->encrypt->encode($cookie_data);
815 }
816 else
817 {
818 // if encryption is not used, we provide an md5 hash to prevent userside tampering
819 $cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);
820 }
Barry Mienydd671972010-10-04 16:33:58 +0200821
Derek Joneseaa71ba2010-09-02 10:32:07 -0500822 $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
Barry Mienydd671972010-10-04 16:33:58 +0200823
Derek Allard2067d1a2008-11-13 22:59:24 +0000824 // Set the cookie
825 setcookie(
freewil4ad0fd82012-03-13 22:37:42 -0400826 $this->sess_cookie_name,
827 $cookie_data,
828 $expire,
829 $this->cookie_path,
830 $this->cookie_domain,
831 $this->cookie_secure,
832 $this->cookie_httponly
833 );
Derek Allard2067d1a2008-11-13 22:59:24 +0000834 }
835
836 // --------------------------------------------------------------------
837
838 /**
839 * Serialize an array
840 *
841 * This function first converts any slashes found in the array to a temporary
842 * marker, so when it gets unserialized the slashes will be preserved
843 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000844 * @param array
845 * @return string
846 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200847 protected function _serialize($data)
Derek Allard2067d1a2008-11-13 22:59:24 +0000848 {
849 if (is_array($data))
850 {
Chris Muench95933492011-10-16 14:14:04 -0400851 array_walk_recursive($data, array(&$this, '_escape_slashes'));
Derek Allard2067d1a2008-11-13 22:59:24 +0000852 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200853 elseif (is_string($data))
Derek Allard2067d1a2008-11-13 22:59:24 +0000854 {
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200855 $data = str_replace('\\', '{{slash}}', $data);
Derek Allard2067d1a2008-11-13 22:59:24 +0000856 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000857 return serialize($data);
858 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200859
Chris Muench95933492011-10-16 14:14:04 -0400860 /**
861 * Escape slashes
862 *
863 * This function converts any slashes found into a temporary marker
864 *
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200865 * @param string
866 * @param string
867 * @return void
Chris Muench95933492011-10-16 14:14:04 -0400868 */
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200869 protected function _escape_slashes(&$val, $key)
Chris Muench95933492011-10-16 14:14:04 -0400870 {
871 if (is_string($val))
872 {
873 $val = str_replace('\\', '{{slash}}', $val);
874 }
875 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000876
877 // --------------------------------------------------------------------
878
879 /**
880 * Unserialize
881 *
882 * This function unserializes a data string, then converts any
883 * temporary slash markers back to actual slashes
884 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000885 * @param array
886 * @return string
887 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200888 protected function _unserialize($data)
Derek Allard2067d1a2008-11-13 22:59:24 +0000889 {
Andrey Andreev6b831232012-03-06 11:16:57 +0200890 $data = @unserialize(strip_slashes(trim($data)));
Derek Allard2067d1a2008-11-13 22:59:24 +0000891
892 if (is_array($data))
893 {
Chris Muench95933492011-10-16 14:14:04 -0400894 array_walk_recursive($data, array(&$this, '_unescape_slashes'));
Derek Allard2067d1a2008-11-13 22:59:24 +0000895 return $data;
896 }
897
Andrey Andreev6b831232012-03-06 11:16:57 +0200898 return is_string($data) ? str_replace('{{slash}}', '\\', $data) : $data;
Derek Allard2067d1a2008-11-13 22:59:24 +0000899 }
Andrey Andreev57ffbbb2011-12-25 04:48:47 +0200900
Andrey Andreev6b831232012-03-06 11:16:57 +0200901 // --------------------------------------------------------------------
902
Chris Muench95933492011-10-16 14:14:04 -0400903 /**
904 * Unescape slashes
905 *
906 * This function converts any slash markers back into actual slashes
907 *
Andrey Andreeveea2ff52012-01-19 13:21:53 +0200908 * @param string
909 * @param string
910 * @return void
Chris Muench95933492011-10-16 14:14:04 -0400911 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200912 protected function _unescape_slashes(&$val, $key)
Chris Muench95933492011-10-16 14:14:04 -0400913 {
Chris Muench3e414f92011-10-16 23:03:55 -0400914 if (is_string($val))
915 {
916 $val= str_replace('{{slash}}', '\\', $val);
917 }
Chris Muench95933492011-10-16 14:14:04 -0400918 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000919
920 // --------------------------------------------------------------------
921
922 /**
923 * Garbage collection
924 *
925 * This deletes expired session rows from database
926 * if the probability percentage is met
927 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000928 * @return void
929 */
Andrey Andreev2c79b762011-12-26 16:54:44 +0200930 protected function _sess_gc()
Derek Allard2067d1a2008-11-13 22:59:24 +0000931 {
Alex Bilbied261b1e2012-06-02 11:12:16 +0100932 if ($this->sess_use_database !== TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000933 {
934 return;
935 }
936
Christopher Guiney7a142862012-06-29 20:34:28 -0700937 $probability = ini_get('session.gc_probability');
938 $divisor = ini_get('session.gc_divisor');
939
Derek Allard2067d1a2008-11-13 22:59:24 +0000940 srand(time());
Christopher Guiney7a142862012-06-29 20:34:28 -0700941 if ((mt_rand(0, $divisor) / $divisor) < $probability)
Derek Allard2067d1a2008-11-13 22:59:24 +0000942 {
943 $expire = $this->now - $this->sess_expiration;
944
Andrey Andreev6b831232012-03-06 11:16:57 +0200945 $this->CI->db->where('last_activity < '.$expire);
Derek Allard2067d1a2008-11-13 22:59:24 +0000946 $this->CI->db->delete($this->sess_table_name);
947
948 log_message('debug', 'Session garbage collection performed.');
949 }
950 }
951
Derek Allard2067d1a2008-11-13 22:59:24 +0000952}
Derek Allard2067d1a2008-11-13 22:59:24 +0000953
954/* End of file Session.php */
Andrey Andreev38d0e932012-04-03 19:27:45 +0300955/* Location: ./system/libraries/Session.php */