#include #include #include #include #include #include "License.hh" using namespace std; License::License(const JSON& json) : serial_number(0), flags(0), ban_end_time(0), ep3_current_meseta(0), ep3_total_meseta_earned(0), bb_team_id(0) { this->serial_number = json.get_int("SerialNumber"); this->access_key = json.get_string("AccessKey", ""); this->gc_password = json.get_string("GCPassword", ""); this->xb_gamertag = json.get_string("XBGamerTag", ""); this->xb_user_id = json.get_int("XBUserID", 0); this->xb_account_id = json.get_int("XBAccountID", 0); this->bb_username = json.get_string("BBUsername", ""); this->bb_password = json.get_string("BBPassword", ""); this->flags = json.get_int("Flags", 0); this->ban_end_time = json.get_int("BanEndTime", 0); this->last_player_name = json.get_string("LastPlayerName", ""); this->auto_reply_message = json.get_string("AutoReplyMessage", ""); this->ep3_current_meseta = json.get_int("Ep3CurrentMeseta", 0); this->ep3_total_meseta_earned = json.get_int("Ep3TotalMesetaEarned", 0); this->bb_team_id = json.get_int("BBTeamID", 0); } JSON License::json() const { return JSON::dict({ {"SerialNumber", this->serial_number}, {"AccessKey", this->access_key}, {"GCPassword", this->gc_password}, {"XBGamerTag", this->xb_gamertag}, {"XBUserID", this->xb_user_id}, {"XBAccountID", this->xb_account_id}, {"BBUsername", this->bb_username}, {"BBPassword", this->bb_password}, {"Flags", this->flags}, {"BanEndTime", this->ban_end_time}, {"LastPlayerName", this->last_player_name}, {"AutoReplyMessage", this->auto_reply_message}, {"Ep3CurrentMeseta", this->ep3_current_meseta}, {"Ep3TotalMesetaEarned", this->ep3_total_meseta_earned}, {"BBTeamID", this->bb_team_id}, }); } void License::save() const {} void License::delete_file() const {} string License::str() const { vector tokens; tokens.emplace_back(string_printf("serial_number=%010" PRIu32 "/%08" PRIX32, this->serial_number, this->serial_number)); if (!this->access_key.empty()) { tokens.emplace_back("access_key=" + this->access_key); } if (!this->gc_password.empty()) { tokens.emplace_back("gc_password=" + this->gc_password); } if (!this->xb_gamertag.empty()) { tokens.emplace_back("xb_gamertag=" + this->xb_gamertag); } if (this->xb_user_id != 0) { tokens.emplace_back(string_printf("xb_user_id=%016" PRIX64, this->xb_user_id)); } if (this->xb_account_id != 0) { tokens.emplace_back(string_printf("xb_account_id=%016" PRIX64, this->xb_account_id)); } if (!this->bb_username.empty()) { tokens.emplace_back("bb_username=" + this->bb_username); } if (!this->bb_password.empty()) { tokens.emplace_back("bb_password=" + this->bb_password); } tokens.emplace_back(string_printf("flags=%08" PRIX32, this->flags)); if (this->ban_end_time) { tokens.emplace_back(string_printf("ban_end_time=%016" PRIX64, this->ban_end_time)); } if (this->ep3_current_meseta) { tokens.emplace_back(string_printf("ep3_current_meseta=%" PRIu32, this->ep3_current_meseta)); } if (this->ep3_total_meseta_earned) { tokens.emplace_back(string_printf("ep3_total_meseta_earned=%" PRIu32, this->ep3_total_meseta_earned)); } return "[License: " + join(tokens, ", ") + "]"; } DiskLicense::DiskLicense(const JSON& json) : License(json) {} void DiskLicense::save() const { auto json = this->json(); string json_data = json.serialize(JSON::SerializeOption::FORMAT | JSON::SerializeOption::HEX_INTEGERS); string filename = string_printf("system/licenses/%010" PRIu32 ".json", this->serial_number); save_file(filename, json_data); } void DiskLicense::delete_file() const { string filename = string_printf("system/licenses/%010" PRIu32 ".json", this->serial_number); remove(filename.c_str()); } shared_ptr LicenseIndex::create_license() const { return make_shared(); } shared_ptr LicenseIndex::create_temporary_license() const { return make_shared(); } size_t LicenseIndex::count() const { return this->serial_number_to_license.size(); } shared_ptr LicenseIndex::get(uint32_t serial_number) const { try { return this->serial_number_to_license.at(serial_number); } catch (const out_of_range&) { throw missing_license(); } } shared_ptr LicenseIndex::get_by_bb_username(const string& bb_username) const { try { return this->bb_username_to_license.at(bb_username); } catch (const out_of_range&) { throw missing_license(); } } vector> LicenseIndex::all() const { vector> ret; ret.reserve(this->serial_number_to_license.size()); for (const auto& it : this->serial_number_to_license) { ret.emplace_back(it.second); } return ret; } void LicenseIndex::add(shared_ptr l) { this->serial_number_to_license[l->serial_number] = l; if (!l->bb_username.empty()) { this->bb_username_to_license[l->bb_username] = l; } if (!l->xb_gamertag.empty()) { this->xb_gamertag_to_license[l->xb_gamertag] = l; } } void LicenseIndex::remove(uint32_t serial_number) { auto l = this->serial_number_to_license.at(serial_number); this->serial_number_to_license.erase(l->serial_number); if (!l->bb_username.empty()) { this->bb_username_to_license.erase(l->bb_username); } if (!l->xb_gamertag.empty()) { this->xb_gamertag_to_license.erase(l->xb_gamertag); } } shared_ptr LicenseIndex::verify_v1_v2(uint32_t serial_number, const string& access_key) const { if (serial_number == 0) { throw no_username(); } try { auto& license = this->serial_number_to_license.at(serial_number); if (license->access_key.compare(0, 8, access_key) != 0) { throw incorrect_access_key(); } if (license->ban_end_time && (license->ban_end_time >= now())) { throw invalid_argument("user is banned"); } return license; } catch (const out_of_range&) { throw missing_license(); } } shared_ptr LicenseIndex::verify_gc(uint32_t serial_number, const string& access_key) const { if (serial_number == 0) { throw no_username(); } try { auto& license = this->serial_number_to_license.at(serial_number); if (license->access_key != access_key) { throw incorrect_access_key(); } if (license->ban_end_time && (license->ban_end_time >= now())) { throw invalid_argument("user is banned"); } return license; } catch (const out_of_range&) { throw missing_license(); } } shared_ptr LicenseIndex::verify_gc(uint32_t serial_number, const string& access_key, const string& password) const { if (serial_number == 0) { throw no_username(); } try { auto& license = this->serial_number_to_license.at(serial_number); if (license->access_key != access_key) { throw incorrect_access_key(); } if (license->gc_password != password) { throw incorrect_password(); } if (license->ban_end_time && (license->ban_end_time >= now())) { throw invalid_argument("user is banned"); } return license; } catch (const out_of_range&) { throw missing_license(); } } shared_ptr LicenseIndex::verify_xb(const string& gamertag, uint64_t user_id, uint64_t account_id) const { if (user_id == 0 || account_id == 0) { throw incorrect_access_key(); } try { auto& license = this->xb_gamertag_to_license.at(gamertag); if (license->xb_user_id && (license->xb_user_id != user_id)) { throw incorrect_access_key(); } if (license->xb_account_id && (license->xb_account_id != account_id)) { throw incorrect_access_key(); } if (license->ban_end_time && (license->ban_end_time >= now())) { throw invalid_argument("user is banned"); } return license; } catch (const out_of_range&) { throw missing_license(); } } shared_ptr LicenseIndex::verify_bb(const string& username, const string& password) const { if (username.empty() || password.empty()) { throw no_username(); } try { auto& license = this->bb_username_to_license.at(username); if (license->bb_password != password) { throw incorrect_password(); } if (license->ban_end_time && (license->ban_end_time >= now())) { throw invalid_argument("user is banned"); } return license; } catch (const out_of_range&) { throw missing_license(); } } DiskLicenseIndex::DiskLicenseIndex() { struct BinaryLicense { pstring username; // BB username (max. 16 chars; should technically be Unicode) pstring bb_password; // BB password (max. 16 chars) uint32_t serial_number; // PC/GC serial number. MUST BE PRESENT FOR BB LICENSES TOO; this is also the player's guild card number. pstring access_key; // PC/GC access key. (to log in using PC on a GC license, just enter the first 8 characters of the GC access key) pstring gc_password; // GC password uint32_t privileges; // privilege level uint64_t ban_end_time; // end time of ban (zero = not banned) } __attribute__((packed)); if (!isdir("system/licenses")) { mkdir("system/licenses", 0755); } // Convert binary licenses to JSON licenses and save them if (isfile("system/licenses.nsi")) { auto bin_licenses = load_vector_file("system/licenses.nsi"); for (const auto& bin_license : bin_licenses) { // Only add licenses from the binary file if there isn't a JSON version of // the same license try { this->get(bin_license.serial_number); } catch (const missing_license&) { License license; license.serial_number = bin_license.serial_number; license.access_key = bin_license.access_key.decode(); license.gc_password = bin_license.gc_password.decode(); license.bb_username = bin_license.username.decode(); license.bb_password = bin_license.bb_password.decode(); license.flags = bin_license.privileges; license.ban_end_time = bin_license.ban_end_time; license.ep3_current_meseta = 0; license.ep3_total_meseta_earned = 0; license.save(); } } ::remove("system/licenses.nsi"); } for (const auto& item : list_directory("system/licenses")) { if (ends_with(item, ".json")) { JSON json = JSON::parse(load_file("system/licenses/" + item)); auto license = make_shared(json); this->add(license); } } } shared_ptr DiskLicenseIndex::create_license() const { return make_shared(); }