Remove old action.php and replays

Our new API replacement for action.php is in
https://github.com/smogon/pokemon-showdown-loginserver

I haven't completely deleted the old-replays folder because there
are some things we haven't replaced that I don't know what to do
with... I guess that'll come in a future commit.
This commit is contained in:
Guangcong Luo 2023-11-14 07:12:56 +00:00
parent dbd4a9ec66
commit 21de8d92c6
13 changed files with 0 additions and 21336 deletions

View File

@ -1,39 +0,0 @@
<?php
/*
License: GPLv2 or later
<http://www.gnu.org/licenses/gpl-2.0.html>
*/
error_reporting(E_ALL);
include_once __DIR__ . '/config/config.inc.php';
if (@$_GET['act'] === 'dlteam') {
header("Content-Type: text/plain; charset=utf-8");
if (substr(@$_SERVER['HTTP_REFERER'], 0, 32) !== 'https://' . $psconfig['routes']['client']) {
// since this is only to support Chrome on HTTPS, we can get away with a very specific referer check
die("access denied");
}
echo base64_decode(@$_GET['team']);
die();
}
if (preg_match('/^http\\:\\/\\/[a-z0-9]+\\.psim\\.us\\//', $_SERVER['HTTP_REFERER'] ?? '')) {
header("Access-Control-Allow-Origin: *");
} else if ($_POST['sid'] ?? null) {
header("Access-Control-Allow-Origin: *");
}
// header("X-Debug: " . @$_SERVER['HTTP_REFERER']);
require_once __DIR__ . '/lib/ntbb-session.lib.php';
include_once __DIR__ . '/config/servers.inc.php';
include_once __DIR__ . '/lib/dispatcher.lib.php';
$dispatcher = new ActionDispatcher(array(
new DefaultActionHandler(),
new LadderActionHandler()
));
$dispatcher->executeActions();

View File

