12 #include "json/json.h"
18 #include "SQLiteCpp/Database.h"
19 #include "SQLiteCpp/Statement.h"
48 static const AString CertString =
53 "-----BEGIN CERTIFICATE-----\n"
54 "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n"
55 "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
56 "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"
57 "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n"
58 "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
59 "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n"
60 "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n"
61 "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n"
62 "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n"
63 "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n"
64 "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n"
65 "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n"
66 "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n"
67 "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n"
68 "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n"
69 "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n"
70 "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n"
71 "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n"
72 "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n"
73 "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n"
74 "-----END CERTIFICATE-----\n"
78 "-----BEGIN CERTIFICATE-----\n"
79 "MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb\n"
80 "MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow\n"
81 "GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj\n"
82 "YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL\n"
83 "MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n"
84 "BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM\n"
85 "GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP\n"
86 "ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua\n"
87 "BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n"
88 "3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4\n"
89 "YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR\n"
90 "rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm\n"
91 "ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU\n"
92 "oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\n"
93 "MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v\n"
94 "QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t\n"
95 "b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF\n"
96 "AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\n"
97 "GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\n"
98 "Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2\n"
99 "G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi\n"
100 "l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3\n"
101 "smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n"
102 "-----END CERTIFICATE-----\n"
118 const cUUID & a_UUID,
119 const Json::Value & a_Properties,
122 m_PlayerName(a_PlayerName),
125 m_TexturesSignature(),
126 m_DateTime(a_DateTime)
141 if (!a_Properties.isArray())
146 Json::UInt Size = a_Properties.size();
147 for (Json::UInt i = 0; i < Size; i++)
149 const Json::Value & Prop = a_Properties[i];
150 if (Prop.get(
"name",
"").asString() !=
"textures")
154 m_Textures = Prop.get(
"value",
"").asString();
175 Super(
"MojangAPI Updater"),
260 if (!a_UseOnlyCached)
268 cProfileMap::const_iterator itr =
m_NameToUUID.find(lcPlayerName);
274 return itr->second.m_UUID;
289 return itr->second.m_PlayerName;
297 return itr->second.m_PlayerName;
302 if (!a_UseOnlyCached)
320 for (AStringVector::const_iterator itr = a_PlayerNames.begin(), end = a_PlayerNames.end(); itr != end; ++itr)
326 if (!a_UseOnlyCached)
333 std::vector<cUUID> res;
334 res.resize(PlayerNames.size());
336 for (AStringVector::const_iterator itr = PlayerNames.begin(), end = PlayerNames.end(); itr != end; ++itr, ++idx)
338 cProfileMap::const_iterator itrN =
m_NameToUUID.find(*itr);
341 res[idx] = itrN->second.m_UUID;
353 Int64 Now = time(
nullptr);
371 Int64 Now = time(
nullptr);
396 SQLite::Database db(
"MojangAPI.sqlite", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
397 db.exec(
"CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)");
398 db.exec(
"CREATE TABLE IF NOT EXISTS UUIDToProfile (UUID, PlayerName, Textures, TexturesSignature, DateTime)");
402 SQLite::Statement stmt(db,
"SELECT PlayerName, UUID, DateTime FROM PlayerNameToUUID");
403 while (stmt.executeStep())
405 AString PlayerName = stmt.getColumn(0);
406 AString StringUUID = stmt.getColumn(1);
407 Int64 DateTime = stmt.getColumn(2);
420 SQLite::Statement stmt(db,
"SELECT PlayerName, UUID, Textures, TexturesSignature, DateTime FROM UUIDToProfile");
421 while (stmt.executeStep())
423 AString PlayerName = stmt.getColumn(0);
424 AString StringUUID = stmt.getColumn(1);
425 AString Textures = stmt.getColumn(2);
426 AString TexturesSignature = stmt.getColumn(2);
427 Int64 DateTime = stmt.getColumn(4);
439 catch (
const SQLite::Exception & ex)
441 LOGINFO(
"Loading MojangAPI cache failed: %s", ex.what());
454 SQLite::Database db(
"MojangAPI.sqlite", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
455 db.exec(
"CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)");
456 db.exec(
"CREATE TABLE IF NOT EXISTS UUIDToProfile (UUID, PlayerName, Textures, TexturesSignature, DateTime)");
459 db.exec(
"DELETE FROM PlayerNameToUUID");
460 db.exec(
"DELETE FROM UUIDToProfile");
465 SQLite::Statement stmt(db,
"INSERT INTO PlayerNameToUUID(PlayerName, UUID, DateTime) VALUES (?, ?, ?)");
469 auto & Profile = NameToUUID.second;
470 if (Profile.m_DateTime < LimitDateTime)
475 stmt.bind(1, Profile.m_PlayerName);
476 stmt.bind(2, Profile.m_UUID.ToShortString());
477 stmt.bind(3, Profile.m_DateTime);
485 SQLite::Statement stmt(db,
"INSERT INTO UUIDToProfile(UUID, PlayerName, Textures, TexturesSignature, DateTime) VALUES (?, ?, ?, ?, ?)");
489 auto & Profile = UUIDToProfile.second;
490 if (Profile.m_DateTime < LimitDateTime)
495 stmt.bind(1, Profile.m_UUID.ToShortString());
496 stmt.bind(2, Profile.m_PlayerName);
497 stmt.bind(3, Profile.m_Textures);
498 stmt.bind(4, Profile.m_TexturesSignature);
499 stmt.bind(5, Profile.m_DateTime);
505 catch (
const SQLite::Exception & ex)
507 LOGINFO(
"Saving MojangAPI cache failed: %s", ex.what());
519 NamesToQuery.reserve(a_PlayerNames.size());
522 for (AStringVector::const_iterator itr = a_PlayerNames.begin(), end = a_PlayerNames.end(); itr != end; ++itr)
526 NamesToQuery.push_back(*itr);
540 while (!a_NamesToQuery.empty())
545 auto itr = a_NamesToQuery.begin();
546 auto end = a_NamesToQuery.end();
547 for (; (itr != end) && (Count <
MAX_PER_QUERY); ++itr, ++Count)
549 Json::Value req(*itr);
552 a_NamesToQuery.erase(a_NamesToQuery.begin(), itr);
567 LOGWARNING(
"%s failed: Cannot parse received data (NameToUUID) to JSON: \"%s\"", __METHOD_NAME__, ParseError);
568 LOGD(
"Response body:\n%s",
CreateHexDump(HexDump, Response.data(), Response.size(), 16));
573 Json::Value::UInt JsonCount = root.size();
574 Int64 Now = time(
nullptr);
577 for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx)
579 Json::Value & Val = root[idx];
580 AString JsonName = Val.get(
"name",
"").asString();
582 if (!JsonUUID.
FromString(Val.get(
"id",
"").asString()))
594 for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx)
596 Json::Value & Val = root[idx];
597 AString JsonName = Val.get(
"name",
"").asString();
599 if (!JsonUUID.
FromString(Val.get(
"id",
"").asString()))
647 LOGWARNING(
"%s failed: Cannot parse received data (NameToUUID) to JSON: \"%s\"", __FUNCTION__, ParseError);
650 LOGD(
"Response body:\n%s",
CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str());
671 AString PlayerName = root.get(
"name",
"").asString();
672 if (PlayerName.empty())
677 Json::Value Properties = root.get(
"properties",
"");
678 Int64 Now = time(
nullptr);
722 if (NameToUUID.second.m_DateTime < LimitDateTime)
724 PlayerNames.push_back(NameToUUID.first);
728 if (!PlayerNames.empty())
730 LOG(
"%s: Updating name-to-uuid cache for %u names", __METHOD_NAME__,
static_cast<unsigned>(PlayerNames.size()));
735 std::vector<cUUID> ProfileUUIDs;
740 if (UUIDToProfile.second.m_DateTime < LimitDateTime)
742 ProfileUUIDs.push_back(UUIDToProfile.first);
746 if (!ProfileUUIDs.empty())
748 LOG(
"%s: Updating uuid-to-profile cache for %u uuids", __METHOD_NAME__,
static_cast<unsigned>(ProfileUUIDs.size()));
749 for (
const auto & UUID : ProfileUUIDs)
void LOGWARNING(std::string_view a_Format, const Args &... args)
void LOG(std::string_view a_Format, const Args &... args)
void LOGINFO(std::string_view a_Format, const Args &... args)
const int MAX_PER_QUERY
The maximum number of names to send in a single query.
constexpr char DEFAULT_UUID_TO_PROFILE_SERVER[]
constexpr char DEFAULT_NAME_TO_UUID_ADDRESS[]
constexpr char DEFAULT_NAME_TO_UUID_SERVER[]
const Int64 MAX_AGE
The maximum age for items to be kept in the cache.
constexpr char DEFAULT_UUID_TO_PROFILE_ADDRESS[]
AString URLEncode(const AString &a_Text)
URL-encodes the given string.
AString StrToLower(const AString &s)
Returns a lower-cased copy of the string.
void ReplaceString(AString &iHayStack, const AString &iNeedle, const AString &iReplaceWith)
Replaces each occurence of iNeedle in iHayStack with iReplaceWith.
AString & CreateHexDump(AString &a_Out, const void *a_Data, size_t a_Size, size_t a_BytesPerLine)
format binary data this way: 00001234: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 1234567890abcd...
std::vector< AString > AStringVector
std::map< AString, AString > AStringMap
A string dictionary, used for key-value pairs.
AString WriteFastString(const Json::Value &a_Root)
bool ParseString(const AString &a_JsonStr, Json::Value &a_Root, AString *a_ErrorMsg)
static const AStringMap & UrlClientOptions()
Returns the Options that should be used for cUrlClient queries to the Mojang APIs.
static std::pair< bool, AString > BlockingPost(const AString &a_URL, AStringMap &&a_Headers, const AString &a_Body, const AStringMap &a_Options={})
Alias for BlockingRequest("POST", ...)
static std::pair< bool, AString > BlockingGet(const AString &a_URL, AStringMap a_Headers={}, const AString &a_Body={}, const AStringMap &a_Options={})
Alias for BlockingRequest("GET", ...)
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
void Wait(void)
Waits until the event has been set.
void Set(void)
Sets the event - releases one thread that has been waiting in Wait().
std::atomic< bool > m_ShouldTerminate
The overriden Execute() method should check this value periodically and terminate if this is true.
cIsThread(AString &&a_ThreadName)
void Stop(void)
Signals the thread to terminate and waits until it's finished.
virtual void Execute(void) override
This function, overloaded by the descendants, is called in the new thread.
cMojangAPI & m_MojangAPI
The cMojangAPI instance to update.
cEvent m_evtNotify
The event used for notifying that the thread should terminate, as well as timing.
virtual ~cUpdateThread() override
cUpdateThread(cMojangAPI &a_MojangAPI)
void AddPlayerNameToUUIDMapping(const AString &a_PlayerName, const cUUID &a_UUID)
Called by the Authenticator to add a PlayerName -> UUID mapping that it has received from authenticat...
cCriticalSection m_CSNameToUUID
Protects m_NameToUUID against simultaneous multi-threaded access.
AString GetPlayerNameFromUUID(const cUUID &a_UUID, bool a_UseOnlyCached=false)
Converts a UUID into a playername.
void NotifyNameUUID(const AString &a_PlayerName, const cUUID &a_PlayerUUID)
Called for each name-uuid pairing that is discovered.
void CacheNamesToUUIDs(const AStringVector &a_PlayerNames)
Makes sure all specified names are in the m_PlayerNameToUUID cache.
void LoadCachesFromDisk(void)
Loads the caches from a disk storage.
void Update(void)
Updates the stale values in the DB from the Mojang servers.
cUUIDProfileMap m_UUIDToProfile
Cache for the UUID-to-profile lookups.
cCriticalSection m_CSUUIDToName
Protects m_UUIDToName against simultaneous multi-threaded access.
void AddPlayerProfile(const AString &a_PlayerName, const cUUID &a_UUID, const Json::Value &a_Properties)
Called by the Authenticator to add a profile that it has received from authenticating a user.
std::vector< cUUID > GetUUIDsFromPlayerNames(const AStringVector &a_PlayerName, bool a_UseOnlyCached=false)
Converts the player names into UUIDs.
cCriticalSection m_CSUUIDToProfile
Protects m_UUIDToProfile against simultaneous multi-threaded access.
AString m_NameToUUIDUrl
The full URL to check when converting player names to UUIDs.
cProfileMap m_NameToUUID
Cache for the Name-to-UUID lookups.
void QueryNamesToUUIDs(AStringVector &a_PlayerNames)
Queries all the specified names and stores them into the m_PlayerNameToUUID cache.
void Start(cSettingsRepositoryInterface &a_Settings, bool a_ShouldAuth)
Initializes the API; reads the settings from the specified ini file.
cRankManager * m_RankMgr
The rank manager that is notified of the name-uuid pairings.
cCriticalSection m_CSRankMgr
Protects m_RankMgr agains simultaneous multi-threaded access.
void SaveCachesToDisk(void)
Saves the caches to a disk storage.
AString m_UUIDToProfileUrl
The full URL to use for converting UUID to profile.
void CacheUUIDToProfile(const cUUID &a_UUID)
Makes sure the specified UUID is in the m_UUIDToProfile cache.
void QueryUUIDToProfile(const cUUID &a_UUID)
Queries the specified UUID's profile and stores it in the m_UUIDToProfile cache.
cUUID GetUUIDFromPlayerName(const AString &a_PlayerName, bool a_UseOnlyCached=false)
Converts a player name into a UUID.
cUUIDProfileMap m_UUIDToName
Cache for the Name-to-UUID lookups.
std::shared_ptr< cUpdateThread > m_UpdateThread
The thread that periodically updates the stale data in the DB from the Mojang servers.
Holds data for a single player profile.
sProfile(void)
Default constructor for the container's sake.
AString m_TexturesSignature
void NotifyNameUUID(const AString &a_PlayerName, const cUUID &a_UUID)
Called by cMojangAPI whenever the playername-uuid pairing is discovered.
virtual AString GetValueSet(const AString &keyname, const AString &valuename, const AString &defValue="")=0
Gets the value; if not found, write the default to the repository.
AString ToShortString() const
Converts the UUID to a short form string (i.e without dashes).
bool FromString(const AString &a_StringUUID)
Tries to interpret the string as a short or long form UUID and assign from it.