// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved // Please see LICENSE.md in repository root for license information // https://github.com/AtomicGameEngine/AtomicGameEngine // BEGIN LICENSE MANAGEMENT #ifdef ATOMIC_PLATFORM_WINDOWS #ifndef _MSC_VER #define _WIN32_IE 0x501 #endif #include #include #include #include #include #include #endif #include #include #include #include #include #include "LicenseSystem.h" #include #include namespace ToolCore { LicenseSystem::LicenseSystem(Context* context) : Object(context) , eulaAgreementConfirmed_(false) { ResetLicense(); } LicenseSystem::~LicenseSystem() { } void LicenseSystem::Initialize() { FileSystem* filesystem = GetSubsystem(); String eulaConfirmedFilePath = filesystem->GetAppPreferencesDir("AtomicEditor", "License"); eulaConfirmedFilePath = AddTrailingSlash(eulaConfirmedFilePath); eulaConfirmedFilePath += "EulaConfirmed"; eulaAgreementConfirmed_ = filesystem->FileExists(eulaConfirmedFilePath); if (!LoadLicense() || !key_.Length() || !eulaAgreementConfirmed_) { ResetLicense(); /* UIModalOps* ops = GetSubsystem(); if (eulaAgreementConfirmed_) ops->ShowActivation(); else ops->ShowEulaAgreement(); */ } else { RequestServerVerification(key_); } } void LicenseSystem::LicenseAgreementConfirmed() { eulaAgreementConfirmed_ = true; FileSystem* filesystem = GetSubsystem(); String eulaConfirmedFilePath = filesystem->GetAppPreferencesDir("AtomicEditor", "License"); eulaConfirmedFilePath = AddTrailingSlash(eulaConfirmedFilePath); eulaConfirmedFilePath += "EulaConfirmed"; SharedPtr file(new File(context_, eulaConfirmedFilePath, FILE_WRITE)); file->WriteInt(1); file->Close(); /* UIModalOps* ops = GetSubsystem(); ops->ShowActivation(); */ } String LicenseSystem::GenerateMachineID() { #ifdef ATOMIC_PLATFORM_OSX String path = getenv("HOME"); #else wchar_t pathName[MAX_PATH]; pathName[0] = 0; SHGetSpecialFolderPathW(0, pathName, CSIDL_PERSONAL, 0); String path(pathName); #endif Poco::MD5Engine md5; md5.update(path.CString(), path.Length()); String id = Poco::MD5Engine::digestToHex(md5.digest()).c_str(); return id; } void LicenseSystem::ResetLicense() { key_ = ""; licenseWindows_ = false; licenseMac_ = false; licenseAndroid_ = false; licenseIOS_ = false; licenseHTML5_ = false; licenseModule3D_ = false; } bool LicenseSystem::LoadLicense() { ResetLicense(); FileSystem* filesystem = GetSubsystem(); String licenseFilePath = filesystem->GetAppPreferencesDir("AtomicEditor", "License"); licenseFilePath = AddTrailingSlash(licenseFilePath); licenseFilePath += "AtomicLicense"; if (!filesystem->FileExists(licenseFilePath)) return false; SharedPtr file(new File(context_, licenseFilePath, FILE_READ)); file->ReadInt(); // version String key = file->ReadString(); if (!ValidateKey(key)) return false; key_ = key; licenseWindows_ = file->ReadBool(); licenseMac_ = file->ReadBool(); licenseAndroid_ = file->ReadBool(); licenseIOS_ = file->ReadBool(); licenseHTML5_ = file->ReadBool(); licenseModule3D_ = file->ReadBool(); return true; } bool LicenseSystem::ValidateKey(const String& key) { if (!key.StartsWith("ATOMIC-")) return false; Vector elements = key.Split('-'); if (elements.Size() != 5) return false; for (unsigned i = 1; i < elements.Size(); i++) { String element = elements[i]; if (element.Length() != 4) return false; for (unsigned j = 0; j < 4; j++) { char c = element[j]; if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) continue; return false; } } return true; } void LicenseSystem::SaveLicense() { FileSystem* filesystem = GetSubsystem(); String licenseFilePath = filesystem->GetAppPreferencesDir("AtomicEditor", "License"); licenseFilePath = AddTrailingSlash(licenseFilePath); if (!filesystem->DirExists(licenseFilePath)) { Poco::File dirs(licenseFilePath.CString()); dirs.createDirectories(); } licenseFilePath += "AtomicLicense"; SharedPtr file(new File(context_, licenseFilePath, FILE_WRITE)); file->WriteInt(1); // version file->WriteString(key_); file->WriteBool(licenseWindows_); file->WriteBool(licenseMac_); file->WriteBool(licenseAndroid_); file->WriteBool(licenseIOS_); file->WriteBool(licenseHTML5_); file->WriteBool(licenseModule3D_); file->Close(); } void LicenseSystem::RemoveLicense() { FileSystem* filesystem = GetSubsystem(); String licenseFilePath = filesystem->GetAppPreferencesDir("AtomicEditor", "License"); licenseFilePath = AddTrailingSlash(licenseFilePath); licenseFilePath += "AtomicLicense"; if (filesystem->FileExists(licenseFilePath)) { filesystem->Delete(licenseFilePath); } } bool LicenseSystem::IsStandardLicense() { return !licenseAndroid_; } void LicenseSystem::RequestServerVerification(const String& key) { if (serverVerification_.NotNull()) { LOGERROR("LicenseSystem::RequestServerLicense - request already exists"); return; } key_ = key; CurlManager* cm = GetSubsystem(); String post; String id = GenerateMachineID(); post.AppendWithFormat("key=%s&id=%s", key.CString(), id.CString()); serverVerification_ = cm->MakeRequest("https://store.atomicgameengine.com/licenses/license_verify.php", post); SubscribeToEvent(serverVerification_, E_CURLCOMPLETE, HANDLER(LicenseSystem, HandleVerification)); } int LicenseSystem::ParseResponse(const String& response, LicenseParse& parse) { LOGINFOF("%s", response.CString()); if (response.StartsWith("AC_ACTIVATIONSEXCEEDED")) { return 1; } if (response.StartsWith("AC_IDNOTACTIVATED")) { return 4; } if (response.StartsWith("AC_FAILED")) { return 2; } if (!response.StartsWith("WINDOWS")) { LOGERRORF("Error Parsing Server Response %s", response.CString()); return 3; } String codes = response; codes.Replace("\n", ""); codes.Replace("\r", ""); Vector cvector = codes.Split(' '); for (unsigned i = 0; i < cvector.Size(); i++) { Vector feature = cvector[i].Split('='); if (feature.Size() != 2) continue; if (feature[0] == "WINDOWS") parse.licenseWindows_ = !feature[1].StartsWith("0"); else if (feature[0] == "MAC") parse.licenseMac_ = !feature[1].StartsWith("0"); else if (feature[0] == "ANDROID") parse.licenseAndroid_ = !feature[1].StartsWith("0"); else if (feature[0] == "IOS") parse.licenseIOS_ = !feature[1].StartsWith("0"); else if (feature[0] == "HTML5") parse.licenseHTML5_ = !feature[1].StartsWith("0"); else if (feature[0] == "THREED") parse.licenseModule3D_ = !feature[1].StartsWith("0"); } return 0; } void LicenseSystem::Activate(const String& key, const LicenseParse& parse) { key_ = key; licenseWindows_ = parse.licenseWindows_; licenseMac_ = parse.licenseMac_; licenseAndroid_ = parse.licenseAndroid_; licenseIOS_= parse.licenseIOS_; licenseHTML5_= parse.licenseHTML5_; licenseModule3D_= parse.licenseModule3D_; SaveLicense(); } SharedPtr& LicenseSystem::Deactivate() { if (deactivate_.NotNull()) { LOGERROR("LicenseSystem::Deactivate - request already exists"); return deactivate_; } if (!key_.Length()) { LOGERROR("LicenseSystem::Deactivate - zero length key"); return deactivate_; } CurlManager* cm = GetSubsystem(); String post; String id = GenerateMachineID(); post.AppendWithFormat("key=%s&id=%s", key_.CString(), id.CString()); deactivate_ = cm->MakeRequest("https://store.atomicgameengine.com/licenses/license_deactivate.php", post); SubscribeToEvent(deactivate_, E_CURLCOMPLETE, HANDLER(LicenseSystem, HandleDeactivate)); return deactivate_; } void LicenseSystem::HandleVerification(StringHash eventType, VariantMap& eventData) { CurlRequest* request = (CurlRequest*) (eventData[CurlComplete::P_CURLREQUEST].GetPtr()); if (serverVerification_.NotNull()) { assert(request == serverVerification_); if (serverVerification_->GetError().Length()) { LOGERRORF("Unable to verify with server: %s", serverVerification_->GetError().CString()); } else { LicenseParse parse; int code = ParseResponse(serverVerification_->GetResponse(), parse); // Not activated if (code == 4) { } else if (code == 2) { // something is wrong with the key LOGERRORF("Error with product key"); RemoveLicense(); ResetLicense(); /* UIModalOps* ops = GetSubsystem(); ops->Hide(); ops->ShowActivation(); */ } else if (code == 3) { // something is wrong on the activation server key_ = ""; } else if (code == 1) { // exceeded code, should not happen here as we aren't activating key_ = ""; } else if (code == 0) { // we should raise an error if there is a mismatch between local and server keys // when the local says there are more enabled than server? // otherwise, they could be being added bool mismatch = false; if (parse.licenseWindows_ != licenseWindows_) mismatch = true; if (parse.licenseMac_ != licenseMac_) mismatch = true; if (parse.licenseWindows_ != licenseWindows_) mismatch = true; if (parse.licenseAndroid_ != licenseAndroid_) mismatch = true; if (parse.licenseIOS_ != licenseIOS_) mismatch = true; if (parse.licenseHTML5_ != licenseHTML5_) mismatch = true; if (parse.licenseModule3D_ != licenseModule3D_) mismatch = true; if (mismatch) { LOGERROR("License Mismatch, reseting"); licenseWindows_ = parse.licenseWindows_; licenseMac_ = parse.licenseMac_; licenseAndroid_ = parse.licenseAndroid_; licenseIOS_= parse.licenseIOS_; licenseHTML5_= parse.licenseHTML5_; licenseModule3D_= parse.licenseModule3D_; SaveLicense(); } //if (!HasPlatformLicense()) //{ // UIModalOps* ops = GetSubsystem(); // if (!ops->ModalActive()) // ops->ShowPlatformsInfo(); // } } } UnsubscribeFromEvents(serverVerification_); serverVerification_ = 0; } } void LicenseSystem::HandleDeactivate(StringHash eventType, VariantMap& eventData) { CurlRequest* request = (CurlRequest*) (eventData[CurlComplete::P_CURLREQUEST].GetPtr()); if (deactivate_.NotNull()) { assert(request == deactivate_); if (deactivate_->GetError().Length()) { String msg; msg.AppendWithFormat("Unable to deactivate with server: %s", deactivate_->GetError().CString()); //editor->PostModalError("Deactivation Error", msg); LOGERROR(msg); } else { String response = request->GetResponse(); if (response.StartsWith("AC_FAILED")) { String msg; msg.AppendWithFormat("Unable to deactivate with server: %s", response.CString()); //editor->PostModalError("Deactivation Error", msg); LOGERROR(msg); } else if (response.StartsWith("AC_NOTACTIVATED") || response.StartsWith("AC_SUCCESS")) { ResetLicense(); RemoveLicense(); /* UIModalOps* ops = GetSubsystem(); ops->Hide(); ops->ShowActivation(); */ } } UnsubscribeFromEvents(deactivate_); deactivate_ = 0; } } } // END LICENSE MANAGEMENT