exec('PRAGMA foreign_keys = ON;'); DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); date_default_timezone_set('UTC'); foreach (explode(',', preg_replace('/[A-Z0-9]|q=|;|-|\./', '', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '')) as $client_locale) if (in_array($client_locale, array_diff(scandir(ROOT_PATH . '/locales'), ['..', '.']), true)) { $locale = $client_locale; break; } define('LOCALE', $locale ?? 'en'); putenv('LANG=C.UTF-8'); setlocale(LC_MESSAGES, 'C.UTF-8'); bindtextdomain('messages', ROOT_PATH . '/locales/' . LOCALE); header('Content-Language: ' . LOCALE); const SERVICES_USER = ['reg', 'ns', 'ht']; const LF = "\n"; const PLACEHOLDER_DOMAIN = 'example'; // From RFC2606: Reserved Top Level DNS Names > 2. TLDs for Testing, & Documentation Examples const PLACEHOLDER_IPV6 = '2001:db8::3'; // From RFC3849: IPv6 Address Prefix Reserved for Documentation const PLACEHOLDER_IPV4 = '203.0.113.42'; // From RFC5737: IPv4 Address Blocks Reserved for Documentation foreach (array_diff(scandir(ROOT_PATH . '/fn'), ['..', '.']) as $file) require ROOT_PATH . '/fn/' . $file; require ROOT_PATH . '/pages.php'; if ($_SERVER['REQUEST_URI'] === '/sftpgo-auth.php') return; $pageAddress = substr($_SERVER['REQUEST_URI'], strlen(CONF['common']['prefix']) + 1); if (strpos($pageAddress, '?') !== false) { parse_str(substr($pageAddress, strpos($pageAddress, '?') + 1), $_GET); $pageAddress = substr($pageAddress, 0, strpos($pageAddress, '?')); } define('PAGE_URL', $pageAddress); define('PAGE_ADDRESS', $pageAddress . ((substr($pageAddress, -1) === '/' OR $pageAddress === '') ? 'index' : '')); define('PAGE_LINEAGE', explode('/', PAGE_ADDRESS)); define('SERVICE', dirname(PAGE_ADDRESS)); function getPageInformations($pages, $pageElements) { if (!isset($pages['index']) OR $pageElements[0] === 'index') return [ 'titles_lineage' => [$pages[$pageElements[0]]['title'] ?? false], 'page_metadata' => $pages[$pageElements[0]] ?? NULL, 'terminal' => $pageElements[0] !== 'index' ]; $result = $pages['index']['title']; if (!isset($pageElements[1])) unset($pages['index']); else $pages = $pages[array_shift($pageElements)] ?? false; $results = getPageInformations($pages, $pageElements); $results['titles_lineage'][] = $result; return $results; } $pageInformations = getPageInformations(PAGES, PAGE_LINEAGE); define('TITLES_LINEAGE', array_reverse($pageInformations['titles_lineage'])); define('PAGE_METADATA', $pageInformations['page_metadata']); define('PAGE_TERMINAL', $pageInformations['terminal']); if (!TITLES_LINEAGE[array_key_last(TITLES_LINEAGE)]) { http_response_code(404); exit('Page not found.'); } if (isset($_SERVER['SERVER_NAME']) !== true) exit('Missing $_SERVER[\'SERVER_NAME\']'); if (in_array($_SERVER['SERVER_NAME'], CONF['common']['public_domains'], true) !== true) exit('The current $_SERVER[\'SERVER_NAME\'] is not allowed in configuration.'); define('SERVER_NAME', $_SERVER['SERVER_NAME']); const SESSION_COOKIE_NAME = 'servnest-session-key'; function startSession() { session_start([ 'name' => SESSION_COOKIE_NAME, 'sid_length' => 64, 'sid_bits_per_character' => 6, 'cookie_secure' => true, 'cookie_httponly' => true, 'cookie_samesite' => 'Strict', 'cookie_path' => CONF['common']['prefix'] . '/', 'cookie_lifetime' => 432000, // = 60*60*24*5 = 5 days 'gc_maxlifetime' => 10800, 'use_strict_mode' => true, 'use_cookies' => true, 'use_only_cookies' => true, ]); } if (isset($_COOKIE[SESSION_COOKIE_NAME])) startSession(); // Resume session if (isset($_SESSION['id'])) { // Decrypt display username if (!isset($_COOKIE['display-username-decryption-key'])) output(403, 'The display username decryption key has not been sent.'); $decryption_result = htmlspecialchars(sodium_crypto_aead_xchacha20poly1305_ietf_decrypt( $_SESSION['display-username-cyphertext'], '', $_SESSION['display-username-nonce'], base64_decode($_COOKIE['display-username-decryption-key']) )); if ($decryption_result === false) output(403, 'Unable to decrypt display username.'); define('DISPLAY_USERNAME', $decryption_result); // Enable not already enabled services for this user $user_services = array_filter(explode(',', query('select', 'users', ['id' => $_SESSION['id']], 'services')[0])); if (in_array(SERVICE, SERVICES_USER, true) AND !in_array(SERVICE, $user_services, true) AND CONF['common']['services'][SERVICE] === 'enabled') { $user_services[] = SERVICE; DB->prepare('UPDATE users SET services = :services WHERE id = :id') ->execute([':services' => implode(',', $user_services), ':id' => $_SESSION['id']]); if (SERVICE === 'ht') htSetupUserFs($_SESSION['id']); } } function displayFinalMessage($data) { if (isset($data['final_message'])) { echo $data['final_message']; unset($data['final_message']); } } if ($_POST !== []) { if (!in_array(CONF['common']['services']['auth'], ['enabled', 'no-registration'], true) OR (in_array(SERVICE, SERVICES_USER, true) AND CONF['common']['services'][SERVICE] !== 'enabled')) output(503, _('This service is currently under maintenance. No action can be taken on it until an administrator finishes repairing it.')); // Protect against cross-site request forgery if a POST request is received if (isset($_SERVER['HTTP_SEC_FETCH_SITE']) !== true) output(403, 'The Sec-Fetch-Site HTTP header is required when submitting a POST request to prevent Cross-Site Request Forgery (CSRF).'); if (!in_array($_SERVER['HTTP_SEC_FETCH_SITE'], ['none', 'same-origin'], true)) output(403, 'The Sec-Fetch-Site HTTP header must be same-origin or none when submitting a POST request to prevent Cross-Site Request Forgery (CSRF).'); if (PAGE_METADATA['require-login'] ?? true !== false) { if (isset($_SESSION['id']) !== true) output(403, _('You need to be logged in to do this.')); if (isset(query('select', 'users', ['id' => $_SESSION['id']], 'id')[0]) !== true) output(403, _('This account doesn\'t exist anymore. Log out to end this ghost session.')); } if (file_exists(ROOT_PATH . '/pg-act/' . PAGE_ADDRESS . '.php')) require ROOT_PATH . '/pg-act/' . PAGE_ADDRESS . '.php'; } function displayPage($data) { require ROOT_PATH . '/view.php'; exit(); } displayPage($data ??= NULL);