_init(); // Check for a persistent authentication token if (!$this->_loggedIn) $this->checkPersistentAuthToken(); // Override mobile user setting with the value in the latest request URL if (isset($_REQUEST['isMobile'])) $this->isMobile = intval($_REQUEST['isMobile']); } public function __destruct() { session_write_close(); $_SESSION = array(); $this->_localSessionData = array(); } /** * Sets the $value for a $key * * @param string $key (Name of the paramter to set) * @param mixed $value (Value of the paramter to set) */ public function __set($key, $value) { $this->_localSessionData[$key] = $value; } /** * Returns the $value for a $key * * @param string $key (Name of the paramter to retreive) * @return mixed $value (Value associated with $key) */ public function __get($key) { return $this->_localSessionData[$key] ?? null; } /** * Logs the user out of the current session and resets all session variables. */ public function __isset($key) { return isset($this->_localSessionData[$key]); } /** * Initialize the current session with defaults * This is called for every page request * sets _lastError to an empty string. * @param boolean $reInit (true = Reinit current session) */ private function _init($reInit = false) { if ((session_id() == false && session_start()) || $reInit) { // Local session storage if (!isset($_SESSION['store'])) $_SESSION['store'] = []; $this->_localSessionData = &$_SESSION['store']; // Session user if (!isset($_SESSION['user'])) $_SESSION['user'] = new User(0); $this->_user = &$_SESSION['user']; // User logged in? $this->_loggedIn = $this->_user->isExistingObject(); // Session user settings if (!isset($_SESSION['user_settings'])) $_SESSION['user_settings'] = new UserSettings(0); $this->_userSettings = &$_SESSION['user_settings']; // Station view counter deduplication if (!isset($_SESSION['station_view_counter'])) $_SESSION['station_view_counter'] = []; $this->_stationViewCounter = &$_SESSION['station_view_counter']; // Initital session start time (only set once) if (!isset($this->start_time)) $this->start_time = time(); // Flush session hit counter store thats used to de-dupe station views if (!isset($this->view_counter_history)) $this->view_counter_history = []; $expire = time() - (60 * (getWebsiteConfig("view_counter_hysteresis") ?? 60)); foreach($this->view_counter_history AS $k => $v) { if ($v < $expire) unset($this->view_counter_history[$k]); } // Mobile User? Override setting with the value in the latest request URL if (isset($_REQUEST['isMobile'])) $this->isMobile = intval($_REQUEST['isMobile']); if (is_null($this->isMobile)) $this->isMobile = false; // Set the session defaults to the user preferences (if logged in) if (is_null($this->imperialUnits) && $this->_loggedIn && $this->_userSettings->isExistingObject()) { $this->imperialUnits = $this->_userSettings->useImperialUnits; } // Set the session defaults to the user preferences (if logged in) if (is_null($this->useMilTime) && $this->_loggedIn && $this->_userSettings->isExistingObject()) { $this->useMilTime = $this->_userSettings->useMilTime; } // Set defaults if not overridden by user preferences (check browser locale) if (!isset($this->imperialUnits) || is_null($this->imperialUnits)) $this->isImperialUnitUser(); // Map settings use a default location, if the user has set one, use that, otherwise get from config if ($this->_userSettings->isExistingObject() && $this->_userSettings->loginApplyMapCenterZoom) { $this->defaultMapCenterLatitude = $this->_userSettings->defaultMapCenterLatitude; $this->defaultMapCenterLongitude = $this->_userSettings->defaultMapCenterLongitude; $this->defaultMapZoom = $this->_userSettings->defaultMapZoom; } else { $this->defaultMapCenterLatitude = getWebsiteConfig('default_latitude'); $this->defaultMapCenterLongitude = getWebsiteConfig('default_longitude'); $this->defaultMapZoom = getWebsiteConfig('default_zoom'); } // This is a failsafe for ensuring that basic map settings are set. if (!isset($this->showAprsPositions)) $this->showAprsPositions = true; if (!isset($this->showCwopPositions)) $this->showCwopPositions = true; if (!isset($this->showOgnPositions)) $this->showOgnPositions = true; if (!isset($this->showCbAprsPositions)) $this->showCbAprsPositions = true; if (!isset($this->showInternetMarkers)) $this->showInternetMarkers = true; if (!isset($this->showStationaryMarkers)) $this->showStationaryMarkers = true; if (!isset($this->showOgflymMarkers)) $this->showOgflymMarkers = true; if (!isset($this->showUnknownMarkers)) $this->showUnknownMarkers = true; if (!isset($this->showMaidenheadGrid)) $this->showMaidenheadGrid = false; if (!isset($this->showDayNightOverlay)) $this->showDayNightOverlay = false; if (!isset($this->dayNightRefreshInterval)) $this->dayNightRefreshInterval = 5000; if (!isset($this->tailTimeLength)) $this->tailTimeLength = 60; if (!isset($this->showStationMarkers)) $this->showStationMarkers = true; if (!isset($this->showObjectMarkers)) $this->showObjectMarkers = true; if (!isset($this->showItemMarkers)) $this->showItemMarkers = true; } } /** * Returnes an initialized Session * * @return Session */ public static function getInstance() { if (self::$_singletonInstance === null) { self::$_singletonInstance = new Session(); } return self::$_singletonInstance; } /** * Returnes the user record associated with the initialized Session * * @return User (User object, or an unititlized user object if none loaded) */ public function getUser() { if ($this->_user) return $this->_user; return new User(0); } /** * Returns the user settings object associated with the initialized Session * * @param boolean $reload (Reload settings from the database) * @return UserSettings (User settings object, or an unititlized user object if none loaded) */ public function getUserSettings($reload = false) { if ($reload) $this->_userSettings = UserSettingsRepository::getInstance()->getObjectByUserId($this->_user->_id); if (!$this->_userSettings->isExistingObject()) { $this->_userSettings->mpShowPath = 1; $this->_userSettings->mpShowCourseSpeed = 1; $this->_userSettings->mpShowMice = 1; $this->_userSettings->mpShowComment = 1; $this->_userSettings->mpShowPhgRng = 1; $this->_userSettings->mpShowDistance = 1; $this->_userSettings->mpShowWeather = 1; $this->_userSettings->mpUseLargeIcon = 1; $this->_userSettings->useMaxModal = 0; } return $this->_userSettings; } /** * Returns all Session Values * @return array All currently saves session settings */ public function getAll() { return $this->_localSessionData ?? []; } /** * Process a user login in the active session * This will attempt to load the user record and validate the password. * * @param string $username (Username of the user) * @param string $password (Password of the user) * @return boolean (True if the login was successful, false if not) */ public function loginUser($username, $password) { // Remove whitespace $username = trim($username); // Only process login if not already logged in if (!$this->_loggedIn) { // Test the username and set it if passes (no ) if (strlen($username) <= 4 || preg_match('/[^a-z_\-0-9]/i', $username) == 0) { // Username is good, attempt to pull the record $user = UserRepository::getInstance()->getObjectByUsername($username); if ($user->isExistingObject()) { if ($user->verifyPassword($password)) { $this->_loggedIn = true; $this->_user = $user; // Get the user User Settings $this->_userSettings = UserSettingsRepository::getInstance()->getObjectByUserId($this->_user->_id); return true; } else { $this->_lastError = "Password specified was not correct."; } } else { $this->_lastError = "The username could not found."; } } else { $this->_lastError = "Username is not valid for this site."; } } else { $this->_lastError = "Already logged in."; } } /** * Logs the user out of the current session and resets all session variables. */ public function logOutUser() { unset($this->_user); unset($this->_userSettings); $this->_loggedIn = false; // Delete cookies $cookie_prefix = getWebsiteConfig('cookie_prefix'); $cookie_domain = getWebsiteConfig('cookie_domain'); $cookie_secure = getWebsiteConfig('cookie_secure') == 1 ? true : false; $cookie_httponly = getWebsiteConfig('cookie_httponly') == 1 ? true : false; setcookie("{$cookie_prefix}_login", '', time()-3600, '/', $cookie_domain, $cookie_secure, $cookie_httponly); setcookie("{$cookie_prefix}_identifier", '', time()-3600, '/', $cookie_domain, $cookie_secure, $cookie_httponly); setcookie("{$cookie_prefix}_selector", '', time()-3600, '/', $cookie_domain, $cookie_secure, $cookie_httponly); // Delete the cookie entries before re-initalizing session unset($_COOKIE['aprs_login']); unset($_COOKIE['aprs_identifier']); unset($_COOKIE['aprs_selector']); $_SESSION = []; $this->_init(true); } /** * Sets a persistent authentication token and cookies ("rememeber me") * @return bool (True if the token was set, otherwise false) */ public function setPersistentAuthToken() { // No point in doing anything if not logged in if ($this->_loggedIn) { $token = $this->_user->generatePersistentAuthToken(); if (is_array($token) && $token['id']) { // Set Cookie expiration for 1 month $cookie_prefix = getWebsiteConfig('cookie_prefix'); $cookie_domain = getWebsiteConfig('cookie_domain'); $cookie_secure = getWebsiteConfig('cookie_secure') == 1 ? true : false; $cookie_httponly = getWebsiteConfig('cookie_httponly') == 1 ? true : false; setcookie("{$cookie_prefix}_login", $this->_user->userName, $token['expiration'], '/', $cookie_domain, $cookie_secure, $cookie_httponly); setcookie("{$cookie_prefix}_identifier", $token['identifier'], $token['expiration'], '/', $cookie_domain, $cookie_secure, $cookie_httponly); setcookie("{$cookie_prefix}_selector", $token['selector'], $token['expiration'], '/', $cookie_domain, $cookie_secure, $cookie_httponly); return true; } else { $this->_lastError = "An error occured creating persistent login token."; } } else { $this->_lastError = "Not logged in."; } return false; } /** * Checks the persistent authentication token and legs the user in if set * @return bool (True if the token was set, otherwise false) */ public function checkPersistentAuthToken() { // No point in doing anything if logged in if (!$this->_loggedIn && isset($_COOKIE['aprs_login'])) { // Attempt to find the user record $username = trim($_COOKIE['aprs_login']); $user = UserRepository::getInstance()->getObjectByUsername($username); if ($user->isExistingObject()) { if ($user->validatePersistentAuthToken($_COOKIE['aprs_selector'] ?? '', $_COOKIE['aprs_identifier'] ?? '')) { // Get the user User Settings $this->_userSettings = UserSettingsRepository::getInstance()->getObjectByUserId($user->_id); $this->_loggedIn = true; $this->_user = $user; return true; } } } } /** * Checks the station view cache to see if a station has been viewed recenely */ public function incrementStationCounter($station) { if (getWebsiteConfig("view_counter_increment_enabled") == '0') return; if ($station->isExistingObject() && !array_key_exists($station->id, $this->_stationViewCounter) && !$this->isBot()) { // Check the deduplication/spam table $pdo = PDOMysqlConnection::getInstance(); $expire = time() - (60 * (getWebsiteConfig("view_counter_hysteresis") ?? 60)); $sql = "SELECT * FROM station_view_count_tracker WHERE station_id = ? AND expire_timestamp > ? AND (user_id = ? OR ip_address = INET_ATON(?)) LIMIT 1"; $stmt = $pdo->prepareAndExec($sql, [$station->id, $expire, $this->getUser()->id, $this->getClientIp()]); if ($record = $stmt->fetch(PDO::FETCH_ASSOC)) { // Already recorded a hit, skip return; } $this->_stationViewCounter[$station->id] = time(); // Add to the the deduplication/spam table $expire = time() + (60 * (getWebsiteConfig("view_counter_hysteresis") ?? 60)); $sql = "INSERT INTO station_view_count_tracker (user_id, ip_address, station_id, expire_timestamp) VALUES(?, INET_ATON(?), ?, ?) LIMIT 1"; $stmt = $pdo->prepareAndExec($sql, [$this->getUser()->id ?? 0, $this->getClientIp(), $station->id, $expire]); } } /** * Records the a station browse (view) event */ public function recordStationBrowseHistory($station_id) { if (!isInt($station_id) || (isset($this->lastViewedStationId) && $station_id == $this->lastViewedStationId) || !$this->isLoggedIn()) return false; // Only record history for valid users if ($this->getUser()->isExistingObject()) { $ret = $this->getUser()->recordStationBrowseHistory($station_id); $this->_lastError = $this->getUser()->getLastError(); $this->lastViewedStationId = $station_id; return $ret; } } /** * Logs the user out of the current session and resets all session variables. */ public function isLoggedIn() { return $this->_loggedIn; } public function getURL($baseUrl, array $queryArray = []) { $path = array_slice(explode('/', $baseUrl), 1); $section = array_shift($path); $view = str_replace('.php', '', array_shift($path)); $newUrl = $baseUrl; if ($section == "views") { $newUrl = '/v/' . $view; } if ($section == "account") { $newUrl = '/a/' . $view; } else if (isset($queryArray['id']) && $section == 'station') { $newUrl = '/' . $section . '/' . $queryArray['id'] . ($section != $view ? '/' . $view : ''); unset($queryArray['id']); } else if (isset($queryArray['s']) && $section == 'station') { $newUrl = '/' . $section . '/' . $queryArray['s'] . ($section != $view ? '/' . $view : ''); unset($queryArray['s']); } // Make the URL return $newUrl . $this->getQuerySting($queryArray, '?'); } public function getQuerySting(array $queryArray = [], $append = '&') { // Make the URL return (sizeof($queryArray) ? $append . http_build_query($queryArray) : ''); } public function isImperialUnitUser() { // See if the current value is being overridden if (isset($_GET['imperialUnits']) && $_GET['imperialUnits']) { $this->imperialUnits = 1; } else if (isset($_GET['imperialUnits']) && !$_GET['imperialUnits']) { $this->imperialUnits = 0; } // Return stored (or just set) session value if (isset($this->imperialUnits)) return $this->imperialUnits; // Initialize the default for the users location if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { // USA if (substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 5) == 'en-US' // Myanmar / Burma || substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) == 'my' // Liberia || substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 5) == 'en-LR') { $this->imperialUnits = 1; } else { $this->imperialUnits = 0; } } } /** * Returns the client IP address * * @return string IP address of the client */ public function getClientIp() { static $proxies; if (is_null($proxies)) { $proxies = explode(',', getWebsiteConfig("reverse_proxy_ips") ?? ''); } if (!is_array($proxies) || sizeof($proxies) == 0) { return $_SERVER['REMOTE_ADDR'] ?? $_SERVER['HTTP_CLIENT_IP'] ?? ''; } return (in_array($_SERVER['REMOTE_ADDR'], $proxies) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']) ?? $_SERVER['REMOTE_ADDR'] ?? $_SERVER['HTTP_CLIENT_IP'] ?? ''; } /** * Detects whether the user agent is believed to be a robot or spider * * @return string IP address of the client */ public function isBot() { if ( !empty($_SERVER['HTTP_USER_AGENT']) and preg_match('/abacho|accona|AddThis|AdsBot|ahoy|AhrefsBot|AISearchBot|alexa|altavista|anthill|appie|applebot|arale|araneo|AraybOt|ariadne|arks|aspseek|ATN_Worldwide|Atomz|baiduspider|baidu|bbot|bingbot|bing|Bjaaland|BlackWidow|BotLink|bot|boxseabot|bspider|calif|CCBot|ChinaClaw|christcrawler|CMC\/0\.01|combine|confuzzledbot|contaxe|CoolBot|cosmos|crawler|crawlpaper|crawl|curl|cusco|cyberspyder|cydralspider|dataprovider|digger|DIIbot|DotBot|downloadexpress|DragonBot|DuckDuckBot|dwcp|EasouSpider|ebiness|ecollector|elfinbot|esculapio|ESI|esther|eStyle|Ezooms|facebookexternalhit|facebook|facebot|fastcrawler|FatBot|FDSE|FELIX IDE|fetch|fido|find|Firefly|fouineur|Freecrawl|froogle|gammaSpider|gazz|gcreep|geona|Getterrobo-Plus|get|girafabot|golem|googlebot|\-google|grabber|GrabNet|griffon|Gromit|gulliver|gulper|hambot|havIndex|hotwired|htdig|HTTrack|ia_archiver|iajabot|IDBot|Informant|InfoSeek|InfoSpiders|INGRID\/0\.1|inktomi|inspectorwww|Internet Cruiser Robot|irobot|Iron33|JBot|jcrawler|Jeeves|jobo|KDD\-Explorer|KIT\-Fireball|ko_yappo_robot|label\-grabber|larbin|legs|libwww-perl|linkedin|Linkidator|linkwalker|Lockon|logo_gif_crawler|Lycos|m2e|majesticsEO|marvin|mattie|mediafox|mediapartners|MerzScope|MindCrawler|MJ12bot|mod_pagespeed|moget|Motor|msnbot|muncher|muninn|MuscatFerret|MwdSearch|NationalDirectory|naverbot|NEC\-MeshExplorer|NetcraftSurveyAgent|NetScoop|NetSeer|newscan\-online|nil|none|Nutch|ObjectsSearch|Occam|openstat.ru\/Bot|packrat|pageboy|ParaSite|patric|pegasus|perlcrawler|phpdig|piltdownman|Pimptrain|pingdom|pinterest|pjspider|PlumtreeWebAccessor|PortalBSpider|psbot|rambler|Raven|RHCS|RixBot|roadrunner|Robbie|robi|RoboCrawl|robofox|Scooter|Scrubby|Search\-AU|searchprocess|search|SemrushBot|Senrigan|seznambot|Shagseeker|sharp\-info\-agent|sift|SimBot|Site Valet|SiteSucker|skymob|SLCrawler\/2\.0|slurp|snooper|solbot|speedy|spider_monkey|SpiderBot\/1\.0|spiderline|spider|suke|tach_bw|TechBOT|TechnoratiSnoop|templeton|teoma|titin|topiclink|twitterbot|twitter|UdmSearch|Ukonline|UnwindFetchor|URL_Spider_SQL|urlck|urlresolver|Valkyrie libwww\-perl|verticrawl|Victoria|void\-bot|Voyager|VWbot_K|wapspider|WebBandit\/1\.0|webcatcher|WebCopier|WebFindBot|WebLeacher|WebMechanic|WebMoose|webquest|webreaper|webspider|webs|WebWalker|WebZip|wget|whowhere|winona|wlm|WOLP|woriobot|WWWC|XGET|xing|yahoo|YandexBot|YandexMobileBot|yandex|yeti|Zeus/i', $_SERVER['HTTP_USER_AGENT'])) { return true; // 'Above given bots detected' } return false; } /** * Returns the last error message set during a session action then * sets _lastError to an empty string. * * @return string (Text of the last recorded error) */ public function getLastError() { $last_error = $this->_lastError; $this->_lastError = ''; return $last_error; } }