router.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. <?php declare(strict_types=1);
  2. require 'init.php';
  3. $pageAddress = substr($_SERVER['REQUEST_URI'], strlen(CONF['common']['prefix']) + 1);
  4. if (strpos($pageAddress, '?') !== false) {
  5. parse_str(substr($pageAddress, strpos($pageAddress, '?') + 1), $_GET);
  6. $pageAddress = substr($pageAddress, 0, strpos($pageAddress, '?'));
  7. }
  8. define('PAGE_URL', $pageAddress);
  9. define('PAGE_ADDRESS', $pageAddress . ((substr($pageAddress, -1) === '/' OR $pageAddress === '') ? 'index' : ''));
  10. define('PAGE_LINEAGE', explode('/', PAGE_ADDRESS));
  11. define('SERVICE', dirname(PAGE_ADDRESS));
  12. if (PAGE_ADDRESS === 'config.ini') {
  13. header('Content-Type: text/plain');
  14. echo file_get_contents(ROOT_PATH . '/config.ini');
  15. exit();
  16. }
  17. function pageNotFound(): never {
  18. http_response_code(404);
  19. exit(_('Page not found.'));
  20. }
  21. function getPageInformations(array $pages, array $pageElements): array { // Recursively retrieves page metadata from pages.php
  22. if (!isset($pages['index']) OR $pageElements[0] === 'index') {
  23. if (count($pageElements) !== 1)
  24. pageNotFound();
  25. return [
  26. 'titles_lineage' => [$pages[$pageElements[0]]['title'] ?? pageNotFound()],
  27. 'page_metadata' => $pages[$pageElements[0]],
  28. 'terminal' => $pageElements[0] !== 'index'
  29. ];
  30. }
  31. $result = $pages['index']['title'];
  32. if (!isset($pageElements[1]))
  33. unset($pages['index']);
  34. else
  35. $pages = $pages[array_shift($pageElements)] ?? [];
  36. $results = getPageInformations($pages, $pageElements);
  37. $results['titles_lineage'][] = $result;
  38. return $results;
  39. }
  40. $pageInformations = getPageInformations(PAGES, PAGE_LINEAGE);
  41. define('TITLES_LINEAGE', array_reverse($pageInformations['titles_lineage']));
  42. define('PAGE_METADATA', $pageInformations['page_metadata']);
  43. define('PAGE_TERMINAL', $pageInformations['terminal']);
  44. if (isset($_SERVER['SERVER_NAME']) !== true)
  45. exit('Missing <code>$_SERVER[\'SERVER_NAME\']</code>');
  46. if (in_array($_SERVER['SERVER_NAME'], CONF['common']['public_domains'], true) !== true)
  47. exit('The current <code>$_SERVER[\'SERVER_NAME\']</code> is not allowed in configuration.');
  48. define('SERVER_NAME', $_SERVER['SERVER_NAME']);
  49. const SESSION_COOKIE_NAME = 'servnest-session-key';
  50. function startSession(): void {
  51. session_start([
  52. 'name' => SESSION_COOKIE_NAME,
  53. 'sid_length' => 64,
  54. 'sid_bits_per_character' => 6,
  55. 'cookie_secure' => true,
  56. 'cookie_httponly' => true,
  57. 'cookie_samesite' => 'Strict',
  58. 'cookie_path' => CONF['common']['prefix'] . '/',
  59. 'cookie_lifetime' => 432000, // = 60*60*24*5 = 5 days
  60. 'gc_maxlifetime' => 10800,
  61. 'use_strict_mode' => true,
  62. 'use_cookies' => true,
  63. 'use_only_cookies' => true,
  64. ]);
  65. }
  66. if (isset($_COOKIE[SESSION_COOKIE_NAME]))
  67. startSession(); // Resume session
  68. if (isset($_SESSION['id'])) {
  69. if (!isset(query('select', 'users', ['id' => $_SESSION['id']], ['id'])[0]))
  70. logout();
  71. // Decrypt display username
  72. if (!isset($_COOKIE['display-username-decryption-key']))
  73. output(403, 'The display username decryption key has not been sent.');
  74. $decryption_result = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
  75. $_SESSION['display-username-cyphertext'],
  76. '',
  77. $_SESSION['display-username-nonce'],
  78. base64_decode($_COOKIE['display-username-decryption-key'])
  79. );
  80. if ($decryption_result === false)
  81. output(403, 'Unable to decrypt display username.');
  82. define('DISPLAY_USERNAME', htmlspecialchars($decryption_result));
  83. // Enable not already enabled services for this user
  84. $user_services = array_filter(explode(',', query('select', 'users', ['id' => $_SESSION['id']], ['services'])[0]));
  85. foreach (SERVICES_USER as $service)
  86. if (!in_array($service, $user_services, true) AND CONF['common']['services'][$service] === 'enabled') {
  87. $user_services[] = $service;
  88. DB->prepare('UPDATE users SET services = :services WHERE id = :id')
  89. ->execute([':services' => implode(',', $user_services), ':id' => $_SESSION['id']]);
  90. if ($service === 'ht')
  91. htSetupUserFs($_SESSION['id']);
  92. }
  93. }
  94. function displayFinalMessage(?array $data): void {
  95. if (isset($data['final_message'])) {
  96. echo $data['final_message'];
  97. unset($data['final_message']);
  98. }
  99. }
  100. if ($_POST !== []) {
  101. if (!in_array(CONF['common']['services']['auth'], ['enabled', 'no-registration'], true) OR (in_array(SERVICE, SERVICES_USER, true) AND CONF['common']['services'][SERVICE] !== 'enabled'))
  102. output(503, _('This service is currently under maintenance. No action can be taken on it until an administrator finishes repairing it.'));
  103. // Protect against cross-site request forgery if a POST request is received
  104. if (isset($_SERVER['HTTP_SEC_FETCH_SITE']) !== true)
  105. output(403, 'The <code>Sec-Fetch-Site</code> HTTP header is required when submitting a POST request to prevent Cross-Site Request Forgery (<abbr>CSRF</abbr>).');
  106. if (!in_array($_SERVER['HTTP_SEC_FETCH_SITE'], ['none', 'same-origin'], true))
  107. output(403, 'The <code>Sec-Fetch-Site</code> HTTP header must be <code>same-origin</code> or <code>none</code> when submitting a POST request to prevent Cross-Site Request Forgery (<abbr>CSRF</abbr>).');
  108. if (PAGE_METADATA['require-login'] ?? true AND !isset($_SESSION['id']))
  109. output(403, _('You need to be logged in to do this.'));
  110. if (file_exists(ROOT_PATH . '/pg-act/' . PAGE_ADDRESS . '.php'))
  111. require ROOT_PATH . '/pg-act/' . PAGE_ADDRESS . '.php';
  112. }
  113. function displayPage(?array $data): never {
  114. require ROOT_PATH . '/view.php';
  115. exit();
  116. }
  117. displayPage($data ??= NULL);