pokemon-showdown-client/website/users.php
2021-01-22 12:38:50 -05:00

556 lines
19 KiB
PHP

<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
include_once __DIR__ . '/../config/config.inc.php';
$ntbb_groups = array(
array(
'name' => 'Guest',
'symbol' => '',
),
array(
'name' => '',
'symbol' => '',
),
array(
'name' => 'Administrator',
'symbol' => '~',
),
array(
'name' => 'Voice',
'symbol' => '+',
),
array(
'name' => 'Driver',
'symbol' => '%',
),
array(
'name' => 'Moderator',
'symbol' => '@',
),
array(
'name' => 'Leader',
'symbol' => '&',
),
);
$STANDINGS = $psconfig['standings'];
include '../lib/ntbb-session.lib.php';
include '../lib/ntbb-ladder.lib.php';
include 'lib/panels.lib.php';
$authLevel = 0;
$auth2FA = substr($curuser['email'] ?? '', -1) === '@';
if ($curuser['group'] == 4) $authLevel = 2; // driver
if ($curuser['group'] == 5) $authLevel = 3; // mod
if ($curuser['group'] == 6) $authLevel = 4; // leader
if ($curuser['group'] == 6 && $auth2FA) $authLevel = 5; // leader with 2FA
if ($curuser['group'] == 2 && $auth2FA) $authLevel = 6; // admin
$userid = false;
$user = false;
$formats = array(
'gen8randombattle' => 'Random Battle',
'gen8ou' => 'OverUsed',
'gen8ubers' => 'Ubers',
'gen8uu' => 'UnderUsed',
'gen8ru' => 'RarelyUsed',
'gen8nu' => 'NeverUsed',
'gen8pu' => 'PU',
'gen8lc' => 'Little Cup',
'gen8monotype' => 'Monotype',
'gen8battlestadiumsingles' => 'Battle Stadium Singles',
'gen8cap' => 'CAP',
'gen8randomdoublesbattle' => 'Random Doubles Battle',
'gen8doublesou' => 'Doubles OU',
'gen8vgc2020' => 'VGC 2020',
'gen8balancedhackmons' => 'Balanced Hackmons',
'gen8mixandmega' => 'Mix and Mega',
'gen8almostanyability' => 'Almost Any Ability',
'gen8stabmons' => 'STABmons',
'gen8nfe' => 'NFE',
'gen7randombattle' => '[Gen 7] Random Battle',
'gen7ou' => '[Gen 7] OU',
'gen6randombattle' => '[Gen 6] Random Battle',
'gen6ou' => '[Gen 6] OU',
'gen5randombattle' => '[Gen 5] Random Battle',
'gen5ou' => '[Gen 5] OU',
'gen4ou' => '[Gen 4] OU',
'gen3ou' => '[Gen 3] OU',
'gen2ou' => '[Gen 2] OU',
'gen1ou' => '[Gen 1] OU',
);
if (isset($_REQUEST['user']) && strlen($_REQUEST['user'])) {
$userid = $users->userid($_REQUEST['user']);
// 0 is falsy
// I'm hardcoding here to fix a crash, but the rest of the system
// should continue to reject 0 as a valid userid
if ($_REQUEST['user'] === '0') $userid = '0';
if (!strlen($userid)) {
header('HTTP/1.1 404 Not Found');
die("Invalid userid");
}
$user = $users->getUser($userid);
if (substr($_SERVER['REQUEST_URI'], 0, 13) === '/users/?user=') {
// really wish this could be done with mod_rewrite
header('Location: https://' . $psconfig['routes']['users'] . '/'.$userid);
die();
}
if (!$user || $user['banstate'] == 100) {
if ($panels->output !== 'html') header('HTTP/1.1 404 Not Found');
if (!$user) {
$user = [
'username' => $userid,
'userid' => $userid,
'group' => 0,
];
}
}
}
if ($authLevel >= 3) {
//file_put_contents(__DIR__ . '/../config/altaccesslog.txt', "{$curuser['username']} - $userid\n", FILE_APPEND);
}
if (isset($_REQUEST['json'])) {
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
if (!$user) die('null');
$ladder = new NTBBLadder('');
$ladder->getAllRatings($user);
$ratings = [];
foreach ($user['ratings'] as $rating) {
$ratings[$rating['formatid']] = [
'elo' => $rating['elo'],
'gxe' => $rating['gxe'],
'rpr' => $rating['rpr'],
'rprd' => $rating['rprd'],
];
}
echo json_encode([
'username' => $user['username'],
'userid' => $user['userid'],
'registertime' => intval(@$user['registertime']/(60*60*24))*60*60*24,
'group' => intval($user['group'] ?? 0),
'ratings' => $ratings,
], JSON_FORCE_OBJECT);
die();
}
if (!$user) {
$panels->setPageTitle('Users');
$panels->setPageDescription('Pokémon Showdown users');
} else {
$panels->setPageTitle(''.$user['username'].' - Users');
$panels->setPageDescription(''.$user['username'].'\'s user profile');
}
$panels->setTab('ladder');
$panels->start();
if (!$user) {
?>
<div class="pfx-panel"><div class="pfx-body ladderlist">
<h1>
Find a user
</h1>
<form action="/users/" data-target="push">
<input class="textbox" type="text" name="user" placeholder="Username" autofocus />
<button class="button" type="submit">Go</button>
</form>
</div></div>
<?php
} else {
?>
<div class="pfx-panel"><div class="pfx-body ladder">
<a href="/ladder/" class="pfx-backbutton" data-target="back"><i class="fa fa-chevron-left"></i> Ladder</a>
<h1><?php echo htmlspecialchars($user['username']); ?></h1>
<?php
// User management
if ($authLevel >= 4 && substr($user['email'] ?? '', -1) === '@') echo '[2FA]';
if ($user['group'] && $user['group'] != 2 && $authLevel >= 3) {
$csrfOk = (!!$users->csrfCheck() && $authLevel >= 4);
if ($csrfOk && isset($_POST['group'])) {
$group = intval($_POST['group']);
if ($group != 3 && $group != 4 && $group != 5 && $group != 6) $group = 1;
$psdb->query("UPDATE ntbb_users SET `group` = ".intval($group)." WHERE userid = '".$psdb->escape($user['userid'])."' LIMIT 1");
$user['group'] = $group;
$modlogentry = "Group changed to $group ({$ntbb_groups[$group]['name']})";
$psdb->query(
"INSERT INTO `{$psdb->prefix}usermodlog` (`userid`,`actorid`,`date`,`ip`,`entry`) VALUES (?, ?, ?, ?, ?)",
[$user['userid'], $curuser['userid'], time(), $users->getIp(), $modlogentry]
);
?>
<div style="border: 1px solid #DDAA88; padding: 0 1em; margin-bottom: 1em">
<p>Group updated</p>
</div>
<?php
} else if ($csrfOk && isset($_POST['standing'])) {
$newStanding = intval($_POST['standing']);
$psdb->query(
"UPDATE {$psdb->prefix}users SET banstate = ? WHERE userid = ? LIMIT 1",
[$newStanding, $user['userid']]
);
if ($newStanding === 30 || $newStanding === 100) {
$psdb->query(
"UPDATE ntbb_ladder SET elo = -abs(elo) WHERE userid = ?;",
[$user['userid']]
);
} else {
$psdb->query(
"UPDATE ntbb_ladder SET elo = abs(elo) WHERE userid = ?;",
[$user['userid']]
);
}
$modlogentry = "Standing changed to $newStanding ({$STANDINGS[$newStanding]}): {$_POST['reason']}";
$psdb->query(
"INSERT INTO `{$psdb->prefix}usermodlog` (`userid`,`actorid`,`date`,`ip`,`entry`) VALUES (?, ?, ?, ?, ?)",
[$user['userid'], $curuser['userid'], time(), $users->getIp(), $modlogentry]
);
$user['banstate'] = @$_POST['standing'];
$count = $psdb->query("SELECT COUNT(*) FROM ntbb_users WHERE ip = '".$psdb->escape($user['ip'])."' LIMIT 1");
$count = $psdb->fetch_assoc($count);
$count = $count['COUNT(*)'];
?>
<div style="border: 1px solid #DDAA88; padding: 0 1em; margin-bottom: 1em">
<p>Standing updated</p>
<?php if ($count > 1) echo '<p><a href="/usersearch/?ip='.$user['ip'].'" data-target="push">(Consider updating standing for '.$count.' alts)</a></p>' ?>
</div>
<?php
} else if ($csrfOk && isset($_POST['moveladder'])) {
$newName = $_POST['moveladder'];
$newUserid = $users->userid($newName);
if (!$newUserid || $newUserid === $user['userid']) die("invalid username");
$psdb->query(
"UPDATE {$psdb->prefix}ladder SET userid = ? WHERE userid = ?",
['_'.$user['userid'], $newUserid]
);
$psdb->query(
"UPDATE {$psdb->prefix}ladder SET userid = ?, username = ?, elo = abs(elo) WHERE userid = ?",
[$newUserid, $newName, $user['userid']]
);
$psdb->query(
"UPDATE {$psdb->prefix}ladder SET userid = ?, username = ?, elo = abs(elo) WHERE userid = ?",
[$user['userid'], $user['username'], '_'.$user['userid']]
);
$modlogentry = "Ladder swapped with " . $user['userid'];
$psdb->query(
"INSERT INTO `{$psdb->prefix}usermodlog` (`userid`,`actorid`,`date`,`ip`,`entry`) VALUES (?, ?, ?, ?, ?)",
[$newUserid, $curuser['userid'], time(), $users->getIp(), $modlogentry]
);
$modlogentry = "Ladder swapped with " . $newUserid;
$psdb->query(
"INSERT INTO `{$psdb->prefix}usermodlog` (`userid`,`actorid`,`date`,`ip`,`entry`) VALUES (?, ?, ?, ?, ?)",
[$user['userid'], $curuser['userid'], time(), $users->getIp(), $modlogentry]
);
?>
<div style="border: 1px solid #DDAA88; padding: 0 1em; margin-bottom: 1em">
<p>Ladder record swapped</p>
</div>
<?php
} else if ($csrfOk && isset($_POST['googlelogin'])) {
$email = $_POST['googlelogin'];
$remove = ($email === 'remove');
$psdb->query(
"UPDATE {$psdb->prefix}users SET email = ? WHERE userid = ?",
[$remove ? '' : $email . '@', $user['userid']]
);
$modlogentry = $remove ? "Login method set to password" : "Login method set to Google " . $email;
$psdb->query(
"INSERT INTO `{$psdb->prefix}usermodlog` (`userid`,`actorid`,`date`,`ip`,`entry`) VALUES (?, ?, ?, ?, ?)",
[$user['userid'], $curuser['userid'], time(), $users->getIp(), $modlogentry]
);
?>
<div style="border: 1px solid #DDAA88; padding: 0 1em; margin-bottom: 1em">
<p>Login method updated</p>
</div>
<?php
} else if ($csrfOk && $authLevel >= 5 && @$_POST['passreset']) {
$token = $users->createPasswordResetToken($user['userid']);
?>
<div style="border: 1px solid #DDAA88; padding: 0 1em; margin-bottom: 1em">
<p>
Use this link:
</p>
<p style="margin: 1em -13px">
<small><code>https://<?= $psconfig['routes']['root'] ?>/resetpassword/<?php echo $token; ?></code></small>
</p>
</div>
<?php
}
?>
<button onclick="$('.usermanagement').show();$(this).hide();return false">User management</button>
<div style="border: 1px solid #DDAA88; padding: 0 1em; display:none" class="usermanagement">
<form action="" method="post" data-target="replace"><p>
<?php $users->csrfData(); ?>
<label class="label"><strong>Group:</strong><br />
<select name="group" class="textbox"<?php if ($authLevel < 4) echo ' disabled'; ?>>
<?php
foreach ($ntbb_groups as $i => $group) {
if (!$i) continue;
?>
<option value="<?php echo $i ?>"<?php if ($user['group'] == $i) echo ' selected="selected"'; ?>><?php echo $group['name']; ?></option>
<?php
}
?>
</select></label><?php if ($authLevel >= 4) { ?> <button type="submit"><strong>Change</strong></button><?php } ?>
</p></form>
<p>
<strong class="label"><label>IP: </label></strong><br />
<?php echo $user['ip'] ?> (<a href="/usersearch/?ip=<?php echo $user['ip'] ?>" data-target="push">Alts</a> | <a href="https://whatismyipaddress.com/ip/<?php echo $user['ip'] ?>" target="_blank">GeoIP</a>)
</p>
<?php
if ($user['outdatedpassword']) echo '<p>&#x2713; Abandoned account</p>';
?>
<form action="" method="post" data-target="replace"><p>
<?php $users->csrfData(); ?>
<strong class="label"><label>Standing: </label></strong><br />
<select name="standing" class="textbox"<?php if ($authLevel < 4) echo ' disabled'; ?>><?php
$userstanding = intval($user['banstate']);
// standings:
$userstandingshown = false;
foreach ($STANDINGS as $standing => $name) {
if (!$userstandingshown && (!$name || $userstanding < $standing)) {
echo '<option value="',$userstanding,'" selected>',$userstanding,'</option>';
$userstandingshown = true;
}
if ($name) {
echo '<option value="',$standing,'"',($userstanding==$standing?' selected':''),'>',$standing,' = ',$name,'</option>';
if ($userstanding == $standing) $userstandingshown = true;
}
}
?></select><?php if ($authLevel >= 4) { ?> <input name="reason" type="text" class="textbox" size="46" placeholder="Reason" style="display:none" /> <button type="submit"><strong>Change</strong></button><?php } ?>
</p></form>
<?php
if ($authLevel >= 4) {
?>
<form action="" method="post" data-target="replace"><p>
<?php $users->csrfData(); ?>
<strong>Swap ladder rating:</strong><br /> <input type="text" name="moveladder" placeholder="New username" value="" /> <button type="submit">Swap rating</button>
</p></form>
<form action="" method="post" data-target="replace"><p>
<?php $users->csrfData(); ?>
<strong>Login with Google account:</strong><br /> <input type="text" name="googlelogin" placeholder="Email" value="<?= substr($user['email'] ?? '', -1) === '@' ? substr($user['email'], 0, -1) : '' ?>" /> <button type="submit">Update login method</button> (<code>remove</code> to remove)
</p></form>
<?php
}
if ($authLevel >= 5) {
?>
<form action="" method="post" data-target="replace"><p>
<?php $users->csrfData(); ?>
<input type="hidden" name="passreset" value="1" />
<strong>Password:</strong><br /> <button type="submit">Create password reset link</button>
</p></form>
<?php
}
?>
</div>
<?php
} else if (!$user['group'] && ($curuser['group'] == 2 || $curuser['group'] == 6)) {
$csrfOk = false;
if ($users->csrfCheck()) {
$csrfOk = true;
}
if ($csrfOk && $_POST['standing'] ?? null) {
$ctime = time();
$newStanding = $_POST['standing'];
$psdb->query(
"INSERT INTO ntbb_users (`userid`,`username`,`passwordhash`,`email`,`registertime`,`ip`,`banstate`) VALUES (?,?,'','',?,'',?)",
[$user['userid'], $user['userid'], $ctime, $newStanding]
);
$modlogentry = "Created dummy user with standing $newStanding ({$STANDINGS[$newStanding]})";
$psdb->query(
"INSERT INTO `{$psdb->prefix}usermodlog` (`userid`,`actorid`,`date`,`ip`,`entry`) VALUES (?, ?, ?, ?, ?)",
[$user['userid'], $curuser['userid'], time(), $users->getIp(), $modlogentry]
);
$user['banstate'] = $_POST['standing'];
?>
<div style="border: 1px solid #DDAA88; padding: 0 1em; margin-bottom: 1em">
<p>Dummy user created; Standing updated</p>
</div>
<?php
}
?>
<button onclick="$('.usermanagement').show();$(this).hide();return false">User management</button>
<div style="border: 1px solid #DDAA88; padding: 0 1em; display:none" class="usermanagement">
<form action="" method="post" data-target="replace"><p>
<?php $users->csrfData(); ?>
<input type="hidden" name="standing" value="100" />
<button type="submit"><strong>Disable</strong></button>
</p></form>
</div>
<?php
}
// Group
if (@$user['banstate'] == 100 || @$user['outdatedpassword']) {
$user['ratings'] = [];
?>
<p>
<small>(Account disabled)<?php
if ($authLevel >= 2) echo ' <a href="/users/'.$userid.'/modlog" data-target="push">(Usermodlog)</a>';
?></small>
</p>
<?php
} else if (@$user['banstate'] == 20 || @$user['banstate'] == 30) {
?>
<p>
<small style="color: #CC2222">(Banned indefinitely)</small>
</p>
<p>
<small><em>Joined:</em> <?php echo date("M j, Y", $user['registertime']); ?><?php
if ($authLevel >= 2) echo ' <a href="/users/'.$userid.'/modlog" data-target="push">(Usermodlog)</a>';
?></small>
</p>
<?php
} else if ($user['group']) {
$groupName = $users->getGroupName($user);
$groupSymbol = $users->getGroupSymbol($user);
if ($groupSymbol === '~' || $groupSymbol === '&') {
?>
<p>
<strong><?php echo $groupSymbol,' ',$groupName; ?></strong>
</p>
<?php
}
?>
<p>
<small><em>Joined:</em> <?php echo date("M j, Y", $user['registertime']); ?><?php
if ($authLevel >= 2) echo ' <a href="/users/'.$userid.'/modlog" data-target="push">(Usermodlog)</a>';
?></small>
</p>
<?php
} else {
?>
<p>
<small>(Unregistered)</small>
</p>
<?php
}
if ($user['userid'] === 'slarty' || $user['userid'] === 'peterthegreeat' || $user['userid'] === 'chrisloud' || $user['userid'] === 'skitty') {
echo '<p>;_;7</p>';
}
// Ladder
if ($user['userid'] === $curuser['userid']) {
if ($users->csrfCheck() && @$_POST['resetLadder']) {
$formatLadder = new NTBBLadder(@$_POST['resetLadder']);
if (substr($formatLadder->formatid, -7) !== 'current' && substr($formatLadder->formatid, -11) !== 'suspecttest') {
$formatLadder->clearWL($curuser);
}
}
}
if ((@$user['banstate'] != 100 && @$user['banstate'] != 30) || $authLevel >= 4) {
$ladder = new NTBBLadder('');
$ladder->getAllRatings($user);
}
$bufs = ['official' => '', 'unofficial' => ''];
if (@$user['ratings']) foreach (@$user['ratings'] as $row) {
if ($row['w'] + $row['l'] + $row['t'] == 0 && $row['elo'] < 1050) continue;
$buftype = isset($formats[$row['formatid']])?'official':'unofficial';
$bufs[$buftype] .= '<tr><td>'.htmlspecialchars($row['formatid']).'</td><td style="text-align:center"><strong>'.round($row['elo']).'</strong></td>';
if ($row['rprd'] < 100) {
$bufs[$buftype] .= '<td style="text-align:center">'.number_format($row['gxe'],1).'<small>%</small></td><td style="text-align:center">'.'<em>'.round($row['rpr']).'<small> &#177; '.round($row['rprd']).'</small></em>';
} else {
$bufs[$buftype] .= '<td style="text-align:center" colspan="2"><small style="color:#777">(more games needed)</small>';
}
if ($user['userid'] === $curuser['userid']) {
$bufs[$buftype] .= '</td><td style="text-align:center"><small>' . $row['w'] . '</small></td><td style="text-align:center"><small>' . $row['l'] . '</small></td>';
if (substr($row['formatid'], -7) !== 'current' && substr($row['formatid'], -11) !== 'suspecttest') {
$bufs[$buftype] .= '<td><button name="openReset" value="'.htmlspecialchars($row['formatid']).'"><small>Reset</small></button></td>';
}
}
$bufs[$buftype] .= '</tr>';
}
if ($bufs['official'] || $bufs['unofficial']) {
?>
<h2>Ratings</h2>
<div><table>
<?php
if ($bufs['official']) {
?>
<tr>
<th width="150">Official ladder</th>
<th width="55" style="text-align:center"><abbr title="Elo rating">Elo</abbr></th>
<th width="50" style="text-align:center"><abbr title="user's percentage chance of winning a random battle (aka GLIXARE)">GXE</abbr></th>
<th width="80" style="text-align:center"><abbr title="Glicko-1 rating: rating&#177;deviation">Glicko-1</abbr></th>
<?php
if ($user['userid'] === $curuser['userid']) {
?>
<th width="20" style="text-align:center"><abbr title="Wins">W</abbr></th>
<th width="20" style="text-align:center"><abbr title="Losses">L</abbr></th>
<?php
}
?>
</tr>
<?php
echo $bufs['official'];
}
if ($bufs['unofficial']) {
?>
<tr>
<th width="150">Unofficial ladder</th>
<th width="55" style="text-align:center"><abbr title="Elo rating">Elo</abbr></th>
<th width="50" style="text-align:center"><abbr title="user's percentage chance of winning a random battle (aka GLIXARE)">GXE</abbr></th>
<th width="80" style="text-align:center"><abbr title="Glicko-1 rating: rating&#177;deviation">Glicko-1</abbr></th>
<?php
if ($user['userid'] === $curuser['userid']) {
?>
<th width="20" style="text-align:center"><abbr title="Wins">W</abbr></th>
<th width="20" style="text-align:center"><abbr title="Losses">L</abbr></th>
<?php
}
?>
</tr>
<?php
echo $bufs['unofficial'];
}
?>
<?php
if ($user['userid'] === $curuser['userid']) {
?>
<tr style="display:none" class="ladderresetform">
<td colspan="7">
<form action="" method="post" data-target="replace">
<?php $users->csrfData(); ?><input type="hidden" name="resetLadder" value="" />
<p style="margin: 5px 0"><span class="message"></span></p>
<p style="margin: 5px 0"><button type="submit"><strong>Reset W/L</strong></button> <button name="cancelReset">Cancel</button></p>
</form>
</td>
</tr>
<?php
}
?>
</table></div>
<?php
}
?>
</div></div>
<?php
}
$panels->end();
?>