/** * Constructor * * If valid session cookie received: pull session from DB * Otherwise, INSERT a session into DB */ function Session() { global $DB, $Debuglog, $current_User, $localtimenow, $Messages, $Settings, $UserSettings; global $Hit; global $cookie_session, $cookie_expires, $cookie_path, $cookie_domain; $Debuglog->add('Session: cookie_domain=' . $cookie_domain, 'request'); $Debuglog->add('Session: cookie_path=' . $cookie_path, 'request'); $session_cookie = param_cookie($cookie_session, 'string', ''); if (empty($session_cookie)) { $Debuglog->add('Session: No session cookie received.', 'request'); } else { // session ID sent by cookie if (!preg_match('~^(\\d+)_(\\w+)$~', $session_cookie, $match)) { $Debuglog->add('Session: Invalid session cookie format!', 'request'); } else { // We have a valid session cookie: $session_id_by_cookie = $match[1]; $session_key_by_cookie = $match[2]; $Debuglog->add('Session: Session ID received from cookie: ' . $session_id_by_cookie, 'request'); $timeout_sessions = NULL; if ($this->user_ID != NULL) { // User is not anonymous, get custom session timeout (may return NULL): $timeout_sessions = $UserSettings->get('timeout_sessions', $this->user_ID); } if (empty($timeout_sessions)) { // User is anonymous or has no custom session timeout. So, we use default session timeout: $timeout_sessions = $Settings->get('timeout_sessions'); } $row = $DB->get_row(' SELECT sess_ID, sess_key, sess_data, sess_user_ID, sess_start_ts, sess_lastseen_ts, sess_device FROM T_sessions WHERE sess_ID = ' . $DB->quote($session_id_by_cookie) . ' AND sess_key = ' . $DB->quote($session_key_by_cookie) . ' AND UNIX_TIMESTAMP(sess_lastseen_ts) > ' . ($localtimenow - $timeout_sessions)); if (empty($row)) { $Debuglog->add('Session: Session ID/key combination is invalid!', 'request'); } else { // ID + key are valid: load data $Debuglog->add('Session: Session ID is valid.', 'request'); $this->ID = $row->sess_ID; $this->key = $row->sess_key; $this->user_ID = $row->sess_user_ID; $this->start_ts = mysql2timestamp($row->sess_start_ts); $this->lastseen_ts = mysql2timestamp($row->sess_lastseen_ts); $this->is_validated = true; $this->sess_device = $row->sess_device; $Debuglog->add('Session: Session user_ID: ' . var_export($this->user_ID, true), 'request'); if (empty($row->sess_data)) { $Debuglog->add('Session: No session data available.', 'request'); $this->_data = array(); } else { // Some session data has been previsouly stored: // Unserialize session data (using an own callback that should provide class definitions): $old_callback = ini_set('unserialize_callback_func', 'session_unserialize_callback'); if ($old_callback === false || is_null($old_callback)) { // NULL if ini_set has been disabled for security reasons // Brutally load all classes that we might need: session_unserialize_load_all_classes(); } // TODO: dh> This can fail, if there are special chars in sess_data: // It will be encoded in $evo_charset _after_ "SET NAMES", but // get retrieved here, _before_ any "SET NAMES" (if $db_config['connection_charset'] is not set (default))! $this->_data = @unserialize($row->sess_data); if ($old_callback !== false) { // Restore the old callback if we changed it: ini_set('unserialize_callback_func', $old_callback); } if (!is_array($this->_data)) { $Debuglog->add('Session: Session data corrupted!<br /> connection_charset: ' . var_export($DB->connection_charset, true) . '<br /> Serialized data was: --[' . var_export($row->sess_data, true) . ']--', array('session', 'error')); $this->_data = array(); } else { $Debuglog->add('Session: Session data loaded.', 'request'); // Load a Messages object from session data, if available: if (($sess_Messages = $this->get('Messages')) && is_a($sess_Messages, 'Messages')) { // dh> TODO: "old" messages should rather get prepended to any existing ones from the current request, rather than appended $Messages->add_messages($sess_Messages); $Debuglog->add('Session: Added Messages from session data.', 'request'); $this->delete('Messages'); } } } } } } if ($this->ID) { // there was a valid session before if ($this->lastseen_ts < $localtimenow - 60) { // lastseen timestamp is older then a minute, it needs to be updated at page exit $this->session_needs_save(true); } } else { // create a new session! : $this->key = generate_random_key(32); // Detect user device global $user_devices; $this->sess_device = ''; if (!empty($_SERVER['HTTP_USER_AGENT'])) { foreach ($user_devices as $device_name => $device_regexp) { if (preg_match('~' . $device_regexp . '~i', $_SERVER['HTTP_USER_AGENT'])) { $this->sess_device = $device_name; break; } } } // We need to INSERT now because we need an ID now! (for the cookie) $DB->query("\n\t\t\t\tINSERT INTO T_sessions( sess_key, sess_start_ts, sess_lastseen_ts, sess_ipaddress, sess_device )\n\t\t\t\tVALUES (\n\t\t\t\t\t'" . $this->key . "',\n\t\t\t\t\t'" . date('Y-m-d H:i:s', $localtimenow) . "',\n\t\t\t\t\t'" . date('Y-m-d H:i:s', $localtimenow) . "',\n\t\t\t\t\t" . $DB->quote($Hit->IP) . ",\n\t\t\t\t\t" . $DB->quote($this->sess_device) . "\n\t\t\t\t)"); $this->ID = $DB->insert_id; // Set a cookie valid for ~ 10 years: setcookie($cookie_session, $this->ID . '_' . $this->key, time() + 315360000, $cookie_path, $cookie_domain); $Debuglog->add('Session: ID (generated): ' . $this->ID, 'request'); $Debuglog->add('Session: Cookie sent.', 'request'); } }
/** * Constructor */ function Session() { global $DB, $Debuglog, $current_User, $localtimenow, $Messages, $Settings; global $Hit; global $cookie_session, $cookie_expires, $cookie_path, $cookie_domain; $Debuglog->add('cookie_domain=' . $cookie_domain, 'session'); $Debuglog->add('cookie_path=' . $cookie_path, 'session'); $session_cookie = param_cookie($cookie_session, 'string', ''); if (empty($session_cookie)) { $Debuglog->add('No session cookie received.', 'session'); } else { // session ID sent by cookie if (!preg_match('~^(\\d+)_(\\w+)$~', $session_cookie, $match)) { $Debuglog->add('Invalid session cookie format!', 'session'); } else { // We have a valid session cookie: $session_id_by_cookie = $match[1]; $session_key_by_cookie = $match[2]; $Debuglog->add('Session ID received from cookie: ' . $session_id_by_cookie, 'session'); $row = $DB->get_row(' SELECT sess_ID, sess_key, sess_data, sess_user_ID FROM T_sessions WHERE sess_ID = ' . $DB->quote($session_id_by_cookie) . ' AND sess_key = ' . $DB->quote($session_key_by_cookie) . ' AND UNIX_TIMESTAMP(sess_lastseen) > ' . ($localtimenow - $Settings->get('timeout_sessions'))); if (empty($row)) { $Debuglog->add('Session ID/key combination is invalid!', 'session'); } else { // ID + key are valid: load data $Debuglog->add('Session ID is valid.', 'session'); $this->ID = $row->sess_ID; $this->key = $row->sess_key; $this->user_ID = $row->sess_user_ID; $this->is_validated = true; $Debuglog->add('Session user_ID: ' . var_export($this->user_ID, true), 'session'); if (empty($row->sess_data)) { $Debuglog->add('No session data available.', 'session'); $this->_data = array(); } else { // Some session data has been previsouly stored: // Unserialize session data (using an own callback that should provide class definitions): $old_callback = ini_set('unserialize_callback_func', 'session_unserialize_callback'); if ($old_callback === false) { // this can fail, if "ini_set" has been disabled for security reasons.. :/ // Brutally load add classes that we might need: session_unserialize_load_all_classes(); } // TODO: dh> This can fail, if there are special chars in sess_data: // It will be encoded in $evo_charset _after_ "SET NAMES", but // get retrieved here, _before_ any "SET NAMES" (if $db_config['connection_charset'] is not set (default))! $this->_data = @unserialize($row->sess_data); if ($old_callback !== false) { // Restore the old callback if we changed it: ini_set('unserialize_callback_func', $old_callback); } if (!is_array($this->_data)) { $Debuglog->add('Session data corrupted!<br /> connection_charset: ' . var_export($DB->connection_charset, true) . '<br /> Serialized data was: --[' . var_export($row->sess_data, true) . ']--', array('session', 'error')); $this->_data = array(); } else { $Debuglog->add('Session data loaded.', 'session'); // Load a Messages object from session data, if available: if (($sess_Messages = $this->get('Messages')) && is_a($sess_Messages, 'log')) { // dh> TODO: "old" messages should rather get prepended to any existing ones from the current request, rather than appended $Messages->add_messages($sess_Messages->messages); $Debuglog->add('Added Messages from session data.', 'session'); $this->delete('Messages'); } } } } } } if ($this->ID) { // there was a valid session before; update data (lastseen) $this->_session_needs_save = true; } else { // create a new session $this->key = generate_random_key(32); // We need to INSERT now because we need an ID now! (for the cookie) $DB->query("\r\n\t\t\t\tINSERT INTO T_sessions( sess_key, sess_lastseen, sess_ipaddress )\r\n\t\t\t\tVALUES (\r\n\t\t\t\t\t'" . $this->key . "',\r\n\t\t\t\t\t'" . date('Y-m-d H:i:s', $localtimenow) . "',\r\n\t\t\t\t\t'" . $Hit->IP . "'\r\n\t\t\t\t)"); $this->ID = $DB->insert_id; // Set a cookie valid for ~ 10 years: setcookie($cookie_session, $this->ID . '_' . $this->key, time() + 315360000, $cookie_path, $cookie_domain); $Debuglog->add('ID (generated): ' . $this->ID, 'session'); $Debuglog->add('Cookie sent.', 'session'); } register_shutdown_function(array(&$this, 'dbsave')); }