SetCookieDomain('my.domain.com'); $session->SetCookiePath('/myscript'); $session->SetCookieName('my_sid_cookie'); $session->SetGETName('sid'); $session->InitSession(); ... //link output: echo "NeedQueryString() ? 'sid='.$session->SID : '' ) .">My Link"; */ //Implements session storage in the database class SessionStorage extends kDBBase { var $Expiration; var $SessionTimeout=0; var $DirectVars = Array(); var $ChangedDirectVars = Array(); var $PersistentVars = Array (); var $OriginalData=Array(); var $TimestampField; var $SessionDataTable; var $DataValueField; var $DataVarField; function Init($prefix,$special) { parent::Init($prefix,$special); $this->setTableName('sessions'); $this->setIDField('sid'); $this->TimestampField = 'expire'; $this->SessionDataTable = 'SessionData'; $this->DataValueField = 'value'; $this->DataVarField = 'var'; } function setSessionTimeout($new_timeout) { $this->SessionTimeout = $new_timeout; } function StoreSession(&$session, $additional_fields = Array()) { if (defined('IS_INSTALL') && IS_INSTALL && !$this->Application->TableFound($this->TableName)) { return false; } $fields_hash = Array ( $this->IDField => $session->SID, $this->TimestampField => $session->Expiration ); $this->Conn->doInsert($fields_hash, $this->TableName); foreach ($additional_fields as $field_name => $field_value) { $this->SetField($session, $field_name, $field_value); } } function DeleteSession(&$session) { $query = ' DELETE FROM '.$this->TableName.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); $this->Conn->Query($query); $query = ' DELETE FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); $this->Conn->Query($query); $this->OriginalData = Array(); } function UpdateSession(&$session, $timeout=0) { $this->SetField($session, $this->TimestampField, $session->Expiration); $query = ' UPDATE '.$this->TableName.' SET '.$this->TimestampField.' = '.$session->Expiration.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); $this->Conn->Query($query); } function LocateSession($sid) { $query = ' SELECT * FROM '.$this->TableName.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($sid); $result = $this->Conn->GetRow($query); if($result===false) return false; $this->DirectVars = $result; $this->Expiration = $result[$this->TimestampField]; return true; } function GetExpiration() { return $this->Expiration; } function LoadData(&$session) { $query = 'SELECT '.$this->DataValueField.','.$this->DataVarField.' FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID); $this->OriginalData = $this->Conn->GetCol($query, $this->DataVarField); return $this->OriginalData; } /** * Enter description here... * * @param Session $session * @param string $var_name * @param mixed $default */ function GetField(&$session, $var_name, $default = false) { return isset($this->DirectVars[$var_name]) ? $this->DirectVars[$var_name] : $default; //return $this->Conn->GetOne('SELECT '.$var_name.' FROM '.$this->TableName.' WHERE `'.$this->IDField.'` = '.$this->Conn->qstr($session->GetID()) ); } function SetField(&$session, $var_name, $value) { $value_changed = !isset($this->DirectVars[$var_name]) || ($this->DirectVars[$var_name] != $value); if ($value_changed) { $this->DirectVars[$var_name] = $value; $this->ChangedDirectVars[] = $var_name; $this->ChangedDirectVars = array_unique($this->ChangedDirectVars); } //return $this->Conn->Query('UPDATE '.$this->TableName.' SET '.$var_name.' = '.$this->Conn->qstr($value).' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->GetID()) ); } function SaveData(&$session) { if(!$session->SID) return false; // can't save without sid $ses_data = $session->Data->GetParams(); $replace = ''; foreach ($ses_data as $key => $value) { if ( isset($this->OriginalData[$key]) && $this->OriginalData[$key] == $value) { continue; //skip unchanged session data } else { $replace .= sprintf("(%s, %s, %s),", $this->Conn->qstr($session->SID), $this->Conn->qstr($key), $this->Conn->qstr($value)); } } $replace = rtrim($replace, ','); if ($replace != '') { $query = ' REPLACE INTO '.$this->SessionDataTable. ' ('.$this->IDField.', '.$this->DataVarField.', '.$this->DataValueField.') VALUES '.$replace; $this->Conn->Query($query); } if ($this->ChangedDirectVars) { $changes = array(); foreach ($this->ChangedDirectVars as $var) { $changes[] = $var.' = '.$this->Conn->qstr($this->DirectVars[$var]); } $query = 'UPDATE '.$this->TableName.' SET '.implode(',', $changes).' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->GetID()); $this->Conn->Query($query); } } function RemoveFromData(&$session, $var) { $query = 'DELETE FROM '.$this->SessionDataTable.' WHERE '.$this->IDField.' = '.$this->Conn->qstr($session->SID). ' AND '.$this->DataVarField.' = '.$this->Conn->qstr($var); $this->Conn->Query($query); unset($this->OriginalData[$var]); } function GetFromData(&$session, $var) { return getArrayValue($this->OriginalData, $var); } function GetExpiredSIDs() { $query = ' SELECT '.$this->IDField.' FROM '.$this->TableName.' WHERE '.$this->TimestampField.' > '.adodb_mktime(); return $this->Conn->GetCol($query); } function DeleteExpired() { $expired_sids = $this->GetExpiredSIDs(); if ($expired_sids) { $where_clause=' WHERE '.$this->IDField.' IN ("'.implode('","',$expired_sids).'")'; $sql = 'DELETE FROM '.$this->SessionDataTable.$where_clause; $this->Conn->Query($sql); $sql = 'DELETE FROM '.$this->TableName.$where_clause; $this->Conn->Query($sql); // delete debugger ouputs left of expired sessions foreach ($expired_sids as $expired_sid) { $debug_file = KERNEL_PATH.'/../cache/debug_@'.$expired_sid.'@.txt'; if (file_exists($debug_file)) { @unlink($debug_file); } } } return $expired_sids; } function LoadPersistentVars(&$session) { $user_id = $session->RecallVar('user_id'); if ($user_id != -2) { // root & normal users $sql = 'SELECT VariableValue, VariableName FROM '.TABLE_PREFIX.'PersistantSessionData WHERE PortalUserId = '.$user_id; $this->PersistentVars = $this->Conn->GetCol($sql, 'VariableName'); } else { $this->PersistentVars = Array (); } } function StorePersistentVar(&$session, $var_name, $var_value) { $user_id = $session->RecallVar('user_id'); if ($user_id == -2 || $user_id === false) { // -2 (when not logged in), false (when after u:OnLogout event) return ; } $this->PersistentVars[$var_name] = $var_value; $key_clause = 'PortalUserId = '.$user_id.' AND VariableName = '.$this->Conn->qstr($var_name); $sql = 'SELECT VariableValue FROM '.TABLE_PREFIX.'PersistantSessionData WHERE '.$key_clause; $record_found = $this->Conn->GetOne($sql); $fields_hash = Array ( 'PortalUserId' => $user_id, 'VariableName' => $var_name, 'VariableValue' => $var_value, ); if ($record_found) { $this->Conn->doUpdate($fields_hash, TABLE_PREFIX.'PersistantSessionData', $key_clause); } else { $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'PersistantSessionData'); } } function RecallPersistentVar(&$session, $var_name, $default = false) { if (isset($this->PersistentVars[$var_name])) { return $this->PersistentVars[$var_name]; } elseif ($default == '_USE_DEFAULT_USER_DATA_') { $default_user_id = $this->Application->ConfigValue('DefaultSettingsUserId'); if (!$default_user_id) $default_user_id = -1; $sql = 'SELECT VariableValue, VariableName FROM '.TABLE_PREFIX.'PersistantSessionData WHERE VariableName = '.$this->Conn->qstr($var_name).' AND PortalUserId = '.$default_user_id; $value = $this->Conn->GetOne($sql); if ($value !== false) { $this->PersistentVars[$var_name] = $value; $this->StorePersistentVar($session, $var_name, $value); //storing it, so next time we don't load default user setting } return $value; } else return $default; } function RemovePersistentVar(&$session, $var_name) { unset($this->PersistentVars[$var_name]); $user_id = $session->RecallVar('user_id'); if ($user_id != -2) { $sql = 'DELETE FROM '.TABLE_PREFIX.'PersistantSessionData WHERE PortalUserId = '.$user_id.' AND VariableName = '.$this->Conn->qstr($var_name); $this->Conn->Query($sql); } } } define('smAUTO', 1); define('smCOOKIES_ONLY', 2); define('smGET_ONLY', 3); define('smCOOKIES_AND_GET', 4); class Session extends kBase { var $Checkers; var $Mode; var $OriginalMode = null; var $GETName = 'sid'; var $CookiesEnabled = true; var $CookieName = 'sid'; var $CookieDomain; var $CookiePath; var $CookieSecure = 0; var $SessionTimeout = 3600; var $Expiration; var $SID; /** * Enter description here... * * @var SessionStorage */ var $Storage; var $CachedNeedQueryString = null; var $Data; function Session($mode=smAUTO) { parent::kBase(); $this->SetMode($mode); } function SetMode($mode) { $this->Mode = $mode; $this->CachedNeedQueryString = null; $this->CachedSID = null; } function SetCookiePath($path) { $this->CookiePath = $path; } function SetCookieDomain($domain) { $this->CookieDomain = '.'.ltrim($domain, '.'); } function SetGETName($get_name) { $this->GETName = $get_name; } function SetCookieName($cookie_name) { $this->CookieName = $cookie_name; } function InitStorage($special) { $this->Storage =& $this->Application->recallObject('SessionStorage.'.$special); $this->Storage->setSessionTimeout($this->SessionTimeout); } function Init($prefix,$special) { parent::Init($prefix,$special); $this->CheckIfCookiesAreOn(); if ($this->CookiesEnabled) $_COOKIE['cookies_on'] = 1; $this->Checkers = Array(); $this->InitStorage($special); $this->Data = new Params(); $tmp_sid = $this->GetPassedSIDValue(); $check = $this->Check(); if( !(defined('IS_INSTALL') && IS_INSTALL) ) { $expired_sids = $this->DeleteExpired(); if ( ( $expired_sids && in_array($tmp_sid,$expired_sids) ) || ( $tmp_sid && !$check ) ) { $this->SetSession(); $this->Application->HandleEvent($event, 'u:OnSessionExpire'); return ; } } if ($check) { $this->SID = $this->GetPassedSIDValue(); $this->Refresh(); $this->LoadData(); } else { $this->SetSession(); } if (!is_null($this->OriginalMode)) $this->SetMode($this->OriginalMode); } function IsHTTPSRedirect() { $http_referer = getArrayValue($_SERVER, 'HTTP_REFERER'); return ( ( PROTOCOL == 'https://' && preg_match('#http:\/\/#', $http_referer) ) || ( PROTOCOL == 'http://' && preg_match('#https:\/\/#', $http_referer) ) ); } function CheckReferer($for_cookies=0) { if (!$for_cookies) { if ( !$this->Application->ConfigValue('SessionReferrerCheck') || $_SERVER['REQUEST_METHOD'] != 'POST') { return true; } } $path = preg_replace('/admin[\/]{0,1}$/', '', $this->CookiePath); // removing /admin for compatability with in-portal (in-link/admin/add_link.php) $reg = '#^'.preg_quote(PROTOCOL.ltrim($this->CookieDomain, '.').$path).'#'; return preg_match($reg, getArrayValue($_SERVER, 'HTTP_REFERER') ) || (defined('IS_POPUP') && IS_POPUP); } /*function CheckDuplicateCookies() { if (isset($_SERVER['HTTP_COOKIE'])) { $cookie_str = $_SERVER['HTTP_COOKIE']; $cookies = explode('; ', $cookie_str); $all_cookies = array(); foreach ($cookies as $cookie) { list($name, $value) = explode('=', $cookie); if (isset($all_cookies[$name])) { //double cookie name!!! $this->RemoveCookie($name); } else $all_cookies[$name] = $value; } } } function RemoveCookie($name) { $path = $_SERVER['PHP_SELF']; $path_parts = explode('/', $path); $cur_path = ''; setcookie($name, false, null, $cur_path); foreach ($path_parts as $part) { $cur_path .= $part; setcookie($name, false, null, $cur_path); $cur_path .= '/'; setcookie($name, false, null, $cur_path); } }*/ function CheckIfCookiesAreOn() { // $this->CheckDuplicateCookies(); if ($this->Mode == smGET_ONLY) { //we don't need to bother checking if we would not use it $this->CookiesEnabled = false; return; } $http_query =& $this->Application->recallObject('HTTPQuery'); $cookies_on = isset($http_query->Cookie['cookies_on']); // not good here $get_sid = getArrayValue($http_query->Get, $this->GETName); if ($this->IsHTTPSRedirect() && $get_sid) { //Redirect from http to https on different domain $this->OriginalMode = $this->Mode; $this->SetMode(smGET_ONLY); } if (!$cookies_on || $this->IsHTTPSRedirect()) { //If referer is our server, but we don't have our cookies_on, it's definetly off $is_install = defined('IS_INSTALL') && IS_INSTALL; if (!$is_install && $this->CheckReferer(1) && !$this->Application->GetVar('admin') && !$this->IsHTTPSRedirect()) { $this->CookiesEnabled = false; } else { //Otherwise we still suppose cookies are on, because may be it's the first time user visits the site //So we send cookies on to get it next time (when referal will tell us if they are realy off $this->SetCookie('cookies_on', 1, adodb_mktime() + 31104000); //one year should be enough } } else $this->CookiesEnabled = true; return $this->CookiesEnabled; } /** * Sets cookie for current site using path and domain * * @param string $name * @param mixed $value * @param int $expires */ function SetCookie($name, $value, $expires = null) { setcookie($name, $value, $expires, $this->CookiePath, $this->CookieDomain, $this->CookieSecure); } function Check() { // we should check referer if cookies are disabled, and in combined mode // auto mode would detect cookies, get only mode would turn it off - so we would get here // and we don't care about referal in cookies only mode if ( $this->Mode != smCOOKIES_ONLY && (!$this->CookiesEnabled || $this->Mode == smCOOKIES_AND_GET) ) { if (!$this->CheckReferer()) return false; } $sid = $this->GetPassedSIDValue(); if (empty($sid)) return false; //try to load session by sid, if everything is fine $result = $this->LoadSession($sid); return $result; } function LoadSession($sid) { if( $this->Storage->LocateSession($sid) ) { //if we have session with such SID - get its expiration $this->Expiration = $this->Storage->GetExpiration(); //If session has expired if ($this->Expiration < adodb_mktime()) return false; //Otherwise it's ok return true; } else //fake or deleted due to expiration SID return false; } function GetPassedSIDValue($use_cache = 1) { if (!empty($this->CachedSID) && $use_cache) return $this->CachedSID; $http_query =& $this->Application->recallObject('HTTPQuery'); $get_sid = getArrayValue($http_query->Get, $this->GETName); if ($this->Application->GetVar('admin') == 1 && $get_sid) { $sid = $get_sid; } else { switch ($this->Mode) { case smAUTO: //Cookies has the priority - we ignore everything else $sid = $this->CookiesEnabled ? $this->GetSessionCookie() : $get_sid; break; case smCOOKIES_ONLY: $sid = $this->GetSessionCookie(); break; case smGET_ONLY: $sid = $get_sid; break; case smCOOKIES_AND_GET: $cookie_sid = $this->GetSessionCookie(); //both sids should match if cookies are enabled if (!$this->CookiesEnabled || ($cookie_sid == $get_sid)) { $sid = $get_sid; //we use get here just in case cookies are disabled } else { $sid = ''; } break; } } $this->CachedSID = $sid; return $this->CachedSID; } /** * Returns session id * * @return int * @access public */ function GetID() { return $this->SID; } /** * Generates new session id * * @return int * @access private */ function GenerateSID() { list($usec, $sec) = explode(" ",microtime()); $sid_part_1 = substr($usec, 4, 4); $sid_part_2 = mt_rand(1,9); $sid_part_3 = substr($sec, 6, 4); $digit_one = substr($sid_part_1, 0, 1); if ($digit_one == 0) { $digit_one = mt_rand(1,9); $sid_part_1 = ereg_replace("^0","",$sid_part_1); $sid_part_1=$digit_one.$sid_part_1; } $this->setSID($sid_part_1.$sid_part_2.$sid_part_3); return $this->SID; } /** * Set's new session id * * @param int $new_sid * @access private */ function setSID($new_sid) { $this->SID=$new_sid; $this->Application->SetVar($this->GETName,$new_sid); } function SetSession() { $this->GenerateSID(); $this->Expiration = adodb_mktime() + $this->SessionTimeout; switch ($this->Mode) { case smAUTO: if ($this->CookiesEnabled) { $this->SetSessionCookie(); } break; case smGET_ONLY: break; case smCOOKIES_ONLY: case smCOOKIES_AND_GET: $this->SetSessionCookie(); break; } $this->Storage->StoreSession($this); if ($this->Application->IsAdmin() || $this->Special == 'admin') { $this->StoreVar('admin', 1); } if ($this->Special != '') { // front-session called from admin or otherwise, then save it's data $this->SaveData(); } $this->Application->resetCounters('UserSession'); } /** * Returns SID from cookie * * @return int */ function GetSessionCookie() { $keep_session_on_browser_close = $this->Application->ConfigValue('KeepSessionOnBrowserClose'); if (isset($this->Application->HttpQuery->Cookie[$this->CookieName]) && ( $keep_session_on_browser_close || ( !$keep_session_on_browser_close && isset($this->Application->HttpQuery->Cookie[$this->CookieName.'_live']) && $this->Application->HttpQuery->Cookie[$this->CookieName] == $this->Application->HttpQuery->Cookie[$this->CookieName.'_live'] ) ) ) { return $this->Application->HttpQuery->Cookie[$this->CookieName]; } return false; } /** * Updates SID in cookie with new value * */ function SetSessionCookie() { $this->SetCookie($this->CookieName, $this->SID, $this->Expiration); $this->SetCookie($this->CookieName.'_live', $this->SID); $_COOKIE[$this->CookieName] = $this->SID; // for compatibility with in-portal } /** * Refreshes session expiration time * * @access private */ function Refresh() { if ($this->CookiesEnabled) $this->SetSessionCookie(); //we need to refresh the cookie $this->Storage->UpdateSession($this); } function Destroy() { $this->Storage->DeleteSession($this); $this->Data = new Params(); $this->SID = ''; if ($this->CookiesEnabled) $this->SetSessionCookie(); //will remove the cookie due to value (sid) is empty $this->SetSession(); //will create a new session } function NeedQueryString($use_cache = 1) { if ($this->CachedNeedQueryString != null && $use_cache) return $this->CachedNeedQueryString; $result = false; switch ($this->Mode) { case smAUTO: if (!$this->CookiesEnabled) $result = true; break; /*case smCOOKIES_ONLY: break;*/ case smGET_ONLY: case smCOOKIES_AND_GET: $result = true; break; } $this->CachedNeedQueryString = $result; return $result; } function LoadData() { $this->Data->AddParams($this->Storage->LoadData($this)); } function PrintSession($comment='') { if($this->Application->isDebugMode() && constOn('DBG_SHOW_SESSIONDATA')) { // dump session data $this->Application->Debugger->appendHTML('SessionStorage ('.$comment.'):'); $session_data = $this->Data->GetParams(); ksort($session_data); foreach ($session_data as $session_key => $session_value) { if (IsSerialized($session_value)) { $session_data[$session_key] = unserialize($session_value); } } $this->Application->Debugger->dumpVars($session_data); } if ($this->Application->isDebugMode() && constOn('DBG_SHOW_PERSISTENTDATA')) { // dump persistent session data if ($this->Storage->PersistentVars) { $this->Application->Debugger->appendHTML('Persistant Session:'); $session_data = $this->Storage->PersistentVars; ksort($session_data); foreach ($session_data as $session_key => $session_value) { if (IsSerialized($session_value)) { $session_data[$session_key] = unserialize($session_value); } } $this->Application->Debugger->dumpVars($session_data); } } } function SaveData() { if (!$this->Application->GetVar('skip_last_template') && $this->Application->GetVar('ajax') != 'yes') { $this->SaveLastTemplate( $this->Application->GetVar('t') ); } $this->PrintSession('after save'); $this->Storage->SaveData($this); } function SaveLastTemplate($t) { // save last_template $wid = $this->Application->GetVar('m_wid'); $last_env = $this->getLastTemplateENV($t, Array('m_opener' => 'u')); $last_template = basename($_SERVER['PHP_SELF']).'|'.substr($last_env, strlen(ENV_VAR_NAME) + 1); $this->StoreVar(rtrim('last_template_'.$wid, '_'), $last_template); $last_env = $this->getLastTemplateENV($t, null, false); $last_template = basename($_SERVER['PHP_SELF']).'|'.substr($last_env, strlen(ENV_VAR_NAME) + 1); $this->StoreVar(rtrim('last_template_popup_'.$wid, '_'), $last_template); // save other last... variables for mistical purposes (customizations may be) $this->StoreVar('last_url', $_SERVER['REQUEST_URI']); // needed by ord:StoreContinueShoppingLink $this->StoreVar('last_env', substr($last_env, strlen(ENV_VAR_NAME)+1)); // save last_template in persistant session if (!$wid) { if ($this->Application->IsAdmin()) { // only for main window, not popups, not login template, not temp mode (used in adm:MainFrameLink tag) $temp_mode = false; $passed = explode(',', $this->Application->GetVar('passed')); foreach ($passed as $passed_prefix) { if ($this->Application->GetVar($passed_prefix.'_mode')) { $temp_mode = true; break; } } if (!$temp_mode) { if (isset($this->Application->HttpQuery->Get['section'])) { // check directly in GET, bacause LinkVar (session -> request) used on these vars $last_template .= '§ion='.$this->Application->GetVar('section').'&module='.$this->Application->GetVar('module'); } $this->StorePersistentVar('last_template_popup', $last_template); } } elseif ($this->Application->GetVar('admin') == 1) { $admin_session =& $this->Application->recallObject('Session.admin'); /* @var $admin_ses Session */ $admin_session->StorePersistentVar('last_template_popup', '../'.$last_template); } } } function getLastTemplateENV($t, $params = null, $encode = true) { if (!isset($params)) { $params = Array (); } $params['__URLENCODE__'] = 1; // uses "&" instead of "&" for url part concatenation + replaces "\" to "%5C" (works in HTML) $ret = $this->Application->BuildEnv($t, $params, 'all'); if (!$encode) { // cancels 2nd part of replacements, that URLENCODE does $ret = str_replace('%5C', '\\', $ret); } return $ret; } function StoreVar($name, $value) { $this->Data->Set($name, $value); } function StorePersistentVar($name, $value) { $this->Storage->StorePersistentVar($this, $name, $value); } function LoadPersistentVars() { $this->Storage->LoadPersistentVars($this); } function StoreVarDefault($name, $value) { $tmp = $this->RecallVar($name); if($tmp === false || $tmp == '') { $this->StoreVar($name, $value); } } function RecallVar($name, $default = false) { $ret = $this->Data->Get($name); return ($ret === false) ? $default : $ret; } function RecallPersistentVar($name, $default = false) { return $this->Storage->RecallPersistentVar($this, $name, $default); } function RemoveVar($name) { $this->Storage->RemoveFromData($this, $name); $this->Data->Remove($name); } function RemovePersistentVar($name) { return $this->Storage->RemovePersistentVar($this, $name); } /** * Ignores session varible value set before * * @param string $name */ function RestoreVar($name) { return $this->StoreVar($name, $this->Storage->GetFromData($this, $name)); } function GetField($var_name, $default = false) { return $this->Storage->GetField($this, $var_name, $default); } function SetField($var_name, $value) { $this->Storage->SetField($this, $var_name, $value); } /** * Deletes expired sessions * * @return Array expired sids if any * @access private */ function DeleteExpired() { return $this->Storage->DeleteExpired(); } /** * Allows to check if user in this session is logged in or not * * @return bool */ function LoggedIn() { $user_id = $this->RecallVar('user_id'); $ret = $user_id > 0; if ($this->RecallVar('admin') == 1 && ($user_id == -1)) { $ret = true; } return $ret; } } ?>