diff options
Diffstat (limited to 'site/cake/libs/session.php')
-rw-r--r-- | site/cake/libs/session.php | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/site/cake/libs/session.php b/site/cake/libs/session.php new file mode 100644 index 0000000..cab0498 --- /dev/null +++ b/site/cake/libs/session.php @@ -0,0 +1,777 @@ +<?php +/* SVN FILE: $Id: session.php 6305 2008-01-02 02:33:56Z phpnut $ */ +/** + * Session class for Cake. + * + * Cake abstracts the handling of sessions. + * There are several convenient methods to access session information. + * This class is the implementation of those methods. + * They are mostly used by the Session Component. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework <http://www.cakephp.org/> + * Copyright 2005-2008, Cake Software Foundation, Inc. + * 1785 E. Sahara Avenue, Suite 490-204 + * Las Vegas, Nevada 89104 + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v .0.10.0.1222 + * @version $Revision: 6305 $ + * @modifiedby $LastChangedBy: phpnut $ + * @lastmodified $Date: 2008-01-01 20:33:56 -0600 (Tue, 01 Jan 2008) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Database name for cake sessions. + * + */ + if (!defined('CAKE_SESSION_TABLE')) { + define('CAKE_SESSION_TABLE', 'cake_sessions'); + } + + if (CAKE_SESSION_SAVE === 'database') { + uses('model' . DS . 'connection_manager'); + } + uses('set'); +/** + * Session class for Cake. + * + * Cake abstracts the handling of sessions. There are several convenient methods to access session information. + * This class is the implementation of those methods. They are mostly used by the Session Component. + * + * @package cake + * @subpackage cake.cake.libs + */ +class CakeSession extends Object { +/** + * True if the Session is still valid + * + * @var boolean + * @access public + */ + var $valid = false; +/** + * Error messages for this session + * + * @var array + * @access public + */ + var $error = false; +/** + * User agent string + * + * @var string + * @access protected + */ + var $_userAgent = ''; +/** + * Path to where the session is active. + * + * @var string + * @access public + */ + var $path = false; +/** + * Error number of last occurred error + * + * @var integer + * @access public + */ + var $lastError = null; +/** + * CAKE_SECURITY setting, "high", "medium", or "low". + * + * @var string + * @access public + */ + var $security = null; +/** + * Start time for this session. + * + * @var integer + * @access public + */ + var $time = false; +/** + * Time when this session becomes invalid. + * + * @var integer + * @access public + */ + var $sessionTime = false; +/** + * Keeps track of keys to watch for writes on + * + * @var array + * @access public + */ + var $watchKeys = array(); +/** + * Current Session id + * + * @var string + * @access public + */ + var $id = null; +/** + * Constructor. + * + * @param string $base The base path for the Session + * @param boolean $start Should session be started right now + * @access public + */ + function __construct($base = null, $start = true) { + if (Configure::read('Session.checkAgent') === true) { + if (env('HTTP_USER_AGENT') != null) { + $this->_userAgent = md5(env('HTTP_USER_AGENT') . CAKE_SESSION_STRING); + } + } + $this->time = time(); + + if ($start === true) { + $this->host = env('HTTP_HOST'); + + if (empty($base) || strpos($base, '?') === 0 || strpos($base, 'index.php') === 0) { + $this->path = '/'; + } else { + $this->path = $base; + } + + if (strpos($this->host, ':') !== false) { + $this->host = substr($this->host, 0, strpos($this->host, ':')); + } + + $this->sessionTime = $this->time + (Security::inactiveMins() * CAKE_SESSION_TIMEOUT); + $this->security = CAKE_SECURITY; + } + parent::__construct(); + } +/** + * Starts the Session. + * + * @param string $cookieLifeTime How long for the cookie + * @return boolean True if variable is there + * @access public + */ + /** BEGIN MOZILLA PATCH (@author rdoherty@mozilla.com)**/ + function start($cookieLifeTime = null) { + if (function_exists('session_write_close')) { + session_write_close(); + } + + $this->__initSession($cookieLifeTime); + /** END MOZILLA PATCH **/ + return $this->__startSession(); + } +/** + * Determine if Session has been started. + * + * @access public + */ + function started(){ + if (isset($_SESSION)) { + return true; + } + return false; + } +/** + * Returns true if given variable is set in session. + * + * @param string $name Variable name to check for + * @return boolean True if variable is there + * @access public + */ + function check($name) { + $var = $this->__validateKeys($name); + if (empty($var)) { + return false; + } + $result = Set::extract($_SESSION, $var); + return isset($result); + } +/** + * + * @param id $name string + * @return string Session id + * @access public + */ + function id($id = null) { + if ($id) { + $this->id = $id; + session_id($this->id); + } + if (isset($_SESSION)) { + return session_id(); + } else { + return $this->id; + } + } +/** + * Temp method until we are able to remove the last eval(). + * Builds an expression to fetch a session variable with specified name. + * + * @param string $name Name of variable (in dot notation) + * @access private + */ + function __sessionVarNames($name) { + if (is_string($name) && preg_match("/^[ 0-9a-zA-Z._-]*$/", $name)) { + if (strpos($name, ".")) { + $names = explode(".", $name); + } else { + $names = array($name); + } + $expression = "\$_SESSION"; + foreach ($names as $item) { + $expression .= is_numeric($item) ? "[$item]" : "['$item']"; + } + return $expression; + } + $this->__setError(3, "$name is not a string"); + return false; + } +/** + * Removes a variable from session. + * + * @param string $name Session variable to remove + * @return boolean Success + * @access public + */ + function del($name) { + if ($this->check($name)) { + if ($var = $this->__validateKeys($name)) { + if (in_array($var, $this->watchKeys)) { + trigger_error('Deleting session key {' . $var . '}', E_USER_NOTICE); + } + $this->__overwrite($_SESSION, Set::remove($_SESSION, $var)); + return ($this->check($var) == false); + } + } + $this->__setError(2, "$name doesn't exist"); + return false; + } +/** + * Used to write new data to _SESSION, since PHP doesn't like us setting the _SESSION var itself + * + * @param array $old Set of old variables => values + * @param array $new New set of variable => value + * @access private + */ + function __overwrite(&$old, $new) { + if(!empty($old)) { + foreach ($old as $key => $var) { + if (!isset($new[$key])) { + unset($old[$key]); + } + } + } + foreach ($new as $key => $var) { + $old[$key] = $var; + } + } +/** + * Return error description for given error number. + * + * @param integer $errorNumber Error to set + * @return string Error as string + * @access private + */ + function __error($errorNumber) { + if (!is_array($this->error) || !array_key_exists($errorNumber, $this->error)) { + return false; + } else { + return $this->error[$errorNumber]; + } + } +/** + * Returns last occurred error as a string, if any. + * + * @return mixed Error description as a string, or false. + * @access public + */ + function error() { + if ($this->lastError) { + return $this->__error($this->lastError); + } else { + return false; + } + } +/** + * Returns true if session is valid. + * + * @return boolean Success + * @access public + */ + function valid() { + if ($this->read('Config')) { + if (Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read("Config.userAgent") && $this->time <= $this->read("Config.time")) { + if ($this->error === false) { + $this->valid = true; + } + } else { + $this->valid = false; + $this->__setError(1, "Session Highjacking Attempted !!!"); + } + } + return $this->valid; + } +/** + * Returns given session variable, or all of them, if no parameters given. + * + * @param mixed $name The name of the session variable (or a path as sent to Set.extract) + * @return mixed The value of the session variable + * @access public + */ + function read($name = null) { + if (is_null($name)) { + return $this->__returnSessionVars(); + } + if (empty($name)) { + return false; + } + $result = Set::extract($_SESSION, $name); + + if (!is_null($result)) { + return $result; + } + $this->__setError(2, "$name doesn't exist"); + return null; + } +/** + * Returns all session variables. + * + * @return mixed Full $_SESSION array, or false on error. + * @access private + */ + function __returnSessionVars() { + if (!empty($_SESSION)) { + return $_SESSION; + } + $this->__setError(2, "No Session vars set"); + return false; + } +/** + * Tells Session to write a notification when a certain session path or subpath is written to + * + * @param mixed $var The variable path to watch + * @access public + */ + function watch($var) { + $var = $this->__validateKeys($var); + if (empty($var)) { + return false; + } + $this->watchKeys[] = $var; + } +/** + * Tells Session to stop watching a given key path + * + * @param mixed $var The variable path to watch + * @access public + */ + function ignore($var) { + $var = $this->__validateKeys($var); + if (!in_array($var, $this->watchKeys)) { + return; + } + foreach ($this->watchKeys as $i => $key) { + if ($key == $var) { + unset($this->watchKeys[$i]); + $this->watchKeys = array_values($this->watchKeys); + return; + } + } + } +/** + * Writes value to given session variable name. + * + * @param mixed $name Name of variable + * @param string $value Value to write + * @return boolean True if the write was successful, false if the write failed + * @access public + */ + function write($name, $value) { + $var = $this->__validateKeys($name); + + if (empty($var)) { + return false; + } + if (in_array($var, $this->watchKeys)) { + trigger_error('Writing session key {' . $var . '}: ' . print_r($value), E_USER_NOTICE); + } + $this->__overwrite($_SESSION, Set::insert($_SESSION, $var, $value)); + return (Set::extract($_SESSION, $var) === $value); + } +/** + * Helper method to destroy invalid sessions. + * + * @access public + */ + function destroy() { + $sessionpath = session_save_path(); + if (empty($sessionpath)) { + $sessionpath = "/tmp"; + } + + if (isset($_COOKIE[session_name()])) { + setcookie(CAKE_SESSION_COOKIE, '', time() - 42000, $this->path); + } + + $_SESSION = array(); + $file = $sessionpath . DS . "sess_" . session_id(); + @session_destroy(); + @unlink ($file); + $this->__construct($this->path); + $this->renew(); + } +/** + * Helper method to initialize a session, based on Cake core settings. + * + * @param int $cookieLifeTime how long the cookie should live in seconds + * @access private + */ + /** BEGIN MOZILLA PATCH (@author rdoherty@mozilla.com)**/ + function __initSession($cookieLifeTime = null) { + /** END MOZILLA PATCH (@author clouserw@mozilla.com)**/ + switch($this->security) { + case 'high': + $this->cookieLifeTime = 0; + if (function_exists('ini_set')) { + ini_set('session.referer_check', $this->host); + } + break; + case 'medium': + $this->cookieLifeTime = 7 * 86400; + if (function_exists('ini_set')) { + ini_set('session.referer_check', $this->host); + } + break; + case 'low': + default: + $this->cookieLifeTime = 788940000; + break; + } + + /** BEGIN MOZILLA PATCH (@author rdoherty@mozilla.com)**/ + if($cookieLifeTime) { + $this->cookieLifeTime = $cookieLifeTime; + } + /** END MOZILLA PATCH **/ + + switch(CAKE_SESSION_SAVE) { + case 'cake': + if (!isset($_SESSION)) { + if (function_exists('ini_set')) { + ini_set('session.use_trans_sid', 0); + ini_set('url_rewriter.tags', ''); + ini_set('session.serialize_handler', 'php'); + ini_set('session.use_cookies', 1); + ini_set('session.name', CAKE_SESSION_COOKIE); + ini_set('session.cookie_lifetime', $this->cookieLifeTime); + ini_set('session.cookie_path', $this->path); + ini_set('session.auto_start', 0); + ini_set('session.save_path', TMP . 'sessions'); + } + } + break; + case 'database': + if (!isset($_SESSION)) { + if (function_exists('ini_set')) { + ini_set('session.use_trans_sid', 0); + ini_set('url_rewriter.tags', ''); + ini_set('session.save_handler', 'user'); + ini_set('session.serialize_handler', 'php'); + ini_set('session.use_cookies', 1); + ini_set('session.name', CAKE_SESSION_COOKIE); + ini_set('session.cookie_lifetime', $this->cookieLifeTime); + ini_set('session.cookie_path', $this->path); + ini_set('session.auto_start', 0); + } + } + session_set_save_handler(array('CakeSession','__open'), + array('CakeSession', '__close'), + array('CakeSession', '__read'), + array('CakeSession', '__write'), + array('CakeSession', '__destroy'), + array('CakeSession', '__gc')); + break; + case 'php': + if (!isset($_SESSION)) { + if (function_exists('ini_set')) { + ini_set('session.use_trans_sid', 0); + ini_set('session.name', CAKE_SESSION_COOKIE); + ini_set('session.cookie_lifetime', $this->cookieLifeTime); + ini_set('session.cookie_path', $this->path); + } + } + break; + default: + if (!isset($_SESSION)) { + $config = CONFIGS . CAKE_SESSION_SAVE . '.php'; + + if (is_file($config)) { + require_once ($config); + } + } + break; + } + } +/** + * Helper method to start a session + * + * @access private + */ + function __startSession() { + if (headers_sent()) { + if (!isset($_SESSION)) { + $_SESSION = array(); + } + return false; + } elseif (!isset($_SESSION)) { + session_cache_limiter ("must-revalidate"); + session_start(); + header ('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"'); + return true; + } else { + return true; + } + } +/** + * Helper method to create a new session. + * + * @access protected + */ + function _checkValid() { + if ($this->read('Config')) { + if (Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read("Config.userAgent") && $this->time <= $this->read("Config.time")) { + $time = $this->read("Config.time"); + $this->write("Config.time", $this->sessionTime); + + if ($this->security === 'high') { + $check = $this->read("Config.timeout"); + $check = $check - 1; + $this->write("Config.timeout", $check); + + if (time() > ($time - (Security::inactiveMins() * CAKE_SESSION_TIMEOUT) + 2) || $check < 1) { + $this->renew(); + $this->write('Config.timeout', 10); + } + } + $this->valid = true; + } else { + $this->destroy(); + $this->valid = false; + $this->__setError(1, "Session Highjacking Attempted !!!"); + } + } else { + srand ((double)microtime() * 1000000); + $this->write("Config.userAgent", $this->_userAgent); + $this->write("Config.time", $this->sessionTime); + $this->write('Config.rand', rand()); + $this->write('Config.timeout', 10); + $this->valid = true; + $this->__setError(1, "Session is valid"); + } + } +/** + * Helper method to restart a session. + * + * @access private + */ + function __regenerateId() { + $oldSessionId = session_id(); + if ($oldSessionId) { + $sessionpath = session_save_path(); + if (empty($sessionpath)) { + $sessionpath = "/tmp"; + } + + if (isset($_COOKIE[session_name()])) { + setcookie(CAKE_SESSION_COOKIE, '', time() - 42000, $this->path); + } + session_regenerate_id(); + $newSessid = session_id(); + + if (function_exists('session_write_close')) { + session_write_close(); + } + $this->__initSession(); + session_id($oldSessionId); + session_start(); + session_destroy(); + $file = $sessionpath . DS . "sess_$oldSessionId"; + @unlink($file); + $this->__initSession(); + session_id($newSessid); + session_start(); + } + } +/** + * Restarts this session. + * + * @access public + */ + function renew() { + /** BEGIN MOZILLA PATCH (@author fwenzel@mozilla.com) **/ + // if we (legitimately) regenerate the session ID during a POST request, + // we need to remember the old ID, so we can check the secret submitted + // by the user against it. + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + global $csrf_old_session_id; + $csrf_old_session_id = session_id(); + } + /** END MOZILLA PATCH **/ + $this->__regenerateId(); + } +/** + * Validate that the $name is in correct dot notation + * example: $name = 'ControllerName.key'; + * + * @param string $name Session key names as string. + * @return mixed false is $name is not correct format, or $name if it is correct + * @access private + */ + function __validateKeys($name) { + if (is_string($name) && preg_match("/^[ 0-9a-zA-Z._-]*$/", $name)) { + return $name; + } + $this->__setError(3, "$name is not a string"); + return false; + } +/** + * Helper method to set an internal error message. + * + * @param integer $errorNumber Number of the error + * @param string $errorMessage Description of the error + * @access private + */ + function __setError($errorNumber, $errorMessage) { + if ($this->error === false) { + $this->error = array(); + } + $this->error[$errorNumber] = $errorMessage; + $this->lastError = $errorNumber; + } +/** + * Method called on open of a database session. + * + * @return boolean Success + * @access private + */ + function __open() { + return true; + } +/** + * Method called on close of a database session. + * + * @return boolean Success + * @access private + */ + function __close() { + $probability = mt_rand(1, 150); + if ($probability <= 3) { + CakeSession::__gc(); + } + return true; + } +/** + * Method used to read from a database session. + * + * @param mixed $key The key of the value to read + * @return mixed The value of the key or false if it does not exist + * @access private + */ + function __read($key) { + $db =& ConnectionManager::getDataSource('default'); + $table = $db->fullTableName(CAKE_SESSION_TABLE, false); + $row = $db->query("SELECT " . $db->name($table.'.data') . " FROM " . $db->name($table) . " WHERE " . $db->name($table.'.id') . " = " . $db->value($key), false); + + if ($row && !isset($row[0][$table]) && isset($row[0][0])) { + $table = 0; + } + + if ($row && $row[0][$table]['data']) { + return $row[0][$table]['data']; + } else { + return false; + } + } +/** + * Helper function called on write for database sessions. + * + * @param mixed $key The name of the var + * @param mixed $value The value of the var + * @return boolean Success + * @access private + */ + function __write($key, $value) { + $db =& ConnectionManager::getDataSource('default'); + $table = $db->fullTableName(CAKE_SESSION_TABLE); + + switch(CAKE_SECURITY) { + case 'high': + $factor = 10; + break; + case 'medium': + $factor = 100; + break; + case 'low': + $factor = 300; + break; + default: + $factor = 10; + break; + } + $expires = time() + CAKE_SESSION_TIMEOUT * $factor; + $row = $db->query("SELECT COUNT(id) AS count FROM " . $db->name($table) . " WHERE " + . $db->name('id') . " = " + . $db->value($key), false); + + if ($row[0][0]['count'] > 0) { + $db->execute("UPDATE " . $db->name($table) . " SET " . $db->name('data') . " = " + . $db->value($value) . ", " . $db->name('expires') . " = " + . $db->value($expires) . " WHERE " . $db->name('id') . " = " + . $db->value($key)); + } else { + $db->execute("INSERT INTO " . $db->name($table) . " (" . $db->name('data') . "," + . $db->name('expires') . "," . $db->name('id') + . ") VALUES (" . $db->value($value) . ", " . $db->value($expires) . ", " + . $db->value($key) . ")"); + } + return true; + } +/** + * Method called on the destruction of a database session. + * + * @param int $key Key that uniquely identifies session in database + * @return boolean Success + * @access private + */ + function __destroy($key) { + $db =& ConnectionManager::getDataSource('default'); + $table = $db->fullTableName(CAKE_SESSION_TABLE); + $db->execute("DELETE FROM " . $db->name($table) . " WHERE " . $db->name($table.'.id') . " = " . $db->value($key, 'integer')); + return true; + } +/** + * Helper function called on gc for database sessions. + * + * @param int $expires Timestamp (defaults to current time) + * @return boolean Success + * @access private + */ + function __gc($expires = null) { + $db =& ConnectionManager::getDataSource('default'); + $table = $db->fullTableName(CAKE_SESSION_TABLE); + $db->execute("DELETE FROM " . $db->name($table) . " WHERE " . $db->name($table.'.expires') . " < ". $db->value(time())); + return true; + } +} +?> |