diff --git a/README.md b/README.md index 478fd00f4..6500136d6 100644 --- a/README.md +++ b/README.md @@ -42,19 +42,16 @@ Everything else can be tested, though. Warning ------------------------------------------------------------------------ -This repository is not "batteries included". It does NOT include everything -necessary to run a full Pokémon Showdown client. - -In particular, it doesn't include a login/authentication server, nor does it -include the database abstraction library used by the ladder library (although -it's similar enough to `mysqli` that you can use that with minimal changes). +This repository is not "batteries included". It does NOT include instructions +to run a full Pokémon Showdown client, and we will not provide them. Please +do not ask for help on this; you will be turned away. It also doesn't include several resource files (namely, the `/audio/` and `/sprites/` directories) for size reasons. In other words, this repository is incomplete and NOT intended for people who wish to serve their own Pokémon Showdown client (you can, but it'll -require you to rewrite some things). Rather, it's intended for people who +require you figure it out yourself). Rather, it's intended for people who wish to contribute and submit pull requests to Pokémon Showdown's client. License diff --git a/action.php b/action.php index fee0ab2a4..d47e543d0 100644 --- a/action.php +++ b/action.php @@ -24,7 +24,7 @@ if (preg_match('/^http\\:\\/\\/[a-z0-9]+\\.psim\\.us\\//', @$_SERVER['HTTP_REFER } // header("X-Debug: " . @$_SERVER['HTTP_REFERER']); -include_once '../pokemonshowdown.com/lib/ntbb-session.lib.php'; +include_once 'lib/ntbb-session.lib.php'; include_once '../pokemonshowdown.com/config/servers.inc.php'; include_once 'lib/dispatcher.lib.php'; diff --git a/config/config-example.inc.php b/config/config-example.inc.php new file mode 100644 index 000000000..ed291e0e3 --- /dev/null +++ b/config/config-example.inc.php @@ -0,0 +1,46 @@ + ['zarel'], + +// password and SID hashing settings + + 'password_cost' => 12, + 'sid_length' => 15, + 'sid_cost' => 4, + +// database + + 'server' => 'localhost', + 'username' => '', + 'password' => '', + 'database' => '', + 'prefix' => 'ps_', + 'charset' => 'utf8', + +// CORS requests + + 'cors' => [ + '/^http:\/\/smogon\.com$/' => 'smogon.com_', + '/^http:\/\/www\.smogon\.com$/' => 'www.smogon.com_', + '/^http:\/\/logs\.psim\.us$/' => 'logs.psim.us_', + '/^http:\/\/logs\.psim\.us:8080$/' => 'logs.psim.us_', + '/^http:\/\/[a-z0-9]+\.psim\.us$/' => '', + '/^http:\/\/play\.pokemonshowdown\.com$/' => '', + ], + +// key signing for SSO + + 'privatekeys' => [ + +1 => '-----BEGIN PRIVATE KEY----- +[insert RSA private key] +-----END PRIVATE KEY----- +', + + ] + +]; diff --git a/config/servers-example.inc.php b/config/servers-example.inc.php new file mode 100644 index 000000000..453b925ad --- /dev/null +++ b/config/servers-example.inc.php @@ -0,0 +1,11 @@ + + array ( + 'name' => 'Smogon University', + 'id' => 'showdown', + 'server' => 'sim.psim.us', + 'port' => 8000, + ), +); diff --git a/lib/ntbb-database.lib.php b/lib/ntbb-database.lib.php new file mode 100644 index 000000000..4a18a9e74 --- /dev/null +++ b/lib/ntbb-database.lib.php @@ -0,0 +1,65 @@ +server = $server; + $this->username = $username; + $this->password = $password; + $this->database = $database; + $this->prefix = $prefix; + $this->charset = $charset; + } + + function connect() { + if (!$this->db) { + $this->db = mysqli_connect($this->server, $this->username, $this->password, $this->database); + if ($this->charset) { + mysqli_set_charset($this->db, $this->charset); + } + } + } + function query($query) { + $this->connect(); + //$this->queries[] = $query; + return mysqli_query($this->db, $query); + } + function fetch_assoc($resource) { + return mysqli_fetch_assoc($resource); + } + function fetch($resource) { + return mysqli_fetch_assoc($resource); + } + function escape($data) { + $this->connect(); + return mysqli_real_escape_string($this->db, $data); + } + function error() { + if ($this->db) { + return mysqli_error($this->db); + } + } + function insert_id() { + if ($this->db) { + return mysqli_insert_id($this->db); + } + } +} + +$psdb = new NTBBDatabase($psconfig['server'], + $psconfig['username'], + $psconfig['password'], + $psconfig['database'], + $psconfig['prefix'], + $psconfig['charset']); diff --git a/lib/ntbb-ladder.lib.php b/lib/ntbb-ladder.lib.php old mode 100755 new mode 100644 index ab0da2b5c..f888b0c31 --- a/lib/ntbb-ladder.lib.php +++ b/lib/ntbb-ladder.lib.php @@ -4,7 +4,7 @@ error_reporting(E_ALL); // An implementation of the Glicko2 rating system. -@include_once dirname(__FILE__).'/../../pokemonshowdown.com/lib/ntbb-database.lib.php'; +@include_once dirname(__FILE__).'/ntbb-database.lib.php'; // connect to the ladder database (if we aren't already connected) if (empty($ladderdb)) { diff --git a/lib/ntbb-session.lib.php b/lib/ntbb-session.lib.php new file mode 100644 index 000000000..1da9cd2cf --- /dev/null +++ b/lib/ntbb-session.lib.php @@ -0,0 +1,640 @@ +getGuest(); + + // see if we're logged in + $osid = @$_COOKIE['sid']; + if (!$osid) { + // nope, not logged in + return; + } + $osidsplit = explode('x', $osid, 2); + if (count($osidsplit) !== 2) { + // malformed `sid` cookie + $this->killCookie(); + return; + } + $session = intval($osidsplit[0]); + $res = $psdb->query( + "SELECT sid, timeout, `{$psdb->prefix}users`.* " . + "FROM `{$psdb->prefix}sessions`, `{$psdb->prefix}users` " . + "WHERE `session` = $session " . + "AND `{$psdb->prefix}sessions`.`userid` = `{$psdb->prefix}users`.`userid` " . + "LIMIT 1"); + if (!$res) { + // query problem? + $this->killCookie(); + return; + } + $sess = $psdb->fetch_assoc($res); + if (!$sess || !password_verify(base64_decode($osidsplit[1]), $sess['sid'])) { + // invalid session ID + $this->killCookie(); + return; + } + if (intval($sess['timeout'])<$ctime) { + // session expired + // delete all sessions that will expire within 30 minutes + $ctime += 60 * 30; + $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->sid = $osid; + $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 array( + 'username' => $username, + 'userid' => $userid, + 'group' => 0, + 'loggedin' => false + ); + } + + function qsid() { return $this->sid ? '?sid='.$this->sid : ''; } + function asid() { return $this->sid ? '&sid='.$this->sid : ''; } + function hasid() { return $this->sid ? '&sid='.$this->sid : ''; } + function fsid() { return $this->sid ? '' : ''; } + + /** + * New SID and password hashing functions. + */ + function mksid() { + global $psconfig; + if (!function_exists('mcrypt_create_iv')) { + error_log('mcrypt_create_iv is not defined!'); + die; + } + return mcrypt_create_iv($psconfig['sid_length'], MCRYPT_DEV_URANDOM); + } + 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` = '".$psdb->escape($userid)."' " . + "LIMIT 1" + ); + if (!$res) return false; + + $user = $psdb->fetch_assoc($res); + return $this->passwordVerifyInner($userid, $pass, $user); + } + + private function passwordVerifyInner($userid, $pass, $user) { + global $psdb; + + // throttle + $ip = $this->getIp(); + $res = $psdb->query( + "SELECT `count`, `time` " . + "FROM `{$psdb->prefix}loginthrottle` " . + "WHERE `ip` = '".$ip."' " . + "LIMIT 1" + ); + $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 = {$loginthrottle['count']}, lastuserid = '".$userid."', `time` = '".time()."' WHERE ip = '".$ip."'"); + return false; + } else if ($loginthrottle['time'] + 24 * 60 * 60 < time()) { + $loginthrottle = [ + 'count' => 0, + 'time' => time(), + ]; + } + } + + $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 = {$loginthrottle['count']}, lastuserid = '".$userid."', `time` = '".time()."' WHERE ip = '".$ip."'"); + } else { + $psdb->query("INSERT INTO `{$psdb->prefix}loginthrottle` (ip, count, lastuserid, `time`) VALUES ('".$ip."', 1, '".$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`='" . $psdb->escape($hash) . "', `password`=NULL, `nonce`=NULL WHERE `userid`='" . $psdb->escape($userid) . "'"); + } + } + return true; + } + + function login($name, $pass, $timeout = false, $debug = false) { + global $psdb, $curuser; + $ctime = time(); + + $this->logout(); + $userid = $this->userid($name); + $res = $psdb->query("SELECT * FROM `{$psdb->prefix}users` WHERE `userid` = '".$psdb->escape($userid)."' LIMIT 1"); + 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 a week and 30 minutes + $timeout = (14*24*60+30)*60; + } + $timeout += $ctime; + + $nsid = $this->mksid(); + $nsidhash = $this->sidHash($nsid); + $res = $psdb->query("INSERT INTO `{$psdb->prefix}sessions` (`userid`,`sid`,`time`,`timeout`,`ip`) VALUES ('".$psdb->escape($user['userid'])."', '" . $psdb->escape($nsidhash) . "',$ctime,$timeout,'".$psdb->escape($this->getIp())."')"); + if (!$res) die; + + $this->session = $psdb->insert_id(); + $this->sid = $this->session . 'x' . base64_encode($nsid); + + $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->sid, $timeout, '/', $this->cookiedomain, false, true); + + return $curuser; + } + + function killCookie() { + setcookie('sid', '', time()-60*60*24*2, + '/', $this->cookiedomain, false, true); + } + + function csrfData() { + echo ''; + 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` = '{$this->session}' LIMIT 1"); + $this->sid = ''; + $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` = '".$psdb->escape($userid)."' LIMIT 1"); + 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 ('".$psdb->escape($user['userid'])."','".$psdb->escape($curuser['userid'])."','" . $psdb->escape(time()) . "','".$psdb->escape($this->getIp())."','".$psdb->escape($modlogentry)."')"); + + // magical character string... + $nsid = bin2hex($this->mksid()); + $res = $psdb->query("INSERT INTO `{$psdb->prefix}sessions` (`userid`,`sid`,`time`,`timeout`,`ip`) VALUES ('".$psdb->escape($user['userid'])."', '$nsid',$ctime,$timeout,'".$psdb->escape($this->getIp())."')"); + if (!$res) die($psdb->error()); + } + + return $nsid; + } + + 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` = '".$psdb->escape($token)."' LIMIT 1"); + $session = $psdb->fetch_assoc($res); + if (!$session) return false; + + if (intval($session['timeout'])userid($userid); + if (!$userid || + (($userid === $curuser['userid']) && !empty($user['loggedin']))) { + return $curuser; + } + + $res = $psdb->query("SELECT * FROM `{$psdb->prefix}users` WHERE `userid` = '".$psdb->escape($userid)."' LIMIT 1"); + 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 (($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 account is disabled.'; + } else if (@$user['banstate'] >= 40) { + if ($serverhostname === 'sim.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 = '".$psdb->escape($userid)."' LIMIT 1"); + if ($psdb->fetch_assoc($res)) { + $usertype = '4'; + $psdb->query("UPDATE ntbb_users SET banstate = -10 WHERE userid = '".$psdb->escape($userid)."' LIMIT 1"); + } + } + } + } + if (!@$user['logintime'] || time() - $user['logintime'] > 24*60*60) { + $psdb->query("UPDATE ntbb_users SET logintime = " . time() . ", loginip = '".$psdb->escape($ip)."' WHERE userid = '".$psdb->escape($userid)."' LIMIT 1"); + } + $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` = '".$psdb->escape($userid)."' LIMIT 1"); + 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 account is disabled.'; + } + if ($row['password'] && $row['nonce']) { + return ';;Your account is disabled.'; + } + return ';'; + } else { + // Unregistered username. + $usertype = '1'; + if ($forceUsertype) $usertype = $forceUsertype; + $data = $userid.','.$usertype.','.time().','.$serverhostname; + } + } + + $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]; + } + + 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); + } + + $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` = '".$psdb->escape($userid)."' LIMIT 1"); + 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 ('".$psdb->escape($user['userid'])."','".$psdb->escape($curuser['userid'])."','" . $psdb->escape(time()) . "','".$psdb->escape($this->getIp())."','".$psdb->escape($modlogentry)."')"); + $passwordhash = $this->passwordHash($changes['password']); + $psdb->query("UPDATE `{$psdb->prefix}users` SET `passwordhash`='" . $psdb->escape($passwordhash) . "', `password`=NULL, `nonce`=NULL WHERE `userid` = '".$psdb->escape($user['userid'])."'"); + if ($psdb->error()) { + return false; + } + $psdb->query("DELETE FROM `{$psdb->prefix}sessions` WHERE `userid` = '".$psdb->escape($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` = '".$psdb->escape($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` = '".$psdb->escape($newUsername)."' WHERE `userid` = '".$psdb->escape($userid)."'"); + if ($psdb->error()) { + return false; + } + } + if (!empty($changes['userdata'])) { + $psdb->query("UPDATE `{$psdb->prefix}users` SET `userdata` = '".$psdb->escape($changes['userdata'])."' WHERE `userid` = '".$psdb->escape($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` = '" . $psdb->escape($ip) . "' AND `registertime` > '" . $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; + } + + $psdb->query("INSERT INTO `{$psdb->prefix}users` (`userid`,`username`,`passwordhash`,`email`,`registertime`,`ip`) VALUES ('".$psdb->escape($user['userid'])."','".$psdb->escape($user['username'])."','" . $psdb->escape($user['passwordhash']) . "','".$psdb->escape(@$user['email'])."',$ctime,'".$psdb->escape($this->getIp())."')"); + if ($psdb->error()) + { + return false; + } + + $user['usernum'] = $psdb->insert_id(); + $user['loggedin'] = true; + $this->login($user['username'], $password); + return $user; + } + + 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(); + diff --git a/lib/ntbb-users.sql b/lib/ntbb-users.sql new file mode 100644 index 000000000..856e46dc3 --- /dev/null +++ b/lib/ntbb-users.sql @@ -0,0 +1,51 @@ +-- Database: showdown + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `ntbb_users` +-- + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE IF NOT EXISTS `ntbb_users` ( + `userid` varbinary(255) NOT NULL, + `usernum` int(11) NOT NULL AUTO_INCREMENT, + `username` varbinary(255) NOT NULL, + `password` varbinary(255) DEFAULT NULL, + `nonce` varbinary(255) DEFAULT NULL, + `passwordhash` varbinary(255) DEFAULT NULL, + `email` varbinary(255) DEFAULT NULL, + `registertime` bigint(20) NOT NULL, + `group` int(11) NOT NULL DEFAULT '1', + `banstate` int(11) NOT NULL DEFAULT '0', + `ip` varchar(255) NOT NULL DEFAULT '', + `avatar` int(11) NOT NULL DEFAULT '0', + `account` varbinary(255) DEFAULT NULL, + `logintime` bigint(20) NOT NULL DEFAULT '0', + `loginip` varbinary(255) DEFAULT NULL, + PRIMARY KEY (`userid`), + UNIQUE KEY `usernum` (`usernum`), + KEY `ip` (`ip`), + KEY `loginip` (`loginip`), + KEY `account` (`account`) +) ENGINE=InnoDB AUTO_INCREMENT=7379773 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; diff --git a/lib/ntbb_sessions.sql b/lib/ntbb_sessions.sql new file mode 100644 index 000000000..6ddf1061b --- /dev/null +++ b/lib/ntbb_sessions.sql @@ -0,0 +1,41 @@ +-- Database: showdown + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `ntbb_sessions` +-- + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE IF NOT EXISTS `ntbb_sessions` ( + `session` bigint(20) NOT NULL AUTO_INCREMENT, + `sid` varchar(255) NOT NULL, + `userid` varchar(255) NOT NULL, + `time` int(11) NOT NULL, + `timeout` int(11) NOT NULL, + `ip` varchar(255) NOT NULL, + PRIMARY KEY (`session`), + KEY `userid` (`userid`), + KEY `sid` (`sid`), + KEY `timeout` (`timeout`) +) ENGINE=InnoDB AUTO_INCREMENT=1700523 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;