handle_errors(); $Debug->set_flag('Debug constructed'); $DB = new DB_MYSQL; $Cache = new CACHE($MemcachedServers); G::$Cache = $Cache; G::$DB = $DB; G::$Twig = new Environment( new FilesystemLoader(__DIR__ . '/../templates'), ['cache' => __DIR__ . '/../cache/twig'] ); //Begin browser identification if (session_status() === PHP_SESSION_NONE) { session_start(); } if (!isset($_SESSION['WhichBrowser'])) { $Debug->set_flag('start parsing user agent'); if (preg_match("/^Lidarr\/([0-9\.]+) \((.+)\)$/", $_SERVER['HTTP_USER_AGENT'], $Matches) === 1) { $OS = explode(" ", $Matches[2]); $_SESSION['WhichBrowser'] = [ 'Browser' => 'Lidarr', 'BrowserVersion' => substr($Matches[1], 0, strrpos($Matches[1], '.')), 'OperatingSystem' => $OS[0] === 'macos' ? 'macOS' : ucfirst($OS[0]), 'OperatingSystemVersion' => $OS[1] ?? null ]; } elseif (preg_match("/^VarroaMusica\/([0-9]+(?:dev)?)$/", $_SERVER['HTTP_USER_AGENT'], $Matches) === 1) { $_SESSION['WhichBrowser'] = [ 'Browser' => 'VarroaMusica', 'BrowserVersion' => str_replace('dev', '', $Matches[1]), 'OperatingSystem' => null, 'OperatingSystemVersion' => null ]; } elseif (in_array($_SERVER['HTTP_USER_AGENT'], ['Headphones/None', 'whatapi [isaaczafuta]'])) { $_SESSION['WhichBrowser'] = [ 'Browser' => $_SERVER['HTTP_USER_AGENT'], 'BrowserVersion' => null, 'OperatingSystem' => null, 'OperatingSystemVersion' => null ]; } else { $Result = new WhichBrowser\Parser($_SERVER['HTTP_USER_AGENT']); $Browser = $Result->browser; if (empty($Browser->getName()) && !empty($Browser->using)) { $Browser = $Browser->using; } $_SESSION['WhichBrowser'] = [ 'Browser' => $Browser->getName(), 'BrowserVersion' => explode('.', $Browser->getVersion())[0], 'OperatingSystem' => $Result->os->getName(), 'OperatingSystemVersion' => $Result->os->getVersion() ]; } foreach (['Browser', 'BrowserVersion', 'OperatingSystem', 'OperatingSystemVersion'] as $Key) { if ($_SESSION['WhichBrowser'][$Key] === "") { $_SESSION['WhichBrowser'][$Key] = null; } } $Debug->set_flag('end parsing user agent'); } $Browser = $_SESSION['WhichBrowser']['Browser']; $BrowserVersion = $_SESSION['WhichBrowser']['BrowserVersion']; $OperatingSystem = $_SESSION['WhichBrowser']['OperatingSystem']; $OperatingSystemVersion = $_SESSION['WhichBrowser']['OperatingSystemVersion']; $Debug->set_flag('start user handling'); // Get classes // TODO: Remove these globals, replace by calls into Users list($Classes, $ClassLevels) = Users::get_classes(); //-- Load user information // User info is broken up into many sections // Heavy - Things that the site never has to look at if the user isn't logged in (as opposed to things like the class, donor status, etc) // Light - Things that appear in format_user // Stats - Uploaded and downloaded - can be updated by a script if you want super speed // Session data - Information about the specific session // Enabled - if the user's enabled or not // Permissions if (isset($_COOKIE['session'])) { $LoginCookie = Crypto::decrypt($_COOKIE['session'], ENCKEY); } if (isset($LoginCookie)) { list($SessionID, $LoggedUser['ID']) = explode('|~|', Crypto::decrypt($LoginCookie, ENCKEY)); $LoggedUser['ID'] = (int)$LoggedUser['ID']; if (!$LoggedUser['ID'] || !$SessionID) { logout($LoggedUser['ID'], $SessionID); } $User = new \Gazelle\User($DB, $Cache, $LoggedUser['ID']); $Session = new \Gazelle\Session($DB, $Cache, $LoggedUser['ID']); $UserSessions = $Session->sessions(); if (!array_key_exists($SessionID, $UserSessions)) { logout($LoggedUser['ID'], $SessionID); } if ($User->isDisabled()) { logout($LoggedUser['ID'], $SessionID); } // TODO: These globals need to die, and just use $LoggedUser // TODO: And then instantiate $LoggedUser from \Gazelle\Session when needed $LightInfo = Users::user_info($LoggedUser['ID']); if (empty($LightInfo['Username'])) { // Ghost logout($LoggedUser['ID'], $SessionID); } $UserStats = Users::user_stats($LoggedUser['ID']); $HeavyInfo = Users::user_heavy_info($LoggedUser['ID']); $LoggedUser = array_merge($HeavyInfo, $LightInfo, $UserStats); G::$LoggedUser =& $LoggedUser; // No conditions will force a logout from this point, can hit the DB more. // Complete the $LoggedUser array $LoggedUser['Permissions'] = Permissions::get_permissions_for_user($LoggedUser['ID'], $LoggedUser['CustomPermissions']); $LoggedUser['Permissions']['MaxCollages'] += Donations::get_personal_collages($LoggedUser['ID']); $LoggedUser['RSS_Auth'] = md5($LoggedUser['ID'] . RSS_HASH . $LoggedUser['torrent_pass']); // Notifications if (isset($LoggedUser['Permissions']['site_torrents_notify'])) { $LoggedUser['Notify'] = $User->notifyFilters(); } // Stylesheet $Stylesheets = new \Gazelle\Stylesheet($DB, $Cache); $LoggedUser['StyleName'] = $Stylesheets->getName($LoggedUser['StyleID']); // We've never had to disable the wiki privs of anyone. if ($LoggedUser['DisableWiki']) { unset($LoggedUser['Permissions']['site_edit_wiki']); } // $LoggedUser['RatioWatch'] as a bool to disable things for users on Ratio Watch $LoggedUser['RatioWatch'] = ( $LoggedUser['RatioWatchEnds'] != '0000-00-00 00:00:00' && time() < strtotime($LoggedUser['RatioWatchEnds']) && ($LoggedUser['BytesDownloaded'] * $LoggedUser['RequiredRatio']) > $LoggedUser['BytesUploaded'] ); // Change necessary triggers in external components $Cache->CanClear = check_perms('admin_clear_cache'); // Because we <3 our staff if (check_perms('site_disable_ip_history')) { $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; } // IP changed if ($LoggedUser['IP'] != $_SERVER['REMOTE_ADDR'] && !check_perms('site_disable_ip_history')) { if (Tools::site_ban_ip($_SERVER['REMOTE_ADDR'])) { error('Your IP address has been banned.'); } $User->updateIP($LoggedUser['IP'], $_SERVER['REMOTE_ADDR']); } // Update LastUpdate every 10 minutes if (strtotime($UserSessions[$SessionID]['LastUpdate']) + 600 < time()) { $Session->update([ 'ip-address' => $_SERVER['REMOTE_ADDR'], 'browser' => $Browser, 'browser-version' => $BrowserVersion, 'os' => $OperatingSystem, 'os-version' => $OperatingSystemVersion, 'session-id' => $SessionID ]); } } $Debug->set_flag('end user handling'); $Debug->set_flag('start function definitions'); /** * Log out the current session */ function logout($userId, $sessionId = false) { $epoch = time() - 60 * 60 * 24 * 365; setcookie('session', '', $epoch, '/', '', false); setcookie('keeplogged', '', $epoch, '/', '', false); setcookie('session', '', $epoch, '/', '', false); if ($sessionId) { $session = new \Gazelle\Session(G::$DB, G::$Cache, $userId); $session->drop($sessionId); } G::$Cache->delete_value('user_info_' . $userId); G::$Cache->delete_value('user_stats_' . $userId); G::$Cache->delete_value('user_info_heavy_' . $userId); header('Location: login.php'); die(); } /** * Logout all sessions */ function logout_all_sessions($userId) { $session = new \Gazelle\Session(G::$DB, G::$Cache, $userId); $session->dropAll(); logout($userId); } function enforce_login() { if (!G::$LoggedUser) { header('Location: login.php'); die(); } global $SessionID; if (!$SessionID) { setcookie('redirect', $_SERVER['REQUEST_URI'], time() + 60 * 30, '/', '', false); logout(G::$LoggedUser['ID']); } } /** * Make sure $_GET['auth'] is the same as the user's authorization key * Should be used for any user action that relies solely on GET. * * @param bool Are we using ajax? * @return bool authorisation status. Prints an error message to LAB_CHAN on IRC on failure. */ function authorize($Ajax = false) { if (empty($_REQUEST['auth']) || $_REQUEST['auth'] != G::$LoggedUser['AuthKey']) { send_irc("PRIVMSG ".LAB_CHAN." :".G::$LoggedUser['Username']." just failed authorize on ".$_SERVER['REQUEST_URI'].(!empty($_SERVER['HTTP_REFERER']) ? " coming from ".$_SERVER['HTTP_REFERER'] : "")); error('Invalid authorization key. Go back, refresh, and try again.', $Ajax); return false; } return true; } function authorizeIfPost($Ajax = false) { if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (empty($_POST['auth']) || $_POST['auth'] != G::$LoggedUser['AuthKey']) { send_irc("PRIVMSG " . LAB_CHAN . " :" . G::$LoggedUser['Username'] . " just failed authorize on " . $_SERVER['REQUEST_URI'] . (!empty($_SERVER['HTTP_REFERER']) ? " coming from " . $_SERVER['HTTP_REFERER'] : "")); error('Invalid authorization key. Go back, refresh, and try again.', $Ajax); return false; } } return true; } $Debug->set_flag('ending function definitions'); // load the appropriate /sections/*/index.php $Document = basename(parse_url($_SERVER['SCRIPT_NAME'], PHP_URL_PATH), '.php'); if (!preg_match('/^[a-z0-9]+$/i', $Document)) { error(404); } $Cache->cache_value('php_' . getmypid(), [ 'start' => sqltime(), 'document' => $Document, 'query' => $_SERVER['QUERY_STRING'], 'get' => $_GET, 'post' => array_diff_key( $_POST, array_fill_keys(['password', 'cur_pass', 'new_pass_1', 'new_pass_2', 'verifypassword', 'confirm_password', 'ChangePassword', 'Password'], true) ) ], 600 ); G::$Router = new \Gazelle\Router($LoggedUser['AuthKey']); if (isset($LoggedUser['LockedAccount']) && !in_array($Document, ['staffpm', 'ajax', 'locked', 'logout', 'login'])) { require(__DIR__ . '/../sections/locked/index.php'); } else { $file = __DIR__ . '/../sections/' . $Document . '/index.php'; if (!file_exists($file)) { error(404); } else { require($file); } } if (G::$Router->hasRoutes()) { $action = $_REQUEST['action'] ?? ''; try { /** @noinspection PhpIncludeInspection */ require_once(G::$Router->getRoute($action)); } catch (\Gazelle\Exception\RouterException $exception) { error(404); } catch (\Gazelle\Exception\InvalidAccessException $exception) { error(403); } } $Debug->set_flag('completed module execution'); /* Required in the absence of session_start() for providing that pages will change * upon hit rather than being browser cached for changing content. * Old versions of Internet Explorer choke when downloading binary files over HTTPS with disabled cache. * Define the following constant in files that handle file downloads. */ if (!defined('SKIP_NO_CACHE_HEADERS')) { header('Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0'); header('Pragma: no-cache'); } ob_end_flush(); $Debug->set_flag('set headers and send to user'); //Attribute profiling $Debug->profile();