@ -1,555 +0,0 @@
<?php
class ActionDispatcher {
private $reqs;
private $multiReqs = false;
private $reqData;
/**
* API request output should not be valid JavaScript.
*
* This is to protect against a CSRF-like attack. Imagine you have an API:
*
* https://example.com/getmysecrets.json
*
* Which returns:
*
* {"yoursecrets": [1, 2, 3]}
*
* An attacker could trick a user into visiting a site overriding the
* Array or Object constructor, and then containing:
*
* <script src="https://example.com/getmysecrets.json"></script>
*
* This could let them steal the secrets. In modern times, browsers
* are protected against this kind of attack, but our `]` adds some
* safety for older browsers.
*
* Adding `]` to the beginning makes sure that the output is a syntax
* error in JS, so treating it as a JS file will simply crash and fail.
*/
private $outPrefix = ']';
private $outArray = array();
public function __construct($handlers) {
$this->handlers = $handlers;
if (empty($_REQUEST)) {
$this->reqs = null;
if (substr($_SERVER["CONTENT_TYPE"] ?? '', 0, 16) === 'application/json') {
// screw you too Axios
// also come on PHP, you could just support JSON natively instead of putting me through this
$input = trim(file_get_contents('php://input'));
if ($input[0] === '[') {
$this->reqs = json_decode($input, true);
} else if ($input[0] === '{') {
$this->reqs = [json_decode($input, true)];
}
}
if (empty($this->reqs)) die("no request data found - you need to send some sort of data");
$_POST['is_post_request'] = true;
} else {
$this->reqs = [$_REQUEST];
}
if (@$_REQUEST['json']) {
$this->reqs = json_decode($_REQUEST['json'], true);
$this->multiReqs = true;
}
}
public function setPrefix($prefix) {
$this->outPrefix = $prefix;
}
public function getServerHostName($serverid) {
global $PokemonServers;
$server = @$PokemonServers[$serverid];
return $server ? $server['server'] : $serverid;
}
public function verifyCrossDomainRequest() {
global $psconfig;
// No cross-domain multi-requests for security reasons.
// No need to do anything if this isn't a cross-domain request.
if ($this->multiReqs || !isset($_SERVER['HTTP_ORIGIN'])) {
return '';
}
$origin = $_SERVER['HTTP_ORIGIN'];
$prefix = null;
foreach ($psconfig['cors'] as $i => &$j) {
if (!preg_match($i, $origin)) continue;
$prefix = $j;
break;
}
if ($prefix === null) {
// Bogus request.
return '';
}
// Valid CORS request.
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Credentials: true');
return $prefix;
}
public function getIp() {
global $users;
return $users->getIp();
}
public function findServer() {
global $PokemonServers;
$serverid = @$this->reqData['serverid'];
$server = null;
$ip = $this->getIp();
if (!isset($PokemonServers[$serverid])) {
// Try to find the server by source IP, rather than by serverid.
if ($serverid === 'testtimeout') {
foreach ($PokemonServers as &$i) {
gethostbyname($i['server']);
}
}
return null;
} else {
$server = & $PokemonServers[$serverid];
if (empty($server['skipipcheck']) && empty($server['token']) && $serverid !== 'showdown') {
if (!isset($server['ipcache'])) {
$server['ipcache'] = gethostbyname($server['server']);
}
if ($ip !== $server['ipcache']) return null;
}
}
if (!empty($server['token'])) {
if ($server['token'] !== md5($this->reqData['servertoken'] ?? '')) return null;
}
return $server;
}
public function executeActions() {
$outArray = null;
if ($this->multiReqs) $outArray = array();
foreach ($this->reqs as $this->reqData) {
$this->reqData = array_merge($_REQUEST, $this->reqData);
if (!isset($this->reqData['act'])) die("action not found - make sure your request data includes act=something");
$action = $this->reqData['act'];
if (!ctype_alnum($action)) die("invalid action: " . var_export($action, true));
$out = array();
foreach ($this->handlers as &$i) {
if (is_callable(array($i, $action))) {
$i->$action($this, $this->reqData, $out);
}
}
if ($this->multiReqs) $outArray[] = $out;
}
// json output
if ($this->outPrefix !== '') {
// Technically this is not JSON because of the initial prefix.
header('Content-Type: text/plain; charset=utf-8');
} else {
header('Content-Type: application/json');
}
if ($this->multiReqs) {
die($this->outPrefix . json_encode($outArray));
} else {
die($this->outPrefix . json_encode($out));
}
}
}
class DefaultActionHandler {
public function login($dispatcher, &$reqData, &$out) {
global $users, $curuser;
$challengeprefix = $dispatcher->verifyCrossDomainRequest();
if (!$_POST) die('for security reasons, logins must happen with POST data');
if (empty($reqData['name']) || empty($reqData['pass'])) die('incorrect login data, you need "name" and "pass" fields');
try {
$users->login($reqData['name'], $reqData['pass']);
} catch (Exception $e) {
$out['error'] = $e->getMessage() . "\n" . $e->getFile() . '(' . $e->getLine() . ')' . "\n" . $e->getTraceAsString();
}
unset($curuser['userdata']);
$out['curuser'] = $curuser;
$out['actionsuccess'] = ($curuser ? $curuser['loggedin'] : false);
$serverhostname = '' . $dispatcher->getServerHostName(@$reqData['serverid']);
$challengekeyid = !isset($reqData['challengekeyid']) ? -1 : intval($reqData['challengekeyid']);
$challenge = !isset($reqData['challenge']) ? '' : $reqData['challenge'];
if (!$challenge) {
$challenge = !isset($reqData['challstr']) ? '' : $reqData['challstr'];
}
$out['assertion'] = $users->getAssertion($curuser['userid'], $serverhostname, null,
$challengekeyid, $challenge, $challengeprefix);
}
public function register($dispatcher, &$reqData, &$out) {
global $users, $curuser;
$challengeprefix = $dispatcher->verifyCrossDomainRequest();
$serverhostname = '' . $dispatcher->getServerHostName(@$reqData['serverid']);
$user = [
'username' => @$_POST['username'],
];
$userid = $users->userid($user['username']);
if ((mb_strlen($userid) < 1) || ctype_digit($userid)) {
$out['actionerror'] = 'Your username must contain at least one letter.';
} else if (substr($userid, 0, 5) === 'guest') {
$out['actionerror'] = 'Your username cannot start with \'guest\'.';
} else if (mb_strlen($user['username']) > 18) {
$out['actionerror'] = 'Your username must be less than 19 characters long.';
} else if (mb_strlen(@$_POST['password']) < 5) {
$out['actionerror'] = 'Your password must be at least 5 characters long.';
} else if (@$_POST['password'] !== @$_POST['cpassword']) {
$out['actionerror'] = 'Your passwords do not match.';
} else if (trim(strtolower(@$_POST['captcha'])) !== 'pikachu') {
$out['actionerror'] = 'Please answer the anti-spam question given.';
} else if (($registrationcount = $users->getRecentRegistrationCount()) === false) {
$out['actionerror'] = 'A database error occurred. Please try again.';
} else if ($registrationcount >= 2) {
$out['actionerror'] = 'You can\'t register more than two usernames every two hours. Try again later.';
} else if ($user = $users->addUser($user, $_POST['password'])) {
$challengekeyid = !isset($reqData['challengekeyid']) ? -1 : intval($reqData['challengekeyid']);
$challenge = !isset($reqData['challenge']) ? '' : $reqData['challenge'];
if (!$challenge) {
$challenge = !isset($reqData['challstr']) ? '' : $reqData['challstr'];
}
$out['curuser'] = $user;
$out['assertion'] = $users->getAssertion($user['userid'],
$serverhostname, $user, $challengekeyid, $challenge, $challengeprefix);
$out['actionsuccess'] = true;
} else {
$out['actionerror'] = 'Your username is already taken.';
}
}
public function changepassword($dispatcher, &$reqData, &$out) {
global $users, $curuser;
if (!$_POST ||
!isset($reqData['oldpassword']) ||
!isset($reqData['password']) ||
!isset($reqData['cpassword'])) {
$out['actionerror'] = 'Invalid request.';
} else if (!$curuser['loggedin']) {
$out['actionerror'] = 'Your session has expired. Please log in again.';
} else if ($reqData['password'] !== $reqData['cpassword']) {
$out['actionerror'] = 'Your new passwords do not match.';
} else if (!$users->passwordVerify($curuser['userid'], $reqData['oldpassword'])) {
$out['actionerror'] = 'Your old password was incorrect.';
} else if (mb_strlen($reqData['password']) < 5) {
$out['actionerror'] = 'Your new password must be at least 5 characters long.';
} else if (!$users->modifyUser($curuser['userid'], array(
'password' => $reqData['password']))) {
$out['actionerror'] = 'A database error occurred. Please try again.';
} else {
$out['actionsuccess'] = true;
}
}
public function changeusername($dispatcher, &$reqData, &$out) {
global $users, $curuser;
if (!$_POST ||
!isset($reqData['username'])) {
$out['actionerror'] = 'Invalid request.';
} else if (!$curuser['loggedin']) {
$out['actionerror'] = 'Your session has expired. Please log in again.';
} else if (!$users->modifyUser($curuser['userid'], array(
'username' => $reqData['username']))) {
$out['actionerror'] = 'A database error occurred. Please try again.';
} else {
$out['actionsuccess'] = true;
}
}
public function logout($dispatcher, &$reqData, &$out) {
global $users, $curuser, $psconfig;
if (!$_POST ||
!isset($reqData['userid']) ||
// some CSRF protection (client must know current userid)
($reqData['userid'] !== $curuser['userid'])) {
die;
}
$users->logout(); // this kills the `sid` cookie
setcookie('showdown_username', '', time()-60*60*24*2, '/', $psconfig['routes']['client']);
$out['actionsuccess'] = true;
}
public function getassertion($dispatcher, &$reqData, &$out) {
global $users, $curuser;
$challengeprefix = $dispatcher->verifyCrossDomainRequest();
$serverhostname = '' . $dispatcher->getServerHostName(@$reqData['serverid']);
$challengekeyid = !isset($reqData['challengekeyid']) ? -1 : intval($reqData['challengekeyid']);
$challenge = !isset($reqData['challenge']) ? '' : $reqData['challenge'];
if (!$challenge) {
$challenge = !isset($reqData['challstr']) ? '' : $reqData['challstr'];
}
header('Content-Type: text/plain; charset=utf-8');
if (empty($reqData['userid'])) {
$userid = $curuser['userid'];
if ($userid === 'guest') {
// Special error message for this case.
die(';');
}
} else {
$userid = $users->userid($reqData['userid']);
}
$serverhostname = htmlspecialchars($serverhostname); // Protect against theoretical IE6 XSS
die($users->getAssertion($userid, $serverhostname, null, $challengekeyid, $challenge, $challengeprefix));
}
public function upkeep($dispatcher, &$reqData, &$out) {
global $users, $curuser;
$challengeprefix = $dispatcher->verifyCrossDomainRequest();
$out['loggedin'] = $curuser['loggedin'];
$userid = '';
if ($curuser['loggedin']) {
$out['username'] = $curuser['username'];
$userid = $curuser['userid'];
} else if (isset($_COOKIE['showdown_username'])) {
$out['username'] = $_COOKIE['showdown_username'];
$userid = $users->userid($out['username']);
}
if ($userid !== '') {
$serverhostname = '' . $dispatcher->getServerHostName(@$reqData['serverid']);
$challengekeyid = !isset($reqData['challengekeyid']) ? -1 : intval($reqData['challengekeyid']);
$challenge = !isset($reqData['challenge']) ? '' : $reqData['challenge'];
if (!$challenge) {
$challenge = !isset($reqData['challstr']) ? '' : $reqData['challstr'];
}
$out['assertion'] = $users->getAssertion($userid, $serverhostname, null, $challengekeyid, $challenge, $challengeprefix);
}
}
public function updateuserstats($dispatcher, &$reqData, &$out) {
global $psdb;
$server = $dispatcher->findServer();
if (!$server) {
$out = 0;
return;
}
$date = @$reqData['date'];
$usercount = @$reqData['users'];
if (!is_numeric($date) || !is_numeric($usercount)) {
$out = 0;
return;
}
$out = !!$psdb->query(
"INSERT INTO `ntbb_userstats` (`serverid`, `date`, `usercount`) " .
"VALUES ('" . $psdb->escape($server['id']) . "', '" . $psdb->escape($date) . "', '" . $psdb->escape($usercount) . "') " .
"ON DUPLICATE KEY UPDATE `date`='" . $psdb->escape($date) . "', `usercount`='" . $psdb->escape($usercount) . "'");
if ($server['id'] === 'showdown') {
$psdb->query(
"INSERT INTO `ntbb_userstatshistory` (`date`, `usercount`) " .
"VALUES ('" . $psdb->escape($date) . "', '" . $psdb->escape($usercount) . "')");
}
$dispatcher->setPrefix(''); // No need for prefix since only usable by server.
}
public function updatenamecolor($dispatcher, &$reqData, &$out) {
global $psdb, $psconfig, $users;
$server = $dispatcher->findServer();
if (!isset($psconfig['mainserver']) || !$server || $server['id'] !== $psconfig['mainserver']) {
$out['actionerror'] = 'Access denied';
return;
}
if (!isset($reqData['userid']) || !mb_strlen($users->userid($reqData['userid']))) {
$out['actionerror'] = 'No userid was specified.';
return;
}
$userid = $users->userid($reqData['userid']);
if (!isset($reqData['source'])) {
$out['actionerror'] = 'No color adjustment was specified.';
return;
}
if (strlen($userid) > 18) {
$out['actionerror'] = 'Usernames can only be 18 characters long';
return;
}
if (!isset($reqData['by']) || !mb_strlen($users->userid($reqData['by']))) {
$out['actionerror'] = 'Specify the action\'s actor.';
return;
}
$colors = array();
$path = realpath(__DIR__ . '/../config/colors.json');
try {
$data = file_get_contents($path, true);
$colors = json_decode($data, true);
} catch (Exception $e) {}
$modlog_entry = '';
if (!$reqData['source']) {
if (!isset($colors[$userid])) {
$out['actionerror'] = (
'That user does not have a custom color set by the loginserver. ' .
'Ask an admin to remove it manually if they have one.'
);
return;
} else {
unset($colors[$userid]);
$modlog_entry = 'Username color was removed';
}
} else {
$colors[$userid] = $reqData['source'];
$modlog_entry = "Username color was set to \"{$reqData['source']}\"";
}
file_put_contents($path, json_encode($colors));
$psdb->query(
"INSERT INTO `{$psdb->prefix}usermodlog`".
"(`userid`, `actorid`, `date`, `ip`, `entry`) " .
"VALUES(?, ?, ?, ?, ?)",
[$userid, $users->userid($reqData['by']), time(), $dispatcher->getIp(), $modlog_entry]
);
$out['success'] = true;
return $out;
}
public function prepreplay($dispatcher, &$reqData, &$out) {
global $psdb, $users;
// include_once dirname(__FILE__) . '/ntbb-ladder.lib.php'; // not clear if this is needed
$server = $dispatcher->findServer();
if (!$server) {
$out['errorip'] = $dispatcher->getIp();
return;
}
if (
// the server must be registered
!$server ||
// the server must send all the required values
!isset($reqData['id']) ||
!isset($reqData['format']) ||
!isset($reqData['loghash']) ||
!isset($reqData['p1']) ||
!isset($reqData['p2']) ||
// player usernames cannot be longer than 18 characters
(mb_strlen($reqData['p1']) > 18) ||
(mb_strlen($reqData['p2']) > 18) ||
// the battle ID must be valid
!preg_match('/^([a-z0-9]+)-[0-9]+$/', $reqData['id'], $m1) ||
// the format ID must be valid
!preg_match('/^([a-z0-9]+)$/', $reqData['format'], $m2) ||
// the format from the battle ID must match the format ID
($m1[1] !== $m2[1])) {
$out = 0;
return;
}
if ($server['id'] !== 'showdown') {
$reqData['id'] = $server['id'].'-'.$reqData['id'];
}
$reqData['serverid'] = $server['id'];
include_once __DIR__.'/../replays/replays.lib.php';
$out = $GLOBALS['Replays']->prepUpload($reqData);
$dispatcher->setPrefix(''); // No need for prefix since only usable by server.
}
public function uploadreplay($dispatcher, &$reqData, &$out) {
global $psdb, $users;
header('Content-Type: text/plain; charset=utf-8');
include __DIR__.'/../replays/replays.lib.php';
die($GLOBALS['Replays']->upload($reqData));
}
public function invalidatecss($dispatcher, &$reqData, &$out) {
$server = $dispatcher->findServer();
if (!$server) {
$out['errorip'] = $dispatcher->getIp();
return;
}
// No need to sanitise $server['id'] because it should be safe already.
$cssfile = __DIR__ . '/../config/customcss/' . $server['id'] . '.css';
@unlink($cssfile);
}
}
// This class should not depend on ntbb-session.lib.php.
class LadderActionHandler {
// There's no need to make a database query for this.
private function getUserData($username) {
if (!$username) $username = '';
$userid = strtr($username, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz");
$userid = preg_replace('/[^A-Za-z0-9]+/', '', $userid);
if (mb_strlen($userid) > 18) return false;
return array('userid' => $userid, 'username' => $username);
}
public function ladderupdate($dispatcher, &$reqData, &$out) {
include_once dirname(__FILE__) . '/ntbb-ladder.lib.php';
$server = $dispatcher->findServer();
if (!$server || $server['id'] !== 'showdown') {
$out['errorip'] = "Your version of PS is too old for this ladder system. Please update.";
return;
}
$ladder = new NTBBLadder(@$reqData['format']);
$p1 = $this->getUserData(@$reqData['p1']);
$p2 = $this->getUserData(@$reqData['p2']);
if (!$p1 || !$p2) {
// The server should not send usernames > 18 characters long.
$out = 0;
return;
}
$ladder->updateRating($p1, $p2, floatval($reqData['score']));
$out['actionsuccess'] = true;
$out['p1rating'] = $p1['rating'];
$out['p2rating'] = $p2['rating'];
unset($out['p1rating']['rpdata']);
unset($out['p2rating']['rpdata']);
$dispatcher->setPrefix(''); // No need for prefix since only usable by server.
}
public function ladderget($dispatcher, &$reqData, &$out) {
global $PokemonServers;
include_once dirname(__FILE__) . '/ntbb-ladder.lib.php';
$server = @$PokemonServers[@$reqData['serverid']];
if (!$server || $server['id'] !== 'showdown') {
die;
}
$ladder = new NTBBLadder(@$reqData['format']);
$user = $this->getUserData(@$reqData['user']);
if (!$user) die;
$ladder->getAllRatings($user);
$out = $user['ratings'];
}
public function mmr($dispatcher, &$reqData, &$out) {
global $PokemonServers;
include_once dirname(__FILE__) . '/ntbb-ladder.lib.php';
$server = $dispatcher->findServer();
if (!$server || $server['id'] !== 'showdown') {
$out['errorip'] = "Your version of PS is too old for this ladder system. Please update.";
return;
}
$ladder = new NTBBLadder(@$reqData['format']);
$user = $this->getUserData(@$reqData['user']);
$out = 1000;
if ($user) {
$ladder->getRating($user);
if (@$user['rating']) {
$out = intval($user['rating']['elo']);
}
}
}
}

View File

@ -1,40 +0,0 @@
# -FrontPage-
IndexIgnore .htaccess */.??* *~ *# */HEADER* */README* */_vti*
<Limit GET POST>
order deny,allow
deny from all
allow from all
</Limit>
<Limit PUT DELETE>
order deny,allow
deny from all
</Limit>
AddType text/plain .phps
AddType application/x-tgz .tgz
AddType application/x-chrome-extension .crx
AddType application/x-web-app-manifest+json .webapp
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP:X-Forwarded-Proto} ^http$
RewriteRule ^(.*)$ https://replay.pokemonshowdown.com/$1 [R=302,NE,L]
RewriteRule ^replay\/(.*)$ /$1 [R=302,L]
RewriteRule ^battle-([A-Za-z0-9-]+)?$ /$1 [R=302,L]
RewriteRule ^search/?$ search.php [L,QSA]
RewriteRule ^search\.json$ search.json.php [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)$ battle.php?name=$1 [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)/manage$ battle.php?name=$1&manage [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)\.log$ battle.log.php?name=$1 [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)\.json$ battle.log.php?json&name=$1 [L,QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(apple-touch-icon-precomposed\.png)$ - [R=404,L]
</IfModule>

View File

@ -1,46 +0,0 @@
<?php
error_reporting(0);
header('HTTP/1.1 404 Not Found');
include_once 'theme/panels.lib.php';
$page = '404';
$pageTitle = "Replay not found";
$panels->setPageTitle("Replay not found");
$panels->noScripts();
$panels->start();
?>
<div class="pfx-panel"><div class="pfx-body pfx-body-padded" style="max-width:8in;">
<div class="main">
<h1>Not Found</h1>
<p>
The battle you're looking for has expired. Battles expire after 15 minutes of inactivity unless they're saved.
</p>
<p>
In the future, remember to click <strong>Upload and share replay</strong> to save a replay permanently.
</p>
<script type="text/javascript"><!--
google_ad_client = "ca-pub-6535472412829264";
/* PS replay */
google_ad_slot = "8206205605";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="//pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div></div>
<?php
$panels->end();
?>

View File

@ -1,43 +0,0 @@
<?php
error_reporting(0);
header('HTTP/1.1 503 Service Unavailable');
include_once 'theme/panels.lib.php';
$page = '503';
$pageTitle = "Service Unavailable";
$panels->setPageTitle("Replay not found");
$panels->noScripts();
$panels->start();
?>
<div class="pfx-panel"><div class="pfx-body pfx-body-padded" style="max-width:8in;">
<div class="main">
<h1>Technical difficulties</h1>
<p>
<?= @$Replays->offlineReason ? $Replays->offlineReason : "We're currently experiencing some technical difficulties. Please try again later. Sorry." ?>
</p>
<script type="text/javascript"><!--
google_ad_client = "ca-pub-6535472412829264";
/* PS replay */
google_ad_slot = "8206205605";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="//pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div></div>
<?php
$panels->end();
?>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

View File

@ -1,48 +0,0 @@
#!/usr/bin/env node
/**
* This script parses index.html and sets the version query string of each
* resource to be the MD5 hash of that resource.
*/
const fs = require('fs');
const crypto = require('crypto');
process.chdir(__dirname);
function updateIndex() {
let indexContents = fs.readFileSync('theme/wrapper.inc.template.php', {encoding: 'utf8'});
// add hashes to js and css files
process.stdout.write("Updating hashes... ");
// Check for <script, <link or <img so we don't add useless hashes to <a
indexContents = indexContents.replace(/(<script[^>]+?src|<link[^>]+?href|<img[^>]+?src)="\/(.*?)(\?[a-z0-9]*?)?"/g, runReplace);
console.log("DONE");
process.stdout.write("Writing new `wrapper.inc.php` file... ");
fs.writeFileSync('theme/wrapper.inc.php', indexContents);
console.log("DONE");
}
function runReplace(a, b, c) {
let hash = Math.random(); // just in case creating the hash fails
const routes = JSON.parse(fs.readFileSync('../config/routes.json'));
try {
var filepath = c;
if (c.includes('/' + routes.client + '/')) {
const filename = c.replace('/' + routes.client + '/', '');
filepath = '../' + filename;
}
const fstr = fs.readFileSync(filepath, {encoding: 'utf8'});
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
} catch (e) {}
c = c.replace('/replay.pokemonshowdown.com/', '/' + routes.replays + '/');
c = c.replace('/dex.pokemonshowdown.com/', '/' + routes.dex + '/');
c = c.replace('/play.pokemonshowdown.com/', '/' + routes.client + '/');
c = c.replace('/pokemonshowdown.com/users/', '/' + routes.users + '/');
c = c.replace('/pokemonshowdown.com/', '/' + routes.root + '/');
return b + '="/' + c + '?' + hash + '"';
}
updateIndex();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

View File

@ -1,246 +0,0 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
include 'theme/panels.lib.php';
// require_once '../../pokemonshowdown.com/lib/ntbb-database.lib.php';
require_once __DIR__ . '/../config/servers.inc.php';
$panels->setPageTitle('Replays');
$panels->setPageDescription('Watch replays of battles on Pokémon Showdown!');
$panels->setTab('replay');
$panels->start();
?>
<div class="pfx-panel"><div class="pfx-body pfx-body-padded">
<h1>Upload replays</h1>
<p>
To upload a replay, click "Share" or use the command <code>/savereplay</code> in a Pok&eacute;mon Showdown battle!
</p>
<h1>Search replays</h1>
<!--p>Username search temporarily down, will be back up soon</p-->
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="user" class="textbox" placeholder="Username" size="24" /></label>
<button type="submit"><strong>Search for user</strong></button>
</p>
</form>
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="format" class="textbox" placeholder="Format" size="24" /></label>
<button type="submit"><strong>Search by format</strong></button>
</p>
</form>
<h1>Featured replays</h1>
<ul class="linklist" style="max-width:480px;margin:0 auto;text-align:center">
<h3>Fun</h3>
<li><a href="/oumonotype-82345404" data-target="push">
<small>[oumonotype]<br /></small>
<strong>kdarewolf</strong> vs. <strong>Onox</strong>
<small><br />Protean + prediction</small>
</a></li>
<li><a href="/anythinggoes-218380995" data-target="push">
<small>[anythinggoes]<br /></small>
<strong>Anta2</strong> vs. <strong>dscottnew</strong>
<small><br />Cheek Pouch</small>
</a></li>
<li><a href="/uberssuspecttest-147833524" data-target="push">
<small>[ubers]<br /></small>
<strong>Metal Brellow</strong> vs. <strong>zig100</strong>
<small><br />Topsy-Turvy</small>
</a></li>
<li><button class="button" onclick="$('.older2').show();$(this).parent().hide()">Older</button></li>
<li class="older2" style="display:none"><a href="/smogondoubles-75588440" data-target="push">
<small>[smogondoubles]<br /></small>
<strong>jamace6</strong> vs. <strong>DubsWelder</strong>
<small><br />Garchomp sweeps 11 pokemon</small>
</a></li>
<li class="older2" style="display:none"><a href="/ou-20651579" data-target="push">
<small>[ou]<br /></small>
<strong>RainSeven07</strong> vs. <strong>my body is regi</strong>
<small><br />An entire team based on Assist V-create</small>
</a></li>
<li class="older2" style="display:none"><a href="/balancedhackmons7322360" data-target="push">
<small>[balancedhackmons]<br /></small>
<strong>a ver</strong> vs. <strong>Shuckie</strong>
<small><br />To a ver's frustration, PP stall is viable in Balanced Hackmons</small>
</a></li>
<h3>Competitive</h3>
<li><a href="/doublesou-232753081" data-target="push" style="white-space:normal">
<small>[doubles ou]<br /></small>
<strong>Electrolyte</strong> vs. <strong>finally</strong>
<small><br />finally steals Electrolyte's spot in the finals of the Doubles Winter Seasonal by outplaying Toxic Aegislash.</small>
</a></li>
<li><a href="/smogtours-gen5ou-59402" data-target="push" style="white-space:normal">
<small>[bw ou]<br /></small>
<strong>Reymedy</strong> vs. <strong>Leftiez</strong>
<small><br />Reymedy's superior grasp over BW OU lead to his claim of victory over Leftiez in the No Johns Tournament.</small>
</a></li>
<li><a href="/smogtours-gen3ou-56583" data-target="push" style="white-space:normal">
<small>[adv ou]<br /></small>
<strong>pokebasket</strong> vs. <strong>Alf'</strong>
<small><br />pokebasket proved Blissey isn't really one to take a Focus Punch well in his victory match over Alf' in the Fuck Trappers ADV OU tournament.</small>
</a></li>
<li><a href="/smogtours-ou-55891" data-target="push" style="white-space:normal">
<small>[oras ou]<br /></small>
<strong>Marshall.Law</strong> vs. <strong>Malekith</strong>
<small><br />In a "match full of reverses", Marshall.Law takes on Malekith in the finals of It's No Use.</small>
</a></li>
<li><a href="/smogtours-ubers-54583" data-target="push" style="white-space:normal">
<small>[custom]<br /></small>
<strong>hard</strong> vs. <strong>panamaxis</strong>
<small><br />Dark horse panamaxis proves his worth as the rightful winner of The Walkthrough Tournament in this exciting final versus hard.</small>
</a></li>
<li><button class="button" onclick="$('.older1').show();$(this).parent().hide()">Older</button></li>
<li class="older1" style="display:none"><a href="/smogtours-ubers-34646" data-target="push" style="white-space:normal">
<small>[oras ubers]<br /></small>
<strong>steelphoenix</strong> vs. <strong>Jibaku</strong>
<small><br />In this SPL Week 4 battle, Jibaku's clever plays with Mega Sableye keep the momentum mostly in his favor.</small>
</a></li>
<li class="older1" style="display:none"><a href="/smogtours-uu-36860" data-target="push" style="white-space:normal">
<small>[oras uu]<br /></small>
<strong>IronBullet93</strong> vs. <strong>Laurel</strong>
<small><br />Laurel outplays IronBullet's Substitute Tyrantrum with the sly use of a Shuca Berry Cobalion, but luck was inevitably the deciding factor in this SPL Week 6 match.</small>
</a></li>
<li class="older1" style="display:none"><a href="/smogtours-gen5ou-36900" data-target="push" style="white-space:normal">
<small>[bw ou]<br /></small>
<strong>Lowgock</strong> vs. <strong>Meridian</strong>
<small><br />This SPL Week 6 match features impressive plays, from Jirachi sacrificing itself to paralysis to avoid a burn to some clever late-game switches.</small>
</a></li>
<li class="older1" style="display:none"><a href="/smogtours-gen4ou-36782" data-target="push" style="white-space:normal">
<small>[dpp ou]<br /></small>
<strong>Heist</strong> vs. <strong>liberty32</strong>
<small><br />Starting out as an entry hazard-filled stallfest, this close match is eventually decided by liberty32's efficient use of Aerodactyl.</small>
</a></li>
<li class="older1" style="display:none"><a href="/randombattle-213274483" data-target="push" style="white-space:normal">
<small>[randombattle]<br /></small>
<strong>The Immortal</strong> vs. <strong>Amphinobite</strong>
<small><br />Substitute Lugia and Rotom-Fan take advantage of Slowking's utility and large HP stat, respectively, in this high ladder match.</small>
</a></li>
</ul>
<h1>Recent replays</h1>
<ul class="linklist" style="max-width:480px;margin:0 auto;text-align:center">
<?php
// $replays = [];
// echo "<p>";
// echo "Recent replays are currently unavailable due to database load. They'll be back very soon!";
// echo "</p>";
require_once 'replays.lib.php';
if (!$Replays->db) {
echo "<p>";
echo @$Replays->offlineReason ? $Replays->offlineReason : "Replays are currently offline due to technical difficulties. We'll be back up soon!";
echo "</p>";
} else {
$replays = $Replays->recent();
$time = time();
$timeoffset = 60;
foreach ($replays as $replay) {
$timetext = '';
while ($timeoffset >= 0 && $replay['uploadtime'] < $time - $timeoffset) {
switch ($timeoffset) {
case 60:
$timetext = '<h3>1 minute ago</h3>';
$timeoffset = 120;
break;
case 120:
$timetext = '<h3>2 minutes ago</h3>';
$timeoffset = 180;
break;
case 180:
$timetext = '<h3>3 minutes ago</h3>';
$timeoffset = 240;
break;
case 240:
$timetext = '<h3>4 minutes ago</h3>';
$timeoffset = 300;
break;
case 300:
$timetext = '<h3>5 minutes ago</h3>';
$timeoffset = 600;
break;
case 600:
$timetext = '<h3>10 minutes ago</h3>';
$timeoffset = 1200;
break;
case 1200:
$timetext = '<h3>20 minutes ago</h3>';
$timeoffset = 1800;
break;
case 1800:
$timetext = '<h3>30 minutes ago</h3>';
$timeoffset = 3600;
break;
case 3600:
$timetext = '<h3>1-2 hours ago</h3>';
$timeoffset = 2*3600;
break;
case 2*3600:
$timetext = '<h3>2-3 hours ago</h3>';
$timeoffset = 3*3600;
break;
case 3*3600:
$timetext = '<h3>3-4 hours ago</h3>';
$timeoffset = 4*3600;
break;
case 4*3600:
$timetext = '<h3>4-8 hours ago</h3>';
$timeoffset = 8*3600;
break;
case 8*3600:
$timetext = '<h3>8-12 hours ago</h3>';
$timeoffset = 12*3600;
break;
case 12*3600:
$timetext = '<h3>12-24 hours ago</h3>';
$timeoffset = 24*3600;
break;
case 24*3600:
$timetext = '<h3>1-2 days ago</h3>';
$timeoffset = 2*24*3600;
break;
case 2*24*3600:
$timetext = '<h3>2-3 days ago</h3>';
$timeoffset = 3*24*3600;
break;
case 3*24*3600:
$timetext = '<h3>3 days ago</h3>';
$timeoffset = 5*24*3600;
break;
default:
$timeoffset = -1;
break;
}
}
echo $timetext;
$server = '';
if (preg_match('/^([a-z0-9]+)-[a-z0-9]+-[0-9]+$/', $replay['id'], $matches)) {
$serverid = $matches[1];
if (!isset($PokemonServers[$serverid])) {
// This should be impossible.
$server = 'unknown server';
} else {
$server = strtolower($PokemonServers[$serverid]['name']);
}
}
?>
<li><a href="/<?php echo htmlspecialchars($replay['id']); ?>" data-target="push"><small>[<?php echo htmlspecialchars($replay['format']); ?><?php if ($server) echo ' - ' . htmlspecialchars($server) ?>]<br /></small> <strong><?php echo htmlspecialchars($replay['p1']); ?></strong> vs. <strong><?php echo htmlspecialchars($replay['p2']); ?></strong></a></li>
<?php
}
}
?>
</ul>
</div></div>
<?php
$panels->end();
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +0,0 @@
<?php
$username = @$_REQUEST['user'];
$username2 = @$_REQUEST['user2'];
$format = @$_REQUEST['format'];
$contains = (@$_REQUEST['contains']);
$byRating = isset($_REQUEST['rating']);
$isPrivate = isset($_REQUEST['private']);
header('Content-Type: application/json');
if (!$isPrivate) header('Access-Control-Allow-Origin: *');
require_once 'replays.lib.php';
include_once '../lib/ntbb-session.lib.php';
$username = $users->userid($username);
$isPrivateAllowed = ($username === $curuser['userid'] || $curuser['userid'] === 'zarel');
if ($isPrivate && !$isPrivateAllowed) {
die('"ERROR: access denied"');
}
$page = intval($_REQUEST['page'] ?? 0);
$replays = null;
if ($page > 25) {
die('"ERROR: page limit is 25"');
} else if ($username || $format) {
$replays = $Replays->search([
"username" => $username,
"username2" => $username2,
"format" => $format,
"byRating" => $byRating,
"isPrivate" => $isPrivate,
"page" => $page
]);
} else if ($contains) {
$replays = $Replays->fullSearch($contains, $page);
} else {
$replays = $Replays->recent();
}
if ($replays) {
foreach ($replays as &$replay) {
if ($replay['password'] ?? null) {
$replay['id'] .= '-' . $replay['password'];
}
unset($replay['password']);
}
}
echo json_encode($replays);

View File

@ -1,277 +0,0 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
include 'theme/panels.lib.php';
$username = @$_REQUEST['user'];
$format = ($username ? '' : @$_REQUEST['format']);
$contains = (@$_REQUEST['contains']);
$byRating = isset($_REQUEST['rating']);
$isPrivate = isset($_REQUEST['private']);
$noRequest = (!$username && !$format && !$contains);
if (!$noRequest) {
header('Cache-Control: max-age=0, no-cache, no-store, must-revalidate');
}
if ($username) {
$panels->setPageTitle($username."'s replays");
} else if ($format) {
$panels->setPageTitle($username." replays");
} else {
$panels->setPageTitle("Search replays");
}
$panels->setTab('replay');
$panels->start();
$page = 0;
if (@$_REQUEST['page']) $page = intval($_REQUEST['page']);
if (!$page) {
?>
<div class="pfx-panel"><div class="pfx-body pfx-body-padded">
<?php
if (!$contains) {
?>
<h1>Search replays</h1>
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="user" class="textbox" placeholder="Username" size="24"<?= !$format ? ' autofocus="autofocus"' : '' ?> value="<?php echo htmlspecialchars($username) ?>" /></label>
<button type="submit"><strong>Search</strong></button>
</p>
</form>
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="format" class="textbox" placeholder="Format" size="24"<?= $format ? ' autofocus="autofocus"' : '' ?> value="<?php echo htmlspecialchars($format) ?>" /></label>
<button type="submit"><strong>Search</strong></button>
</p>
</form>
<?php
}
if ($noRequest || $contains) {
?>
<h1>Experimental full-text search</h1>
<form action="/search/" method="get" data-target="replace">
<p style="text-align:center">
<label><input type="text" name="contains" class="textbox" placeholder="EXPERIMENTAL FULL TEXT SEARCH" size="24"<?= $format ? ' autofocus="autofocus"' : '' ?> value="<?php echo htmlspecialchars($contains) ?>" /></label>
<button type="submit"><strong>Search</strong></button>
</p>
</form>
<?php
}
}
if ($username || $format || $contains) {
require_once 'replays.lib.php';
include_once '../lib/ntbb-session.lib.php';
$username = $users->userid($username);
$isPrivateAllowed = ($username === $curuser['userid'] || $curuser['userid'] === 'zarel');
if (!$page) {
?>
<h1>Search results for "<?php echo htmlspecialchars($username ? $username : ($contains ? $contains : $format)); ?>"</h1>
<?php
if ($format) {
$format = $Replays->toID($format);
?>
<ul class="tabbar centered" style="margin-bottom: 18px"><li><a class="button nav-first<?= $byRating ? '' : ' cur' ?>" href="/search/?format=<?= $format ?>" data-target="replace">Newest</a></li><li><a class="button nav-last<?= $byRating ? ' cur' : '' ?>" href="/search/?format=<?= $format ?>&amp;rating" data-target="replace">Highest rated</a></li></ul>
<?php
} else if ($isPrivateAllowed) {
?>
<ul class="tabbar centered" style="margin-bottom: 18px"><li><a class="button nav-first<?= $isPrivate ? '' : ' cur' ?>" href="/search/?user=<?= $username ?>" data-target="replace">Public</a></li><li><a class="button nav-last<?= $isPrivate ? ' cur' : '' ?>" href="/search/?user=<?= $username ?>&amp;private" data-target="replace">Private</a></li></ul>
<?php
}
?>
<ul class="linklist" style="max-width:480px;margin:0 auto;text-align:center">
<?php
}
$replays = null;
if ($page > 25) {
// no
} else if ($username) {
if (!$isPrivate || $isPrivateAllowed) {
$replays = $Replays->search(["username" => $username, "isPrivate" => $isPrivate, "page" => $page]);
}
} else if ($contains) {
$replays = $Replays->fullSearch($contains, $page);
} else {
$replays = $Replays->search(["format" => $format, "byRating" => $byRating, "page" => $page]);
}
$time = time();
$timeoffset = 600;
$count = 0;
// $newoffset = 0;
if ($isPrivate && !$isPrivateAllowed) {
?>
<li>Error: You must be logged into account "<?= htmlspecialchars($username) ?>" to see their private replays (You're currently "<?= $curuser['userid'] ?>").</li>
<?php
} else if (!$replays || !count($replays)) {
if ($page > 25) {
?>
<li>Can't search any further back</li>
<?php
} else if (!$Replays->db) {
?>
<li><?= $Replays->offlineReason ?></li>
<?php
} else {
?>
<li>No results found</li>
<?php
}
} else foreach ($replays as $replay) {
if ($count == 50) {
$count++;
// $newoffset = $replay['num'];
break;
}
$timetext = '';
if (!$byRating) while ($timeoffset >= 0 && $replay['uploadtime'] < $time - $timeoffset) {
$prevtimeoffset = $timeoffset;
switch ($timeoffset) {
case 600:
$timetext = '<h3>10 minutes ago</h3>';
$timeoffset = 1200;
break;
case 1200:
$timetext = '<h3>20 minutes ago</h3>';
$timeoffset = 1800;
break;
case 1800:
$timetext = '<h3>30 minutes ago</h3>';
$timeoffset = 3600;
break;
case 3600:
$timetext = '<h3>1-2 hours ago</h3>';
$timeoffset = 2*3600;
break;
case 2*3600:
$timetext = '<h3>2-3 hours ago</h3>';
$timeoffset = 3*3600;
break;
case 3*3600:
$timetext = '<h3>3-4 hours ago</h3>';
$timeoffset = 4*3600;
break;
case 4*3600:
$timetext = '<h3>4-8 hours ago</h3>';
$timeoffset = 8*3600;
break;
case 8*3600:
$timetext = '<h3>8-12 hours ago</h3>';
$timeoffset = 12*3600;
break;
case 12*3600:
$timetext = '<h3>12-24 hours ago</h3>';
$timeoffset = 24*3600;
break;
case 24*3600:
$timetext = '<h3>1-2 days ago</h3>';
$timeoffset = 2*24*3600;
break;
case 2*24*3600:
$timetext = '<h3>2-3 days ago</h3>';
$timeoffset = 3*24*3600;
break;
case 3*24*3600:
$timetext = '<h3>3-7 days ago</h3>';
$timeoffset = 7*24*3600;
break;
case 7*24*3600:
$timetext = '<h3>1 week ago</h3>';
$timeoffset = 14*24*3600;
break;
case 14*24*3600:
$timetext = '<h3>2 weeks ago</h3>';
$timeoffset = 21*24*3600;
break;
case 21*24*3600:
$timetext = '<h3>3 weeks ago</h3>';
$timeoffset = 30*24*3600;
break;
case 30*24*3600:
$timetext = '<h3>1 month ago</h3>';
$timeoffset = 2*30*24*3600;
break;
case 2*30*24*3600:
$timetext = '<h3>2 months ago</h3>';
$timeoffset = 3*30*24*3600;
break;
case 3*30*24*3600:
$timetext = '<h3>3-6 months ago</h3>';
$timeoffset = 183*24*3600;
break;
case 183*24*3600:
$timetext = '<h3>6-9 months ago</h3>';
$timeoffset = 274*24*3600;
break;
case 274*24*3600:
$timetext = '<h3>9-12 months ago</h3>';
$timeoffset = 365*24*3600;
break;
case 365*24*3600:
$timetext = '<h3>more than 1 year ago</h3>';
$timeoffset = 2*365*24*3600;
break;
case 2*365*24*3600:
$timetext = '<h3>more than 2 year ago</h3>';
$timeoffset = 3*365*24*3600;
break;
case 3*365*24*3600:
$timetext = '<h3>more than 3 years ago</h3>';
$timeoffset = 4*365*24*3600;
break;
case 4*365*24*3600:
$timetext = '<h3>more than 4 years ago</h3>';
$timeoffset = 5*365*24*3600;
break;
case 5*365*24*3600:
$timetext = '<h3>more than 5 years ago</h3>';
$timeoffset = -1;
break;
}
if ($prevtimeoffset === $timeoffset) break;
}
echo $timetext;
$replayid = $replay['id'];
if ($replay['password'] ?? null) $replayid .= '-' . $replay['password'] . 'pw';
?>
<li><a href="/<?php echo $replayid; ?>" data-target="push"><small>[<?php echo $replay['format']; ?>]<?php if (@$replay['rating']) echo ' rating: '.$replay['rating']; ?><br /></small> <strong><?php echo htmlspecialchars($replay['p1']); ?></strong> vs. <strong><?php echo htmlspecialchars($replay['p2']); ?></strong></a></li>
<?php
$count++;
}
if (!$page) {
?>
</ul>
<?php
if ($count === 51) {
?>
<p style="text-align: center"><button class="button" name="moreResults" value="<?= /* $newoffset */ '' ?>" data-user="<?= htmlspecialchars($username) ?>" data-format="<?= htmlspecialchars($format) ?>" data-private="<?= $isPrivate ? '1' : '' ?>">More<br /><i class="fa fa-caret-down"></i></button></p>
<?php
}
} else {
// echo '<input type="hidden" name="offset" class="offset" value="' . $newoffset . '" />';
if ($count === 51) {
echo '<input type="hidden" name="more" class="more" value="1" />';
}
}
}
if (!$page) {
?>
</div></div>
<?php
}
$panels->end();
?>