mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -05:00
The website API now reports userid '0' as an unregistered user. Apparently PHP treats the string '0' as falsy. In the interests of not overhauling literally all our APIs, I've decided to only change the code in the website API. The rest doesn't need to be changed because number-only userids haven't been allowed for a very long time. Fixes https://github.com/smogon/pokemon-showdown/pull/7704
717 lines
22 KiB
PHP
717 lines
22 KiB
PHP
<?php
|
|
|
|
require_once __DIR__ . '/../config/config.inc.php';
|
|
require_once __DIR__ . '/ntbb-database.lib.php';
|
|
// require_once dirname(__FILE__) . '/password_compat/lib/password.php';
|
|
|
|
$curuser = false;
|
|
|
|
class NTBBSession {
|
|
var $trustedproxies = array(
|
|
'103.21.244.0/22',
|
|
'103.22.200.0/22',
|
|
'103.31.4.0/22',
|
|
'104.16.0.0/12',
|
|
'108.162.192.0/18',
|
|
'131.0.72.0/22',
|
|
'141.101.64.0/18',
|
|
'162.158.0.0/15',
|
|
'172.64.0.0/13',
|
|
'173.245.48.0/20',
|
|
'188.114.96.0/20',
|
|
'190.93.240.0/20',
|
|
'197.234.240.0/22',
|
|
'198.41.128.0/17',
|
|
'199.27.128.0/21',
|
|
);
|
|
|
|
var $sid = '';
|
|
var $session = 0;
|
|
var $cookiename = '';
|
|
var $scookie = '';
|
|
|
|
function __construct() {
|
|
global $psdb, $curuser;
|
|
$ctime = time();
|
|
$curuser = $this->getGuest();
|
|
|
|
// see if we're logged in
|
|
$scookie = $_POST['sid'] ?? $_COOKIE['sid'] ?? null;
|
|
if (!$scookie) {
|
|
// nope, not logged in
|
|
return;
|
|
}
|
|
$sid = '';
|
|
$session = 0;
|
|
$scsplit = explode(',', $scookie);
|
|
if (count($scsplit) === 3) {
|
|
$this->cookiename = $scsplit[0];
|
|
$session = intval($scsplit[1]);
|
|
$sid = $scsplit[2];
|
|
$this->sid = $sid;
|
|
}
|
|
if (!$session) {
|
|
return;
|
|
}
|
|
$res = $psdb->query(
|
|
"SELECT sid, timeout, `{$psdb->prefix}users`.* " .
|
|
"FROM `{$psdb->prefix}sessions`, `{$psdb->prefix}users` " .
|
|
"WHERE `session` = ? " .
|
|
"AND `{$psdb->prefix}sessions`.`userid` = `{$psdb->prefix}users`.`userid` " .
|
|
"LIMIT 1", [$session]);
|
|
if (!$res) {
|
|
// query problem?
|
|
$this->killCookie();
|
|
return;
|
|
}
|
|
$sess = $psdb->fetch_assoc($res);
|
|
if (!$sess || !password_verify($sid, $sess['sid'])) {
|
|
// invalid session ID
|
|
$this->killCookie();
|
|
return;
|
|
}
|
|
if (intval($sess['timeout'])<$ctime) {
|
|
// session expired
|
|
$psdb->query("DELETE FROM `{$psdb->prefix}sessions` WHERE `timeout` < ?", [$ctime]);
|
|
$this->killCookie();
|
|
return;
|
|
}
|
|
|
|
// okay, legit session ID - you're logged in now.
|
|
$curuser = $sess;
|
|
$curuser['loggedin'] = true;
|
|
// unset these values to avoid them being leaked accidentally
|
|
$curuser['outdatedpassword'] = !!$curuser['password'];
|
|
unset($curuser['password']);
|
|
unset($curuser['nonce']);
|
|
unset($curuser['passwordhash']);
|
|
|
|
$this->scookie = $scookie;
|
|
$this->session = $session;
|
|
}
|
|
|
|
// taken from http://stackoverflow.com/questions/594112/matching-an-ip-to-a-cidr-mask-in-php5
|
|
function cidr_match($ip, $range) {
|
|
list($subnet, $bits) = explode('/', $range);
|
|
$ip = ip2long($ip);
|
|
$subnet = ip2long($subnet);
|
|
$mask = -1 << (32 - $bits);
|
|
$subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
|
|
return ($ip & $mask) == $subnet;
|
|
}
|
|
|
|
function getIp() {
|
|
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
|
|
foreach ($this->trustedproxies as &$proxyip) {
|
|
if ($this->cidr_match($ip, $proxyip)) {
|
|
$parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '');
|
|
$ip = array_pop($parts);
|
|
break;
|
|
}
|
|
}
|
|
return $ip;
|
|
}
|
|
|
|
function userid($username) {
|
|
if (!$username) $username = '';
|
|
$username = strtr($username, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz");
|
|
return preg_replace('/[^A-Za-z0-9]+/','',$username);
|
|
}
|
|
function getGuest($username='') {
|
|
$userid = $this->userid($username);
|
|
if (!$userid)
|
|
{
|
|
$username = 'Guest';
|
|
$userid = 'guest';
|
|
}
|
|
return [
|
|
'username' => $username,
|
|
'userid' => $userid,
|
|
'group' => 0,
|
|
'loggedin' => false,
|
|
'ip' => $this->getIp(),
|
|
];
|
|
}
|
|
|
|
function qsid() { return $this->scookie ? '?sid='.$this->scookie : ''; }
|
|
function asid() { return $this->scookie ? '&sid='.$this->scookie : ''; }
|
|
function hasid() { return $this->scookie ? '&sid='.$this->scookie : ''; }
|
|
function fsid() { return $this->scookie ? '<input type="hidden" name="sid" id="sid" value="'.$this->scookie.'" />' : ''; }
|
|
|
|
/**
|
|
* New SID and password hashing functions.
|
|
*/
|
|
function mksid($osid) {
|
|
if (function_exists('psconfig_mksid')) {
|
|
return psconfig_mksid($osid);
|
|
}
|
|
return substr(base64_encode(random_bytes(18)), 0, 24);
|
|
}
|
|
function sidHash($sid) {
|
|
global $psconfig;
|
|
return password_hash($sid, PASSWORD_DEFAULT, array('cost' => $psconfig['sid_cost']));
|
|
}
|
|
function passwordNeedsRehash($hash) {
|
|
global $psconfig;
|
|
return password_needs_rehash($hash, PASSWORD_DEFAULT,
|
|
array('cost' => $psconfig['password_cost'])
|
|
);
|
|
}
|
|
function passwordHash($pass) {
|
|
global $psconfig;
|
|
return password_hash($pass, PASSWORD_DEFAULT,
|
|
array('cost' => $psconfig['password_cost'])
|
|
);
|
|
}
|
|
|
|
public function passwordVerify($name, $pass) {
|
|
global $psdb;
|
|
|
|
$userid = $this->userid($name);
|
|
|
|
$res = $psdb->query(
|
|
"SELECT `password`, `nonce`, `passwordhash` " .
|
|
"FROM `{$psdb->prefix}users` " .
|
|
"WHERE `userid` = ? " .
|
|
"LIMIT 1",
|
|
[$userid]
|
|
);
|
|
if (!$res) return false;
|
|
|
|
$user = $psdb->fetch_assoc($res);
|
|
return $this->passwordVerifyInner($userid, $pass, $user);
|
|
}
|
|
|
|
private function passwordVerifyInner($userid, $pass, $user) {
|
|
global $psdb, $psconfig;
|
|
|
|
// throttle
|
|
$ip = $this->getIp();
|
|
$res = $psdb->query(
|
|
"SELECT `count`, `time` FROM `{$psdb->prefix}loginthrottle` WHERE `ip` = ? LIMIT 1",
|
|
[$ip]
|
|
);
|
|
$loginthrottle = null;
|
|
if ($res) $loginthrottle = $psdb->fetch_assoc($res);
|
|
if ($loginthrottle) {
|
|
if ($loginthrottle['count'] > 500) {
|
|
$loginthrottle['count']++;
|
|
$psdb->query("UPDATE `{$psdb->prefix}loginthrottle` SET count = ?, lastuserid = ?, `time` = ? WHERE ip = ?", [$loginthrottle['count'], $userid, time(), $ip]);
|
|
return false;
|
|
} else if ($loginthrottle['time'] + 24 * 60 * 60 < time()) {
|
|
$loginthrottle = [
|
|
'count' => 0,
|
|
'time' => time(),
|
|
];
|
|
}
|
|
}
|
|
|
|
if (substr(@$user['email'], -1) === '@') {
|
|
// Forgive me, gods, for I have hardcoded way more than I really should have
|
|
$valResult = shell_exec("cd /var/www/html/play.pokemonshowdown.com && node lib/validate-token.js " . escapeshellarg($pass));
|
|
$payload = json_decode($valResult, true);
|
|
if (!$payload) return false;
|
|
if (strpos($payload['aud'], $psconfig['gapi_clientid']) === false) return false;
|
|
if ($payload['email'] === substr($user['email'], 0, -1)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
$rehash = false;
|
|
if ($user['passwordhash']) {
|
|
// new password hashes
|
|
if (!password_verify($pass, $user['passwordhash'])) {
|
|
// wrong password
|
|
if ($loginthrottle) {
|
|
$loginthrottle['count']++;
|
|
$psdb->query("UPDATE `{$psdb->prefix}loginthrottle` SET count = ?, lastuserid = ?, `time` = ? WHERE ip = ?", [$loginthrottle['count'], $userid, time(), $ip]);
|
|
} else {
|
|
$psdb->query("INSERT INTO `{$psdb->prefix}loginthrottle` (ip, count, lastuserid, `time`) VALUES (?, 1, ?, ?)", [$ip, $userid, time()]);
|
|
}
|
|
return false;
|
|
}
|
|
$rehash = $this->passwordNeedsRehash($user['passwordhash']);
|
|
} else if ($user['password'] && $user['nonce']) {
|
|
// original ntbb-session password hashes
|
|
return false;
|
|
} else {
|
|
// error
|
|
return false;
|
|
}
|
|
if ($rehash) {
|
|
// create a new password hash for the user
|
|
$hash = $this->passwordHash($pass);
|
|
if ($hash) {
|
|
$psdb->query(
|
|
"UPDATE `{$psdb->prefix}users` SET `passwordhash`=?, `password`=NULL, `nonce`=NULL WHERE `userid`=?",
|
|
[$hash, $userid]
|
|
);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function login($name, $pass, $timeout = false, $debug = false) {
|
|
global $psdb, $curuser, $psconfig;
|
|
$ctime = time();
|
|
|
|
$this->logout();
|
|
$userid = $this->userid($name);
|
|
$res = $psdb->query("SELECT * FROM `{$psdb->prefix}users` WHERE `userid` = ? LIMIT 1", [$userid]);
|
|
if (!$res) {
|
|
if ($debug) error_log('no such user');
|
|
return $curuser;
|
|
}
|
|
$user = $psdb->fetch_assoc($res);
|
|
if ($user['banstate'] >= 100) {
|
|
return $curuser;
|
|
}
|
|
if (!$this->passwordVerifyInner($userid, $pass, $user)) {
|
|
if ($debug) error_log('wrong password');
|
|
return $curuser;
|
|
}
|
|
if (!$timeout) {
|
|
// expire in two weeks
|
|
$timeout = 2*7*24*60*60;
|
|
}
|
|
$timeout += $ctime;
|
|
|
|
$this->sid = $this->mksid($this->sid);
|
|
$nsidhash = $this->sidHash($this->sid);
|
|
$res = $psdb->query(
|
|
"INSERT INTO `{$psdb->prefix}sessions` (`userid`,`sid`,`time`,`timeout`,`ip`) VALUES (?,?,?,?,?)",
|
|
[$user['userid'], $nsidhash, $ctime, $timeout, $this->getIp()]
|
|
);
|
|
if (!$res) die;
|
|
|
|
$this->session = $psdb->insert_id();
|
|
$this->scookie = $name . ',' . $this->session . ',' . $this->sid;
|
|
|
|
$curuser = $user;
|
|
$curuser['loggedin'] = true;
|
|
// unset these values to avoid them being leaked accidentally
|
|
$curuser['outdatedpassword'] = !!$curuser['password'];
|
|
unset($curuser['password']);
|
|
unset($curuser['nonce']);
|
|
unset($curuser['passwordhash']);
|
|
|
|
// setcookie('sid', $this->scookie, ['expires' => time() + (363)*24*60*60, 'path' => '/', 'domain' => $psconfig['routes']['root'], 'secure' => true, 'httponly' => true, 'samesite' => 'None']);
|
|
$encodedcookie = rawurlencode($this->scookie);
|
|
header("Set-Cookie: sid=$encodedcookie; Max-Age=31363200; Domain={$psconfig['routes']['root']}; Path=/; Secure; SameSite=None");
|
|
|
|
return $curuser;
|
|
}
|
|
|
|
function updateCookie() {
|
|
global $psconfig;
|
|
if (!$this->sid) {
|
|
$this->sid = $this->mksid($this->sid);
|
|
$this->scookie = ',,' . $this->sid;
|
|
// setcookie('sid', $this->scookie, ['expires' => time() + (363)*24*60*60, 'path' => '/', 'domain' => $psconfig['routes']['root'], 'secure' => true, 'httponly' => true, 'samesite' => 'None']);
|
|
$encodedcookie = rawurlencode($this->scookie);
|
|
header("Set-Cookie: sid=$encodedcookie; Max-Age=31363200; Domain={$psconfig['routes']['root']}; Path=/; Secure; SameSite=None");
|
|
}
|
|
}
|
|
function killCookie() {
|
|
global $psconfig;
|
|
if ($this->sid) {
|
|
$this->scookie = ',,' . $this->sid;
|
|
// setcookie('sid', $this->scookie, ['expires' => time() + (363)*24*60*60, 'path' => '/', 'domain' => $psconfig['routes']['root'], 'secure' => true, 'httponly' => true, 'samesite' => 'None']);
|
|
$encodedcookie = rawurlencode($this->scookie);
|
|
header("Set-Cookie: sid=$encodedcookie; Max-Age=31363200; Domain={$psconfig['routes']['root']}; Path=/; Secure; SameSite=None");
|
|
} else {
|
|
// setcookie('sid', '', ['expires' => time() - 60*60*24*2, 'path' => '/', 'domain' => $psconfig['routes']['root'], 'secure' => true, 'httponly' => true, 'samesite' => 'None']);
|
|
header("Set-Cookie: sid=; Max-Age=0; Domain={$psconfig['routes']['root']}; Path=/; Secure; SameSite=None");
|
|
}
|
|
}
|
|
|
|
function csrfData() {
|
|
echo '<input type="hidden" name="csrf" value="'.$this->scookie.'" />';
|
|
return '';
|
|
}
|
|
|
|
function csrfCheck() {
|
|
if (empty($_POST['csrf'])) return false;
|
|
$csrf = $_POST['csrf'];
|
|
if ($csrf === @$_COOKIE['sid']) return true;
|
|
return false;
|
|
}
|
|
|
|
function logout() {
|
|
global $psdb,$curuser;
|
|
|
|
if (!$this->session) return $curuser;
|
|
|
|
$curuser = $this->getGuest();
|
|
$psdb->query("DELETE FROM `{$psdb->prefix}sessions` WHERE `session` = ? LIMIT 1", [$this->session]);
|
|
$this->sid = '';
|
|
$this->scookie = '';
|
|
$this->session = 0;
|
|
|
|
$this->killCookie();
|
|
|
|
return $curuser;
|
|
}
|
|
|
|
function createPasswordResetToken($name, $timeout=false) {
|
|
global $psdb, $curuser;
|
|
$ctime = time();
|
|
|
|
$userid = $this->userid($name);
|
|
$res = $psdb->query("SELECT * FROM `{$psdb->prefix}users` WHERE `userid` = ? LIMIT 1", [$userid]);
|
|
if (!$res) // user doesn't exist
|
|
return false;
|
|
$user = $psdb->fetch_assoc($res);
|
|
if (!$timeout) {
|
|
$timeout = 7*24*60*60;
|
|
}
|
|
$timeout += $ctime;
|
|
{
|
|
$modlogentry = "Password reset token generated";
|
|
$psdb->query(
|
|
"INSERT INTO `{$psdb->prefix}usermodlog` (`userid`,`actorid`,`date`,`ip`,`entry`) VALUES (?, ?, ?, ?, ?)",
|
|
[$user['userid'], $curuser['userid'], time(), $this->getIp(), $modlogentry]
|
|
);
|
|
|
|
// magical character string...
|
|
$token = bin2hex(random_bytes(15));
|
|
$res = $psdb->query(
|
|
"INSERT INTO `{$psdb->prefix}sessions` (`userid`,`sid`,`time`,`timeout`,`ip`) VALUES (?, ?, ?, ?, ?)",
|
|
[$user['userid'], $token, $ctime, $timeout, $this->getIp()]
|
|
);
|
|
if (!$res) die($psdb->error());
|
|
}
|
|
|
|
return $token;
|
|
}
|
|
|
|
function validatePasswordResetToken($token) {
|
|
global $psdb, $psconfig;
|
|
if (strlen($token) !== ($psconfig['sid_length'] * 2)) return false;
|
|
$res = $psdb->query("SELECT * FROM `{$psdb->prefix}sessions` WHERE `sid` = ? LIMIT 1", [$token]);
|
|
$session = $psdb->fetch_assoc($res);
|
|
if (!$session) return false;
|
|
|
|
if (intval($session['timeout'])<time()) {
|
|
// session expired
|
|
return false;
|
|
}
|
|
|
|
return $session['userid'];
|
|
}
|
|
|
|
function getUser($userid=false) {
|
|
global $psdb, $curuser;
|
|
|
|
if ($userid === '0') return false;
|
|
if (is_array($userid)) $userid = $userid['userid'];
|
|
$userid = $this->userid($userid);
|
|
if (!$userid ||
|
|
(($userid === $curuser['userid']) && !empty($user['loggedin']))) {
|
|
return $curuser;
|
|
}
|
|
|
|
$res = $psdb->query("SELECT * FROM `{$psdb->prefix}users` WHERE `userid` = ? LIMIT 1", [$userid]);
|
|
if (!$res) // query failed for weird reason
|
|
return false;
|
|
$user = $psdb->fetch_assoc($res);
|
|
|
|
if (!$user)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// unset these values to avoid them being leaked accidentally
|
|
$user['outdatedpassword'] = !!$user['password'];
|
|
unset($user['password']);
|
|
unset($user['nonce']);
|
|
unset($user['passwordhash']);
|
|
|
|
return $user;
|
|
}
|
|
|
|
function getGroupName($user=false) {
|
|
global $ntbb_cache;
|
|
$user = $this->getUser($user);
|
|
return @$ntbb_cache['groups'][$user['group']]['name'];
|
|
}
|
|
|
|
function getGroupSymbol($user=false) {
|
|
global $ntbb_cache;
|
|
$user = $this->getUser($user);
|
|
return @$ntbb_cache['groups'][$user['group']]['symbol'];
|
|
}
|
|
|
|
function getUserData($username) {
|
|
$userdata = $this->getUser($username);
|
|
|
|
if ($userdata) return $userdata;
|
|
|
|
$userdata = $this->getGuest($username);
|
|
|
|
return $userdata;
|
|
}
|
|
|
|
function getAssertion($userid, $serverhostname, $user = null, $challengekeyid = -1, $challenge = '', $challengeprefix = '') {
|
|
global $psdb, $curuser, $psconfig;
|
|
|
|
if (substr($userid, 0, 5) === 'guest') {
|
|
return ';;Your username cannot start with \'guest\'.';
|
|
} else if (strlen($userid) > 18) {
|
|
return ';;Your username must be less than 19 characters long.';
|
|
} else if (!$this->isUseridAllowed($userid)) {
|
|
return ';;Your username contains disallowed text.';
|
|
}
|
|
|
|
$data = '';
|
|
if (!$user) {
|
|
$user = $curuser;
|
|
}
|
|
$ip = $this->getIp();
|
|
$forceUsertype = false;
|
|
if (in_array($ip, $psconfig['autolock_ip']) || in_array($user['ip'], $psconfig['autolock_ip'])) $forceUsertype = '5';
|
|
|
|
if (($user['userid'] === $userid) && !empty($user['loggedin'])) {
|
|
// already logged in
|
|
$usertype = '2';
|
|
if (in_array($userid, $psconfig['sysops'], true)) {
|
|
$usertype = '3';
|
|
} else {
|
|
// check autoconfirmed
|
|
if ($forceUsertype) {
|
|
$usertype = $forceUsertype;
|
|
} else if (intval(@$user['banstate']) <= -10) {
|
|
$usertype = '4';
|
|
} else if (@$user['banstate'] >= 100) {
|
|
return ';;Your username is no longer available.';
|
|
} else if (@$user['banstate'] >= 40) {
|
|
if ($serverhostname === 'sim3.psim.us') {
|
|
$usertype = '40';
|
|
} else {
|
|
$usertype = '2';
|
|
}
|
|
} else if (@$user['banstate'] >= 30) {
|
|
$usertype = '6';
|
|
} else if (@$user['banstate'] >= 20) {
|
|
$usertype = '5';
|
|
} else if (@$user['banstate'] == 0) {
|
|
if (@$user['registertime'] && time() - $user['registertime'] > 7*24*60*60) {
|
|
$res = $psdb->query("SELECT formatid FROM ntbb_ladder WHERE userid = ? AND w != 0 LIMIT 1", [$userid]);
|
|
if ($psdb->fetch_assoc($res)) {
|
|
$usertype = '4';
|
|
$psdb->query("UPDATE ntbb_users SET banstate = -10 WHERE userid = ? LIMIT 1", [$userid]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!@$user['logintime'] || time() - $user['logintime'] > 24*60*60) {
|
|
$psdb->query(
|
|
"UPDATE ntbb_users SET logintime = ?, loginip = ? WHERE userid = ? LIMIT 1",
|
|
[time(), $ip, $userid]
|
|
);
|
|
}
|
|
$data = $user['userid'].',' . $usertype . ','.time().','.$serverhostname;
|
|
} else {
|
|
if ((strlen($userid) < 1) || ctype_digit($userid)) {
|
|
return ';;Your username must contain at least one letter.';
|
|
}
|
|
$res = $psdb->query("SELECT * FROM `{$psdb->prefix}users` WHERE `userid` = ? LIMIT 1", [$userid]);
|
|
if (!$res) {
|
|
// query failed for weird reason
|
|
return ';;The login server is under heavy load, please try logging in again later';
|
|
} else if ($row = $psdb->fetch_assoc($res)) {
|
|
// Username exists, but the user isn't logged in: require authentication.
|
|
if ($row['banstate'] >= 100) {
|
|
return ';;Your username is no longer available.';
|
|
}
|
|
if ($row['password'] && $row['nonce']) {
|
|
return ';;Your username is no longer available.';
|
|
}
|
|
if (substr($row['email'], -1) === '@') return ';;@gmail';
|
|
return ';';
|
|
} else {
|
|
// Unregistered username.
|
|
$usertype = '1';
|
|
if ($forceUsertype) $usertype = $forceUsertype;
|
|
$data = $userid.','.$usertype.','.time().','.$serverhostname;
|
|
$this->updateCookie();
|
|
}
|
|
}
|
|
|
|
$splitChallenge = explode(';', $challenge);
|
|
if (count($splitChallenge) == 1) {
|
|
$splitChallenge = explode('|', $challenge);
|
|
}
|
|
if (count($splitChallenge) == 1) {
|
|
$splitChallenge = explode('%7C', $challenge);
|
|
}
|
|
$challengetoken = @$_REQUEST['challengetoken'];
|
|
if (count($splitChallenge) > 1) {
|
|
$challengekeyid = intval($splitChallenge[0]);
|
|
$challenge = $splitChallenge[1];
|
|
if (@$splitChallenge[2] && !$challengetoken) $challengetoken = $splitChallenge[2];
|
|
$_REQUEST['challengetoken'] = $challengetoken;
|
|
}
|
|
|
|
if ($challengekeyid < 1) {
|
|
return ';;This server is requesting an invalid login key. This probably means that either you are not connected to a server, or the server is set up incorrectly.';
|
|
} else if ($challengekeyid < 2) {
|
|
// Compromised keys - no longer supported.
|
|
return ';;This server is using login key ' . $challengekeyid . ', which is no longer supported. Please tell the server operator to update their config.js file.';
|
|
} else if (empty($psconfig['privatekeys'][$challengekeyid])) {
|
|
// Bogus key id.
|
|
return ';;Unknown key ID';
|
|
} else if (!preg_match('/^[0-9a-f]*$/', $challenge)) {
|
|
// Bogus challenge.
|
|
return ';;Corrupt challenge';
|
|
} else {
|
|
// Include the challenge in the assertion.
|
|
$data = $challengeprefix . $challenge . ',' . $data;
|
|
}
|
|
|
|
if (strpos($challengeprefix . $challenge, ',') !== false) {
|
|
trigger_error("challenge contains comma? ".$challengeprefix." // ".$challenge, E_USER_ERROR);
|
|
}
|
|
|
|
if (function_exists('psconfig_validate_assertion')) {
|
|
psconfig_validate_assertion($data, $serverhostname, $user);
|
|
}
|
|
|
|
$sig = '';
|
|
openssl_sign($data, $sig, openssl_get_privatekey($psconfig['privatekeys'][$challengekeyid]));
|
|
return $data.';'.bin2hex($sig);
|
|
}
|
|
|
|
function modifyUser($user, $changes) {
|
|
global $psdb, $curuser;
|
|
$userid = $user;
|
|
if (is_array($user)) $userid = $user['userid'];
|
|
|
|
$res = $psdb->query("SELECT * FROM `{$psdb->prefix}users` WHERE `userid` = ? LIMIT 1", [$userid]);
|
|
if (!$res) // query failed for weird reason
|
|
return false;
|
|
$user = $psdb->fetch_assoc($res);
|
|
if (!$user['userid']) return false;
|
|
|
|
if (@$changes['password']) {
|
|
$modlogentry = "Password changed from: {$user['passwordhash']}";
|
|
$psdb->query(
|
|
"INSERT INTO `{$psdb->prefix}usermodlog` (`userid`,`actorid`,`date`,`ip`,`entry`) VALUES (?,?,?,?,?)",
|
|
[$user['userid'], $curuser['userid'], time(), $this->getIp(), $modlogentry]
|
|
);
|
|
$passwordhash = $this->passwordHash($changes['password']);
|
|
$psdb->query(
|
|
"UPDATE `{$psdb->prefix}users` SET `passwordhash`=?, `password`=NULL, `nonce`=NULL WHERE `userid` = ?",
|
|
[$passwordhash, $user['userid']]
|
|
);
|
|
if ($psdb->error()) {
|
|
return false;
|
|
}
|
|
$psdb->query("DELETE FROM `{$psdb->prefix}sessions` WHERE `userid` = ?", [$user['userid']]);
|
|
if ($curuser['userid'] === $userid) {
|
|
$this->login($userid, $changes['password']);
|
|
}
|
|
}
|
|
if (!empty($changes['group'])) {
|
|
$group = intval($changes['group']);
|
|
$psdb->query("UPDATE `{$psdb->prefix}users` SET `group` = $group WHERE `userid` = ?", [$user['userid']]);
|
|
if ($psdb->error()) {
|
|
return false;
|
|
}
|
|
}
|
|
if (!empty($changes['username'])) {
|
|
$newUsername = $changes['username'];
|
|
if (strlen($newUsername) > 18) return false;
|
|
$newUserid = $this->userid($newUsername);
|
|
if ($userid !== $newUserid) return false;
|
|
$psdb->query(
|
|
"UPDATE `{$psdb->prefix}users` SET `username` = ? WHERE `userid` = ?",
|
|
[$newUsername, $userid]
|
|
);
|
|
if ($psdb->error()) {
|
|
return false;
|
|
}
|
|
}
|
|
if (!empty($changes['userdata'])) {
|
|
$psdb->query(
|
|
"UPDATE `{$psdb->prefix}users` SET `userdata` = ? WHERE `userid` = ?",
|
|
[$changes['userdata'], $user['userid']]
|
|
);
|
|
if ($psdb->error()) {
|
|
return false;
|
|
}
|
|
$user['userdata'] = $changes['userdata'];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function getRecentRegistrationCount($ip = '', $timeperiod = 7200 /* 2 hours */) {
|
|
global $psdb;
|
|
if ($ip === '') {
|
|
$ip = $this->getIp();
|
|
}
|
|
$timestamp = time() - $timeperiod;
|
|
$res = $psdb->query(
|
|
"SELECT COUNT(*) AS `registrationcount` FROM `{$psdb->prefix}users` WHERE `ip` = ? AND `registertime` > ?",
|
|
[$ip, $timestamp]
|
|
);
|
|
if (!$res) {
|
|
return false;
|
|
}
|
|
$user = $psdb->fetch_assoc($res);
|
|
if ($user === NULL) { // Should be impossible.
|
|
return 0;
|
|
}
|
|
return $user['registrationcount'];
|
|
}
|
|
|
|
function addUser($user, $password) {
|
|
global $psdb, $curuser;
|
|
$ctime = time();
|
|
|
|
$user['userid'] = $this->userid($user['username']);
|
|
$user['passwordhash'] = $this->passwordHash($password);
|
|
|
|
if (!$this->isUseridAllowed($user['userid'])) {
|
|
return false;
|
|
}
|
|
|
|
$res = $psdb->query("SELECT `userid` FROM `{$psdb->prefix}users` WHERE `userid` = ? LIMIT 1", [$user['userid']]);
|
|
if ($row = $psdb->fetch_assoc($res)) {
|
|
return false;
|
|
}
|
|
|
|
$psdb->query(
|
|
"INSERT INTO `{$psdb->prefix}users` (`userid`,`username`,`passwordhash`,`email`,`registertime`,`ip`) VALUES (?, ?, ?, ?, ?, ?)",
|
|
[$user['userid'], $user['username'], $user['passwordhash'], @$user['email'], $ctime, $this->getIp()]
|
|
);
|
|
if ($psdb->error()) {
|
|
return false;
|
|
}
|
|
|
|
$user['usernum'] = $psdb->insert_id();
|
|
$this->login($user['username'], $password);
|
|
if (!$curuser['loggedin'] || $curuser['userid'] !== $user['userid']) return false;
|
|
return $curuser;
|
|
}
|
|
|
|
function wordfilter($text) {
|
|
$text = str_ireplace('lolicon', '*', $text);
|
|
$text = str_ireplace('roricon', '*', $text);
|
|
return $text;
|
|
}
|
|
|
|
function isUseridAllowed($userid) {
|
|
if (strpos($userid, 'nigger') !== false) return false;
|
|
if (strpos($userid, 'nigga') !== false) return false;
|
|
if (strpos($userid, 'faggot') !== false) return false;
|
|
if (strpos($userid, 'lolicon') !== false) return false;
|
|
if (strpos($userid, 'roricon') !== false) return false;
|
|
if (strpos($userid, 'lazyafrican') !== false) return false;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
$users = new NTBBSession();
|