PageRenderTime 27ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/system/libraries/Session.php

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