implement Episode 3 meseta
This commit is contained in:
+153
-157
@@ -10,103 +10,172 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
License::License()
|
||||
License::License(const JSON& json)
|
||||
: serial_number(0),
|
||||
privileges(0),
|
||||
ban_end_time(0) {}
|
||||
flags(0),
|
||||
ban_end_time(0),
|
||||
ep3_current_meseta(0),
|
||||
ep3_total_meseta_earned(0) {
|
||||
this->serial_number = json.get_int("SerialNumber");
|
||||
this->access_key = json.get_string("AccessKey", "");
|
||||
this->gc_password = json.get_string("GCPassword", "");
|
||||
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->ep3_current_meseta = json.get_int("Ep3CurrentMeseta", 0);
|
||||
this->ep3_total_meseta_earned = json.get_int("Ep3TotalMesetaEarned", 0);
|
||||
}
|
||||
|
||||
JSON License::json() const {
|
||||
return JSON::dict({
|
||||
{"SerialNumber", this->serial_number},
|
||||
{"AccessKey", this->access_key},
|
||||
{"GCPassword", this->gc_password},
|
||||
{"BBUsername", this->bb_username},
|
||||
{"BBPassword", this->bb_password},
|
||||
{"Flags", this->flags},
|
||||
{"BanEndTime", this->ban_end_time},
|
||||
{"Ep3CurrentMeseta", this->ep3_current_meseta},
|
||||
{"Ep3TotalMesetaEarned", this->ep3_total_meseta_earned},
|
||||
});
|
||||
}
|
||||
|
||||
void License::save() const {
|
||||
if (!(this->flags & License::Flag::TEMPORARY)) {
|
||||
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 License::delete_file() const {
|
||||
string filename = string_printf("system/licenses/%010" PRIu32 ".json", this->serial_number);
|
||||
remove(filename.c_str());
|
||||
}
|
||||
|
||||
string License::str() const {
|
||||
string ret = string_printf("License(serial_number=%" PRIu32, this->serial_number);
|
||||
if (!this->username.empty()) {
|
||||
ret += ", username=";
|
||||
ret += this->username;
|
||||
}
|
||||
if (!this->bb_password.empty()) {
|
||||
ret += ", bb-password=";
|
||||
ret += this->bb_password;
|
||||
}
|
||||
vector<string> tokens;
|
||||
tokens.emplace_back(string_printf("serial_number=%010" PRIu32 "/%08" PRIX32, this->serial_number, this->serial_number));
|
||||
if (!this->access_key.empty()) {
|
||||
ret += ", access-key=";
|
||||
ret += this->access_key;
|
||||
tokens.emplace_back("access_key=" + this->access_key);
|
||||
}
|
||||
if (!this->gc_password.empty()) {
|
||||
ret += ", gc-password=";
|
||||
ret += this->gc_password;
|
||||
tokens.emplace_back("gc_password=" + this->gc_password);
|
||||
}
|
||||
ret += string_printf(", privileges=%" PRIu32, this->privileges);
|
||||
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) {
|
||||
ret += string_printf(", banned-until=%" PRIu64, this->ban_end_time);
|
||||
tokens.emplace_back(string_printf("ban_end_time=%016" PRIX64, this->ban_end_time));
|
||||
}
|
||||
return ret + ")";
|
||||
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, ", ") + "]";
|
||||
}
|
||||
|
||||
LicenseManager::LicenseManager()
|
||||
: filename(""),
|
||||
autosave(false) {}
|
||||
struct BinaryLicense {
|
||||
ptext<char, 0x14> username; // BB username (max. 16 chars; should technically be Unicode)
|
||||
ptext<char, 0x14> 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.
|
||||
ptext<char, 0x10> 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)
|
||||
ptext<char, 0x0C> gc_password; // GC password
|
||||
uint32_t privileges; // privilege level
|
||||
uint64_t ban_end_time; // end time of ban (zero = not banned)
|
||||
} __attribute__((packed));
|
||||
|
||||
LicenseManager::LicenseManager(const string& filename)
|
||||
: filename(filename),
|
||||
autosave(true) {
|
||||
try {
|
||||
auto licenses = load_vector_file<License>(this->filename);
|
||||
for (const auto& read_license : licenses) {
|
||||
shared_ptr<License> license(new License(read_license));
|
||||
LicenseIndex::LicenseIndex() : autosave(true) {
|
||||
if (!isdir("system/licenses")) {
|
||||
mkdir("system/licenses", 0755);
|
||||
}
|
||||
|
||||
// Before the temporary flag existed, licenses with root privileges would
|
||||
// have the temporary flag set. To migrate these, explicitly unset the
|
||||
// flag for all licenses loaded from the license file.
|
||||
license->privileges &= ~Privilege::TEMPORARY;
|
||||
|
||||
uint32_t serial_number = license->serial_number;
|
||||
this->bb_username_to_license.emplace(license->username, license);
|
||||
this->serial_number_to_license.emplace(serial_number, license);
|
||||
// Convert binary licenses to JSON licenses and save them
|
||||
if (isfile("system/licenses.nsi")) {
|
||||
auto bin_licenses = load_vector_file<BinaryLicense>("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;
|
||||
license.gc_password = bin_license.gc_password;
|
||||
license.bb_username = bin_license.username;
|
||||
license.bb_password = bin_license.bb_password;
|
||||
license.flags = bin_license.privileges & (~License::Flag::TEMPORARY);
|
||||
license.ban_end_time = bin_license.ban_end_time;
|
||||
license.ep3_current_meseta = 0;
|
||||
license.ep3_total_meseta_earned = 0;
|
||||
license.save();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const cannot_open_file&) {
|
||||
license_log.warning("File %s does not exist; no licenses are registered",
|
||||
this->filename.c_str());
|
||||
::remove("system/licenses.nsi");
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseManager::save() const {
|
||||
if (this->filename.empty()) {
|
||||
throw logic_error("license manager has no filename; cannot save");
|
||||
}
|
||||
auto f = fopen_unique(this->filename, "wb");
|
||||
for (const auto& it : this->serial_number_to_license) {
|
||||
if (it.second->privileges & Privilege::TEMPORARY) {
|
||||
continue;
|
||||
for (const auto& item : list_directory("system/licenses")) {
|
||||
if (ends_with(item, ".json")) {
|
||||
JSON json = JSON::parse(load_file("system/licenses/" + item));
|
||||
shared_ptr<License> license(new License(json));
|
||||
this->add(license);
|
||||
}
|
||||
fwritex(f.get(), it.second.get(), sizeof(License));
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseManager::set_autosave(bool autosave) {
|
||||
void LicenseIndex::set_autosave(bool autosave) {
|
||||
this->autosave = autosave;
|
||||
}
|
||||
|
||||
shared_ptr<const License> LicenseManager::verify_pc(uint32_t serial_number,
|
||||
const string& access_key) const {
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (!license->access_key.eq_n(access_key, 8)) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
size_t LicenseIndex::count() const {
|
||||
return this->serial_number_to_license.size();
|
||||
}
|
||||
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
throw invalid_argument("user is banned");
|
||||
}
|
||||
return license;
|
||||
shared_ptr<License> 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<const License> LicenseManager::verify_gc(uint32_t serial_number,
|
||||
const string& access_key) const {
|
||||
vector<shared_ptr<License>> LicenseIndex::all() const {
|
||||
vector<shared_ptr<License>> 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<License> l) {
|
||||
this->serial_number_to_license[l->serial_number] = l;
|
||||
if (!l->bb_username.empty()) {
|
||||
this->bb_username_to_license[l->bb_username] = 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);
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseIndex::verify_v1_v2(uint32_t serial_number, const string& access_key) const {
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (!license->access_key.eq_n(access_key, 12)) {
|
||||
if (license->access_key.compare(0, 8, access_key) != 0) {
|
||||
throw incorrect_access_key();
|
||||
}
|
||||
if (license->ban_end_time && (license->ban_end_time >= now())) {
|
||||
@@ -118,11 +187,25 @@ shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
|
||||
const string& access_key, const string& password) const {
|
||||
shared_ptr<License> LicenseIndex::verify_gc(uint32_t serial_number, const string& access_key) const {
|
||||
try {
|
||||
auto& license = this->serial_number_to_license.at(serial_number);
|
||||
if (!license->access_key.eq_n(access_key, 12)) {
|
||||
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<License> LicenseIndex::verify_gc(uint32_t serial_number, const string& access_key, const string& password) const {
|
||||
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) {
|
||||
@@ -137,14 +220,12 @@ shared_ptr<const License> LicenseManager::verify_gc(uint32_t serial_number,
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const License> LicenseManager::verify_bb(const string& username,
|
||||
const string& password) const {
|
||||
shared_ptr<License> LicenseIndex::verify_bb(const string& username, const string& password) const {
|
||||
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");
|
||||
}
|
||||
@@ -153,88 +234,3 @@ shared_ptr<const License> LicenseManager::verify_bb(const string& username,
|
||||
throw missing_license();
|
||||
}
|
||||
}
|
||||
|
||||
size_t LicenseManager::count() const {
|
||||
return this->serial_number_to_license.size();
|
||||
}
|
||||
|
||||
void LicenseManager::ban_until(uint32_t serial_number, uint64_t end_time) {
|
||||
this->serial_number_to_license.at(serial_number)->ban_end_time = end_time;
|
||||
if (this->autosave) {
|
||||
this->save();
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const License> LicenseManager::get(uint32_t serial_number) const {
|
||||
try {
|
||||
return this->serial_number_to_license.at(serial_number);
|
||||
} catch (const out_of_range&) {
|
||||
throw missing_license();
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseManager::add(shared_ptr<License> l) {
|
||||
this->serial_number_to_license[l->serial_number] = l;
|
||||
if (!l->username.empty()) {
|
||||
this->bb_username_to_license[l->username] = l;
|
||||
}
|
||||
if (this->autosave) {
|
||||
this->save();
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseManager::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->username.empty()) {
|
||||
this->bb_username_to_license.erase(l->username);
|
||||
}
|
||||
if (this->autosave) {
|
||||
this->save();
|
||||
}
|
||||
}
|
||||
|
||||
vector<License> LicenseManager::snapshot() const {
|
||||
vector<License> ret;
|
||||
for (auto it : this->serial_number_to_license) {
|
||||
ret.emplace_back(*it.second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseManager::create_license_pc(
|
||||
uint32_t serial_number, const string& access_key, bool temporary) {
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = access_key;
|
||||
if (temporary) {
|
||||
l->privileges |= Privilege::TEMPORARY;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseManager::create_license_gc(
|
||||
uint32_t serial_number, const string& access_key, const string& password,
|
||||
bool temporary) {
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->access_key = access_key;
|
||||
l->gc_password = password;
|
||||
if (temporary) {
|
||||
l->privileges |= Privilege::TEMPORARY;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
shared_ptr<License> LicenseManager::create_license_bb(
|
||||
uint32_t serial_number, const string& username, const string& password,
|
||||
bool temporary) {
|
||||
shared_ptr<License> l(new License());
|
||||
l->serial_number = serial_number;
|
||||
l->username = username;
|
||||
l->bb_password = password;
|
||||
if (temporary) {
|
||||
l->privileges |= Privilege::TEMPORARY;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user