Rewrote remote interaction thing.
This commit is contained in:
parent
c6026897ed
commit
f322be94b4
9 changed files with 223 additions and 320 deletions
|
@ -3,6 +3,7 @@ namespace Mince;
|
|||
|
||||
use Index\Autoloader;
|
||||
use Index\Environment;
|
||||
use Index\XString;
|
||||
use Index\Data\ConnectionFailedException;
|
||||
use Index\Data\DbTools;
|
||||
|
||||
|
@ -31,11 +32,11 @@ try {
|
|||
die($ex->getMessage());
|
||||
}
|
||||
|
||||
Remote::setUrl($config['remote_url']);
|
||||
Remote::setSecret($config['remote_secret']);
|
||||
$remote = new RemoteV2($config['remotev2_url'], $config['remotev2_secret']);
|
||||
|
||||
if(PHP_SAPI !== 'cli') {
|
||||
if(empty($_COOKIE['mc_random'])) {
|
||||
$sVerification = Utils::generatePassKey(32);
|
||||
$sVerification = XString::random(32);
|
||||
setcookie('mc_random', $sVerification, strtotime('1 day'), '/', $_SERVER['HTTP_HOST']);
|
||||
} else
|
||||
$sVerification = (string)filter_input(INPUT_COOKIE, 'mc_random');
|
||||
|
@ -44,3 +45,4 @@ $sVerification = hash('sha256', $sVerification);
|
|||
|
||||
// replace this with id.flashii.net shit
|
||||
$userInfo = ChatAuth::attempt($db, $config['chat_endpoint'], $config['chat_secret'], (string)filter_input(INPUT_COOKIE, 'msz_auth'));
|
||||
}
|
||||
|
|
|
@ -8,12 +8,8 @@ use Mince\HTML;
|
|||
|
||||
require_once __DIR__ . '/../mince.php';
|
||||
|
||||
$timing = new Timings;
|
||||
|
||||
$router = new HttpFx;
|
||||
|
||||
$timing->lap('httpfx', 'HttpFx Created');
|
||||
|
||||
$loginUrl = $config['login_url'];
|
||||
|
||||
$router->setDefaultErrorHandler(function($response, $request, $code, $text) use ($loginUrl, $userInfo) {
|
||||
|
@ -26,19 +22,15 @@ $router->setDefaultErrorHandler(function($response, $request, $code, $text) use
|
|||
$response->setContent($body);
|
||||
});
|
||||
|
||||
$router->use('/', function($response) use ($timing) {
|
||||
$response->setPoweredBy('Mince+Index');
|
||||
$response->setServerTiming($timing);
|
||||
$router->use('/', function($response) {
|
||||
$response->setPoweredBy('Mince');
|
||||
});
|
||||
|
||||
$router->get('/index.php', function($response) {
|
||||
$response->redirect('/', true);
|
||||
});
|
||||
$router->get('/map.php', function($response) {
|
||||
$response->redirect('/maps/survival', true);
|
||||
});
|
||||
|
||||
$router->get('/', function($response, $request) use ($db, $loginUrl, $userInfo, $sVerification) {
|
||||
$router->get('/', function($response, $request) use ($db, $remote, $loginUrl, $userInfo, $sVerification) {
|
||||
$name = (string)$request->getParam('name');
|
||||
$error = (string)$request->getParam('error');
|
||||
|
||||
|
@ -79,12 +71,12 @@ $router->get('/', function($response, $request) use ($db, $loginUrl, $userInfo,
|
|||
$body .= ' <h2>Add to Whitelist</h2>';
|
||||
$body .= ' <p>This will give you access to the server.</p>';
|
||||
$body .= ' <form method="post" action="/whitelist/add">';
|
||||
$body .= sprintf(' <input type="hidden" name="boob" value="%s"/>', $sVerification);
|
||||
$body .= sprintf(' <input type="hidden" name="boob" value="%s">', $sVerification);
|
||||
$body .= ' <label>';
|
||||
$body .= ' <div class="label-header">Username</div>';
|
||||
$body .= sprintf(' <div class="label-input"><input type="text" name="name" value="%s" /></div>', htmlspecialchars($name));
|
||||
$body .= sprintf(' <div class="label-input"><input type="text" name="name" value="%s"></div>', htmlspecialchars($name));
|
||||
$body .= ' </label>';
|
||||
$body .= ' <input type="submit" value="Add me to the Whitelist"/>';
|
||||
$body .= ' <input type="submit" value="Add me to the Whitelist">';
|
||||
$body .= ' </form>';
|
||||
$body .= '</div>';
|
||||
}
|
||||
|
@ -113,8 +105,8 @@ $router->get('/', function($response, $request) use ($db, $loginUrl, $userInfo,
|
|||
$body .= ' <p>This will revoke your access to the server.</p>';
|
||||
$body .= sprintf(' <p>You are currently whitelisted as <b>%s</b> on <b>%s</b>.</p>', $userInfo->mc_username, date('Y-m-d H:i:s T', $userInfo->mc_whitelisted));
|
||||
$body .= ' <form method="post" action="/whitelist/remove">';
|
||||
$body .= sprintf(' <input type="hidden" name="boob" value="%s"/>', $sVerification);
|
||||
$body .= ' <input type="submit" value="Remove me from the whitelist"/>';
|
||||
$body .= sprintf(' <input type="hidden" name="boob" value="%s">', $sVerification);
|
||||
$body .= ' <input type="submit" value="Remove me from the whitelist">';
|
||||
$body .= ' </form>';
|
||||
$body .= '</div>';
|
||||
}
|
||||
|
@ -146,21 +138,6 @@ $router->get('/', function($response, $request) use ($db, $loginUrl, $userInfo,
|
|||
return $body;
|
||||
});
|
||||
|
||||
$router->get('/maps', function($response) {
|
||||
$response->redirect('/maps/survival');
|
||||
});
|
||||
$router->get('/maps/survival', function($response) use ($loginUrl, $userInfo) {
|
||||
$body = HTML::getHeader($userInfo, $loginUrl);
|
||||
|
||||
$body .= '<div style="text-align: center">';
|
||||
$body .= ' <iframe src="//mc-survival-map.mikoto.best/" width="854" height="480" allow="fullscreen" allowfullscreen style="border-width: 0;"></iframe>';
|
||||
$body .= '</div>';
|
||||
|
||||
$body .= HTML::getFooter();
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
$router->use('/whitelist', function($response, $request) use ($sVerification) {
|
||||
if(!$request->isFormContent()) {
|
||||
$response->redirect('/?error=request');
|
||||
|
@ -183,7 +160,7 @@ $router->post('/whitelist/add', function($response, $request) use ($db, $userInf
|
|||
|
||||
$body = $request->getContent();
|
||||
$name = (string)$body->getParam('name');
|
||||
$resp = Whitelist::add($db, $userInfo, $name);
|
||||
$resp = (new Whitelist($db))->add($userInfo, $name);
|
||||
|
||||
if($resp === '')
|
||||
$response->redirect('/');
|
||||
|
@ -195,7 +172,7 @@ $router->post('/whitelist/add', function($response, $request) use ($db, $userInf
|
|||
});
|
||||
|
||||
$router->post('/whitelist/remove', function($response) use ($db, $userInfo) {
|
||||
$resp = Whitelist::remove($db, $userInfo);
|
||||
$resp = (new Whitelist($db))->remove($userInfo);
|
||||
|
||||
if($resp === '')
|
||||
$response->redirect('/');
|
||||
|
@ -203,35 +180,6 @@ $router->post('/whitelist/remove', function($response) use ($db, $userInfo) {
|
|||
$response->redirect("/?error={$resp}");
|
||||
});
|
||||
|
||||
$router->get('/status', function($response) {
|
||||
$response->redirect('/status/survival');
|
||||
});
|
||||
|
||||
$router->get('/status/survival', function() {
|
||||
return '<!doctype html>todo: make something here';
|
||||
});
|
||||
|
||||
$router->get('/status/survival.json', function() {
|
||||
return ServerQuery::create('mc.flashii.net')->stats();
|
||||
});
|
||||
|
||||
$router->get('/status/survival.png', function($response) {
|
||||
$stats = ServerQuery::create('mc.flashii.net')->stats();
|
||||
|
||||
$image = new \Imagick;
|
||||
$image->newImage(100, 100, 'black', 'png');
|
||||
|
||||
$draw = new \ImagickDraw;
|
||||
$draw->setFillColor('white');
|
||||
|
||||
$image->annotateImage($draw, 10, 10, 0, $stats->motd);
|
||||
|
||||
$response->setContentType('image/png');
|
||||
$response->setContent((string)$image);
|
||||
|
||||
$image->destroy();
|
||||
});
|
||||
|
||||
$router->get('/errors/:code', function($res, $req, $code) {
|
||||
$code = intval($code);
|
||||
if($code < 100 || $code >= 600)
|
||||
|
@ -239,6 +187,4 @@ $router->get('/errors/:code', function($res, $req, $code) {
|
|||
return $code;
|
||||
});
|
||||
|
||||
$timing->lap('routes');
|
||||
|
||||
$router->dispatch();
|
||||
|
|
|
@ -13,16 +13,16 @@ final class HTML {
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta charset="utf-8">
|
||||
<title>{$title}</title>
|
||||
<link href="/mince.css" type="text/css" rel="stylesheet"/>
|
||||
<link href="/mince.css" type="text/css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<nav class="header">
|
||||
<div class="header-inner">
|
||||
<div class="header-logo">
|
||||
<a href="/"><img src="/assets/weblogo.png" alt="Flashii Minecraft"/></a>
|
||||
<a href="/"><img src="/assets/weblogo.png" alt="Flashii Minecraft"></a>
|
||||
</div>
|
||||
<div class="header-fat"></div>
|
||||
<div class="header-user">
|
||||
|
@ -38,7 +38,7 @@ HTML;
|
|||
return <<<HTML
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<a href="https://flash.moe">Flashwave</a> 2022 | Site design "borrowed" from pre-Microsoft Mojang | "Minecraft" is a trademark of Mojang
|
||||
<a href="https://flash.moe">Flashwave</a> 2022-2023 | Site design "borrowed" from pre-Microsoft Mojang | "Minecraft" is a trademark of Mojang
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
namespace Mince;
|
||||
|
||||
final class Remote {
|
||||
private static string $url = '';
|
||||
private static string $secret = '';
|
||||
|
||||
public static function setUrl(string $url): void {
|
||||
self::$url = $url;
|
||||
}
|
||||
|
||||
public static function setSecret(string $secret): void {
|
||||
self::$secret = $secret;
|
||||
}
|
||||
|
||||
public static function call(string $mode, string $name): string {
|
||||
$time = (string)floor(time() / 120);
|
||||
$sign = hash_hmac('sha256', "{$time}#{$mode}#{$name}", self::$secret, true);
|
||||
|
||||
$request = curl_init(self::$url);
|
||||
curl_setopt_array($request, [
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => [
|
||||
'm' => $mode,
|
||||
's' => $sign,
|
||||
'n' => $name,
|
||||
],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TCP_FASTOPEN => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 2,
|
||||
CURLOPT_MAXREDIRS => 2,
|
||||
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_USERAGENT => 'mc.flashii.net',
|
||||
]);
|
||||
$response = curl_exec($request);
|
||||
curl_close($request);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
131
src/RemoteV2.php
Normal file
131
src/RemoteV2.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
namespace Mince;
|
||||
|
||||
use RuntimeException;
|
||||
use Index\Serialisation\Serialiser;
|
||||
|
||||
class RemoteV2 {
|
||||
public function __construct(
|
||||
private string $endPoint,
|
||||
private string $secretKey
|
||||
) {}
|
||||
|
||||
public function getInfo(): object {
|
||||
return $this->getRequest('GET', '/');
|
||||
}
|
||||
|
||||
public function getWhitelist(string $serverId): object {
|
||||
return $this->getRequest('GET', '/whitelist', ['server' => $serverId]);
|
||||
}
|
||||
|
||||
public function addToWhitelist(string $serverId, array $userNames): object {
|
||||
return $this->postRequest('POST', '/whitelist', [
|
||||
'server' => $serverId,
|
||||
'names' => json_encode($userNames),
|
||||
]);
|
||||
}
|
||||
|
||||
public function removeFromWhitelist(string $serverId, array $userNames): object {
|
||||
return $this->getRequest('DELETE', '/whitelist', [
|
||||
'server' => $serverId,
|
||||
'names' => json_encode($userNames),
|
||||
]);
|
||||
}
|
||||
|
||||
public function createSignature(string $method, string $path, array $params, int $time = -1): string {
|
||||
if($time < 0)
|
||||
$time = time();
|
||||
|
||||
ksort($params);
|
||||
$compare = [];
|
||||
|
||||
// other sides supports arrays, not gonna bother here
|
||||
foreach($params as $name => $value)
|
||||
$compare[] = "{$name}:{$value}";
|
||||
|
||||
$input = "{$time}%{$method} {$path}%" . implode('#', $compare);
|
||||
|
||||
return Serialiser::uriBase64()->serialise(
|
||||
hash_hmac('sha256', $input, $this->secretKey, true)
|
||||
);
|
||||
}
|
||||
|
||||
public function getRequest(string $method, string $path, array $params = []): mixed {
|
||||
$time = time();
|
||||
$sign = $this->createSignature($method, $path, $params, $time);
|
||||
|
||||
$url = $this->endPoint . $path;
|
||||
if(!empty($params))
|
||||
$url .= '?' . http_build_query($params);
|
||||
|
||||
$request = curl_init($url);
|
||||
curl_setopt_array($request, [
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TCP_FASTOPEN => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 2,
|
||||
CURLOPT_MAXREDIRS => 2,
|
||||
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_USERAGENT => 'mc.flashii.net',
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Mince-Signature: {$sign}",
|
||||
"X-Mince-Timestamp: {$time}",
|
||||
],
|
||||
]);
|
||||
$response = curl_exec($request);
|
||||
curl_close($request);
|
||||
|
||||
if(empty($response))
|
||||
throw new RuntimeException('Empty response.');
|
||||
|
||||
$response = json_decode($response);
|
||||
if(!empty($response->error))
|
||||
throw new RuntimeException($response->error);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function postRequest(string $method, string $path, array $params): mixed {
|
||||
$time = time();
|
||||
$sign = $this->createSignature($method, $path, $params, $time);
|
||||
$url = $this->endPoint . $path;
|
||||
|
||||
$request = curl_init($url);
|
||||
curl_setopt_array($request, [
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $params,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TCP_FASTOPEN => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 2,
|
||||
CURLOPT_MAXREDIRS => 2,
|
||||
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_USERAGENT => 'mc.flashii.net',
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Mince-Signature: {$sign}",
|
||||
"X-Mince-Timestamp: {$time}",
|
||||
],
|
||||
]);
|
||||
$response = curl_exec($request);
|
||||
curl_close($request);
|
||||
|
||||
if(empty($response))
|
||||
throw new RuntimeException('Empty response.');
|
||||
|
||||
$response = json_decode($response);
|
||||
if(!empty($response->error))
|
||||
throw new RuntimeException($response->error);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
<?php
|
||||
namespace Mince;
|
||||
|
||||
use stdClass;
|
||||
use RuntimeException;
|
||||
use Socket;
|
||||
use Index\AString;
|
||||
use Index\Net\EndPoint;
|
||||
use Index\Net\IPAddress;
|
||||
use Index\Net\IPEndPoint;
|
||||
use Index\Net\DnsEndPoint;
|
||||
|
||||
class ServerQuery { // rewrite this to use https://wiki.vg/Server_List_Ping query is kinda useless
|
||||
public const PORT = 25565;
|
||||
|
||||
private string $addr;
|
||||
private int $port;
|
||||
private Socket $socket;
|
||||
private int $sessionId;
|
||||
private int $challengeToken = 0;
|
||||
|
||||
public function __construct(IPAddress $addr, int $port) {
|
||||
$this->addr = (string)$addr;
|
||||
$this->port = $port;
|
||||
$this->sessionId = random_int(0, 0x7FFFFFFF) & 0x0F0F0F0F;
|
||||
$this->socket = socket_create($addr->isV6() ? AF_INET6 : AF_INET, SOCK_DGRAM, SOL_UDP);
|
||||
$this->handshake();
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
socket_close($this->socket);
|
||||
}
|
||||
|
||||
public function handshake(): void {
|
||||
$response = $this->send(9);
|
||||
$length = strlen($response);
|
||||
$token = '';
|
||||
|
||||
for($i = 0; $i < $length; ++$i) {
|
||||
$char = $response[$i];
|
||||
if($char === "\0")
|
||||
break;
|
||||
$token .= $char;
|
||||
}
|
||||
|
||||
$this->challengeToken = intval($token);
|
||||
}
|
||||
|
||||
public function stats(): object {
|
||||
$response = $this->send(0, pack('N', $this->challengeToken));
|
||||
|
||||
$offset = 0;
|
||||
$data = new stdClass;
|
||||
|
||||
$data->motd = self::readString($response, $offset);
|
||||
$data->gametype = self::readString($response, $offset);
|
||||
$data->map = self::readString($response, $offset);
|
||||
$data->numplayers = self::readString($response, $offset);
|
||||
$data->maxplayers = self::readString($response, $offset);
|
||||
$data->hostport = unpack('v', substr($response, $offset, 2))[1];
|
||||
$offset += 2;
|
||||
$data->hostip = self::readString($response, $offset);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private static function readString(string $source, int &$offset): string {
|
||||
$length = strlen($source);
|
||||
$string = '';
|
||||
|
||||
for(; $offset < $length; ++$offset) {
|
||||
$char = $source[$offset];
|
||||
if($char === "\0")
|
||||
break;
|
||||
$string .= $char;
|
||||
}
|
||||
|
||||
++$offset;
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
private function send(int $type, string $payload = ''): string {
|
||||
$payload = "\xFE\xFD" . pack('CN', $type, $this->sessionId) . $payload;
|
||||
socket_sendto($this->socket, $payload, strlen($payload), 0, $this->addr, $this->port);
|
||||
socket_recv($this->socket, $response, 1024, MSG_WAITALL);
|
||||
|
||||
$data = unpack('Ctype/Nsession', $response);
|
||||
|
||||
if($data['type'] !== $type)
|
||||
throw new RuntimeException('Type does not match.');
|
||||
if($data['session'] !== $this->sessionId)
|
||||
throw new RuntimeException('Session id does not match.');
|
||||
|
||||
return substr($response, 5);
|
||||
}
|
||||
|
||||
public static function create(AString|string $endPoint): ServerQuery {
|
||||
$endPoint = AString::cast($endPoint);
|
||||
$firstChar = $endPoint[0];
|
||||
|
||||
if($firstChar === '[') { // IPv6
|
||||
if($endPoint->contains(']:'))
|
||||
$endPoint = IPEndPoint::parse($endPoint);
|
||||
else
|
||||
$endPoint = new IPEndPoint(IPAddress::parse($endPoint->trim('[]')), self::PORT);
|
||||
|
||||
return new ServerQuery($endPoint->getAddress(), $endPoint->getPort());
|
||||
} elseif(is_numeric($firstChar)) { // IPv4
|
||||
if($endPoint->contains(':'))
|
||||
$endPoint = IPEndPoint::parse($endPoint);
|
||||
else
|
||||
$endPoint = new IPEndPoint(IPAddress::parse($endPoint), self::PORT);
|
||||
|
||||
return new ServerQuery($endPoint->getAddress(), $endPoint->getPort());
|
||||
} else { // DNS
|
||||
if($endPoint->contains(':'))
|
||||
$endPoint = DnsEndPoint::parse($endPoint);
|
||||
else {
|
||||
$endPoint = new DnsEndPoint($endPoint, self::PORT);
|
||||
|
||||
$records = dns_get_record('_minecraft._tcp.' . (string)$endPoint->getHost(), DNS_SRV);
|
||||
|
||||
if(!empty($records)) {
|
||||
usort($records, function($a, $b) {
|
||||
$priority = $a['pri'] <=> $b['pri'];
|
||||
if($priority !== 0)
|
||||
return $priority;
|
||||
$weight = $a['weight'] <=> $b['weight'];
|
||||
if($weight !== 0)
|
||||
return $priority;
|
||||
return 0;
|
||||
});
|
||||
|
||||
foreach($records as $record) {
|
||||
try {
|
||||
return ServerQuery::create($record['target'] . ':' . $record['port']);
|
||||
} catch(Exception $ex) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$records = dns_get_record((string)$endPoint->getHost(), DNS_A);
|
||||
|
||||
if(!empty($records)) {
|
||||
foreach($records as $record) {
|
||||
try {
|
||||
return new ServerQuery(IPAddress::parse($record['ip']), $endPoint->getPort());
|
||||
} catch(Exception $ex) {}
|
||||
}
|
||||
}
|
||||
|
||||
$records = dns_get_record((string)$endPoint->getHost(), DNS_AAAA);
|
||||
|
||||
if(!empty($records)) {
|
||||
foreach($records as $record) {
|
||||
try {
|
||||
return new ServerQuery(IPAddress::parse($record['ipv6']), $endPoint->getPort());
|
||||
} catch(Exception $ex) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException('Failed to connect.');
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
namespace Mince;
|
||||
|
||||
final class Utils {
|
||||
private const CHARS = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789';
|
||||
|
||||
public static function generatePassKey(int $length): string {
|
||||
$keyChars = strlen(self::CHARS) - 1;
|
||||
$bytes = str_repeat("\0", $length);
|
||||
for($i = 0; $i < $length; ++$i)
|
||||
$bytes[$i] = self::CHARS[random_int(0, $keyChars)];
|
||||
return $bytes;
|
||||
}
|
||||
}
|
|
@ -4,8 +4,22 @@ namespace Mince;
|
|||
use Index\Data\IDbConnection;
|
||||
use Index\Data\DbType;
|
||||
|
||||
final class Whitelist {
|
||||
public static function add(IDbConnection $db, object $userInfo, string $userName): string {
|
||||
class Whitelist {
|
||||
public function __construct(
|
||||
private IDbConnection $dbConn
|
||||
) {}
|
||||
|
||||
public function getNames(): array {
|
||||
$names = [];
|
||||
|
||||
$getNames = $this->dbConn->query('SELECT minecraft_username FROM whitelist_2022');
|
||||
while($getNames->next())
|
||||
$names[] = $getNames->getString(0);
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
public function add(object $userInfo, string $userName): string {
|
||||
$length = strlen($userName);
|
||||
if($length < 3)
|
||||
return 'short';
|
||||
|
@ -14,7 +28,7 @@ final class Whitelist {
|
|||
if(!preg_match('#^([a-zA-Z0-9_]{3,16})$#', $userName))
|
||||
return 'invalid';
|
||||
|
||||
$dupeCheck = $db->prepare('SELECT COUNT(`flashii_id`) > 0 FROM `whitelist_2022` WHERE `minecraft_username` = ?');
|
||||
$dupeCheck = $this->dbConn->prepare('SELECT COUNT(`flashii_id`) > 0 FROM `whitelist_2022` WHERE `minecraft_username` = ?');
|
||||
$dupeCheck->addParameter(1, $userName, DbType::STRING);
|
||||
$dupeCheck->execute();
|
||||
$dupeResult = $dupeCheck->getResult();
|
||||
|
@ -25,17 +39,12 @@ final class Whitelist {
|
|||
return 'conflict';
|
||||
|
||||
if(!empty($userInfo->mc_whitelisted) || !empty($userInfo->mc_username)) {
|
||||
$resp = self::remove($db, $userInfo);
|
||||
$resp = $this->remove($userInfo);
|
||||
if($resp !== '')
|
||||
return $resp;
|
||||
}
|
||||
|
||||
$resp = Remote::call('wl:add', $userName);
|
||||
|
||||
if($resp !== 'success')
|
||||
return $resp;
|
||||
|
||||
$insert = $db->prepare('INSERT INTO `whitelist_2022` (`flashii_id`, `minecraft_username`) VALUES (?, ?)');
|
||||
$insert = $this->dbConn->prepare('INSERT INTO `whitelist_2022` (`flashii_id`, `minecraft_username`) VALUES (?, ?)');
|
||||
$insert->addParameter(1, $userInfo->user_id);
|
||||
$insert->addParameter(2, $userName, DbType::STRING);
|
||||
$insert->execute();
|
||||
|
@ -46,16 +55,11 @@ final class Whitelist {
|
|||
return '';
|
||||
}
|
||||
|
||||
public static function remove(IDbConnection $db, object $userInfo): string {
|
||||
public function remove(object $userInfo): string {
|
||||
if(empty($userInfo->mc_whitelisted) || empty($userInfo->mc_username))
|
||||
return 'not-listed';
|
||||
|
||||
$resp = Remote::call('wl:remove', $userInfo->mc_username);
|
||||
|
||||
if($resp !== 'success')
|
||||
return $resp;
|
||||
|
||||
$delete = $db->prepare('DELETE FROM `whitelist_2022` WHERE `flashii_id` = ?');
|
||||
$delete = $this->dbConn->prepare('DELETE FROM `whitelist_2022` WHERE `flashii_id` = ?');
|
||||
$delete->addParameter(1, $userInfo->user_id);
|
||||
$delete->execute();
|
||||
|
||||
|
|
45
tools/sync
Executable file
45
tools/sync
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
use Mince\Whitelist;
|
||||
|
||||
require_once __DIR__ . '/../mince.php';
|
||||
|
||||
echo 'Syncing server whitelists...' . PHP_EOL;
|
||||
|
||||
$rInfo = $remote->getInfo();
|
||||
if(empty($rInfo->servers)) {
|
||||
echo 'There are no active servers.' . PHP_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
echo 'Fetching master list from database...' . PHP_EOL;
|
||||
$myNames = (new Whitelist($db))->getNames();
|
||||
|
||||
foreach($rInfo->servers as $serverId) {
|
||||
try {
|
||||
echo "[{$serverId}] Fetching list on server..." . PHP_EOL;
|
||||
$rWhitelist = $remote->getWhitelist($serverId);
|
||||
$rNames = $rWhitelist->list ?? [];
|
||||
|
||||
echo "[{$serverId}] Filtering..." . PHP_EOL;
|
||||
$addNames = array_values(array_udiff($myNames, $rNames, 'strcasecmp'));
|
||||
$removeNames = array_values(array_udiff($rNames, $myNames, $addNames, 'strcasecmp'));
|
||||
|
||||
if(!empty($addNames)) {
|
||||
echo "[{$serverId}] Adding names..." . PHP_EOL;
|
||||
$addResult = $remote->addToWhitelist($serverId, $addNames);
|
||||
foreach($addResult->results as $name => $result)
|
||||
echo "[{$serverId}] [{$name}] {$result->message}" . PHP_EOL;
|
||||
}
|
||||
|
||||
if(!empty($removeNames)) {
|
||||
echo "[{$serverId}] Removing names..." . PHP_EOL;
|
||||
$removeResult = $remote->removeFromWhitelist($serverId, $removeNames);
|
||||
foreach($removeResult->results as $name => $result)
|
||||
echo "[{$serverId}] [{$name}] {$result->message}" . PHP_EOL;
|
||||
}
|
||||
} catch(RuntimeException $ex) {
|
||||
var_dump((string)$ex);
|
||||
echo PHP_EOL;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue