WebOrbiton
v2.0.0.1

Publisium

247 lines · 10.7 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. require_once __DIR__ . '/includes/config.php';
  6. require_once __DIR__ . '/includes/database.php';
  7. require_once __DIR__ . '/includes/user-auth.php';
  8. require_once __DIR__ . '/includes/csrf.php';
  9. require_once __DIR__ . '/includes/language.php';
  10. require_once __DIR__ . '/includes/site-front.php';
  11. require_once __DIR__ . '/includes/asset.php';
  12. require_once __DIR__ . '/includes/antibot.php';
  13. require_once __DIR__ . '/includes/login-throttle.php';
  14. ​
  15. header('X-Robots-Tag: noindex, nofollow');
  16. UserAuth::boot();
  17. AntiBot::boot('user');
  18. if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  19. AntiBot::refresh('user');
  20. }
  21. ​
  22. $settings = SiteFront::settings();
  23. $loginEnabled = ($settings['login_enabled'] ?? '1') !== '0';
  24. ​
  25. if (isset($_GET['logout'])) {
  26. UserAuth::logout();
  27. header('Location: user-login.php');
  28. exit;
  29. }
  30. ​
  31. if (UserAuth::check()) {
  32. header('Location: ' . SiteFront::homeUrl());
  33. exit;
  34. }
  35. ​
  36. if (!$loginEnabled) {
  37. $siteName = Config::get('APP_NAME', 'Publisium');
  38. ?>
  39. <!DOCTYPE html>
  40. <html lang="<?= htmlspecialchars(Languages::htmlLang(), ENT_QUOTES) ?>">
  41. ​
  42. <head>
  43. <meta charset="utf-8">
  44. <meta name="viewport" content="width=device-width, initial-scale=1">
  45. <meta name="robots" content="noindex, nofollow">
  46. <title><?= htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?> - <?= htmlspecialchars($siteName) ?></title>
  47. <?= Asset::favicon() ?>
  48. <?= Asset::css('assets/site.css') ?>
  49. <style>
  50. <?= SiteFront::renderFontFaces() ?><?= SiteFront::fontVariablesCss() ?>
  51. </style>
  52. <?php if (!empty($settings['custom_css'])): ?>
  53. <style>
  54. <?= $settings['custom_css'] ?>
  55. </style>
  56. <?php endif; ?>
  57. </head>
  58. ​
  59. <body class="auth-page" data-theme="light">
  60. <script>
  61. (function() {
  62. try {
  63. var t = localStorage.getItem('publisium_theme');
  64. if (t === 'dark' || t === 'light') {
  65. document.body.setAttribute('data-theme', t);
  66. } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
  67. document.body.setAttribute('data-theme', 'dark');
  68. }
  69. } catch (e) {}
  70. })();
  71. </script>
  72. <div class="auth-card auth-card-disabled">
  73. <h1><?= htmlspecialchars($siteName) ?></h1>
  74. <p class="auth-subtitle"><?= htmlspecialchars(Language::get('auth_login_disabled', 'Reader login is temporarily disabled. Please check back later.')) ?></p>
  75. </div>
  76. </body>
  77. ​
  78. </html>
  79. <?php
  80. exit;
  81. }
  82. ​
  83. $error = null;
  84. $mode = ($_GET['mode'] ?? 'login') === 'register' ? 'register' : 'login';
  85. ​
  86. if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  87. if (!Csrf::verify($_POST['csrf_token'] ?? null)) {
  88. $error = Language::get('auth_session_expired', 'Your session expired. Please try again.');
  89. } elseif (!AntiBot::verify('user', $_POST['antibot_answer'] ?? null, $_POST['antibot_started'] ?? null, $_POST['website'] ?? null)) {
  90. $error = Language::get('auth_security_code_wrong', 'The security code was wrong. Please try again.');
  91. } else {
  92. $formAction = $_POST['form_action'] ?? '';
  93. ​
  94. if ($formAction === 'login') {
  95. $email = trim((string) ($_POST['email'] ?? ''));
  96. $password = (string) ($_POST['password'] ?? '');
  97. ​
  98. if ($email === '' || $password === '') {
  99. $error = Language::get('auth_enter_email_password', 'Enter your email and password.');
  100. $mode = 'login';
  101. } elseif (($wait = LoginThrottle::secondsUntilAllowed('reader', $email)) > 0) {
  102. $error = str_replace('{minutes}', (string) max(1, (int) ceil($wait / 60)), Language::get('auth_too_many_attempts', 'Too many wrong tries. Please wait about {minutes} min and try again.'));
  103. $mode = 'login';
  104. } elseif (UserAuth::attemptLogin($email, $password)) {
  105. LoginThrottle::clear('reader', $email);
  106. header('Location: ' . SiteFront::homeUrl());
  107. exit;
  108. } else {
  109. LoginThrottle::recordFailure('reader', $email);
  110. $error = Language::get('auth_wrong_credentials', 'That email or password doesn’t match. Try again.');
  111. $mode = 'login';
  112. }
  113. }
  114. ​
  115. if ($formAction === 'register') {
  116. $email = trim((string) ($_POST['email'] ?? ''));
  117. $password = (string) ($_POST['password'] ?? '');
  118. $passwordConfirm = (string) ($_POST['password_confirm'] ?? '');
  119. $displayName = trim((string) ($_POST['display_name'] ?? ''));
  120. ​
  121. if ($password !== $passwordConfirm) {
  122. $error = Language::get('auth_passwords_mismatch', 'The passwords don’t match.');
  123. $mode = 'register';
  124. } else {
  125. [$success, $registerError] = UserAuth::register($email, $password, $displayName);
  126. if ($success) {
  127. header('Location: ' . SiteFront::homeUrl());
  128. exit;
  129. }
  130. $error = $registerError;
  131. $mode = 'register';
  132. }
  133. }
  134. }
  135. }
  136. ​
  137. $siteName = Config::get('APP_NAME', 'Publisium');
  138. ​
  139. $legalLinks = [];
  140. if (($settings['auth_legal_links_enabled'] ?? '0') === '1') {
  141. if (($settings['auth_terms_url'] ?? '') !== '') {
  142. $legalLinks[] = [$settings['auth_terms_url'], Language::get('auth_terms', 'Terms of Service')];
  143. }
  144. if (($settings['auth_privacy_url'] ?? '') !== '') {
  145. $legalLinks[] = [$settings['auth_privacy_url'], Language::get('auth_privacy', 'Privacy Policy')];
  146. }
  147. }
  148. ​
  149. ?>
  150. <!DOCTYPE html>
  151. <html lang="<?= htmlspecialchars(Languages::htmlLang(), ENT_QUOTES) ?>">
  152. ​
  153. <head>
  154. <meta charset="utf-8">
  155. <meta name="viewport" content="width=device-width, initial-scale=1">
  156. <meta name="robots" content="noindex, nofollow">
  157. <title><?= $mode === 'register' ? htmlspecialchars(Language::get('auth_register_button', 'Create account')) : htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?> - <?= htmlspecialchars($siteName) ?></title>
  158. <?= Asset::favicon() ?>
  159. <?= Asset::css('assets/site.css') ?>
  160. <style>
  161. <?= SiteFront::renderFontFaces() ?><?= SiteFront::fontVariablesCss() ?>
  162. </style>
  163. <?php if (!empty($settings['custom_css'])): ?>
  164. <style>
  165. <?= $settings['custom_css'] ?>
  166. </style>
  167. <?php endif; ?>
  168. </head>
  169. ​
  170. <body class="auth-page" data-theme="light">
  171. <script>
  172. (function() {
  173. try {
  174. var t = localStorage.getItem('publisium_theme');
  175. if (t === 'dark' || t === 'light') {
  176. document.body.setAttribute('data-theme', t);
  177. } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
  178. document.body.setAttribute('data-theme', 'dark');
  179. }
  180. } catch (e) {}
  181. })();
  182. </script>
  183. <div class="auth-card">
  184. <h1><?= htmlspecialchars($siteName) ?></h1>
  185. <p class="auth-subtitle"><?= htmlspecialchars(Language::get('auth_reader_account', 'Reader account')) ?></p>
  186. ​
  187. <div class="auth-tabs">
  188. <a href="user-login.php?mode=login" class="<?= $mode === 'login' ? 'active' : '' ?>"><?= htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?></a>
  189. <a href="user-login.php?mode=register" class="<?= $mode === 'register' ? 'active' : '' ?>"><?= htmlspecialchars(Language::get('auth_register_button', 'Create account')) ?></a>
  190. </div>
  191. ​
  192. <?php if ($error !== null): ?>
  193. <div class="auth-error"><?= htmlspecialchars($error) ?></div>
  194. <?php endif; ?>
  195. ​
  196. <?php if ($mode === 'login'): ?>
  197. <form method="post">
  198. <?= Csrf::field() ?>
  199. <?= AntiBot::field('user') ?>
  200. <input type="hidden" name="form_action" value="login">
  201. <label><?= htmlspecialchars(Language::get('auth_email', 'Email')) ?></label>
  202. <input type="email" name="email" required autofocus>
  203. <label><?= htmlspecialchars(Language::get('auth_password', 'Password')) ?></label>
  204. <input type="password" name="password" required>
  205. <div class="antibot-box">
  206. <div class="antibot-image"><?= AntiBot::image('user') ?></div>
  207. <label for="antibot-answer"><?= htmlspecialchars(Language::get('auth_security_code', 'Security code')) ?></label>
  208. <input id="antibot-answer" type="text" name="antibot_answer" required inputmode="text" autocomplete="off" maxlength="6" spellcheck="false">
  209. </div>
  210. <button type="submit"><?= htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?></button>
  211. </form>
  212. <?php else: ?>
  213. <form method="post">
  214. <?= Csrf::field() ?>
  215. <?= AntiBot::field('user') ?>
  216. <input type="hidden" name="form_action" value="register">
  217. <label><?= htmlspecialchars(Language::get('auth_display_name', 'Display name')) ?></label>
  218. <input type="text" name="display_name">
  219. <label><?= htmlspecialchars(Language::get('auth_email', 'Email')) ?></label>
  220. <input type="email" name="email" required>
  221. <label><?= htmlspecialchars(Language::get('auth_password', 'Password')) ?></label>
  222. <input type="password" name="password" required minlength="8">
  223. <label><?= htmlspecialchars(Language::get('auth_confirm_password', 'Confirm password')) ?></label>
  224. <input type="password" name="password_confirm" required minlength="8">
  225. <div class="antibot-box">
  226. <div class="antibot-image"><?= AntiBot::image('user') ?></div>
  227. <label for="antibot-answer"><?= htmlspecialchars(Language::get('auth_security_code', 'Security code')) ?></label>
  228. <input id="antibot-answer" type="text" name="antibot_answer" required inputmode="text" autocomplete="off" maxlength="6" spellcheck="false">
  229. </div>
  230. <button type="submit"><?= htmlspecialchars(Language::get('auth_register_button', 'Create account')) ?></button>
  231. </form>
  232. <?php endif; ?>
  233. ​
  234. <?php if ($legalLinks !== []): ?>
  235. <p class="auth-legal">
  236. <?php foreach ($legalLinks as $legalIndex => [$legalUrl, $legalLabel]): ?>
  237. <?= $legalIndex > 0 ? '&middot;' : '' ?>
  238. <a href="<?= htmlspecialchars($legalUrl, ENT_QUOTES) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($legalLabel) ?></a>
  239. <?php endforeach; ?>
  240. </p>
  241. <?php endif; ?>
  242. ​
  243. </div>
  244. </body>
  245. ​
  246. </html>
  247. ​