1<?php require_once 'engine/init.php';
2protect_page();
3theme_open();
4#region CANCEL CHARACTER DELETE
5$undelete_id = $_GET['cancel_delete_id'] ?? null;
6if($undelete_id) {
7 $undelete_id = (int)$undelete_id;
8 $undelete_q1 = db()->fetchOne("
9 SELECT
10 `character_name`
11 FROM `znote_deleted_characters`
12 WHERE `done` = 0
13 AND `id` = ?
14 AND `original_account_id` = ?
15 AND NOW() < `time`
16 ", [$undelete_id, (int)$session_user_id]);
17 if($undelete_q1) {
18 db()->execute('DELETE FROM `znote_deleted_characters` WHERE `id` = ?', [$undelete_id]);
19 echo t('acc.delete_cancelled', ['name' => $undelete_q1['character_name']]) .'<br/>';
20 }
21}
22#endregion
23
24// Variable used to check if main page should be rendered after handling POST
25$render_page = true;
26
27// Handle GET (verify email)
28if (isset($_GET['authenticate']) && $config['mailserver']['myaccount_verify_email']):
29 // If we need to process email verification
30 if (isset($_GET['u']) && isset($_GET['k'])) {
31 // Authenticate user, fetch user id and activation key
32 $auid = (isset($_GET['u']) && (int)$_GET['u'] > 0) ? (int)$_GET['u'] : false;
33 $akey = (isset($_GET['k']) && (int)$_GET['k'] > 0) ? (int)$_GET['k'] : false;
34 if ($auid !== false && $akey !== false) {
35 // Find a match
36 $user = db()->fetchOne(
37 "SELECT `id`, `active`, `active_email` FROM `znote_accounts` WHERE `account_id` = ? AND `activekey` = ? LIMIT 1;",
38 [$auid, $akey]
39 );
40 if ($user !== false) {
41 $userId = (int)$user['id'];
42 $active = (int)$user['active'];
43 $active_email = (int)$user['active_email'];
44 $verify_points = ($active_email == 0 && $config['mailserver']['verify_email_points'] > 0)
45 ? (int)$config['mailserver']['verify_email_points']
46 : 0;
47 // Enable the account to login
48 if ($active == 0 || $active_email == 0) {
49 $new_activeKey = rand(100000000, 999999999);
50 db()->execute(
51 "UPDATE `znote_accounts`
52 SET `active` = 1, `active_email` = 1, `activekey` = ?, `points` = `points` + ?
53 WHERE `id` = ?
54 LIMIT 1;",
55 [$new_activeKey, $verify_points, $userId]
56 );
57 }
58 echo '<h1>'. t('common.congrats') .'</h1> <p>'. t('acc.email_verified') .'</p>';
59 if ($verify_points > 0) echo "<p>" . t('acc.verify_reward', ['points' => "<a href='/shop.php'>{$verify_points} " . t('char.shop_points2') . "</a>"]) . "</p>";
60 $user_znote_data['active_email'] = 1;
61 $user_znote_data['points'] = (int)$user_znote_data['points'] + $verify_points;
62 } else {
63 echo '<h1>'. t('acc.auth_failed'). '</h1> <p>' . t('acc.auth_failed_or_activated') . '</p>';
64 }
65 } else {
66 echo '<h1>'. t('acc.auth_failed') .'</h1> <p>'. t('acc.auth_failed_text') .'</p>';
67 }
68 } else { // We need to send email verification
69 $verify_account_id = (int)$session_user_id;
70 $user = db()->fetchOne(
71 "SELECT `id`, `activekey`, `active_email` FROM `znote_accounts` WHERE `account_id` = ? LIMIT 1;",
72 [$verify_account_id]
73 );
74 if ($user !== false) {
75 $thisurl = config('site_url') . "/myaccount.php";
76 $thisurl .= "?authenticate&u=".$verify_account_id."&k=".$user['activekey'];
77
78 $mailer = new Mail($config['mailserver']);
79
80 $title = t('acc.mail_subject_verify', ['host' => $_SERVER['HTTP_HOST']]);
81
82 $body = '<h1>' . t('acc.mail_verify_intro') . '</h1>';
83 $body .= "<p><a href='{$thisurl}'>{$thisurl}</a></p>";
84 $body .= '<p>' . t('acc.mail_verify_thanks', ['site' => $config['mailserver']['fromName']]) . '</p>';
85 $body .= '<hr><p>' . t('recovery.mail_noreply') . '</p>';
86
87 $user_name = (znote_server_adapter()->accountIdentityColumn() !== 'id') ? $user_data['name'] : $user_data['id'];
88 //echo "<h1>" . $title . "<h1>" . $body;
89 $mailer->sendMail($user_data['email'], $title, $body, $user_name);
90 ?>
91 <h1><?= t('acc.email_sent') ?></h1>
92 <p><?= t('acc.verify_sent_intro') ?> <strong><?php echo $user_data['email']; ?></strong></p>
93 <p><?= t('acc.check_junk_spam') ?></p>
94 <?php
95 } else {
96 echo '<h1>'. t('acc.auth_failed'). '</h1> <p>' . t('acc.verify_send_failed') . '</p>';
97 }
98 }
99endif;
100
101// Handle POST
102if (!empty($_POST['selected_character'])) {
103 if (!empty($_POST['action'])) {
104 // Validate token
105 if (!Token::isValid($_POST['token'])) {
106 exit();
107 }
108 // Sanitize values
109 $action = getValue($_POST['action'] ?? null);
110 $char_name = getValue($_POST['selected_character'] ?? null);
111
112 // Handle actions
113 switch($action) {
114 // Change character comment PAGE2 (Success).
115 case 'update_comment':
116 if ((int)user_character_account_id($char_name) === $session_user_id) {
117 user_update_comment(user_character_id($char_name), getValue($_POST['comment'] ?? null));
118 echo t('acc.comment_updated');
119 }
120 break;
121 // end
122
123 // Hide character
124 case 'toggle_hide':
125 $hide = (user_character_hide($char_name) == 1 ? 0 : 1);
126 if ((int)user_character_account_id($char_name) === $session_user_id) {
127 user_character_set_hide(user_character_id($char_name), $hide);
128 }
129 break;
130 // end
131
132 // DELETE character
133 case 'delete_character':
134 if ((int)user_character_account_id($char_name) === $session_user_id) {
135 $charid = user_character_id($char_name);
136 if ($charid !== false) {
137 if (!user_is_online_10($charid)) {
138 if (guild_leader_gid($charid) === false) user_delete_character_soft($charid);
139 else echo t('acc.is_guild_leader');
140 } else echo t('acc.must_be_offline');
141 }
142 }
143 break;
144 // end
145
146 // CHANGE character name
147 case 'change_name':
148 $oldname = $char_name;
149 $newname = isset($_POST['newName']) ? getValue($_POST['newName'] ?? null) : '';
150
151 $player = db()->fetchOne("SELECT `id`, `account_id` FROM `players` WHERE `name` = ? LIMIT 1;", [$oldname]);
152 if ($player === false) {
153 $errors[] = t('acc.sync_failed');
154 echo '<font color="red"><b>';
155 echo output_errors($errors);
156 echo '</b></font>';
157 break;
158 }
159 $player['online'] = (user_is_online_10($player['id'])) ? 1 : 0;
160
161 // Check if user is online
162 if ($player['online'] == 1) {
163 $errors[] = t('acc.must_be_offline');
164 }
165
166 // Check if player has bough ticket
167 $accountId = $player['account_id'];
168 $order = db()->fetchOne(
169 "SELECT `id`, `account_id` FROM `znote_shop_orders` WHERE `type` = 4 AND `account_id` = ? LIMIT 1;",
170 [(int)$accountId]
171 );
172 if ($order === false) {
173 $errors[] = t('acc.no_name_tickets');
174 }
175
176 // Check if player and account matches
177 if ($order !== false && ($session_user_id != $accountId || $session_user_id != $order['account_id'])) {
178 if (empty($errors)) {
179 $errors[] = t('acc.sync_failed');
180 }
181 }
182
183 $newname = validate_name($newname);
184 if ($newname === false) {
185 $errors[] = t('acc.name_max_words');
186 } else {
187 if (empty($newname)) {
188 $errors[] = t('acc.name_required');
189 } else if (user_character_exist($newname) !== false) {
190 $errors[] = t('acc.name_taken');
191 } else if (!preg_match("/^[a-zA-Z_ ]+$/", $newname)) {
192 $errors[] = t('acc.name_letters');
193 } else if (strlen($newname) < $config['minL'] || strlen($newname) > $config['maxL']) {
194 $errors[] = t('acc.name_length', ['min' => $config['minL'], 'max' => $config['maxL']]);
195 } else if (!ctype_upper($newname[0])) {
196 $errors[] = t('acc.name_capital');
197 }
198
199 // name restriction
200 $resname = explode(" ", $_POST['newName']);
201 foreach($resname as $res) {
202 if(in_array(strtolower($res), $config['invalidNameTags'])) {
203 $errors[] = t('reg.restricted_word');
204 } else if(strlen($res) == 1) {
205 $errors[] = t('reg.words_too_short');
206 }
207 }
208 }
209
210 if (!empty($newname) && empty($errors)) {
211 $db = db();
212 if (!$db->beginTransaction()) {
213 $errors[] = t('acc.sync_failed');
214 } else {
215 $ok = $db->execute("UPDATE `players` SET `name` = ? WHERE `id` = ? LIMIT 1;", [$newname, (int)$player['id']]);
216 $ok = $ok && $db->execute("DELETE FROM `znote_shop_orders` WHERE `id` = ? LIMIT 1;", [(int)$order['id']]);
217
218 if ($ok) {
219 $db->commit();
220 echo t('acc.name_changed', ['name' => $newname]);
221 } else {
222 $db->rollback();
223 $errors[] = t('acc.sync_failed');
224 }
225 }
226
227 }
228
229 if (!empty($errors)) {
230 echo '<font color="red"><b>';
231 echo output_errors($errors);
232 echo '</b></font>';
233 }
234
235 break;
236 // end
237
238 // Change character sex
239 case 'change_gender':
240 if ((int)user_character_account_id($char_name) === $session_user_id) {
241 $char_id = (int)user_character_id($char_name);
242 $account_id = user_character_account_id($char_name);
243
244 $chr_data['online'] = user_is_online_10($char_id) ? 1 : 0;
245 if ($chr_data['online'] != 1) {
246 // Verify that we are not messing around with data
247 if ($account_id != $user_data['id']) die("wtf? Something went wrong, try relogging.");
248
249 // Fetch character tickets
250 $tickets = shop_account_gender_tickets($account_id);
251 $tickets = is_array($tickets) ? $tickets : array();
252 if (!empty($tickets) || $config['free_sex_change'] == true) {
253 // They are allowed to change gender
254 $last = false;
255 $infinite = false;
256 $tks = 0;
257 // Do we have any infinite tickets?
258 foreach ($tickets as $ticket) {
259 if ($ticket['count'] == 0) $infinite = true;
260 else if ((int)$ticket['count'] > 0 && $infinite === false) $tks += (int)$ticket['count'];
261 }
262 if ($infinite === true) $tks = 0;
263 $dbid = isset($tickets[0]['id']) ? (int)$tickets[0]['id'] : 0;
264 // If they dont have unlimited tickets, remove a count from their ticket.
265 if ($dbid > 0 && $tickets[0]['count'] > 1) { // Decrease count
266 $tks--;
267 $tkr = ((int)$tickets[0]['count'] - 1);
268 shop_update_row_count($dbid, $tkr);
269 } else if ($dbid > 0 && $tickets[0]['count'] == 1) { // Delete record
270 shop_delete_row_order($dbid);
271 $tks--;
272 }
273
274 // Change character gender:
275 //
276 user_character_change_gender($char_name);
277 echo t('acc.gender_changed', ['name' => $char_name]);
278 if ($tks > 0) echo '<br>You have '. $tks .' gender change tickets left.';
279 else if ($infinite !== true) echo '<br>You are out of tickets.';
280 } else echo 'You don\'t have any character gender tickets, buy them in the <a href="shop.php">SHOP</a>!';
281 } else echo t('acc.must_be_offline');
282 }
283 break;
284 // end
285
286 // Change character comment PAGE1:
287 case 'change_comment':
288 $render_page = false; // Regular "myaccount" page should not render
289 if ((int)user_character_account_id($char_name) === $session_user_id) {
290 $comment_data = user_znote_character_data(user_character_id($char_name), 'comment');
291 view('myaccount_edit_comment', ['char_name' => $char_name, 'comment_data' => $comment_data]);
292 }
293 break;
294 //end
295 }
296 }
297}
298
299if ($render_page) {
300 $char_count = user_character_list_count($session_user_id);
301 $pending_delete = user_pending_deletes($session_user_id);
302 if ($pending_delete) {
303 foreach($pending_delete as $delete) {
304 if(new DateTime($delete['time']) > new DateTime())
305 echo '<b>' . t('acc.caution') . '</b> ' . t('acc.character_will_be_deleted', ['name' => htmlspecialchars((string)$delete['character_name'], ENT_QUOTES, 'UTF-8'), 'time' => htmlspecialchars((string)$delete['time'], ENT_QUOTES, 'UTF-8')]) . ' <a href="myaccount.php?cancel_delete_id=' . $delete['id'] . '">'. t('acc.cancel_op'). '</a><br/>';
306 else {
307 user_delete_character(user_character_id($delete['character_name']));
308 db()->execute('UPDATE `znote_deleted_characters` SET `done` = 1 WHERE `id` = ?', [(int)$delete['id']]);
309 echo '<b>' . t('acc.character_deleted', ['name' => htmlspecialchars((string)$delete['character_name'], ENT_QUOTES, 'UTF-8')]) . '</b>. ' . t('acc.requested_by_owner');
310 $char_count--;
311 }
312 }
313 }
314
315 ?>
316 <?php
317 $char_array = user_character_list($user_data['id']);
318
319 $legacy_twofa_status = null;
320 if ($config['twoFactorAuthenticator'] && znote_server_adapter()->supportsLegacyTwoFactor()) {
321 $query = db()->fetchOne("SELECT `secret` FROM `accounts` WHERE `id` = ? LIMIT 1;", [(int)$session_user_id]);
322 $legacy_twofa_status = (is_array($query) && $query['secret'] !== NULL);
323 }
324 $twofa2_status = znote2fa_v2_enabled() ? znote2fa_status((int)$session_user_id) : null;
325 // Backward-compatible alias for third-party themes written before 2FA v2.
326 $myaccount_status = $legacy_twofa_status;
327
328 view('myaccount', [
329 'char_array' => $char_array,
330 'char_count' => $char_count,
331 'myaccount_status' => $myaccount_status,
332 'legacy_twofa_status' => $legacy_twofa_status,
333 'twofa2_status' => $twofa2_status,
334 ]);
335 ?>
336 <?php
337}
338theme_close();
339?>
340