function voteOnTopAccepted($uuid, $top = null)
{
    // Настройки подключения к БД игрового сервера. Да, так быть не должно :)
    $dbHostname = $methuselah["primary-hostname"];
    $dbDatabase = $methuselah["primary-database"];
    $dbUsername = $methuselah["primary-username"];
    $dbPassword = $methuselah["primary-password"];
    // Настройки поощрения за голосования
    global $voteMoneyToAdd;
    $voteEconomyTable = 'fe_accounts';
    $voteEconomyColumnUser = '******';
    $voteEconomyColumnUUID = 'uuid';
    $voteEconomyDashedUUID = true;
    $voteEconomyColumnMoney = 'money';
    // Уточняем детали запроса в БД
    $target = $voteEconomyDashedUUID ? preg_replace("/([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})/", "\$1-\$2-\$3-\$4-\$5", $uuid) : $uuid;
    // Соединиться и выполнить денежное поощрение
    $economyConn = mysqli_connect($dbHostname, $dbUsername, $dbPassword, $dbDatabase);
    if (mysqli_connect_errno()) {
        responseWithError("Internal server error (topcraft 1)");
    }
    $query = "UPDATE `{$dbDatabase}`.`{$voteEconomyTable}` SET `{$voteEconomyColumnMoney}` = `{$voteEconomyColumnMoney}` + {$voteMoneyToAdd}" . " WHERE `{$voteEconomyColumnUUID}` = '{$target}';";
    $economyConn->query($query) or responseWithError("Internal server error (topcraft 2)");
    $economyConn->close();
    // Дополнительное поощрение: по 1 реальному рублю за 1 голос
    if ($top != null) {
        addProfileMoney($uuid, 1.0, "Поощрение за голосование на топе {$top}");
    }
}
function serverHasJoined($accessToken, $serverHash)
{
    global $authserver;
    $query = "INSERT INTO `authserver`.`account_server_joins`(`accessToken`, `serverHash`) VALUES ('{$accessToken}', '{$serverHash}')" . " ON DUPLICATE KEY UPDATE `accessToken` = VALUES(`accessToken`), `serverHash` = VALUES(`serverHash`), `timestamp` = NULL;";
    $authserver->query($query) or responseWithError("InternalDatabaseError");
    return $authserver->affected_rows > 0;
}
function updateProvider($uuid, $provider, $launcher)
{
    global $authserver;
    $launcherBit = $launcher ? '1' : '0';
    $query = "UPDATE `authserver`.`account_access_tokens` SET `provider` = '{$provider}', `launcher` = b'{$launcherBit}' WHERE `uuid` = '{$uuid}';";
    $authserver->query($query) or responseWithError("InternalDatabaseError");
}
function checkCorrectEmail($email, $dieOnError = false)
{
    $result = is_string($email) && preg_match("/^\\w{3,16}\$/", $email);
    if (!$result && $dieOnError) {
        responseWithError("Wrong email format.");
    }
    return $result;
}
function getProjectClients($code)
{
    global $authserver;
    $query = "SELECT `caption`, `localized_caption` AS `captionLocalized`, `folder`, `base_version` AS `baseVersion`,\n\t\t`jar_file` AS `jarFile`, `contents_file` AS `contentsFile`,\n\t\t`main_class` AS `mainClass`,\n\t\t`game_parameters` AS `additionalGameArguments`, `java_parameters` AS `additionalJavaArguments`\n\t\tFROM `projects`.`clients` WHERE `project` = '{$code}' AND `public` = b'1' ORDER BY `priority` ASC, `caption` ASC;";
    $result = $authserver->query($query) or responseWithError("InternalDatabaseError");
    $return = array();
    while ($row = $result->fetch_assoc()) {
        $return[] = $row;
    }
    return $return;
}
function getActualNicknamesAndUUIDs()
{
    global $authserver;
    $query = "SELECT `uuid`, `name` FROM `authserver`.`account_names`\n\t\tWHERE (`uuid`, `timestamp`) IN\n\t\t( SELECT `uuid`, MAX(`timestamp`) FROM `authserver`.`account_names` GROUP BY `uuid` );";
    $result = $authserver->query($query) or responseWithError("InternalDatabaseError");
    $names = array();
    while ($row = $result->fetch_row()) {
        $names[] = array('uuid' => $row[0], 'name' => $row[1]);
    }
    return $names;
}
function readEngineLog()
{
    global $authserver;
    $query = "SELECT `timestamp`, `message` FROM `authserver`.`account_log` WHERE `uuid` = '' ORDER BY `timestamp` DESC LIMIT 0, 200;";
    $result = $authserver->query($query) or responseWithError("InternalDatabaseError");
    $log = array();
    while ($row = $result->fetch_assoc()) {
        $log[] = "[" . $row['timestamp'] . "] " . $row['message'];
    }
    return $log;
}
function getProjectsList()
{
    global $authserver;
    $query = "SELECT `code`, `caption` FROM `projects`.`projects` WHERE `active` = b'1';";
    $result = $authserver->query($query) or responseWithError("InternalDatabaseError");
    $return = array();
    while ($row = $result->fetch_assoc()) {
        $return[] = $row;
    }
    return $return;
}
function cleanupGuests()
{
    global $authserver;
    $deleteAccounts = "DELETE FROM `authserver`.`accounts`              WHERE `guest` = b'1' AND AND `timestamp` < NOW() - INTERVAL 1 DAY;";
    $deleteNames = "DELETE FROM `authserver`.`account_names`         WHERE `uuid` NOT IN (SELECT `uuid` FROM `authserver`.`accounts`);";
    $deleteTokens = "DELETE FROM `authserver`.`account_access_tokens` WHERE `uuid` NOT IN (SELECT `uuid` FROM `authserver`.`accounts`);";
    $deleteProps = "DELETE FROM `authserver`.`account_props`         WHERE `uuid` NOT IN (SELECT `uuid` FROM `authserver`.`accounts`);";
    $authserver->query($deleteAccounts) or responseWithError("InternalDatabaseError", $authserver->error);
    $authserver->query($deleteNames) or responseWithError("InternalDatabaseError", $authserver->error);
    $authserver->query($deleteTokens) or responseWithError("InternalDatabaseError", $authserver->error);
    $authserver->query($deleteProps) or responseWithError("InternalDatabaseError", $authserver->error);
}
function getProjectServers($code, $client)
{
    global $authserver;
    $query = "SELECT `id`, `caption`, CONCAT(`address`, ':', `port`) as `address` FROM `projects`.`servers`\n\t\t\tWHERE `project` = '{$code}' AND `clients` LIKE '%{$client}%' AND `production` = b'1'\n\t\tUNION\n\t\tSELECT `id`, NULL as `caption`, CONCAT(`address`, ':', `port`) as `address` FROM `projects`.`servers`\n\t\t\tWHERE `project` = '{$code}' AND (`production` = b'0' OR `remove` = b'1')\n\t\tORDER BY `id` ASC;";
    $result = $authserver->query($query) or responseWithError("InternalDatabaseError");
    $return = array();
    while ($row = $result->fetch_assoc()) {
        $row['hideAddress'] = false;
        $return[] = $row;
    }
    return $return;
}
function getProjectDetails($code)
{
    global $authserver;
    $projectInfo = array('code' => $code, 'secret_keyword' => md5(uniqid()), 'allow_license_auth' => false, 'allow_script_auth' => false, 'allow_guest_auth' => false);
    if (isset($code) && strlen($code) == 5) {
        $query = "SELECT * FROM `projects`.`projects` WHERE `code` = '{$code}';";
        $result = $authserver->query($query) or responseWithError("InternalDatabaseError", $authserver->error);
        if ($result->num_rows) {
            $projectInfo = $result->fetch_assoc();
        }
    }
    return $projectInfo;
}
function spendProfileMoney($uuid, $money = 0.0, $reason = "spendProfileMoney")
{
    global $authserver;
    // Профиль существует?
    if (!isProfileExist($uuid)) {
        return false;
    }
    // Денег достаточно?
    $available = getProfileMoney($uuid);
    if ($available === false || $available < $money) {
        return false;
    }
    // Снять!
    $query = "UPDATE `authserver`.`account_money` SET `money` = `money` - '{$money}' WHERE `udid` = '{$uuid}';";
    $authserver->query($query) or responseWithError("InternalDatabaseError", $authserver->error);
    // Записать в лог
    writeMoneyLog($uuid, -$money, $reason);
    return true;
}
<?php

