blob: a92db0ecc8a6f8e6651a1e9c4faf95cacaeab4e2 [file] [log] [blame]
Derek Allardd2df9bc2007-04-15 17:41:17 +00001<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
Derek Allard3d879d52008-01-18 19:41:32 +00008 * @author ExpressionEngine Dev Team
Derek Allardd2df9bc2007-04-15 17:41:17 +00009 * @copyright Copyright (c) 2006, EllisLab, Inc.
Derek Jones7a9193a2008-01-21 18:39:20 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
Derek Allardd2df9bc2007-04-15 17:41:17 +000012 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * Session Class
20 *
21 * @package CodeIgniter
22 * @subpackage Libraries
23 * @category Sessions
Derek Allard3d879d52008-01-18 19:41:32 +000024 * @author ExpressionEngine Dev Team
Derek Jones7a9193a2008-01-21 18:39:20 +000025 * @link http://codeigniter.com/user_guide/libraries/sessions.html
Derek Allardd2df9bc2007-04-15 17:41:17 +000026 */
27class CI_Session {
28
29 var $CI;
30 var $now;
31 var $encryption = TRUE;
32 var $use_database = FALSE;
33 var $session_table = FALSE;
34 var $sess_length = 7200;
35 var $sess_cookie = 'ci_session';
36 var $userdata = array();
37 var $gc_probability = 5;
Derek Allard428e9642007-08-10 03:16:16 +000038 var $flashdata_key = 'flash';
Derek Allard34bc8f82007-08-10 03:26:42 +000039 var $time_to_update = 300;
Derek Allardd2df9bc2007-04-15 17:41:17 +000040
41 /**
42 * Session Constructor
43 *
44 * The constructor runs the session routines automatically
45 * whenever the class is instantiated.
46 */
47 function CI_Session()
48 {
49 $this->CI =& get_instance();
50
51 log_message('debug', "Session Class Initialized");
52 $this->sess_run();
53 }
54
55 // --------------------------------------------------------------------
56
57 /**
58 * Run the session routines
59 *
60 * @access public
61 * @return void
62 */
63 function sess_run()
64 {
65 /*
66 * Set the "now" time
67 *
68 * It can either set to GMT or time(). The pref
69 * is set in the config file. If the developer
70 * is doing any sort of time localization they
71 * might want to set the session time to GMT so
72 * they can offset the "last_activity" and
73 * "last_visit" times based on each user's locale.
74 *
75 */
Derek Allard428e9642007-08-10 03:16:16 +000076
77 if (is_numeric($this->CI->config->item('sess_time_to_update')))
78 {
79 $this->time_to_update = $this->CI->config->item('sess_time_to_update');
80 }
81
Derek Allardd2df9bc2007-04-15 17:41:17 +000082 if (strtolower($this->CI->config->item('time_reference')) == 'gmt')
83 {
84 $now = time();
85 $this->now = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now));
86
87 if (strlen($this->now) < 10)
88 {
89 $this->now = time();
90 log_message('error', 'The session class could not set a proper GMT timestamp so the local time() value was used.');
91 }
92 }
93 else
94 {
95 $this->now = time();
96 }
97
98 /*
99 * Set the session length
100 *
101 * If the session expiration is set to zero in
102 * the config file we'll set the expiration
103 * two years from now.
104 *
105 */
106 $expiration = $this->CI->config->item('sess_expiration');
107
108 if (is_numeric($expiration))
109 {
110 if ($expiration > 0)
111 {
112 $this->sess_length = $this->CI->config->item('sess_expiration');
113 }
114 else
115 {
116 $this->sess_length = (60*60*24*365*2);
117 }
118 }
119
120 // Do we need encryption?
121 $this->encryption = $this->CI->config->item('sess_encrypt_cookie');
122
123 if ($this->encryption == TRUE)
124 {
125 $this->CI->load->library('encrypt');
126 }
127
128 // Are we using a database?
129 if ($this->CI->config->item('sess_use_database') === TRUE AND $this->CI->config->item('sess_table_name') != '')
130 {
131 $this->use_database = TRUE;
132 $this->session_table = $this->CI->config->item('sess_table_name');
133 $this->CI->load->database();
134 }
135
136 // Set the cookie name
137 if ($this->CI->config->item('sess_cookie_name') != FALSE)
138 {
139 $this->sess_cookie = $this->CI->config->item('cookie_prefix').$this->CI->config->item('sess_cookie_name');
140 }
141
142 /*
143 * Fetch the current session
144 *
145 * If a session doesn't exist we'll create
146 * a new one. If it does, we'll update it.
147 *
148 */
149 if ( ! $this->sess_read())
150 {
151 $this->sess_create();
152 }
153 else
154 {
155 // We only update the session every five minutes
Derek Allard428e9642007-08-10 03:16:16 +0000156 if (($this->userdata['last_activity'] + $this->time_to_update) < $this->now)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000157 {
158 $this->sess_update();
159 }
160 }
161
162 // Delete expired sessions if necessary
163 if ($this->use_database === TRUE)
164 {
165 $this->sess_gc();
Derek Allard428e9642007-08-10 03:16:16 +0000166 }
167
168 // Delete 'old' flashdata (from last request)
169 $this->_flashdata_sweep();
170
171 // Mark all new flashdata as old (data will be deleted before next request)
172 $this->_flashdata_mark();
Derek Allardd2df9bc2007-04-15 17:41:17 +0000173 }
174
175 // --------------------------------------------------------------------
176
177 /**
178 * Fetch the current session data if it exists
179 *
180 * @access public
181 * @return void
182 */
183 function sess_read()
184 {
185 // Fetch the cookie
186 $session = $this->CI->input->cookie($this->sess_cookie);
187
188 if ($session === FALSE)
189 {
190 log_message('debug', 'A session cookie was not found.');
191 return FALSE;
192 }
193
194 // Decrypt and unserialize the data
195 if ($this->encryption == TRUE)
196 {
197 $session = $this->CI->encrypt->decode($session);
198 }
199
200 $session = @unserialize($this->strip_slashes($session));
201
202 if ( ! is_array($session) OR ! isset($session['last_activity']))
203 {
204 log_message('error', 'The session cookie data did not contain a valid array. This could be a possible hacking attempt.');
205 return FALSE;
206 }
207
208 // Is the session current?
209 if (($session['last_activity'] + $this->sess_length) < $this->now)
210 {
211 $this->sess_destroy();
212 return FALSE;
213 }
214
215 // Does the IP Match?
216 if ($this->CI->config->item('sess_match_ip') == TRUE AND $session['ip_address'] != $this->CI->input->ip_address())
217 {
218 $this->sess_destroy();
219 return FALSE;
220 }
221
222 // Does the User Agent Match?
Derek Allard59c26332007-10-01 12:17:39 +0000223 if ($this->CI->config->item('sess_match_useragent') == TRUE AND trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 50)))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000224 {
225 $this->sess_destroy();
226 return FALSE;
227 }
228
229 // Is there a corresponding session in the DB?
230 if ($this->use_database === TRUE)
231 {
232 $this->CI->db->where('session_id', $session['session_id']);
233
234 if ($this->CI->config->item('sess_match_ip') == TRUE)
235 {
236 $this->CI->db->where('ip_address', $session['ip_address']);
237 }
238
239 if ($this->CI->config->item('sess_match_useragent') == TRUE)
240 {
241 $this->CI->db->where('user_agent', $session['user_agent']);
242 }
243
244 $query = $this->CI->db->get($this->session_table);
245
246 if ($query->num_rows() == 0)
247 {
248 $this->sess_destroy();
249 return FALSE;
250 }
251 else
252 {
253 $row = $query->row();
254 if (($row->last_activity + $this->sess_length) < $this->now)
255 {
256 $this->CI->db->where('session_id', $session['session_id']);
257 $this->CI->db->delete($this->session_table);
258 $this->sess_destroy();
259 return FALSE;
260 }
261 }
262 }
263
264 // Session is valid!
265 $this->userdata = $session;
266 unset($session);
267
268 return TRUE;
269 }
270
271 // --------------------------------------------------------------------
272
273 /**
274 * Write the session cookie
275 *
276 * @access public
277 * @return void
278 */
279 function sess_write()
280 {
281 $cookie_data = serialize($this->userdata);
282
283 if ($this->encryption == TRUE)
284 {
285 $cookie_data = $this->CI->encrypt->encode($cookie_data);
286 }
287
288 setcookie(
289 $this->sess_cookie,
290 $cookie_data,
291 $this->sess_length + time(),
292 $this->CI->config->item('cookie_path'),
293 $this->CI->config->item('cookie_domain'),
294 0
295 );
296 }
297
298 // --------------------------------------------------------------------
299
300 /**
301 * Create a new session
302 *
303 * @access public
304 * @return void
305 */
306 function sess_create()
307 {
308 $sessid = '';
309 while (strlen($sessid) < 32)
310 {
311 $sessid .= mt_rand(0, mt_getrandmax());
312 }
313
314 $this->userdata = array(
315 'session_id' => md5(uniqid($sessid, TRUE)),
316 'ip_address' => $this->CI->input->ip_address(),
317 'user_agent' => substr($this->CI->input->user_agent(), 0, 50),
318 'last_activity' => $this->now
319 );
320
321
322 // Save the session in the DB if needed
323 if ($this->use_database === TRUE)
324 {
325 $this->CI->db->query($this->CI->db->insert_string($this->session_table, $this->userdata));
326 }
327
328 // Write the cookie
Derek Allard428e9642007-08-10 03:16:16 +0000329 $this->userdata['last_visit'] = 0;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000330 $this->sess_write();
331 }
332
333 // --------------------------------------------------------------------
334
335 /**
336 * Update an existing session
337 *
338 * @access public
339 * @return void
340 */
341 function sess_update()
342 {
343 if (($this->userdata['last_activity'] + $this->sess_length) < $this->now)
344 {
345 $this->userdata['last_visit'] = $this->userdata['last_activity'];
346 }
Derek Allard428e9642007-08-10 03:16:16 +0000347
348 // Save the old session id so we know which record to
349 // update in the database if we need it
350 $old_sessid = $this->userdata['session_id'];
351 $new_sessid = '';
352 while (strlen($new_sessid) < 32)
353 {
354 $new_sessid .= mt_rand(0, mt_getrandmax());
355 }
356 $new_sessid = md5(uniqid($new_sessid, TRUE));
357
358 // Update the session data in the session data array
359 $this->userdata['session_id'] = $new_sessid;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000360 $this->userdata['last_activity'] = $this->now;
361
362 // Update the session in the DB if needed
363 if ($this->use_database === TRUE)
364 {
Derek Allard428e9642007-08-10 03:16:16 +0000365 $this->CI->db->query($this->CI->db->update_string($this->session_table, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
Derek Allardd2df9bc2007-04-15 17:41:17 +0000366 }
367
368 // Write the cookie
369 $this->sess_write();
370 }
371
372 // --------------------------------------------------------------------
373
374 /**
375 * Destroy the current session
376 *
377 * @access public
378 * @return void
379 */
380 function sess_destroy()
381 {
382 setcookie(
383 $this->sess_cookie,
384 addslashes(serialize(array())),
385 ($this->now - 31500000),
386 $this->CI->config->item('cookie_path'),
387 $this->CI->config->item('cookie_domain'),
388 0
389 );
390 }
391
392 // --------------------------------------------------------------------
393
394 /**
395 * Garbage collection
396 *
397 * This deletes expired session rows from database
398 * if the probability percentage is met
399 *
400 * @access public
401 * @return void
402 */
403 function sess_gc()
404 {
405 srand(time());
406 if ((rand() % 100) < $this->gc_probability)
407 {
408 $expire = $this->now - $this->sess_length;
409
410 $this->CI->db->where("last_activity < {$expire}");
411 $this->CI->db->delete($this->session_table);
412
413 log_message('debug', 'Session garbage collection performed.');
414 }
415 }
416
417 // --------------------------------------------------------------------
418
419 /**
Derek Allard428e9642007-08-10 03:16:16 +0000420 * Fetch a specific item from the session array
Derek Allardd2df9bc2007-04-15 17:41:17 +0000421 *
422 * @access public
423 * @param string
424 * @return string
425 */
426 function userdata($item)
427 {
428 return ( ! isset($this->userdata[$item])) ? FALSE : $this->userdata[$item];
429 }
Derek Allard428e9642007-08-10 03:16:16 +0000430
431 // --------------------------------------------------------------------
432
433 /**
434 * Fetch all session data
435 *
436 * @access public
437 * @return mixed
438 */
439 function all_userdata()
440 {
441 return ( ! isset($this->userdata)) ? FALSE : $this->userdata;
442 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000443
444 // --------------------------------------------------------------------
445
446 /**
447 * Add or change data in the "userdata" array
448 *
449 * @access public
450 * @param mixed
451 * @param string
452 * @return void
453 */
454 function set_userdata($newdata = array(), $newval = '')
455 {
456 if (is_string($newdata))
457 {
458 $newdata = array($newdata => $newval);
459 }
460
461 if (count($newdata) > 0)
462 {
463 foreach ($newdata as $key => $val)
464 {
465 $this->userdata[$key] = $val;
466 }
467 }
Derek Allard428e9642007-08-10 03:16:16 +0000468
Derek Allardd2df9bc2007-04-15 17:41:17 +0000469 $this->sess_write();
470 }
471
472 // --------------------------------------------------------------------
473
474 /**
475 * Delete a session variable from the "userdata" array
476 *
Derek Allard428e9642007-08-10 03:16:16 +0000477 * @access array
Derek Allardd2df9bc2007-04-15 17:41:17 +0000478 * @return void
479 */
480 function unset_userdata($newdata = array())
481 {
482 if (is_string($newdata))
483 {
484 $newdata = array($newdata => '');
485 }
486
487 if (count($newdata) > 0)
488 {
489 foreach ($newdata as $key => $val)
490 {
491 unset($this->userdata[$key]);
492 }
493 }
494
495 $this->sess_write();
496 }
497
498 // --------------------------------------------------------------------
499
500 /**
501 * Strip slashes
502 *
503 * @access public
504 * @param mixed
505 * @return mixed
506 */
Derek Allard428e9642007-08-10 03:16:16 +0000507 function strip_slashes($vals)
508 {
509 if (is_array($vals))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000510 {
511 foreach ($vals as $key=>$val)
512 {
513 $vals[$key] = $this->strip_slashes($val);
514 }
515 }
516 else
517 {
518 $vals = stripslashes($vals);
519 }
520
521 return $vals;
522 }
523
Derek Allard428e9642007-08-10 03:16:16 +0000524
525 // ------------------------------------------------------------------------
526
527 /**
528 * Add or change flashdata, only available
529 * until the next request
530 *
531 * @access public
532 * @param mixed
533 * @param string
534 * @return void
535 */
536 function set_flashdata($newdata = array(), $newval = '')
537 {
538 if (is_string($newdata))
539 {
540 $newdata = array($newdata => $newval);
541 }
542
543 if (count($newdata) > 0)
544 {
545 foreach ($newdata as $key => $val)
546 {
547 $flashdata_key = $this->flashdata_key.':new:'.$key;
548 $this->set_userdata($flashdata_key, $val);
549 }
550 }
551 }
552
553 // ------------------------------------------------------------------------
554
555 /**
556 * Keeps existing flashdata available to next request.
557 *
558 * @access public
559 * @param string
560 * @return void
561 */
562 function keep_flashdata($key)
563 {
564 // 'old' flashdata gets removed. Here we mark all
565 // flashdata as 'new' to preserve it from _flashdata_sweep()
566 // Note the function will return FALSE if the $key
567 // provided cannot be found
568 $old_flashdata_key = $this->flashdata_key.':old:'.$key;
569 $value = $this->userdata($old_flashdata_key);
570
571 $new_flashdata_key = $this->flashdata_key.':new:'.$key;
572 $this->set_userdata($new_flashdata_key, $value);
573 }
574
575 // ------------------------------------------------------------------------
576
577 /**
578 * Fetch a specific flashdata item from the session array
579 *
580 * @access public
581 * @param string
582 * @return string
583 */
584 function flashdata($key)
585 {
586 $flashdata_key = $this->flashdata_key.':old:'.$key;
587 return $this->userdata($flashdata_key);
588 }
589
590 // ------------------------------------------------------------------------
591
592 /**
593 * Identifies flashdata as 'old' for removal
594 * when _flashdata_sweep() runs.
595 *
596 * @access private
597 * @return void
598 */
599 function _flashdata_mark()
600 {
601 $userdata = $this->all_userdata();
602 foreach ($userdata as $name => $value)
603 {
604 $parts = explode(':new:', $name);
605 if (is_array($parts) && count($parts) === 2)
606 {
607 $new_name = $this->flashdata_key.':old:'.$parts[1];
608 $this->set_userdata($new_name, $value);
609 $this->unset_userdata($name);
610 }
611 }
612 }
613
614 // ------------------------------------------------------------------------
615
616 /**
617 * Removes all flashdata marked as 'old'
618 *
619 * @access private
620 * @return void
621 */
622
623 function _flashdata_sweep()
624 {
625 $userdata = $this->all_userdata();
626 foreach ($userdata as $key => $value)
627 {
628 if (strpos($key, ':old:'))
629 {
630 $this->unset_userdata($key);
631 }
632 }
633
634 }
635
Derek Allardd2df9bc2007-04-15 17:41:17 +0000636}
637// END Session Class
adminb0dd10f2006-08-25 17:25:49 +0000638?>