#include "ANSUtilities.h" #ifdef _WIN32 #include #endif #include #include #include #include #include #include #include #include #include static bool ansutilityLicenceValid = false; static std::once_flag ansutilityLicenseOnceFlag; std::timed_mutex timeImageMutex; namespace ANSCENTER { static void VerifyGlobalANSUltilityLicense(const std::string& licenseKey) { try { static const std::vector> licenseChecks = { {1000, "ANNHUB-LV"}, {1001, "DLHUB-LV"}, {1002, "ODHUB-LV"}, {1003, "ANSVIS"}, {1004, "ANSFR"}, {1005, "ANSOCR"}, {1006, "ANSALPR"}, {1007, "ANSCV"}, {1008, "ANSSRT"} }; ansutilityLicenceValid = false; for (const auto& [productId, productName] : licenseChecks) { if (ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, productId, productName)) { ansutilityLicenceValid = true; break; // Stop at the first valid license } } } catch (const std::exception& e) { ansutilityLicenceValid = false; } } ANSUtilities::ANSUtilities() { _unlockCode = "ANSDRC.CB1122026_MEQCIFwO1IFQCG0BhZwsXFO68QUU6mDB5uge4duOsqOJanEyAiAB67ahqnXin4SRy0vIegISgbFlpldmbuS5gbU21GYVqA==";// "ANSDRC.CB1082025_Ax6P3M7F8B3d";// _fireBaseSessionToken = ""; _proxyHost = ""; _proxyPort = 0; _proxyUsername = ""; _proxyPassword = ""; _bProxy = false; } void ANSUtilities::CheckLicense() { std::lock_guard lock(_mutex); try { // Check once globally std::call_once(ansutilityLicenseOnceFlag, [this]() { VerifyGlobalANSUltilityLicense(_licenseKey); }); // Update this instance's local license flag _isLicenseValid = ansutilityLicenceValid; } catch (const std::exception& e) { this->_logger.LogFatal("ANSUtilities::CheckLicense. Error:", e.what(), __FILE__, __LINE__); } } void ANSUtilities::CheckUnlockCode() { std::lock_guard lock(_mutex); try { CkGlobal glob; _unlockCode = "ANSDRC.CB1122026_MEQCIFwO1IFQCG0BhZwsXFO68QUU6mDB5uge4duOsqOJanEyAiAB67ahqnXin4SRy0vIegISgbFlpldmbuS5gbU21GYVqA==";// "ANSDRC.CB1082025_Ax6P3M7F8B3d"; _isUnlockCodeValid = glob.UnlockBundle(_unlockCode.c_str()); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::CheckUnlockCode", glob.lastErrorText(), __FILE__, __LINE__); return; } int status = glob.get_UnlockStatus(); if (status != 2) { _logger.LogDebug("ANSUtilities::CheckUnlockCode", "Unlocked in trial mode.", __FILE__, __LINE__); } } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::CheckUnlockCode", e.what(), __FILE__, __LINE__); } } ANSUtilities::~ANSUtilities() { _fireBaseSessionToken = ""; } bool ANSUtilities::Initialize(const std::string& licenseKey) { std::lock_guard lock(_mutex); try { _licenseKey = licenseKey; CheckLicense(); CheckUnlockCode(); return _isLicenseValid && _isUnlockCodeValid; } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::Initialize", e.what(), __FILE__, __LINE__); return false; } } bool ANSUtilities::SetServerProxy(const std::string& proxyHost, int proxyPort, const std::string& proxyUsername, const std::string& proxyPassword) { std::lock_guard lock(_mutex); try { _proxyHost = proxyHost; _proxyPort = proxyPort; _proxyUsername = proxyUsername; _proxyPassword = proxyPassword; _bProxy = false; if (!_proxyHost.empty() && (_proxyPort > 0)) { _bProxy = true; } else { _bProxy = false; } return true; } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::SetServerProxy", e.what(), __FILE__, __LINE__); return false; } } std::string ANSUtilities::GetFirebaseCloudMessageAcccessToken(std::string privateKey) { std::lock_guard lock(_mutex); if (!_isLicenseValid) { this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid license key", __FILE__, __LINE__); return "Error: Invalid license key"; } if (!_isUnlockCodeValid) { this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid unlock code", __FILE__, __LINE__); return "Error: Invalid unlock code"; } try { auto currentTime = std::chrono::system_clock::now(); // Check if we need to generate a new token bool needNewToken = false; if (!tokenInitialized) { std::cout << "First time calling getToken(), generating initial token..." << std::endl; needNewToken = true; } else { // Calculate time difference in seconds since token was generated auto timeDiff = std::chrono::duration_cast(currentTime - tokenGeneratedTime); long secondsElapsed = timeDiff.count(); std::cout << "Time since token generated: " << secondsElapsed << " seconds" << std::endl; if (secondsElapsed >= TOKEN_EXPIRY_SECONDS) { std::cout << "Token expired (" << secondsElapsed << "s >= " << TOKEN_EXPIRY_SECONDS << "s), regenerating..." << std::endl; needNewToken = true; } else { long remainingTime = TOKEN_EXPIRY_SECONDS - secondsElapsed; std::cout << "Token still valid, " << remainingTime << " seconds remaining" << std::endl; } } if (needNewToken) { CkFileAccess fac; const char* jsonKey = "{" "\"type\": \"service_account\"," "\"project_id\": \"ansaiboxpushnotifications\"," "\"private_key_id\": \"62f7485c308b9ea613866c24c98736fec498d47d\"," "\"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDSwXb/N4vPkANa\\nA7nwOTxS9NVCup/9mIdyR7FRYJNV8uHY7699I8LCCvG5rIhWGZ/Rb4Z9ocJKuuj9\\nRelsjTDEWeBU/o8ip41Ru8K3KlOO25HIVDckyhbmfDzzlf8Ham1HhOUZmfDUfQ2d\\n5YoUXflqoCyvOi8aqUgfcP9uZuIpEhyQvjqDfY0d0B36NoXpilFE8IuIlBsnpfZn\\nYkeHs/Cl6CDaKxGeUZmrqinyMH5f7m4Nz30V5s8Xdl4WZdoYhVatn1or57Zjy/zl\\ng+4rWeHRbYrBUJlMb8/7IaaZ7KuDkh0awEFIDvLuNvjXkaGKJT33vxKSsaH4yrhg\\nYiR5OVY3AgMBAAECggEADiUS5f0l4ofhWbS/UXqd7FlnSMO6wivvB0H9ih8ntFCJ\\nTOSFTCpOw3Q1lgcY3WJ54fYQujTVk+togLsk9/af68W2cy3kkGhbaT1nS6DJG+Dr\\nr1zLmKoBkHWNJ7IM/EPt0qt+LtIwoipEdDD4K/bEqx3V8eq/R5RN9WJBmnjIPAZO\\nXYGtTBu66yTjMVX9AFqYGJwaW96mHAi3lI5Mso5wTmFA8tSBiWmiYgpDDUNzu+4w\\nPnbDJabMbv4xnC6UtiqLIvv+P3BWWyLz0bzZ7W9P2fGK9Rz04leTXZlNcfC/IIAr\\nXDPVZfF7E5Wici53Cwc/QnxoCVH9n9hqsyTTUrKYbQKBgQD9WuCLVZRwKnAsW2Wx\\nZjh8Z92aGjIoc/lJTYaFsYGT7Hx496izhmg18kUillUDe/L9gHuDJPIMv89irjV1\\n07jN+z+tANyFT+h/NHCSxreG8FDbo8F88snekMEsBym3CicE/P6aWVx+lIig8rPS\\nHAqA1WOwdFzto/rG4zFKIiajYwKBgQDU9LxgERHwoy8mt2U5h/hg4oiKlycFJCFV\\nsgEqhVFN7Cs8jlov4tJh9863tVeYYGSKrP1HS9dVUE+7VY2qgPbyAtR5QZeHIV+b\\nzMx8o4HVLN4ffmld+/mYiAzIQiQeZOoC0mczakQpRDE8sghQQoYLwEcKXcTkLr75\\nk1XIpT4cHQKBgQCmMSzGeZbrlQsMLdAhdHptMPzuj2yDmL/X0+EAZhYn4KMt/tdN\\nHEfTy16Kd67AoFge7l8XAe89ab0ycDBlYEMD62IzrDL7yBUtDEskHPJas912lo7f\\n1auSMcZliTVV+nTqEsM4oJHJ/sk5Oru2gepp5JCGOW6T/FMOkA3PIWPTHQKBgQCG\\n456Om0Fp03OCaphLoLzLYbJrVuL4drJGvcHPVTLy0K1yZhjqTBpGw9jEtLEPa79D\\nt9+W0YtMFtrqJn7diWLiWLiNNebtSU5uOYMtT8Rla04nVMMZLQoke8jc8EhAmFtB\\n/lQwVRdnrDIj6AEsFXci6mAVSN/2SUXegFzOAx0cYQKBgBYCGGKWd/EFvszO5VS6\\nnegR3fpx4GRShA4iWfbQhPcjPunKthUAUguN4m+FfQuSA6xaBsz+om9hRiSCHe2l\\nGio/U7z2QYznnl/furGXEhS6yE+u3cSN82N0PV/c4XY0DUqKycE6cx9GVTOmx7NP\\n897fh7f103RrGgIRivtM1g3g\\n-----END PRIVATE KEY-----\\n\"," "\"client_email\": \"firebase-adminsdk-tcw8f@ansaiboxpushnotifications.iam.gserviceaccount.com\"," "\"client_id\": \"108921284506387607836\"," "\"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\"," "\"token_uri\": \"https://oauth2.googleapis.com/token\"," "\"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\"," "\"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-tcw8f%40ansaiboxpushnotifications.iam.gserviceaccount.com\"," "\"universe_domain\": \"googleapis.com\"" "}"; if (!_isLicenseValid) { this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid license key", __FILE__, __LINE__); _fireBaseSessionToken = ""; return "Error: Invalid license key"; } if (!_isUnlockCodeValid) { this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid unlock code", __FILE__, __LINE__); _fireBaseSessionToken = ""; return "Error: Invalid unlock code"; } if (privateKey != "62f7485c308b9ea613866c24c98736fec498d47d") { this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid private key", __FILE__, __LINE__); _fireBaseSessionToken = ""; return "Error: Invalid private key"; } CkAuthGoogle gAuth; gAuth.put_JsonKey(jsonKey); gAuth.put_Scope("https://www.googleapis.com/auth/cloud-platform"); gAuth.put_ExpireNumSeconds(TOKEN_EXPIRY_SECONDS); gAuth.put_SubEmailAddress(""); CkSocket tlsSock; bool success = tlsSock.Connect("www.googleapis.com", 443, true, 5000); if (success != true) { this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", tlsSock.lastErrorText(), __FILE__, __LINE__); _fireBaseSessionToken = ""; return std::string("Error:") + tlsSock.lastErrorText(); } success = gAuth.ObtainAccessToken(tlsSock); if (success != true) { this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", gAuth.lastErrorText(), __FILE__, __LINE__); _fireBaseSessionToken = ""; return std::string("Error:") + tlsSock.lastErrorText(); } _fireBaseSessionToken = gAuth.accessToken(); tokenGeneratedTime = currentTime; // Reset the time when token is generated tokenInitialized = true; } return _fireBaseSessionToken; } catch (std::exception& e) { this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", e.what(), __FILE__, __LINE__); _fireBaseSessionToken = ""; return "Error: " + std::string(e.what()); } } std::string ANSUtilities::CreateAWSSNSTopic(const std::string& topicName) { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::CreateAWSSNSTopic", "Error: Invalid unlock code", __FILE__, __LINE__); return "Error: Invalid unlock code"; } if (!_isLicenseValid) { _logger.LogFatal("ANSUtilities::CreateAWSSNSTopic", "Error: Invalid license key", __FILE__, __LINE__); return "Error: Invalid license key"; } try { CkRest _rest; _rest.AddQueryParam("Action", "CreateTopic"); _rest.AddQueryParam("Name", topicName.c_str()); const char* responseXml = _rest.fullRequestNoBody("GET", "/"); if (!_rest.get_LastMethodSuccess()) { _logger.LogFatal("ANSUtilities::CreateAWSSNSTopic", _rest.lastErrorText(), __FILE__, __LINE__); return "Error: " + std::string(_rest.lastErrorText()); } if (_rest.get_ResponseStatusCode() != 200) { _logger.LogError("ANSUtilities::CreateAWSSNSTopic", _rest.responseHeader(), __FILE__, __LINE__); return "Error: " + std::string(_rest.responseHeader()); } CkXml xml; xml.LoadXml(responseXml); return xml.chilkatPath("CreateTopicResult|TopicArn|*"); } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::CreateAWSSNSTopic", e.what(), __FILE__, __LINE__); return "Error: " + std::string(e.what()); } } bool ANSUtilities::DeleteAWSSNSTopic(const std::string& topicARN) { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::DeleteAWSSNSTopic", "Error: Invalid unlock code", __FILE__, __LINE__); return false; } if (!_isLicenseValid) { _logger.LogFatal("ANSUtilities::DeleteAWSSNSTopic", "Error: Invalid license key", __FILE__, __LINE__); return false; } try { CkRest _rest; _rest.AddQueryParam("Action", "DeleteTopic"); _rest.AddQueryParam("TopicArn", topicARN.c_str()); const char* responseXml = _rest.fullRequestNoBody("GET", "/"); if (!_rest.get_LastMethodSuccess()) { _logger.LogFatal("ANSUtilities::DeleteAWSSNSTopic", _rest.lastErrorText(), __FILE__, __LINE__); return false; } if (_rest.get_ResponseStatusCode() != 200) { _logger.LogError("ANSUtilities::DeleteAWSSNSTopic", _rest.responseHeader(), __FILE__, __LINE__); return false; } return true; } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::DeleteAWSSNSTopic", e.what(), __FILE__, __LINE__); return false; } } std::string ANSUtilities::SubcribeSMSPhoneNumber(const std::string& topicARN, const std::string& phoneNumber) { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::SubscribeSMSPhoneNumber", "Error: Invalid unlock code", __FILE__, __LINE__); return "Error: Invalid unlock code"; } if (!_isLicenseValid) { _logger.LogFatal("ANSUtilities::SubscribeSMSPhoneNumber", "Error: Invalid license key", __FILE__, __LINE__); return "Error: Invalid license key"; } try { CkRest _rest; _rest.AddQueryParam("Action", "Subscribe"); _rest.AddQueryParam("Endpoint", phoneNumber.c_str()); // Phone number in E.164 format _rest.AddQueryParam("Protocol", "sms"); _rest.AddQueryParam("TopicArn", topicARN.c_str()); const char* responseXml = _rest.fullRequestNoBody("GET", "/"); if (!_rest.get_LastMethodSuccess()) { _logger.LogError("ANSUtilities::SubscribeSMSPhoneNumber", _rest.lastErrorText(), __FILE__, __LINE__); return "Error: " + std::string(_rest.lastErrorText()); } if (_rest.get_ResponseStatusCode() != 200) { _logger.LogError("ANSUtilities::SubscribeSMSPhoneNumber", _rest.responseHeader(), __FILE__, __LINE__); return "Error: " + std::string(_rest.responseHeader()); } CkXml xml; xml.LoadXml(responseXml); return xml.chilkatPath("SubscribeResult|SubscriptionArn|*"); } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::SubscribeSMSPhoneNumber", e.what(), __FILE__, __LINE__); return "Error: " + std::string(e.what()); } } std::string ANSUtilities::SubscribeEmailAddress(const std::string& topicARN, const std::string& emailAddress) { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::SubscribeEmailAddress", "Error: Invalid unlock code", __FILE__, __LINE__); return "Error: Invalid unlock code"; } if (!_isLicenseValid) { _logger.LogFatal("ANSUtilities::SubscribeEmailAddress", "Error: Invalid license key", __FILE__, __LINE__); return "Error: Invalid license key"; } try { CkRest _rest; _rest.AddQueryParam("Action", "Subscribe"); _rest.AddQueryParam("Endpoint", emailAddress.c_str()); _rest.AddQueryParam("Protocol", "email"); _rest.AddQueryParam("TopicArn", topicARN.c_str()); const char* responseXml = _rest.fullRequestNoBody("GET", "/"); if (!_rest.get_LastMethodSuccess()) { _logger.LogError("ANSUtilities::SubscribeEmailAddress", _rest.lastErrorText(), __FILE__, __LINE__); return "Error: " + std::string(_rest.lastErrorText()); } if (_rest.get_ResponseStatusCode() != 200) { _logger.LogError("ANSUtilities::SubscribeEmailAddress", _rest.responseHeader(), __FILE__, __LINE__); return "Error: " + std::string(_rest.responseHeader()); } CkXml xml; xml.LoadXml(responseXml); return xml.chilkatPath("SubscribeResult|SubscriptionArn|*"); // Usually "pending confirmation" } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::SubscribeEmailAddress", e.what(), __FILE__, __LINE__); return "Error: " + std::string(e.what()); } } std::string ANSUtilities::SendMessageToTopic(const std::string& topicARN, const std::string& subjectContent, const std::string& messageContent) { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::SendMessageToTopic", "Error: Invalid unlock code", __FILE__, __LINE__); return "Error: Invalid unlock code"; } if (!_isLicenseValid) { _logger.LogFatal("ANSUtilities::SendMessageToTopic", "Error: Invalid license key", __FILE__, __LINE__); return "Error: Invalid license key"; } try { CkRest _rest; _rest.AddQueryParam("Action", "Publish"); _rest.AddQueryParam("TopicArn", topicARN.c_str()); _rest.AddQueryParam("Subject", subjectContent.c_str()); // Subject line for emails _rest.AddQueryParam("Message", messageContent.c_str()); // The message body const char* responseXml = _rest.fullRequestNoBody("GET", "/"); if (!_rest.get_LastMethodSuccess()) { _logger.LogError("ANSUtilities::SendMessageToTopic", _rest.lastErrorText(), __FILE__, __LINE__); return "Error: " + std::string(_rest.lastErrorText()); } if (_rest.get_ResponseStatusCode() != 200) { _logger.LogError("ANSUtilities::SendMessageToTopic", _rest.responseHeader(), __FILE__, __LINE__); return "Error: " + std::string(_rest.responseHeader()); } CkXml xml; xml.LoadXml(responseXml); return xml.chilkatPath("PublishResult|MessageId|*"); // Return the MessageId } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::SendMessageToTopic", e.what(), __FILE__, __LINE__); return "Error: " + std::string(e.what()); } } std::string ANSUtilities::SendMessageToPhoneNumber(const std::string& phoneNumber, const std::string& messageContent) { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::SendMessageToPhoneNumber", "Error: Invalid unlock code", __FILE__, __LINE__); return "Error: Invalid unlock code"; } if (!_isLicenseValid) { _logger.LogFatal("ANSUtilities::SendMessageToPhoneNumber", "Error: Invalid license key", __FILE__, __LINE__); return "Error: Invalid license key"; } try { CkRest _rest; _rest.AddQueryParam("Action", "Publish"); _rest.AddQueryParam("PhoneNumber", phoneNumber.c_str()); _rest.AddQueryParam("Message", messageContent.c_str()); const char* responseXml = _rest.fullRequestNoBody("GET", "/"); if (!_rest.get_LastMethodSuccess()) { _logger.LogError("ANSUtilities::SendMessageToPhoneNumber", _rest.lastErrorText(), __FILE__, __LINE__); return "Error: " + std::string(_rest.lastErrorText()); } if (_rest.get_ResponseStatusCode() != 200) { _logger.LogError("ANSUtilities::SendMessageToPhoneNumber", _rest.responseHeader(), __FILE__, __LINE__); return "Error: " + std::string(_rest.responseHeader()); } CkXml xml; xml.LoadXml(responseXml); return xml.chilkatPath("PublishResult|MessageId|*"); // Return the MessageId } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::SendMessageToPhoneNumber", e.what(), __FILE__, __LINE__); return "Error: " + std::string(e.what()); } } std::string ANSUtilities::ListAWSSNSTopic() { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::ListAWSSNSTopic", "Error: Invalid unlock code", __FILE__, __LINE__); return "Error: Invalid unlock code"; } if (!_isLicenseValid) { _logger.LogFatal("ANSUtilities::ListAWSSNSTopic", "Error: Invalid license key", __FILE__, __LINE__); return "Error: Invalid license key"; } try { CkRest _rest; _rest.AddQueryParam("Action", "ListTopics"); const char* responseXml = _rest.fullRequestNoBody("GET", "/"); if (!_rest.get_LastMethodSuccess()) { _logger.LogError("ANSUtilities::ListAWSSNSTopic", _rest.lastErrorText(), __FILE__, __LINE__); return "Error: " + std::string(_rest.lastErrorText()); } if (_rest.get_ResponseStatusCode() != 200) { _logger.LogError("ANSUtilities::ListAWSSNSTopic", _rest.responseHeader(), __FILE__, __LINE__); return "Error: " + std::string(_rest.responseHeader()); } CkXml xml; xml.LoadXml(responseXml); // Move to Topics list xml.chilkatPath("ListTopicsResult|Topics|$"); int numTopics = xml.get_NumChildren(); std::vector topics; topics.reserve(numTopics); // Slight optimization for (int i = 0; i < numTopics; ++i) { xml.GetChild2(i); topics.emplace_back(xml.getChildContent("TopicArn")); xml.GetParent2(); } if (topics.empty()) { _logger.LogError("ANSUtilities::ListAWSSNSTopic", "No topics found", __FILE__, __LINE__); return ""; } // Concatenate topics with ';' std::string topicsStr; for (size_t i = 0; i < topics.size(); ++i) { topicsStr += topics[i]; if (i + 1 != topics.size()) { topicsStr += ";"; } } return topicsStr; } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::ListAWSSNSTopic", e.what(), __FILE__, __LINE__); return "Error: " + std::string(e.what()); } } bool ANSUtilities::AuthenticateGCS(const std::string& jsonKeyFile) { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::AuthenticateGCS", "Error: Invalid unlock code", __FILE__, __LINE__); return false; } if (_isAuthenticated) { return true; } try { _authGoogle.put_JsonKey(jsonKeyFile.c_str()); _authGoogle.put_Scope("https://www.googleapis.com/auth/cloud-platform"); _authGoogle.put_ExpireNumSeconds(3600); // 1 hour token _authGoogle.put_SubEmailAddress(""); CkSocket tlsSock; bool success = tlsSock.Connect("www.googleapis.com", 443, true, 5000); if (!success) { _logger.LogError("ANSUtilities::AuthenticateGCS", tlsSock.lastErrorText(), __FILE__, __LINE__); return false; } success = _authGoogle.ObtainAccessToken(tlsSock); if (!success) { _logger.LogError("ANSUtilities::AuthenticateGCS", _authGoogle.lastErrorText(), __FILE__, __LINE__); return false; } if (!_authGoogle.accessToken()) { _logger.LogError("ANSUtilities::AuthenticateGCS", _authGoogle.lastErrorText(), __FILE__, __LINE__); return false; } _isAuthenticated = true; return true; } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::AuthenticateGCS", e.what(), __FILE__, __LINE__); return false; } } bool ANSUtilities::UploadMatToGCS(const std::string& bucketName, const std::string& objectName, const cv::Mat& image) { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::UploadMatToGCS", "Error: Invalid unlock code", __FILE__, __LINE__); return false; } if (!_isAuthenticated) { _logger.LogError("ANSUtilities::UploadMatToGCS", "Not authenticated. Call AuthenticateGCS() first.", __FILE__, __LINE__); return false; } try { CkRest rest; CkSocket _gcsSocket; std::string gcsHost = "www.googleapis.com"; bool bTls = true; bool autoReconnect = true; int port = 443; int maxWaitMs = 15000; if (_bProxy) { if (!_proxyHost.empty() && (_proxyPort > 0)) { _gcsSocket.put_HttpProxyHostname(_proxyHost.c_str()); _gcsSocket.put_HttpProxyPort(_proxyPort); _gcsSocket.put_HttpProxyUsername(_proxyUsername.c_str()); _gcsSocket.put_HttpProxyPassword(_proxyPassword.c_str()); _gcsSocket.put_HttpProxyForHttp(true); if (!_gcsSocket.Connect(gcsHost.c_str(), port, bTls, maxWaitMs)) { _logger.LogFatal("ANSUtilities::UploadMatToGCS", _gcsSocket.lastErrorText(), __FILE__, __LINE__); return false; } // Bind socket to rest if (!rest.UseConnection(_gcsSocket, autoReconnect)) { _logger.LogFatal("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__); return false; } rest.SetAuthGoogle(_authGoogle); } else { _logger.LogFatal("ANSUtilities::UploadMatToGCS", "Invalid proxy hostname and port", __FILE__, __LINE__); return false; } } else { // No proxy rest.SetAuthGoogle(_authGoogle); // Connect to Google Cloud Storage REST API if (!rest.Connect(gcsHost.c_str(), port, bTls, autoReconnect)) { _logger.LogError("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__); return false; } } // Convert cv::Mat to JPEG binary std::vector jpegBuffer; std::vector compressionParams = { cv::IMWRITE_JPEG_QUALITY, 90 }; if (!cv::imencode(".jpg", image, jpegBuffer, compressionParams)) { _logger.LogError("ANSUtilities::UploadMatToGCS", "Failed to encode cv::Mat as JPEG.", __FILE__, __LINE__); return false; } // Convert std::vector to CkByteData CkByteData jpegBytes; if (!jpegBuffer.empty()) { jpegBytes.append2(jpegBuffer.data(), static_cast(jpegBuffer.size())); } // Prepare the HTTP request to upload the file std::string urlPath = "/upload/storage/v1/b/" + bucketName + "/o?uploadType=media&name=" + objectName; rest.AddHeader("Content-Type", "image/jpeg"); // Upload the JPEG data to GCS std::string jsonResponse = rest.fullRequestBinary("POST", urlPath.c_str(), jpegBytes); if (!rest.get_LastMethodSuccess()) { _logger.LogError("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__); return false; } int statusCode = rest.get_ResponseStatusCode(); if (statusCode != 200) { _logger.LogError("ANSUtilities::UploadMatToGCS", rest.responseHeader(), __FILE__, __LINE__); return false; } _logger.LogDebug("ANSUtilities::UploadMatToGCS", "Upload successful. Status Code: " + std::to_string(statusCode), __FILE__, __LINE__); return true; } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::UploadMatToGCS", e.what(), __FILE__, __LINE__); return false; } } bool ANSUtilities::UploadMatToGCS(const std::string& bucketName, const std::string& objectName, unsigned char* jpeg_string, int32 bufferLength) { std::lock_guard lock(_mutex); if (!_isUnlockCodeValid) { _logger.LogFatal("ANSUtilities::UploadMatToGCS", "Error: Invalid unlock code", __FILE__, __LINE__); return false; } if (!_isAuthenticated) { _logger.LogError("ANSUtilities::UploadMatToGCS", "Not authenticated. Call AuthenticateGCS() first.", __FILE__, __LINE__); return false; } if(bufferLength<=0) { _logger.LogError("ANSUtilities::UploadMatToGCS", "Invalid buffer length", __FILE__, __LINE__); return false; } try { CkRest rest; CkSocket _gcsSocket; std::string gcsHost = "www.googleapis.com"; bool bTls = true; bool autoReconnect = true; int port = 443; int maxWaitMs = 15000; if (_bProxy) { if (!_proxyHost.empty() && (_proxyPort > 0)) { _gcsSocket.put_HttpProxyHostname(_proxyHost.c_str()); _gcsSocket.put_HttpProxyPort(_proxyPort); _gcsSocket.put_HttpProxyUsername(_proxyUsername.c_str()); _gcsSocket.put_HttpProxyPassword(_proxyPassword.c_str()); _gcsSocket.put_HttpProxyForHttp(true); if (!_gcsSocket.Connect(gcsHost.c_str(), port, bTls, maxWaitMs)) { _logger.LogFatal("ANSUtilities::UploadMatToGCS", _gcsSocket.lastErrorText(), __FILE__, __LINE__); return false; } // Bind socket to rest if (!rest.UseConnection(_gcsSocket, autoReconnect)) { _logger.LogFatal("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__); return false; } rest.SetAuthGoogle(_authGoogle); } else { _logger.LogFatal("ANSUtilities::UploadMatToGCS", "Invalid proxy hostname and port", __FILE__, __LINE__); return false; } } else { // No proxy rest.SetAuthGoogle(_authGoogle); // Connect to Google Cloud Storage REST API if (!rest.Connect(gcsHost.c_str(), port, bTls, autoReconnect)) { _logger.LogError("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__); return false; } } // Convert std::vector to CkByteData CkByteData jpegBytes; jpegBytes.append2(jpeg_string, static_cast(bufferLength)); // Prepare the HTTP request to upload the file std::string urlPath = "/upload/storage/v1/b/" + bucketName + "/o?uploadType=media&name=" + objectName; rest.AddHeader("Content-Type", "image/jpeg"); // Upload the JPEG data to GCS std::string jsonResponse = rest.fullRequestBinary("POST", urlPath.c_str(), jpegBytes); if (!rest.get_LastMethodSuccess()) { _logger.LogError("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__); return false; } int statusCode = rest.get_ResponseStatusCode(); if (statusCode != 200) { _logger.LogError("ANSUtilities::UploadMatToGCS", rest.responseHeader(), __FILE__, __LINE__); return false; } _logger.LogDebug("ANSUtilities::UploadMatToGCS", "Upload successful. Status Code: " + std::to_string(statusCode), __FILE__, __LINE__); return true; } catch (const std::exception& e) { _logger.LogFatal("ANSUtilities::UploadMatToGCS", e.what(), __FILE__, __LINE__); return false; } } bool ANSUtilities::SendEmail(const std::string& smtpServer, int port, const std::string& userName, const std::string& password, const std::string& subjectContent, const std::string& bodyHTMLContent, const std::string& bodyTextContent, const std::string& fromEmailSender, const std::vector& toEmails, const std::vector& ccEmails, const std::vector& bccEmails) { std::unique_lock lock(timeImageMutex, std::defer_lock); if (!lock.try_lock_for(std::chrono::milliseconds(45000))) { std::cerr << "Error: Mutex timeout in ANSUtilities::SendEmail!" << std::endl; return false; } if (!CheckUnlockCode_S()) { return false; } try { // The mailman object is used for sending and receiving email. CkMailMan mailman; mailman.put_SmtpHost(smtpServer.c_str()); mailman.put_SmtpPort(port); mailman.put_StartTLS(true);//STARTTLS // Set the SMTP login/password mailman.put_SmtpUsername(userName.c_str()); mailman.put_SmtpPassword(password.c_str()); CkEmail email; if (!bodyHTMLContent.empty()) { CkMht mht; mht.put_UseCids(true); const char* mime = 0; mime = mht.htmlToEML(bodyHTMLContent.c_str()); bool setmime = email.SetFromMimeText(mime); email.ConvertInlineImages(); } email.put_Subject(subjectContent.c_str()); if (!bodyTextContent.empty()) email.put_Body(bodyTextContent.c_str()); email.put_From(fromEmailSender.c_str()); for (const auto& toEmail : toEmails) { email.AddTo("", toEmail.c_str()); } for (const auto& ccEmail : ccEmails) { email.AddCC("", ccEmail.c_str()); } for (const auto& bccEmail : bccEmails) { email.AddBcc("", bccEmail.c_str()); } bool success = mailman.SendEmail(email); if (success != true) { return false; } success = mailman.CloseSmtpConnection(); if (success != true) { } return success; } catch (const std::exception& e) { return false; } } bool ANSUtilities::CheckUnlockCode_S() { static std::once_flag unlockOnceFlag; static bool isUnlockValid = false; try { std::call_once(unlockOnceFlag, []() { CkGlobal glob; const std::string unlockCode = "ANSDRC.CB1122026_MEQCIFwO1IFQCG0BhZwsXFO68QUU6mDB5uge4duOsqOJanEyAiAB67ahqnXin4SRy0vIegISgbFlpldmbuS5gbU21GYVqA==";// "ANSDRC.CB1082025_Ax6P3M7F8B3d"; if (glob.UnlockBundle(unlockCode.c_str()) && glob.get_UnlockStatus() == 2) { isUnlockValid = true; } }); return isUnlockValid; } catch (...) { return false; } } std::string ANSUtilities::AESEncryption(const std::string& inputString, const std::string& inputKey) { if (!CheckUnlockCode_S()) { return ""; } try { CkCrypt2 crypt; // AES is also known as Rijndael. crypt.put_CryptAlgorithm("aes"); // CipherMode may be "ecb", "cbc", "ofb", "cfb", "gcm", etc. crypt.put_CipherMode("cbc"); // KeyLength may be 128, 192, 256 crypt.put_KeyLength(256); // The padding scheme determines the contents of the bytes // that are added to pad the result to a multiple of the // encryption algorithm's block size. AES has a block // size of 16 bytes, so encrypted output is always // a multiple of 16. crypt.put_PaddingScheme(0); // 0 = RFC 1423 padding scheme // EncodingMode specifies the encoding of the output for // encryption, and the input for decryption. // It may be "hex", "url", "base64", or "quoted-printable". crypt.put_EncodingMode("base64"); // Ensure inputKey is exactly 16 characters for IV. std::string ivKey = inputKey.substr(0, std::min(16, inputKey.size())); if (ivKey.size() < 16) { ivKey.append(16 - ivKey.size(), 'A'); // Pad with spaces if less than 16 characters } crypt.SetEncodedIV(ivKey.c_str(), "ascii"); // The secret key must equal the size of the key. For // 256-bit encryption, the binary secret key is 32 bytes. // For 128-bit encryption, the binary secret key is 16 bytes. std::string secreteKey = inputKey.substr(0, std::min(32, inputKey.size())); if (secreteKey.size() < 32) { secreteKey.append(32 - secreteKey.size(), 'A'); // Pad with spaces if less than 32 characters } crypt.SetEncodedKey(secreteKey.c_str(), "ascii"); const char* encStr = crypt.encryptStringENC(inputString.c_str()); return encStr ? std::string(encStr) : std::string(); } catch (const std::exception& e) { // Optional: If you want logging, consider static logging in static functions return ""; } } std::string ANSUtilities::AESDecryption(const std::string& encryptString, const std::string& inputKey) { if (!CheckUnlockCode_S()) { return ""; } try { CkCrypt2 crypt; // AES is also known as Rijndael. crypt.put_CryptAlgorithm("aes"); // CipherMode may be "ecb", "cbc", "ofb", "cfb", "gcm", etc. crypt.put_CipherMode("cbc"); // KeyLength may be 128, 192, 256 crypt.put_KeyLength(256); // The padding scheme determines the contents of the bytes // that are added to pad the result to a multiple of the // encryption algorithm's block size. AES has a block // size of 16 bytes, so encrypted output is always // a multiple of 16. crypt.put_PaddingScheme(0); // 0 = RFC 1423 padding scheme // EncodingMode specifies the encoding of the output for // encryption, and the input for decryption. // It may be "hex", "url", "base64", or "quoted-printable". crypt.put_EncodingMode("base64"); // Ensure inputKey is exactly 16 characters for IV. std::string ivKey = inputKey.substr(0, std::min(16, inputKey.size())); if (ivKey.size() < 16) { ivKey.append(16 - ivKey.size(), 'A'); // Pad with spaces if less than 16 characters } crypt.SetEncodedIV(ivKey.c_str(), "ascii"); // The secret key must equal the size of the key. For // 256-bit encryption, the binary secret key is 32 bytes. // For 128-bit encryption, the binary secret key is 16 bytes. std::string secreteKey = inputKey.substr(0, std::min(32, inputKey.size())); if (secreteKey.size() < 32) { secreteKey.append(32 - secreteKey.size(), 'A'); // Pad with spaces if less than 32 characters } crypt.SetEncodedKey(secreteKey.c_str(), "ascii"); const char* decStr = crypt.decryptStringENC(encryptString.c_str()); return decStr ? std::string(decStr) : std::string(); } catch (const std::exception& e) { // Optional: If you want logging, consider static logging in static functions return ""; } } std::string ANSUtilities::MD5HashFile(const std::string& inputFilePath) { if (!CheckUnlockCode_S()) { return ""; } try { CkCrypt2 crypt; // Set the name of the hash algorithm. // Other choices include "sha1", "sha256", "sha384", "sha512", "md2", "md5", and "haval". crypt.put_HashAlgorithm("md5"); // EncodingMode specifies the encoding of the hash output. // It may be "hex", "url", "base64", or "quoted-printable". crypt.put_EncodingMode("hex"); // Files of any type may be hashed -- it doesn't matter // if the file is binary or text... const char* hashStr = nullptr; hashStr = crypt.hashFileENC(inputFilePath.c_str()); return hashStr ? std::string(hashStr) : std::string(); } catch (const std::exception& e) { // Optional: If you want logging, consider static logging in static functions return ""; } } bool ANSUtilities::RestartPC() { try { return system("shutdown /r /t 0") == 0; } catch (...) { return false; } } std::string ANSUtilities::DecodeJsonUnicodeToUTF16LE(const std::string& escapedStr) { std::string result; result.reserve(escapedStr.size() * 2); size_t i = 0; while (i < escapedStr.size()) { if (i + 5 < escapedStr.size() && escapedStr[i] == '\\' && escapedStr[i + 1] == 'u') { char hex[5] = { escapedStr[i + 2], escapedStr[i + 3], escapedStr[i + 4], escapedStr[i + 5], 0 }; uint16_t codepoint = (uint16_t)strtoul(hex, nullptr, 16); result += static_cast(codepoint & 0xFF); result += static_cast((codepoint >> 8) & 0xFF); i += 6; } else { result += escapedStr[i]; result += '\0'; i++; } } return result; } std::string ANSUtilities::ConvertUTF16LEToUTF8(const char* utf16leBytes, int byteLen) { #ifdef _WIN32 if (!utf16leBytes || byteLen <= 0) return ""; int wideLen = byteLen / (int)sizeof(wchar_t); const wchar_t* wideStr = reinterpret_cast(utf16leBytes); // First call: get required UTF-8 buffer size int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, nullptr, 0, nullptr, nullptr); if (utf8Len <= 0) return ""; std::string utf8Str(utf8Len, 0); WideCharToMultiByte(CP_UTF8, 0, wideStr, wideLen, &utf8Str[0], utf8Len, nullptr, nullptr); return utf8Str; #else return std::string(utf16leBytes, byteLen); #endif } std::string ANSUtilities::ConvertUTF8ToUTF16LE(const std::string& utf8Str) { #ifdef _WIN32 if (utf8Str.empty()) return ""; int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), nullptr, 0); if (wideLen <= 0) return ""; std::wstring wideStr(wideLen, 0); MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), (int)utf8Str.size(), &wideStr[0], wideLen); const char* rawBytes = reinterpret_cast(wideStr.data()); return std::string(rawBytes, wideLen * sizeof(wchar_t)); #else return utf8Str; #endif } std::string ANSUtilities::ConvertUTF16LEToUnicodeEscapes(const char* utf16leBytes, int byteLen) { if (!utf16leBytes || byteLen <= 0) return ""; int offset = 0; // Strip BOM (FF FE) if present if (byteLen >= 2 && static_cast(utf16leBytes[0]) == 0xFF && static_cast(utf16leBytes[1]) == 0xFE) { offset = 2; } int remaining = byteLen - offset; if (remaining <= 0 || remaining % 2 != 0) return ""; std::string result; result.reserve(remaining * 3); for (int i = offset; i + 1 < byteLen; i += 2) { uint16_t codepoint = static_cast(utf16leBytes[i]) | (static_cast(utf16leBytes[i + 1]) << 8); if (codepoint >= 0x20 && codepoint <= 0x7E) { result += static_cast(codepoint); } else { char buf[7]; snprintf(buf, sizeof(buf), "\\u%04X", codepoint); result += buf; } } return result; } std::string ANSUtilities::ConvertUnicodeEscapesToUTF8(const std::string& escapedStr) { if (escapedStr.empty()) return ""; // First decode \uXXXX to UTF-16LE, then convert to UTF-8 std::string utf16le; utf16le.reserve(escapedStr.size() * 2); size_t i = 0; while (i < escapedStr.size()) { if (i + 5 < escapedStr.size() && escapedStr[i] == '\\' && escapedStr[i + 1] == 'u') { char hex[5] = { escapedStr[i + 2], escapedStr[i + 3], escapedStr[i + 4], escapedStr[i + 5], 0 }; uint16_t codepoint = (uint16_t)strtoul(hex, nullptr, 16); utf16le += static_cast(codepoint & 0xFF); utf16le += static_cast((codepoint >> 8) & 0xFF); i += 6; } else { utf16le += escapedStr[i]; utf16le += '\0'; i++; } } return ConvertUTF16LEToUTF8(utf16le.data(), (int)utf16le.size()); } }