PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/cake/libs/session.php

https://github.com/hardsshah/bookmarks
PHP | 778 lines | 603 code | 7 blank | 168 comment | 42 complexity | 358032d30f491004a306627305f74a13 MD5 | raw file
  1. <?php
  2. /* SVN FILE: $Id$ */
  3. /**
  4. * Session class for Cake.
  5. *
  6. * Cake abstracts the handling of sessions.
  7. * There are several convenient methods to access session information.
  8. * This class is the implementation of those methods.
  9. * They are mostly used by the Session Component.
  10. *
  11. * PHP versions 4 and 5
  12. *
  13. * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
  14. * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
  15. *
  16. * Licensed under The MIT License
  17. * Redistributions of files must retain the above copyright notice.
  18. *
  19. * @filesource
  20. * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
  21. * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
  22. * @package cake
  23. * @subpackage cake.cake.libs
  24. * @since CakePHP(tm) v .0.10.0.1222
  25. * @version $Revision$
  26. * @modifiedby $LastChangedBy$
  27. * @lastmodified $Date$
  28. * @license http://www.opensource.org/licenses/mit-license.php The MIT License
  29. */
  30. /**
  31. * Database name for cake sessions.
  32. *
  33. */
  34. if (!class_exists('Set')) {
  35. require LIBS . 'set.php';
  36. }
  37. if (!class_exists('Security')) {
  38. require LIBS . 'security.php';
  39. }
  40. /**
  41. * Session class for Cake.
  42. *
  43. * Cake abstracts the handling of sessions. There are several convenient methods to access session information.
  44. * This class is the implementation of those methods. They are mostly used by the Session Component.
  45. *
  46. * @package cake
  47. * @subpackage cake.cake.libs
  48. */
  49. class CakeSession extends Object {
  50. /**
  51. * True if the Session is still valid
  52. *
  53. * @var boolean
  54. * @access public
  55. */
  56. var $valid = false;
  57. /**
  58. * Error messages for this session
  59. *
  60. * @var array
  61. * @access public
  62. */
  63. var $error = false;
  64. /**
  65. * User agent string
  66. *
  67. * @var string
  68. * @access protected
  69. */
  70. var $_userAgent = '';
  71. /**
  72. * Path to where the session is active.
  73. *
  74. * @var string
  75. * @access public
  76. */
  77. var $path = '/';
  78. /**
  79. * Error number of last occurred error
  80. *
  81. * @var integer
  82. * @access public
  83. */
  84. var $lastError = null;
  85. /**
  86. * 'Security.level' setting, "high", "medium", or "low".
  87. *
  88. * @var string
  89. * @access public
  90. */
  91. var $security = null;
  92. /**
  93. * Start time for this session.
  94. *
  95. * @var integer
  96. * @access public
  97. */
  98. var $time = false;
  99. /**
  100. * Time when this session becomes invalid.
  101. *
  102. * @var integer
  103. * @access public
  104. */
  105. var $sessionTime = false;
  106. /**
  107. * Keeps track of keys to watch for writes on
  108. *
  109. * @var array
  110. * @access public
  111. */
  112. var $watchKeys = array();
  113. /**
  114. * Current Session id
  115. *
  116. * @var string
  117. * @access public
  118. */
  119. var $id = null;
  120. /**
  121. * Constructor.
  122. *
  123. * @param string $base The base path for the Session
  124. * @param boolean $start Should session be started right now
  125. * @access public
  126. */
  127. function __construct($base = null, $start = true) {
  128. if (Configure::read('Session.save') === 'database' && !class_exists('ConnectionManager')) {
  129. App::import('Core', 'ConnectionManager');
  130. }
  131. if (Configure::read('Session.checkAgent') === true || Configure::read('Session.checkAgent') === null) {
  132. if (env('HTTP_USER_AGENT') != null) {
  133. $this->_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
  134. }
  135. }
  136. $this->time = time();
  137. if ($start === true) {
  138. $this->host = env('HTTP_HOST');
  139. if (empty($base) || strpos($base, '?') === 0 || strpos($base, 'index.php') === 0) {
  140. $this->path = '/';
  141. } else {
  142. $this->path = $base;
  143. }
  144. if (strpos($this->host, ':') !== false) {
  145. $this->host = substr($this->host, 0, strpos($this->host, ':'));
  146. }
  147. if (!class_exists('Security')) {
  148. App::import('Core', 'Security');
  149. }
  150. $this->sessionTime = $this->time + (Security::inactiveMins() * Configure::read('Session.timeout'));
  151. $this->security = Configure::read('Security.level');
  152. }
  153. parent::__construct();
  154. }
  155. /**
  156. * Starts the Session.
  157. *
  158. * @param string $name Variable name to check for
  159. * @return boolean True if variable is there
  160. * @access public
  161. */
  162. function start() {
  163. if (function_exists('session_write_close')) {
  164. session_write_close();
  165. }
  166. $this->__initSession();
  167. return $this->__startSession();
  168. }
  169. /**
  170. * Determine if Session has been started.
  171. *
  172. * @access public
  173. * @return boolean True if session has been started.
  174. */
  175. function started() {
  176. if (isset($_SESSION)) {
  177. return true;
  178. }
  179. return false;
  180. }
  181. /**
  182. * Returns true if given variable is set in session.
  183. *
  184. * @param string $name Variable name to check for
  185. * @return boolean True if variable is there
  186. * @access public
  187. */
  188. function check($name) {
  189. $var = $this->__validateKeys($name);
  190. if (empty($var)) {
  191. return false;
  192. }
  193. $result = Set::extract($_SESSION, $var);
  194. return isset($result);
  195. }
  196. /**
  197. * Returns the Session id
  198. *
  199. * @param id $name string
  200. * @return string Session id
  201. * @access public
  202. */
  203. function id($id = null) {
  204. if ($id) {
  205. $this->id = $id;
  206. session_id($this->id);
  207. }
  208. if (isset($_SESSION)) {
  209. return session_id();
  210. } else {
  211. return $this->id;
  212. }
  213. }
  214. /**
  215. * Removes a variable from session.
  216. *
  217. * @param string $name Session variable to remove
  218. * @return boolean Success
  219. * @access public
  220. */
  221. function del($name) {
  222. if ($this->check($name)) {
  223. if ($var = $this->__validateKeys($name)) {
  224. if (in_array($var, $this->watchKeys)) {
  225. trigger_error('Deleting session key {' . $var . '}', E_USER_NOTICE);
  226. }
  227. $this->__overwrite($_SESSION, Set::remove($_SESSION, $var));
  228. return ($this->check($var) == false);
  229. }
  230. }
  231. $this->__setError(2, "$name doesn't exist");
  232. return false;
  233. }
  234. /**
  235. * Used to write new data to _SESSION, since PHP doesn't like us setting the _SESSION var itself
  236. *
  237. * @param array $old Set of old variables => values
  238. * @param array $new New set of variable => value
  239. * @access private
  240. */
  241. function __overwrite(&$old, $new) {
  242. if (!empty($old)) {
  243. foreach ($old as $key => $var) {
  244. if (!isset($new[$key])) {
  245. unset($old[$key]);
  246. }
  247. }
  248. }
  249. foreach ($new as $key => $var) {
  250. $old[$key] = $var;
  251. }
  252. }
  253. /**
  254. * Return error description for given error number.
  255. *
  256. * @param integer $errorNumber Error to set
  257. * @return string Error as string
  258. * @access private
  259. */
  260. function __error($errorNumber) {
  261. if (!is_array($this->error) || !array_key_exists($errorNumber, $this->error)) {
  262. return false;
  263. } else {
  264. return $this->error[$errorNumber];
  265. }
  266. }
  267. /**
  268. * Returns last occurred error as a string, if any.
  269. *
  270. * @return mixed Error description as a string, or false.
  271. * @access public
  272. */
  273. function error() {
  274. if ($this->lastError) {
  275. return $this->__error($this->lastError);
  276. } else {
  277. return false;
  278. }
  279. }
  280. /**
  281. * Returns true if session is valid.
  282. *
  283. * @return boolean Success
  284. * @access public
  285. */
  286. function valid() {
  287. if ($this->read('Config')) {
  288. if ((Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read('Config.userAgent')) && $this->time <= $this->read('Config.time')) {
  289. if ($this->error === false) {
  290. $this->valid = true;
  291. }
  292. } else {
  293. $this->valid = false;
  294. $this->__setError(1, 'Session Highjacking Attempted !!!');
  295. }
  296. }
  297. return $this->valid;
  298. }
  299. /**
  300. * Returns given session variable, or all of them, if no parameters given.
  301. *
  302. * @param mixed $name The name of the session variable (or a path as sent to Set.extract)
  303. * @return mixed The value of the session variable
  304. * @access public
  305. */
  306. function read($name = null) {
  307. if (is_null($name)) {
  308. return $this->__returnSessionVars();
  309. }
  310. if (empty($name)) {
  311. return false;
  312. }
  313. $result = Set::extract($_SESSION, $name);
  314. if (!is_null($result)) {
  315. return $result;
  316. }
  317. $this->__setError(2, "$name doesn't exist");
  318. return null;
  319. }
  320. /**
  321. * Returns all session variables.
  322. *
  323. * @return mixed Full $_SESSION array, or false on error.
  324. * @access private
  325. */
  326. function __returnSessionVars() {
  327. if (!empty($_SESSION)) {
  328. return $_SESSION;
  329. }
  330. $this->__setError(2, "No Session vars set");
  331. return false;
  332. }
  333. /**
  334. * Tells Session to write a notification when a certain session path or subpath is written to
  335. *
  336. * @param mixed $var The variable path to watch
  337. * @return void
  338. * @access public
  339. */
  340. function watch($var) {
  341. $var = $this->__validateKeys($var);
  342. if (empty($var)) {
  343. return false;
  344. }
  345. if (!in_array($var, $this->watchKeys, true)) {
  346. $this->watchKeys[] = $var;
  347. }
  348. }
  349. /**
  350. * Tells Session to stop watching a given key path
  351. *
  352. * @param mixed $var The variable path to watch
  353. * @return void
  354. * @access public
  355. */
  356. function ignore($var) {
  357. $var = $this->__validateKeys($var);
  358. if (!in_array($var, $this->watchKeys)) {
  359. return;
  360. }
  361. foreach ($this->watchKeys as $i => $key) {
  362. if ($key == $var) {
  363. unset($this->watchKeys[$i]);
  364. $this->watchKeys = array_values($this->watchKeys);
  365. return;
  366. }
  367. }
  368. }
  369. /**
  370. * Writes value to given session variable name.
  371. *
  372. * @param mixed $name Name of variable
  373. * @param string $value Value to write
  374. * @return boolean True if the write was successful, false if the write failed
  375. * @access public
  376. */
  377. function write($name, $value) {
  378. $var = $this->__validateKeys($name);
  379. if (empty($var)) {
  380. return false;
  381. }
  382. if (in_array($var, $this->watchKeys)) {
  383. trigger_error('Writing session key {' . $var . '}: ' . Debugger::exportVar($value), E_USER_NOTICE);
  384. }
  385. $this->__overwrite($_SESSION, Set::insert($_SESSION, $var, $value));
  386. return (Set::extract($_SESSION, $var) === $value);
  387. }
  388. /**
  389. * Helper method to destroy invalid sessions.
  390. *
  391. * @return void
  392. * @access public
  393. */
  394. function destroy() {
  395. $_SESSION = array();
  396. $this->__construct($this->path);
  397. $this->start();
  398. $this->renew();
  399. $this->_checkValid();
  400. }
  401. /**
  402. * Helper method to initialize a session, based on Cake core settings.
  403. *
  404. * @access private
  405. */
  406. function __initSession() {
  407. $iniSet = function_exists('ini_set');
  408. if ($iniSet && env('HTTPS')) {
  409. ini_set('session.cookie_secure', 1);
  410. }
  411. switch ($this->security) {
  412. case 'high':
  413. $this->cookieLifeTime = 0;
  414. if ($iniSet) {
  415. ini_set('session.referer_check', $this->host);
  416. }
  417. break;
  418. case 'medium':
  419. $this->cookieLifeTime = 7 * 86400;
  420. if ($iniSet) {
  421. ini_set('session.referer_check', $this->host);
  422. }
  423. break;
  424. case 'low':
  425. default:
  426. $this->cookieLifeTime = 788940000;
  427. break;
  428. }
  429. switch (Configure::read('Session.save')) {
  430. case 'cake':
  431. if (empty($_SESSION)) {
  432. if ($iniSet) {
  433. ini_set('session.use_trans_sid', 0);
  434. ini_set('url_rewriter.tags', '');
  435. ini_set('session.serialize_handler', 'php');
  436. ini_set('session.use_cookies', 1);
  437. ini_set('session.name', Configure::read('Session.cookie'));
  438. ini_set('session.cookie_lifetime', $this->cookieLifeTime);
  439. ini_set('session.cookie_path', $this->path);
  440. ini_set('session.auto_start', 0);
  441. ini_set('session.save_path', TMP . 'sessions');
  442. }
  443. }
  444. break;
  445. case 'database':
  446. if (empty($_SESSION)) {
  447. if (Configure::read('Session.table') === null) {
  448. trigger_error(__("You must set the all Configure::write('Session.*') in core.php to use database storage"), E_USER_WARNING);
  449. exit();
  450. } elseif (Configure::read('Session.database') === null) {
  451. Configure::write('Session.database', 'default');
  452. }
  453. if ($iniSet) {
  454. ini_set('session.use_trans_sid', 0);
  455. ini_set('url_rewriter.tags', '');
  456. ini_set('session.save_handler', 'user');
  457. ini_set('session.serialize_handler', 'php');
  458. ini_set('session.use_cookies', 1);
  459. ini_set('session.name', Configure::read('Session.cookie'));
  460. ini_set('session.cookie_lifetime', $this->cookieLifeTime);
  461. ini_set('session.cookie_path', $this->path);
  462. ini_set('session.auto_start', 0);
  463. }
  464. }
  465. session_set_save_handler(array('CakeSession','__open'),
  466. array('CakeSession', '__close'),
  467. array('CakeSession', '__read'),
  468. array('CakeSession', '__write'),
  469. array('CakeSession', '__destroy'),
  470. array('CakeSession', '__gc'));
  471. break;
  472. case 'php':
  473. if (empty($_SESSION)) {
  474. if ($iniSet) {
  475. ini_set('session.use_trans_sid', 0);
  476. ini_set('session.name', Configure::read('Session.cookie'));
  477. ini_set('session.cookie_lifetime', $this->cookieLifeTime);
  478. ini_set('session.cookie_path', $this->path);
  479. }
  480. }
  481. break;
  482. case 'cache':
  483. if (empty($_SESSION)) {
  484. if (!class_exists('Cache')) {
  485. uses('Cache');
  486. }
  487. if ($iniSet) {
  488. ini_set('session.use_trans_sid', 0);
  489. ini_set('url_rewriter.tags', '');
  490. ini_set('session.save_handler', 'user');
  491. ini_set('session.use_cookies', 1);
  492. ini_set('session.name', Configure::read('Session.cookie'));
  493. ini_set('session.cookie_lifetime', $this->cookieLifeTime);
  494. ini_set('session.cookie_path', $this->path);
  495. }
  496. }
  497. session_set_save_handler(array('CakeSession','__open'),
  498. array('CakeSession', '__close'),
  499. array('Cache', 'read'),
  500. array('Cache', 'write'),
  501. array('Cache', 'delete'),
  502. array('CakeSession', '__gc'));
  503. break;
  504. default:
  505. if (empty($_SESSION)) {
  506. $config = CONFIGS . Configure::read('Session.save') . '.php';
  507. if (is_file($config)) {
  508. require_once ($config);
  509. }
  510. }
  511. break;
  512. }
  513. }
  514. /**
  515. * Helper method to start a session
  516. *
  517. * @access private
  518. */
  519. function __startSession() {
  520. if (headers_sent()) {
  521. if (empty($_SESSION)) {
  522. $_SESSION = array();
  523. }
  524. return false;
  525. } elseif (!isset($_SESSION)) {
  526. session_cache_limiter ("must-revalidate");
  527. session_start();
  528. header ('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"');
  529. return true;
  530. } else {
  531. session_start();
  532. return true;
  533. }
  534. }
  535. /**
  536. * Helper method to create a new session.
  537. *
  538. * @return void
  539. * @access protected
  540. */
  541. function _checkValid() {
  542. if ($this->read('Config')) {
  543. if ((Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read('Config.userAgent')) && $this->time <= $this->read('Config.time')) {
  544. $time = $this->read('Config.time');
  545. $this->write('Config.time', $this->sessionTime);
  546. if (Configure::read('Security.level') === 'high') {
  547. $check = $this->read('Config.timeout');
  548. $check = $check - 1;
  549. $this->write('Config.timeout', $check);
  550. if (time() > ($time - (Security::inactiveMins() * Configure::read('Session.timeout')) + 2) || $check < 1) {
  551. $this->renew();
  552. $this->write('Config.timeout', 10);
  553. }
  554. }
  555. $this->valid = true;
  556. } else {
  557. $this->destroy();
  558. $this->valid = false;
  559. $this->__setError(1, 'Session Highjacking Attempted !!!');
  560. }
  561. } else {
  562. $this->write('Config.userAgent', $this->_userAgent);
  563. $this->write('Config.time', $this->sessionTime);
  564. $this->write('Config.timeout', 10);
  565. $this->valid = true;
  566. $this->__setError(1, 'Session is valid');
  567. }
  568. }
  569. /**
  570. * Helper method to restart a session.
  571. *
  572. * @return void
  573. * @access private
  574. */
  575. function __regenerateId() {
  576. $oldSessionId = session_id();
  577. if ($oldSessionId) {
  578. $sessionpath = session_save_path();
  579. if (empty($sessionpath)) {
  580. $sessionpath = "/tmp";
  581. }
  582. if (session_id() != "" || isset($_COOKIE[session_name()])) {
  583. setcookie(Configure::read('Session.cookie'), '', time() - 42000, $this->path);
  584. }
  585. session_regenerate_id(true);
  586. if (PHP_VERSION < 5.1) {
  587. $newSessid = session_id();
  588. if (function_exists('session_write_close')) {
  589. session_write_close();
  590. }
  591. $this->__initSession();
  592. session_id($oldSessionId);
  593. session_start();
  594. session_destroy();
  595. $file = $sessionpath . DS . "sess_$oldSessionId";
  596. @unlink($file);
  597. $this->__initSession();
  598. session_id($newSessid);
  599. session_start();
  600. }
  601. }
  602. }
  603. /**
  604. * Restarts this session.
  605. *
  606. * @access public
  607. */
  608. function renew() {
  609. $this->__regenerateId();
  610. }
  611. /**
  612. * Validate that the $name is in correct dot notation
  613. * example: $name = 'ControllerName.key';
  614. *
  615. * @param string $name Session key names as string.
  616. * @return mixed false is $name is not correct format, or $name if it is correct
  617. * @access private
  618. */
  619. function __validateKeys($name) {
  620. if (is_string($name) && preg_match("/^[ 0-9a-zA-Z._-]*$/", $name)) {
  621. return $name;
  622. }
  623. $this->__setError(3, "$name is not a string");
  624. return false;
  625. }
  626. /**
  627. * Helper method to set an internal error message.
  628. *
  629. * @param integer $errorNumber Number of the error
  630. * @param string $errorMessage Description of the error
  631. * @return void
  632. * @access private
  633. */
  634. function __setError($errorNumber, $errorMessage) {
  635. if ($this->error === false) {
  636. $this->error = array();
  637. }
  638. $this->error[$errorNumber] = $errorMessage;
  639. $this->lastError = $errorNumber;
  640. }
  641. /**
  642. * Method called on open of a database session.
  643. *
  644. * @return boolean Success
  645. * @access private
  646. */
  647. function __open() {
  648. return true;
  649. }
  650. /**
  651. * Method called on close of a database session.
  652. *
  653. * @return boolean Success
  654. * @access private
  655. */
  656. function __close() {
  657. $probability = mt_rand(1, 150);
  658. if ($probability <= 3) {
  659. switch (Configure::read('Session.save')) {
  660. case 'cache':
  661. Cache::gc();
  662. break;
  663. default:
  664. CakeSession::__gc();
  665. break;
  666. }
  667. }
  668. return true;
  669. }
  670. /**
  671. * Method used to read from a database session.
  672. *
  673. * @param mixed $key The key of the value to read
  674. * @return mixed The value of the key or false if it does not exist
  675. * @access private
  676. */
  677. function __read($key) {
  678. $db =& ConnectionManager::getDataSource(Configure::read('Session.database'));
  679. $table = $db->fullTableName(Configure::read('Session.table'), false);
  680. $row = $db->query("SELECT " . $db->name($table.'.data') . " FROM " . $db->name($table) . " WHERE " . $db->name($table.'.id') . " = " . $db->value($key), false);
  681. if ($row && !isset($row[0][$table]) && isset($row[0][0])) {
  682. $table = 0;
  683. }
  684. if ($row && $row[0][$table]['data']) {
  685. return $row[0][$table]['data'];
  686. } else {
  687. return false;
  688. }
  689. }
  690. /**
  691. * Helper function called on write for database sessions.
  692. *
  693. * @param mixed $key The name of the var
  694. * @param mixed $value The value of the var
  695. * @return boolean Success
  696. * @access private
  697. */
  698. function __write($key, $value) {
  699. $db =& ConnectionManager::getDataSource(Configure::read('Session.database'));
  700. $table = $db->fullTableName(Configure::read('Session.table'));
  701. switch (Configure::read('Security.level')) {
  702. case 'high':
  703. $factor = 10;
  704. break;
  705. case 'medium':
  706. $factor = 100;
  707. break;
  708. case 'low':
  709. $factor = 300;
  710. break;
  711. default:
  712. $factor = 10;
  713. break;
  714. }
  715. $expires = time() + Configure::read('Session.timeout') * $factor;
  716. $row = $db->query("SELECT COUNT(id) AS count FROM " . $db->name($table) . " WHERE "
  717. . $db->name('id') . " = "
  718. . $db->value($key), false);
  719. if ($row[0][0]['count'] > 0) {
  720. $db->execute("UPDATE " . $db->name($table) . " SET " . $db->name('data') . " = "
  721. . $db->value($value) . ", " . $db->name('expires') . " = "
  722. . $db->value($expires) . " WHERE " . $db->name('id') . " = "
  723. . $db->value($key));
  724. } else {
  725. $db->execute("INSERT INTO " . $db->name($table) . " (" . $db->name('data') . ","
  726. . $db->name('expires') . "," . $db->name('id')
  727. . ") VALUES (" . $db->value($value) . ", " . $db->value($expires) . ", "
  728. . $db->value($key) . ")");
  729. }
  730. return true;
  731. }
  732. /**
  733. * Method called on the destruction of a database session.
  734. *
  735. * @param integer $key Key that uniquely identifies session in database
  736. * @return boolean Success
  737. * @access private
  738. */
  739. function __destroy($key) {
  740. $db =& ConnectionManager::getDataSource(Configure::read('Session.database'));
  741. $table = $db->fullTableName(Configure::read('Session.table'));
  742. $db->execute("DELETE FROM " . $db->name($table) . " WHERE " . $db->name($table.'.id') . " = " . $db->value($key));
  743. return true;
  744. }
  745. /**
  746. * Helper function called on gc for database sessions.
  747. *
  748. * @param integer $expires Timestamp (defaults to current time)
  749. * @return boolean Success
  750. * @access private
  751. */
  752. function __gc($expires = null) {
  753. $db =& ConnectionManager::getDataSource(Configure::read('Session.database'));
  754. $table = $db->fullTableName(Configure::read('Session.table'));
  755. $db->execute("DELETE FROM " . $db->name($table) . " WHERE " . $db->name($table.'.expires') . " < ". $db->value(time()));
  756. return true;
  757. }
  758. }
  759. ?>