PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/code/web/public_php/webtt/cake/libs/cake_session.php

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