1<?php require_once 'engine/init.php';
2znote_csrf_protect_public_post();
3logged_in_redirect();
4theme_open();
5if (function_exists('tco_recovery_open')) {
6 tco_recovery_open();
7}
8if ($config['mailserver']['accountRecovery']) {
9 // Fetch, sanitize and assign POST and GET variables.
10 $mode = (isset($_GET['mode']) && !empty($_GET['mode'])) ? getValue($_GET['mode'] ?? null) : false;
11 $email = (isset($_POST['email']) && !empty($_POST['email'])) ? getValue($_POST['email'] ?? null) : false;
12 $character = (isset($_POST['character']) && !empty($_POST['character'])) ? getValue($_POST['character'] ?? null) : false;
13 if (!$email && !empty($_POST['email_rcv'])) {
14 $email = getValue($_POST['email_rcv'] ?? null);
15 }
16 if (!$character && !empty($_POST['nick'])) {
17 $character = getValue($_POST['nick'] ?? null);
18 }
19 $password = (isset($_POST['password']) && !empty($_POST['password'])) ? getValue($_POST['password'] ?? null) : false;
20 $username = (isset($_POST['username']) && !empty($_POST['username'])) ? getValue($_POST['username'] ?? null) : false;
21 //data_dump($_GET, $_POST, "Posted data.");
22
23 if (!empty($_POST)) {
24 $status = true;
25 if ($config['use_captcha']) {
26 if(!verifyGoogleReCaptcha($_POST['g-recaptcha-response'])) {
27 $status = false;
28 }
29 }
30 if ($status) {
31 if (isset($_POST['action_type'])) {
32 $actionType = getValue($_POST['action_type'] ?? '');
33
34 if ($actionType === 'email') {
35 $account = false;
36 if ($email) {
37 $account = db()->fetchOne("
38 SELECT `a`.`id`, `a`.`name`, `a`.`email`, `za`.`activekey`
39 FROM `accounts` AS `a`
40 INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id`
41 WHERE `a`.`email` = ?
42 ORDER BY `a`.`id` ASC
43 LIMIT 1;
44 ", [$email]);
45 } elseif ($character) {
46 $account = db()->fetchOne("
47 SELECT `a`.`id`, `a`.`name`, `a`.`email`, `za`.`activekey`
48 FROM `players` AS `p`
49 INNER JOIN `accounts` AS `a` ON `p`.`account_id` = `a`.`id`
50 INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id`
51 WHERE `p`.`name` = ?
52 LIMIT 1;
53 ", [$character]);
54 }
55
56 if (is_array($account) && !empty($account['email'])) {
57 $recoverylink = $config['site_url'] . '/recovery.php?action=lostaccount_reset&a=' . (int)$account['id'] . '&k=' . (int)$account['activekey'];
58 $mailer = new Mail($config['mailserver']);
59 $title = t('recovery.mail_subject_lostaccount', ['host' => $_SERVER['HTTP_HOST']]);
60 $body = '<h1>' . t('recovery.title') . '</h1>';
61 $body .= '<p>' . t('recovery.mail_lostaccount_intro') . '</p>';
62 $body .= '<p><a href="' . htmlspecialchars($recoverylink, ENT_QUOTES, 'UTF-8') . '" target="_BLANK">' . htmlspecialchars($recoverylink, ENT_QUOTES, 'UTF-8') . '</a></p>';
63 $body .= '<p>' . t('recovery.mail_ignore') . '</p>';
64 $body .= '<hr><p>' . t('recovery.mail_noreply') . '</p>';
65 $mailer->sendMail((string)$account['email'], $title, $body, (string)$account['name']);
66 }
67
68 ?>
69 <h1><?= t('recovery.found') ?></h1>
70 <p><?= t('recovery.sent_generic') ?></p>
71 <?php
72 } else {
73 ?>
74 <h1><?= t('recovery.title') ?></h1>
75 <p><?= t('recovery.not_automated') ?></p>
76 <?php
77 }
78 } elseif (!$username) {
79 // Recover username
80 $salt = '';
81 if (znote_server_adapter()->normalizedEngine() === 'TFS_03' && config('salt') === true) {
82 $saltdata = db()->fetchOne(
83 "SELECT `salt` FROM `accounts` WHERE `email` = ? LIMIT 1;",
84 [$email]
85 );
86 if ($saltdata !== false) $salt .= $saltdata['salt'];
87 }
88
89 if (znote_server_adapter()->accountIdentityColumn() !== 'id')
90 $candidate = db()->fetchOne(
91 "SELECT `p`.`id` AS `player_id`, `a`.`id` AS `account_id`, `a`.`name`, `a`.`password`
92 FROM `players` `p`
93 INNER JOIN `accounts` `a` ON `p`.`account_id` = `a`.`id`
94 WHERE `p`.`name` = ? AND `a`.`email` = ?
95 LIMIT 1;",
96 [$character, $email]
97 );
98 else
99 $candidate = db()->fetchOne(
100 "SELECT `p`.`id` AS `player_id`, `a`.`id` AS `account_id`, `a`.`id` AS `name`, `a`.`password`
101 FROM `players` `p`
102 INNER JOIN `accounts` `a` ON `p`.`account_id` = `a`.`id`
103 WHERE `p`.`name` = ? AND `a`.`email` = ?
104 LIMIT 1;",
105 [$character, $email]
106 );
107
108 $user = false;
109 if ($candidate !== false && user_verify_login_password((int)$candidate['account_id'], (string)$password, (string)$candidate['password'], $salt)) {
110 $user = $candidate;
111 }
112
113 if ($user !== false) {
114 // Found user
115
116 $mailer = new Mail($config['mailserver']);
117 $title = t('recovery.mail_subject_username', ['host' => $_SERVER['HTTP_HOST']]);
118 $body = '<h1>' . t('recovery.title') . '</h1>';
119 $body .= '<p>' . t('recovery.your_username2') . ' <b>' . htmlspecialchars((string)$user['name'], ENT_QUOTES, 'UTF-8') . '</b><br>';
120 $body .= t('recovery.mail_enjoy_stay', ['site' => $config['mailserver']['fromName']]) . ' <br>';
121 $body .= '<hr>' . t('recovery.mail_noreply') . '</p>';
122 $mailer->sendMail($email, $title, $body, $user['name']);
123
124 ?>
125 <h1><?= t('recovery.found') ?></h1>
126 <p><?= t('recovery.sent_username2') ?> <b><?php echo $email; ?></b>.</p>
127 <p><?= t('recovery.check_junk') ?></p>
128 <?php
129 } else {
130 // Wrong submitted info
131 ?>
132 <h1><?= t('recovery.failed') ?></h1>
133 <p><?= t('recovery.wrong_data') ?></p>
134 <?php
135 }
136
137 } elseif (!$password) {
138 // Recover password
139 $newpass = rand(100000000, 999999999);
140 $salt = '';
141 if (znote_server_adapter()->normalizedEngine() !== 'TFS_03') {
142 // TFS 0.2 and 1.0
143 $password = sha1($newpass);
144 } else {
145 // TFS 0.3/4
146 if (config('salt') === true) {
147 $saltdata = db()->fetchOne(
148 "SELECT `salt` FROM `accounts` WHERE `email` = ? LIMIT 1;",
149 [$email]
150 );
151 if ($saltdata !== false) $salt .= $saltdata['salt'];
152 }
153 $password = sha1($salt.$newpass);
154 }
155
156 if (znote_server_adapter()->accountIdentityColumn() !== 'id')
157 $user = db()->fetchOne(
158 "SELECT `p`.`id` AS `player_id`, `a`.`name`, `a`.`id` AS `account_id`
159 FROM `players` `p`
160 INNER JOIN `accounts` `a` ON `p`.`account_id` = `a`.`id`
161 WHERE `p`.`name` = ? AND `a`.`email` = ? AND `a`.`name` = ?
162 LIMIT 1;",
163 [$character, $email, $username]
164 );
165 else
166 $user = db()->fetchOne(
167 "SELECT `p`.`id` AS `player_id`, `a`.`id` AS `account_id`, `a`.`id` AS `name`
168 FROM `players` `p`
169 INNER JOIN `accounts` `a` ON `p`.`account_id` = `a`.`id`
170 WHERE `p`.`name` = ? AND `a`.`email` = ? AND `a`.`id` = ?
171 LIMIT 1;",
172 [$character, $email, $username]
173 );
174
175 if ($user !== false) {
176 // Found user
177 // Give him the new password
178 db()->execute(
179 "UPDATE `accounts` SET `password` = ? WHERE `id` = ? LIMIT 1;",
180 [$password, (int)$user['account_id']]
181 );
182 user_set_website_password_hash((int)$user['account_id'], (string)$newpass);
183 // Send him a mail with the new password
184 $mailer = new Mail($config['mailserver']);
185 $title = t('recovery.mail_subject_password', ['host' => $_SERVER['HTTP_HOST']]);
186 $body = '<h1>' . t('recovery.title') . '</h1>';
187 $body .= '<p>' . t('recovery.new_password') . ' <b>' . htmlspecialchars((string)$newpass, ENT_QUOTES, 'UTF-8') . '</b><br>';
188 $body .= t('recovery.recommend_change_password') . ' <br>';
189 $body .= t('recovery.mail_enjoy_stay', ['site' => $config['mailserver']['fromName']]) . ' <br>';
190 $body .= '<hr>' . t('recovery.mail_noreply') . '</p>';
191 $mailer->sendMail($email, $title, $body, $user['name']);
192 ?>
193 <h1><?= t('recovery.found') ?></h1>
194 <p><?= t('recovery.sent_password') ?> <b><?php echo $email; ?></b>.</p>
195 <p><?= t('recovery.check_junk') ?></p>
196 <?php
197 } else {
198 // Wrong submitted info
199 ?>
200 <h1><?= t('recovery.failed') ?></h1>
201 <p><?= t('recovery.wrong_data') ?></p>
202 <?php
203 }
204 } else { // Token
205 $candidate = db()->fetchOne(
206 "SELECT `a`.`id`, `a`.`name`, `a`.`password`, `za`.`activekey`
207 FROM `accounts` AS `a`
208 INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id`
209 WHERE `a`.`name` = ? AND `a`.`email` = ?
210 LIMIT 1;",
211 [$username, $email]
212 );
213 $user = false;
214 if ($candidate !== false && user_verify_login_password((int)$candidate['id'], (string)$password, (string)$candidate['password'])) {
215 $user = $candidate;
216 }
217 if ($user !== false) {
218 // Found user
219 $recoverylink = $config['site_url'] . '/recovery.php?a='.$user['id'].'&k='.$user['activekey'];
220 $mailer = new Mail($config['mailserver']);
221 $title = $config['site_title'] . ': ' . t('recovery.remove_2fa') . ' link';
222 $body = '<h1>' . t('recovery.remove_2fa') . '</h1>';
223 $body .= '<p>' . t('recovery.remove_2fa_confirm_link') . '<br>';
224 $body .= '<a href="' . htmlspecialchars($recoverylink, ENT_QUOTES, 'UTF-8') . '" target="_BLANK">' . htmlspecialchars($recoverylink, ENT_QUOTES, 'UTF-8') . '</a><br>';
225 $body .= t('recovery.mail_enjoy_stay', ['site' => $config['mailserver']['fromName']]) . ' <br>';
226 $body .= '<hr>' . t('recovery.mail_noreply') . '</p>';
227 $mailer->sendMail($email, $title, $body, $user['name']);
228 ?>
229 <h1><?= t('recovery.confirm_email') ?></h1>
230 <p><?= t('recovery.sent_link') ?> <b><?php echo $email; ?></b>.</p>
231 <p><?= t('recovery.click_link') ?> <?= t('common.2fa') ?>.</p>
232 <p><?= t('recovery.check_junk') ?></p>
233 <?php
234 } else {
235 // Wrong submitted info
236 ?>
237 <h1><?= t('recovery.failed') ?></h1>
238 <p><?= t('recovery.wrong_data') ?></p>
239 <?php
240 }
241
242
243 }
244 } else echo t('recovery.captcha_wrong');
245 } else {
246
247 $recoveryAction = (isset($_GET['action']) && !empty($_GET['action'])) ? getValue($_GET['action'] ?? null) : false;
248 $a = (isset($_GET['a']) && !empty($_GET['a'])) ? (int)$_GET['a'] : false;
249 $k = (isset($_GET['k']) && !empty($_GET['k'])) ? (int)$_GET['k'] : false;
250
251 // '. t('recovery.remove_2fa'). '
252 if ($a !== false && $k !== false && $recoveryAction === 'lostaccount_reset') {
253 $account = db()->fetchOne(
254 "SELECT `a`.`id`, `a`.`name`, `a`.`email`
255 FROM `accounts` AS `a`
256 INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id`
257 WHERE `a`.`id` = ? AND `za`.`activekey` = ?
258 LIMIT 1;",
259 [$a, $k]
260 );
261 if ($account !== false && !empty($account['email'])) {
262 $newpass = substr(sha1(random_bytes(32)), 0, 12);
263 if (znote_server_adapter()->normalizedEngine() === 'TFS_03' && config('salt') === true) {
264 user_change_password03((int)$account['id'], $newpass);
265 } else {
266 user_change_password((int)$account['id'], $newpass);
267 }
268 db()->execute("UPDATE `znote_accounts` SET `activekey` = ? WHERE `account_id` = ? LIMIT 1;", [rand(100000000, 999999999), (int)$account['id']]);
269
270 $mailer = new Mail($config['mailserver']);
271 $title = t('recovery.mail_subject_recovered', ['host' => $_SERVER['HTTP_HOST']]);
272 $body = '<h1>' . t('recovery.title') . '</h1>';
273 $body .= '<p>' . t('recovery.your_username2') . ' <b>' . htmlspecialchars((string)$account['name'], ENT_QUOTES, 'UTF-8') . '</b><br>';
274 $body .= t('recovery.new_password') . ' <b>' . htmlspecialchars($newpass, ENT_QUOTES, 'UTF-8') . '</b></p>';
275 $body .= '<p>' . t('recovery.recommend_change_password_now') . '</p>';
276 $body .= '<hr><p>' . t('recovery.mail_noreply') . '</p>';
277 $mailer->sendMail((string)$account['email'], $title, $body, (string)$account['name']);
278 ?>
279 <h1><?= t('recovery.found') ?></h1>
280 <p><?= t('recovery.sent_generic') ?></p>
281 <?php
282 } else {
283 ?>
284 <h1><?= t('recovery.verify_failed2') ?></h1>
285 <p><?= t('recovery.cannot_auth') ?></p>
286 <?php
287 }
288 } elseif ($a !== false && $k !== false && !engineIsCanary()) {
289 $account = db()->fetchOne(
290 "SELECT `a`.`id`, `a`.`secret`, `za`.`secret`
291 FROM `accounts` AS `a`
292 INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id`
293 WHERE `a`.`id` = ? AND `za`.`activekey` = ?
294 LIMIT 1;",
295 [$a, $k]
296 );
297 if ($account !== false) {
298 db()->execute("UPDATE `accounts` SET `secret` = NULL WHERE `id` = ? LIMIT 1;", [$a]);
299 db()->execute("UPDATE `znote_accounts` SET `secret` = NULL WHERE `account_id` = ? LIMIT 1;", [$a]);
300 ?>
301 <h1><?= t('recovery.2fa_disabled') ?></h1>
302 <p><?= t('recovery.2fa_disabled_text') ?></p>
303 <?php
304 } else {
305 ?>
306 <h1><?= t('recovery.verify_failed2') ?></h1>
307 <p><?= t('recovery.cannot_auth') ?></p>
308 <?php
309 }
310 } else { // Regular view
311 ?>
312 <h2><?= t('recovery.welcome_title') ?></h2>
313
314 <p><?= t('recovery.intro_text') ?></p>
315
316 <p><?= t('recovery.can_intro') ?></p>
317
318 <ul class="CustomBulletPointList">
319 <li><?= t('recovery.can_new_password') ?></li>
320 <li><?= t('recovery.can_hacked') ?></li>
321 <li><?= t('recovery.can_change_email') ?></li>
322 <li><?= t('recovery.can_new_key') ?></li>
323 <li><?= t('recovery.can_remove_auth') ?></li>
324 <li><?= t('recovery.can_disable_email_auth') ?></li>
325 </ul>
326
327 <p><?= t('recovery.first_step') ?></p>
328
329 <?php
330 if (in_array($mode, array('username', 'password', 'token'))) {
331 ?>
332 <form action="" method="POST">
333 <label for="email"><?= t('recovery.email_label') ?></label><input type="text" name="email" placeholder="name@mail.com"><br>
334 <label for="<?= t('common.character') ?>"><?= t('common.label_character') ?> </label><input type="text" name="character"><br>
335 <?php
336
337 if ($mode === 'password') {
338 echo '<label for="username">'. t('common.label_username2'). '</label> <input type="text" name="username"><br>';
339 } elseif ($mode === 'username') {
340 echo '<label for="password">'. t('common.label_password') .'</label> <input type="password" name="password"><br>';
341 } elseif ($mode === 'token') {
342 echo '<label for="username">'. t('common.label_username2') .'</label> <input type="text" name="username"><br>';
343 echo '<label for="password">'. t('common.label_password') .'</label> <input type="password" name="password"><br>';
344 }
345
346 if ($config['use_captcha']) {
347 ?>
348 <div class="g-recaptcha" data-sitekey="<?php echo $config['captcha_site_key']; ?>"></div>
349 <?php
350 }
351 ?>
352 <input type="submit" value="<?= t('recovery.submit') ?>">
353 </form>
354 <?php
355 } else {
356 ?>
357 <?php if (function_exists('tco_panel_open')) { tco_panel_open(t('recovery.panel_title')); } ?>
358 <form action="" method="post" class="lostaccount-form">
359 <input type="hidden" name="character" value="">
360 <div class="lostaccount-field-title"><?= t('recovery.field_character') ?></div>
361 <input type="text" name="nick" size="40" autofocus>
362 <div class="lostaccount-field-title"><?= t('recovery.field_email') ?></div>
363 <input type="text" name="email_rcv" size="40">
364 <div class="lostaccount-field-title"><?= t('recovery.field_action') ?></div>
365 <label class="lostaccount-option"><input type="radio" name="action_type" value="email" checked> <?= t('recovery.option_email') ?></label>
366 <label class="lostaccount-option"><input type="radio" name="action_type" value="reckey"> <?= t('recovery.option_reckey') ?></label>
367 <label class="lostaccount-option"><input type="radio" name="action_type" value="no_char"> <?= t('recovery.option_no_char') ?></label>
368 <?php if ($config['use_captcha']) { ?>
369 <div class="g-recaptcha" data-sitekey="<?php echo $config['captcha_site_key']; ?>"></div>
370 <?php } ?>
371 <input type="submit" value="<?= t('recovery.submit') ?>">
372 </form>
373 <?php if (function_exists('tco_panel_close')) { tco_panel_close(); } ?>
374 <?php
375 }
376 }
377 }
378} else {
379 ?>
380 <h1><?= t('recovery.disabled') ?></h1>
381 <p><?= t('recovery.disabled_text2') ?></p>
382 <?php
383}
384if (function_exists('tco_recovery_close')) {
385 tco_recovery_close();
386}
387theme_close(); ?>
388