WebOrbiton
v1.0.0.9

Publisium

228 lines · 9.9 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. ?>
  140. <!DOCTYPE html>
  141. <html lang="<?= htmlspecialchars(Languages::htmlLang(), ENT_QUOTES) ?>">
  142. ​
  143. <head>
  144. <meta charset="utf-8">
  145. <meta name="viewport" content="width=device-width, initial-scale=1">
  146. <meta name="robots" content="noindex, nofollow">
  147. <title><?= $mode === 'register' ? htmlspecialchars(Language::get('auth_register_button', 'Create account')) : htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?> - <?= htmlspecialchars($siteName) ?></title>
  148. <?= Asset::favicon() ?>
  149. <?= Asset::css('assets/site.css') ?>
  150. <style>
  151. <?= SiteFront::renderFontFaces() ?><?= SiteFront::fontVariablesCss() ?>
  152. </style>
  153. <?php if (!empty($settings['custom_css'])): ?>
  154. <style>
  155. <?= $settings['custom_css'] ?>
  156. </style>
  157. <?php endif; ?>
  158. </head>
  159. ​
  160. <body class="auth-page" data-theme="light">
  161. <script>
  162. (function() {
  163. try {
  164. var t = localStorage.getItem('publisium_theme');
  165. if (t === 'dark' || t === 'light') {
  166. document.body.setAttribute('data-theme', t);
  167. } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
  168. document.body.setAttribute('data-theme', 'dark');
  169. }
  170. } catch (e) {}
  171. })();
  172. </script>
  173. <div class="auth-card">
  174. <h1><?= htmlspecialchars($siteName) ?></h1>
  175. <p class="auth-subtitle"><?= htmlspecialchars(Language::get('auth_reader_account', 'Reader account')) ?></p>
  176. ​
  177. <div class="auth-tabs">
  178. <a href="user-login.php?mode=login" class="<?= $mode === 'login' ? 'active' : '' ?>"><?= htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?></a>
  179. <a href="user-login.php?mode=register" class="<?= $mode === 'register' ? 'active' : '' ?>"><?= htmlspecialchars(Language::get('auth_register_button', 'Create account')) ?></a>
  180. </div>
  181. ​
  182. <?php if ($error !== null): ?>
  183. <div class="auth-error"><?= htmlspecialchars($error) ?></div>
  184. <?php endif; ?>
  185. ​
  186. <?php if ($mode === 'login'): ?>
  187. <form method="post">
  188. <?= Csrf::field() ?>
  189. <?= AntiBot::field('user') ?>
  190. <input type="hidden" name="form_action" value="login">
  191. <label><?= htmlspecialchars(Language::get('auth_email', 'Email')) ?></label>
  192. <input type="email" name="email" required autofocus>
  193. <label><?= htmlspecialchars(Language::get('auth_password', 'Password')) ?></label>
  194. <input type="password" name="password" required>
  195. <div class="antibot-box">
  196. <div class="antibot-image"><?= AntiBot::image('user') ?></div>
  197. <label for="antibot-answer"><?= htmlspecialchars(Language::get('auth_security_code', 'Security code')) ?></label>
  198. <input id="antibot-answer" type="text" name="antibot_answer" required inputmode="text" autocomplete="off" maxlength="6" spellcheck="false">
  199. </div>
  200. <button type="submit"><?= htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?></button>
  201. </form>
  202. <?php else: ?>
  203. <form method="post">
  204. <?= Csrf::field() ?>
  205. <?= AntiBot::field('user') ?>
  206. <input type="hidden" name="form_action" value="register">
  207. <label><?= htmlspecialchars(Language::get('auth_display_name', 'Display name')) ?></label>
  208. <input type="text" name="display_name">
  209. <label><?= htmlspecialchars(Language::get('auth_email', 'Email')) ?></label>
  210. <input type="email" name="email" required>
  211. <label><?= htmlspecialchars(Language::get('auth_password', 'Password')) ?></label>
  212. <input type="password" name="password" required minlength="8">
  213. <label><?= htmlspecialchars(Language::get('auth_confirm_password', 'Confirm password')) ?></label>
  214. <input type="password" name="password_confirm" required minlength="8">
  215. <div class="antibot-box">
  216. <div class="antibot-image"><?= AntiBot::image('user') ?></div>
  217. <label for="antibot-answer"><?= htmlspecialchars(Language::get('auth_security_code', 'Security code')) ?></label>
  218. <input id="antibot-answer" type="text" name="antibot_answer" required inputmode="text" autocomplete="off" maxlength="6" spellcheck="false">
  219. </div>
  220. <button type="submit"><?= htmlspecialchars(Language::get('auth_register_button', 'Create account')) ?></button>
  221. </form>
  222. <?php endif; ?>
  223. ​
  224. </div>
  225. </body>
  226. ​
  227. </html>
  228. ​