TableName = 'sessions'; $this->IDField = 'session_id'; $this->sessionKeyField = 'sid'; $this->TimestampField = 'expire'; $this->SessionDataTable = 'SessionData'; $this->DataValueField = 'value'; $this->DataVarField = 'var'; } /** * @inheritDoc * * @throws RuntimeException When session isn't found in the database. */ public function GetID() { $session_id = $this->GetField($this->IDField); if ( $session_id === null ) { throw new RuntimeException( 'Session with "' . $this->Session->GetID(Session::PURPOSE_STORAGE) . '" key not found in the database.' ); } return $session_id; } /** * Sets reference to session * * @param Session $session */ public function setSession(&$session) { $this->Session =& $session; $this->SessionTimeout = $session->SessionTimeout; } /** * Calculates browser signature * * @return string */ function _getBrowserSignature() { $signature_parts = Array( 'HTTP_USER_AGENT', 'SERVER_PROTOCOL', 'HTTP_ACCEPT_CHARSET', 'HTTP_ACCEPT_ENCODING', 'HTTP_ACCEPT_LANGUAGE' ); $ret = ''; foreach ($signature_parts as $signature_part) { if (array_key_exists($signature_part, $_SERVER)) { $ret .= '&|&' . $_SERVER[$signature_part]; } } return md5( substr($ret, 3) ); } function GetSessionDefaults() { $fields_hash = Array ( $this->IDField => null, $this->sessionKeyField => $this->Session->GetID(Session::PURPOSE_STORAGE), $this->TimestampField => $this->Session->Expiration, ); if (!defined('IS_INSTALL') || !IS_INSTALL) { // this column was added only in 5.0.1 version, // so accessing it while database is not upgraded // will result in admin's inability to login inside // installator $fields_hash['BrowserSignature'] = $this->_getBrowserSignature(); } // default values + values set during this script run return array_merge($fields_hash, $this->DirectVars); } /** * Stores session to database * * @param bool $to_database * * @return void * @access public */ public function StoreSession($to_database = true) { if ( defined('IS_INSTALL') && IS_INSTALL && $to_database && !$this->Application->TableFound($this->TableName, true) ) { return; } $fields_hash = $this->GetSessionDefaults(); if ( $to_database ) { $this->Conn->doInsert($fields_hash, $this->TableName); $fields_hash[$this->IDField] = $this->Conn->getInsertID(); } foreach ($fields_hash as $field_name => $field_value) { $this->SetField($field_name, $field_value); } // Reset change flags, because session is already saved to the database. $this->ChangedDirectVars = array(); // ensure user groups are stored in a way, that kPermissionsHelper::CheckUserPermission can understand $this->Session->StoreVar('UserGroups', $this->GetField('GroupList'), !$to_database); } function DeleteSession() { try { $this->DeleteSessions(array($this->GetID()), SESSION_LOG_LOGGED_OUT); } catch ( RuntimeException $e ) { // Session is deleted from the database already by the time we attempt to delete it. } $this->DirectVars = $this->ChangedDirectVars = $this->OriginalData = Array(); } function UpdateSession($timeout = 0) { $this->SetField($this->TimestampField, $this->Session->Expiration); $this->Conn->doUpdate( array($this->TimestampField => $this->Session->Expiration), $this->TableName, $this->IDField . ' = ' . $this->GetID() ); } function LocateSession($sid) { $sql = 'SELECT * FROM ' . $this->TableName . ' WHERE ' . $this->sessionKeyField . ' = ' . $this->Conn->qstr($this->createSignatureFromSID($sid)); $result = $this->Conn->GetRow($sql); if ($result === false) { return false; } // perform security checks to ensure, that session is used by it's creator if ($this->Application->ConfigValue('SessionBrowserSignatureCheck') && ($result['BrowserSignature'] != $this->_getBrowserSignature()) && $this->Application->GetVar('flashsid') === false) { return false; } if ($this->Application->ConfigValue('SessionIPAddressCheck') && ($result['IpAddress'] != $this->Application->getClientIp())) { // most secure, except for cases where NAT (Network Address Translation) // is used and two or more computers can have same IP address return false; } $this->DirectVars = $result; $this->Expiration = $result[$this->TimestampField]; return true; } /** * Creates signature from SID. * * @param string $sid SID. * * @return string */ public function createSignatureFromSID($sid) { /** @var SecurityEncrypter $encrypter */ $encrypter = $this->Application->recallObject('SecurityEncrypter'); return $encrypter->createSignature($sid); } function GetExpiration() { return $this->Expiration; } function LoadData() { $sql = 'SELECT ' . $this->DataValueField . ',' . $this->DataVarField . ' FROM ' . $this->SessionDataTable . ' WHERE ' . $this->IDField . ' = ' . $this->GetID(); $this->OriginalData = $this->Conn->GetCol($sql, $this->DataVarField); return $this->OriginalData; } /** * Enter description here... * * @param string $var_name * @param mixed $default * @return mixed */ function GetField($var_name, $default = false) { return array_key_exists($var_name, $this->DirectVars) ? $this->DirectVars[$var_name] : $default; } function SetField($var_name, $value) { $value_changed = !array_key_exists($var_name, $this->DirectVars) || ($this->DirectVars[$var_name] != $value); if ( $value_changed ) { $this->DirectVars[$var_name] = $value; $this->ChangedDirectVars[] = $var_name; $this->ChangedDirectVars = array_unique($this->ChangedDirectVars); } } /** * Saves changes in session to database using single REPLACE query * * @return void * @access public */ public function SaveData() { $session_id = $this->GetID(); if ( !$session_id ) { // Can't save without sid. return; } $replace = ''; $ses_data = $this->Session->Data->GetParams(); 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),', $session_id, $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); $this->OriginalData = $ses_data; } 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 . ' = ' . $session_id; $this->Conn->Query($query); $this->ChangedDirectVars = array(); } } function RemoveFromData($var) { if ( $this->Session->SessionSet ) { // Only, when session is stored in database. $where_clause = array( $this->IDField . ' = ' . $this->GetID(), $this->DataVarField . ' = ' . $this->Conn->qstr($var), ); $sql = 'DELETE FROM ' . $this->SessionDataTable . ' WHERE (' . implode(') AND (', $where_clause) . ')'; $this->Conn->Query($sql); } unset($this->OriginalData[$var]); } function GetFromData($var, $default = false) { return array_key_exists($var, $this->OriginalData) ? $this->OriginalData[$var] : $default; } function GetExpiredSIDs() { $sql = 'SELECT ' . $this->IDField . ' FROM ' . $this->TableName . ' WHERE ' . $this->TimestampField . ' > ' . adodb_mktime(); return $this->Conn->GetCol($sql); } function DeleteExpired() { $expired_sids = $this->GetExpiredSIDs(); $this->DeleteSessions($expired_sids); return $expired_sids; } function DeleteSessions($session_ids, $delete_reason = SESSION_LOG_EXPIRED) { if (!$session_ids) { return ; } $log_table = $this->Application->getUnitOption('session-log', 'TableName'); if ($log_table) { // mark session with proper status $sub_sql = 'SELECT ' . $this->TimestampField . ' - ' . $this->SessionTimeout . ' FROM ' . $this->TableName . ' WHERE ' . $this->IDField . ' = ' . $log_table . '.SessionId'; $sql = 'UPDATE ' . $log_table . ' SET Status = ' . $delete_reason . ', SessionEnd = (' . $sub_sql . ') WHERE Status = ' . SESSION_LOG_ACTIVE . ' AND SessionId IN (' . implode(',', $session_ids) . ')'; $this->Conn->Query($sql); } $where_clause = ' WHERE ' . $this->IDField . ' IN (' . implode(',', $session_ids) . ')'; $sql = 'SELECT SessionKey FROM ' . $this->TableName . $where_clause; $session_keys = $this->Conn->GetCol($sql); $sql = 'DELETE FROM ' . $this->SessionDataTable . $where_clause; $this->Conn->Query($sql); $sql = 'DELETE FROM ' . $this->TableName . $where_clause; $this->Conn->Query($sql); // Delete debugger outputs left of deleted sessions. foreach ( $session_keys as $session_key ) { $debug_file = (defined('RESTRICTED') ? RESTRICTED : WRITEABLE . '/cache'); $debug_file .= '/debug_@' . $session_key . '@.txt'; if ( file_exists($debug_file) ) { @unlink($debug_file); } } } function LoadPersistentVars() { $user_id = $this->Session->RecallVar('user_id'); if ( $user_id != USER_GUEST ) { // Root & normal users. $this->PersistentVars = $this->getPersistentVars($user_id); } else { $this->PersistentVars = Array (); } $default_user_id = (int)$this->Application->ConfigValue('DefaultSettingsUserId'); if ( !$default_user_id ) { $default_user_id = USER_ROOT; } if ( $user_id != $default_user_id ) { $this->defaultPersistentVars = $this->getPersistentVars($default_user_id); } } /** * Get persistent vars * * @param integer $user_id User Id. * * @return array */ protected function getPersistentVars($user_id) { $sql = 'SELECT VariableValue, VariableName FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE PortalUserId = ' . $user_id; return $this->Conn->GetCol($sql, 'VariableName'); } /** * Stores variable to persistent session * * @param string $var_name * @param mixed $var_value * @param bool $optional * @return void * @access public */ public function StorePersistentVar($var_name, $var_value, $optional = false) { $user_id = $this->Session->RecallVar('user_id'); if ( $user_id == USER_GUEST || $user_id === false ) { // -2 (when not logged in), false (when after u:OnLogout event) $this->Session->StoreVar($var_name, $var_value, $optional); return; } $this->PersistentVars[$var_name] = $var_value; $key_clause = 'PortalUserId = ' . $user_id . ' AND VariableName = ' . $this->Conn->qstr($var_name); $sql = 'SELECT VariableName FROM ' . TABLE_PREFIX . 'UserPersistentSessionData 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 . 'UserPersistentSessionData', $key_clause); } else { $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'UserPersistentSessionData'); } } /** * Gets persistent variable * * @param string $var_name * @param mixed $default * @return mixed * @access public */ public function RecallPersistentVar($var_name, $default = false) { if ( $this->Session->RecallVar('user_id') == USER_GUEST ) { if ( $default == ALLOW_DEFAULT_SETTINGS ) { $default = null; } return $this->Session->RecallVar($var_name, $default); } if ( array_key_exists($var_name, $this->PersistentVars) ) { return $this->PersistentVars[$var_name]; } elseif ( $default == ALLOW_DEFAULT_SETTINGS ) { if ( isset($this->defaultPersistentVars[$var_name]) ) { $value = $this->defaultPersistentVars[$var_name]; $this->StorePersistentVar($var_name, $value); return $value; } $this->PersistentVars[$var_name] = false; return false; } return $default; } /** * Removes variable from persistent session * * @param string $var_name * @return void * @access public */ function RemovePersistentVar($var_name) { unset($this->PersistentVars[$var_name]); $user_id = $this->Session->RecallVar('user_id'); if ( $user_id == USER_GUEST || $user_id === false ) { // -2 (when not logged in), false (when after u:OnLogout event) $this->Session->RemoveVar($var_name); } else { $sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE PortalUserId = ' . $user_id . ' AND VariableName = ' . $this->Conn->qstr($var_name); $this->Conn->Query($sql); } } /** * Checks of object has given field * * @param string $name * * @return bool * @access public * @throws BadMethodCallException Always. */ public function HasField($name) { throw new BadMethodCallException('Unsupported'); } /** * Returns field values * * @return Array * @access public * @throws BadMethodCallException Always. */ public function GetFieldValues() { throw new BadMethodCallException('Unsupported'); } /** * Returns unformatted field value * * @param string $field * * @return string * @access public * @throws BadMethodCallException Always. */ public function GetDBField($field) { throw new BadMethodCallException('Unsupported'); } /** * Returns true, when list/item was queried/loaded * * @return bool * @access public * @throws BadMethodCallException Always. */ public function isLoaded() { throw new BadMethodCallException('Unsupported'); } /** * Returns specified field value from all selected rows. * Don't affect current record index * * @param string $field * * @return Array * @access public * @throws BadMethodCallException Always. */ public function GetCol($field) { throw new BadMethodCallException('Unsupported'); } }