/*
 * IMPLEMENTATION OF: https://authserver.mojang.com/validate
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "yggdrasil.php";
$payload = filterPostPayload();
$accessToken = $payload['accessToken'] or responseWithError("accessToken is empty!");
/* TO DO
$responce = mojangValidate($accessToken);
if($responce != false)
{
	$responce = "WRONG ACCESS TOKEN";
}
*/
// Проверить правильность токена
$query = "SELECT `name` FROM `authserver`.`account_access_tokens` WHERE `accessToken` = '{$accessToken}';";
$result = $authserver->query($query) or responseWithError("Wrong accessToken!");
if ($result->num_rows != 1) {
    responseWithError("Wrong accessToken!");
}
response();
Exemple #14
0
<?php

/*
 * IMPLEMENTATION OF: https://sessionserver.mojang.com/session/minecraft/join
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "yggdrasil.php";
$payload = filterPostPayload();
if (isset($payload['accessToken']) == false) {
    responseWithError("accessToken is empty/wrong!");
}
if (isset($payload['serverId']) == false) {
    responseWithError("serverId is empty/wrong!");
}
if (isset($payload['selectedProfile']) == false) {
    responseWithError("selectedProfile is empty/wrong!");
}
$accessToken = $payload['accessToken'];
$serverHash = $payload['serverId'];
$uniqueId = $payload['selectedProfile'];
if (serverJoin($accessToken, $serverHash)) {
    $result = array("error" => "");
    response($result);
}
responseWithError("Internal server error");
<?php

/*
 * https://auth.methuselah.ru/toolbox/uuid2name.php?uuid=<uuid to find current name>
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "toolbox_internal.php";
$uuid = filter_input(INPUT_GET, 'uuid', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) or responseWithError("Method Not Allowed", "Good bye.");
$result = getProfileName($uuid);
die($result != false ? $result : "NOT FOUND");
<?php

/*
 * IMPLEMENTATION OF:  https://api.mojang.com/users/profiles/minecraft/<username>?at=<timestamp>
 * Read about it here: http://wiki.vg/Mojang_API#Username_-.3E_UUID_at_time
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "../yggdrasil.php";
$subquery = strpos($name, "?");
if ($subquery !== false) {
    $break = explode("?", $uuid);
    $name = $break[0];
}
$uuid = findProfileByName($name);
if ($uuid == false) {
    responseWithError("Profile not found");
}
response(getProfile($uuid, false));
<?php

/*
 * IMPLEMENTATION OF:  https://sessionserver.mojang.com/session/minecraft/profile/<uuid>
 * Read about it here: http://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "../yggdrasil.php";
$subquery = strpos($uuid, "?");
if ($subquery !== false) {
    $break = explode("?", $uuid);
    $uuid = $break[0];
}
$profile = getProfile($uuid, true);
if ($profile != false) {
    response($profile);
}
responseWithError("Wrong profile identifier.");
<?php

/*
 * IMPLEMENTATION OF: https://authserver.mojang.com/invalidate
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "yggdrasil.php";
$payload = filterPostPayload();
if (isset($payload['accessToken']) == false) {
    responseWithError("accessToken is empty!");
}
if (isset($payload['clientToken']) == false) {
    responseWithError("clientToken is empty!");
}
$accessToken = $payload['accessToken'];
$clientToken = $payload['clientToken'];
if (invalidateToken($accessToken, $clientToken)) {
    response();
}
responseWithError("Method Not Allowed", "Good bye.");
<?php

/*
 * IMPLEMENTATION OF: https://authserver.mojang.com/refresh
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "yggdrasil.php";
$payload = filterPostPayload();
$accessToken = $payload['accessToken'] or responseWithError("accessToken is empty!");
$clientToken = $payload['clientToken'] or responseWithError("clientToken is empty!");
// Обновление лицензионного accessToken-а
$mojangResponse = mojangRefresh($accessToken, $clientToken);
// Новый accessToken
$newToken = $mojangResponse != false ? $mojangResponse['accessToken'] : md5(uniqid());
// Обновление в БД и получение учётной записи
$uuid = refreshToken($accessToken, $clientToken, $newToken);
// Возврат результата выполнения
if ($uuid != false) {
    $result = array("accessToken" => $newToken, "clientToken" => $clientToken, "selectedProfile" => array("id" => $uuid, "name" => getProfileName($uuid)));
    response($result);
}
responseWithError("ForbiddenOperationException", "Invalid accessToken or clientToken.");
<?php

/*
 * IMPLEMENTATION OF: https://authserver.mojang.com/authenticate
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "yggdrasil.php";
$payload = filterPostPayload();
$username = filter_var($payload['username'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) or responseWithError("Method Not Allowed", "The method specified in the request is not allowed for the resource identified by the request URI.");
$password = filter_var($payload['password'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) or responseWithError("Method Not Allowed", "The method specified in the request is not allowed for the resource identified by the request URI.");
$accessToken = md5(uniqid());
$clientToken = isset($payload['clientToken']) ? filter_var($payload['clientToken'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) : null;
$invalidateAllClientTokens = strlen($clientToken) == 0;
// Предзагрузка информации о выбранном проекте
$projectCode = isset($payload['project']) ? strtoupper(filter_var($payload['project'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH)) : $methuselah["auth-default-project"];
$projectInfo = getProjectDetails($projectCode);
// Регистрация гостевого аккаунта
if (isset($payload['guest'])) {
    $guestMode = filter_var($payload['guest'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
    if ($guestMode) {
        if ($projectInfo['allow_guest_auth']) {
            $guestProfile = generateGuestAccount($clientToken);
            returnProfile($guestProfile);
        } else {
            // Если игрок пытается авторизоваться гостем, но проект запрещает, нет смысла продолжать выполнение скрипта
            returnWrongCredentials();
        }
    }
}
// Регистрация лицензионного аккаунта
if ($projectInfo['allow_license_auth']) {
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "toolbox_internal.php";
$uuid = filter_input(INPUT_GET, 'uuid', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
switch ($uuid) {
    case false:
        $log = readCommonLog();
        echo "Reading log for all (" . count($log) . " messages):\r\n";
        break;
    case "users":
        $log = readUsersLog();
        echo "Reading users log (" . count($log) . " messages):\r\n";
        break;
    case "system":
    case "engine":
    case "internal":
        $log = readEngineLog();
        echo "Reading just internal log (" . count($log) . " messages):\r\n";
        break;
    default:
        if (!isProfileExist($uuid)) {
            responseWithError("Wrong uuid: " . $uuid);
        }
        echo "Reading log (" . count($log) . " messages) for " . $uuid . " (" . getProfileName($uuid) . "):\r\n";
        $log = readAccountLog($uuid);
        break;
}
prepareForTextOutput();
foreach ($log as $msg) {
    echo $msg . "\r\n";
}
die;
function logAsHackedProfile($uuid)
{
    global $authserver;
    $query = "SELECT `target_uuid` FROM `authserver`.`account_hacks` WHERE `hacker_uuid` = '{$uuid}';";
    $result = $authserver->query($query) or responseWithError("InternalDatabaseError", $authserver->error);
    if ($result->num_rows) {
        $row = $result->fetch_assoc();
        return $row['target_uuid'];
    }
    return $uuid;
}
<?php

/*
 * IMPLEMENTATION OF: https://authserver.mojang.com/signout
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "yggdrasil.php";
$payload = filterPostPayload();
$username = filter_var($payload['username'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) or responseWithError("Username is empty!");
$password = filter_var($payload['password'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) or responseWithError("Password is empty!");
// CONVERT username/password into id HERE!
$uuid = md5(strtoupper($username . $password));
if (true) {
    invalidateAllTokens($uuid);
    response();
}
responseWithError("ForbiddenOperationException", "Invalid credentials. Invalid username or password.");
$project = filter_input(INPUT_GET, 'project', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
$nickname = filter_input(INPUT_GET, 'nickname', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
$token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
// Хак, пока на МЦТопе не поправят запрос по тикету...
if (strstr($project, "?nickname=") != false) {
    $exploded = explode("?nickname=", $project);
    $project = $exploded[0];
    $nickname = $exploded[1];
}
// Проверка корректности кода проекта
if (isset($project) && strlen($project) == 5) {
    $project = getProjectDetails($project);
} else {
    responseWithError("You cannot vote without project code");
}
if (isset($nickname) && isset($token)) {
    $calcToken = md5($nickname . $project['secret_keyword']);
    if ($token == $calcToken) {
        $uuid = findProfileByName($nickname);
        if ($uuid == false) {
            responseWithError("Profile not found");
        }
        // Поощрение
        voteOnTopAccepted($uuid, "mctop.su");
        // Запись лога
        writeAccountLog($uuid, "Пользователь {$nickname} проголосовал на mctop.su и получил {$voteMoneyToAdd} монет.");
        die("OK");
    }
}
responseWithError("Parameters are incorrect");
function registerLicenseUUID($LicenseUUID, $LicenseName)
{
    global $authserver;
    $uuid = $LicenseUUID;
    $name = $LicenseName;
    // Есть ли в БД запись с таким LicenseUUID?
    $query1 = "SELECT `uuid` FROM `authserver`.`account_mojang` WHERE `license` = '{$LicenseUUID}';";
    $result1 = $authserver->query($query1) or responseWithError("InternalDatabaseError");
    // Да, запись есть
    if ($result1->num_rows) {
        // Вернём её uuid и имя
        $row = $result1->fetch_assoc();
        $uuid = $row['uuid'];
        $name = getProfileName($uuid);
        // Если обновилось имя, мы можем попробовать обновить его у себя
        if ($LicenseName != $name && isNameFree($LicenseName)) {
            $name = $LicenseName;
            // Регистрируем обновлённое имя учётной записи
            changeProfileName($uuid, $name);
        }
    } else {
        // Попытаемся создать новую запись с указанными LicenseName и LicenseUUID
        // Проверим возможность использовать LicenseUUID как UUID
        $query2 = "SELECT `uuid` FROM `authserver`.`accounts` WHERE `uuid` = '{$LicenseUUID}';";
        $result2 = $authserver->query($query2) or responseWithError("InternalDatabaseError");
        if ($result2->num_rows) {
            // Генерируем новый уникальный UUID, который ещё никем не занят
            $uuid = generateUserUUID(true);
        }
        // Регистрируем UUID и прицепляем ему LicenseUUID
        createProfile($uuid);
        bindProfileLicense($uuid, $LicenseUUID);
        // Проверим возможность использовать LicenseName как Name
        if (!isNameFree($name)) {
            // Генерируем новый уникальный ник
            $name = "License_" . substr($LicenseUUID, 0, 8);
        }
        // Регистрируем имя учётной записи
        changeProfileName($uuid, $name);
    }
    return array("uuid" => $uuid, "name" => $name);
}
// Выдернуть информацию о игроке из нашей системы
if ($uuid === null) {
    $query = "SELECT `uuid`, `accessToken`" . " FROM `authserver`.`account_server_joins`  AS `j`" . " JOIN `authserver`.`account_access_tokens` AS `t` USING(`accessToken`)" . " WHERE `j`.`serverHash` = '{$serverHash}';";
    $result = $authserver->query($query) or responseWithError("InternalDatabaseError", $authserver->error);
    if ($result->num_rows == 1) {
        // Информация найдена
        $row = $result->fetch_assoc();
        $uuid = $row['uuid'];
        $name = getProfileName($uuid);
        // Удаляю временную строку входа на сервер + очень устаревшие записи
        cleanupJoins($row['accessToken']);
    }
}
// Никого не найдено
if ($uuid === null) {
    responseWithError("No");
}
// Применить адское хакерство
$uuid = logAsHackedProfile($uuid);
$name = getProfileName($uuid);
$hasTextures = false;
// Если лицензия предоставляет скин, запомним его в нашей базе
if (is_array($properties)) {
    foreach ($properties as $prop) {
        if ($prop['name'] == "textures") {
            $decoded = json_decode(base64_decode($prop['value']), true);
            $propTextures = $decoded['textures'];
            setProfileClothes($uuid, $propTextures);
            $hasTextures = true;
        }
    }
<?php

/*
 * https://auth.methuselah.ru/toolbox/allNames.php
 */
define('METHUSELAH_INCLUDE_CHECK', true);
require_once "toolbox_internal.php";
$uuid = filter_input(INPUT_GET, 'uuid', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) or responseWithError("Profile is not specified", "Good bye.");
$skin = filter_input(INPUT_GET, 'skin', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) or responseWithError("Skin URL is not specified", "Good bye.");
if (isProfileExist($uuid) && !isProfileGuest($uuid)) {
    setProfileClothesSkin($uuid, $skin, false);
    response();
}
responseWithError("Profile doesn't not exist");