// LicenceVerifier.cpp // --------------------------------------------------------------------------- // Voir LicenceVerifier.h pour le contexte general. // // Le parsing JSON est volontairement minimal (recherche de sous-chaines) car // le SDK Acrobat ne fournit pas de bibliotheque JSON et on souhaite eviter // toute dependance externe pour un plugin aussi cible. Le format attendu de // license.php est celui deja documente pour le projet "impose" : // { "active": true/false, "offline_timeout_hours": 24, ... } // Si le serveur utilise plutot un champ "valid" au lieu de "active", les // deux sont acceptes. A AJUSTER si le format reel differe (voir TestConsole // pour verifier la reponse brute sans avoir a passer par Acrobat). // --------------------------------------------------------------------------- #include "LicenceVerifier.h" #include #include #include #include #include #pragma comment(lib, "Wininet.lib") #pragma comment(lib, "Advapi32.lib") #pragma comment(lib, "Shlwapi.lib") namespace CDImpLicence { static const wchar_t* CLE_REGISTRE = L"Software\\Copydev\\ImposingAcrobat"; // ------------------------------------------------------------------- // Utilitaires de parsing minimal (pas de dependance JSON externe) // ------------------------------------------------------------------- static bool ContientChampBooleenVrai(const std::string& json, const char* champ) { std::string motif = std::string("\"") + champ + "\""; size_t pos = json.find(motif); if (pos == std::string::npos) { return false; } pos = json.find(':', pos); if (pos == std::string::npos) { return false; } // Ignore les espaces apres ':' pos++; while (pos < json.size() && isspace((unsigned char)json[pos])) { pos++; } return json.compare(pos, 4, "true") == 0; } static int LireChampEntier(const std::string& json, const char* champ, int defaut) { std::string motif = std::string("\"") + champ + "\""; size_t pos = json.find(motif); if (pos == std::string::npos) { return defaut; } pos = json.find(':', pos); if (pos == std::string::npos) { return defaut; } pos++; while (pos < json.size() && isspace((unsigned char)json[pos])) { pos++; } int valeur = atoi(json.c_str() + pos); return (valeur > 0) ? valeur : defaut; } // ------------------------------------------------------------------- // Identifiants machine // ------------------------------------------------------------------- std::wstring ObtenirHostname() { wchar_t nom[256] = { 0 }; DWORD taille = 256; if (GetComputerNameW(nom, &taille)) { return std::wstring(nom); } return L"Inconnu"; } std::wstring ObtenirMachineId() { wchar_t nomVolume[MAX_PATH] = { 0 }; DWORD numeroSerie = 0; // Volume serial number du lecteur systeme : stable pour une // installation Windows donnee, suffisant pour le comptage // d'appareils cote serveur (pas un UUID materiel, mais standard // Win32 fiable, sans dependance WMI/COM). wchar_t racineSysteme[4] = L"C:\\"; GetWindowsDirectoryW(nomVolume, MAX_PATH); if (wcslen(nomVolume) >= 2) { racineSysteme[0] = nomVolume[0]; } GetVolumeInformationW(racineSysteme, nullptr, 0, &numeroSerie, nullptr, nullptr, nullptr, 0); wchar_t buffer[64]; swprintf_s(buffer, 64, L"%08X-%s", numeroSerie, ObtenirHostname().c_str()); return std::wstring(buffer); } // ------------------------------------------------------------------- // Requete HTTP (WinINet) // ------------------------------------------------------------------- static std::wstring EncoderURL(const std::wstring& valeur) { wchar_t tampon[1024]; DWORD taille = 1024; if (UrlEscapeW(valeur.c_str(), tampon, &taille, URL_ESCAPE_PERCENT)) { return std::wstring(tampon); } return valeur; } ResultatVerification VerifierLicence( const std::wstring& serveurUrl, const std::wstring& cleLicence, const std::wstring& machineId, const std::wstring& hostname, const std::wstring& produit ) { ResultatVerification resultat; std::wstring url = serveurUrl + L"?key=" + EncoderURL(cleLicence) + L"&machine_id=" + EncoderURL(machineId) + L"&hostname=" + EncoderURL(hostname) + L"&product=" + EncoderURL(produit); HINTERNET hInternet = InternetOpenW(L"CopydevImposingAcrobat/1.0", INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0); if (!hInternet) { resultat.erreur = "InternetOpenW a echoue"; return resultat; } // Timeout raisonnable : ne pas bloquer le demarrage d'Acrobat // indefiniment si le serveur ne repond pas. DWORD timeoutMs = 5000; InternetSetOptionW(hInternet, INTERNET_OPTION_CONNECT_TIMEOUT, &timeoutMs, sizeof(timeoutMs)); InternetSetOptionW(hInternet, INTERNET_OPTION_RECEIVE_TIMEOUT, &timeoutMs, sizeof(timeoutMs)); HINTERNET hUrl = InternetOpenUrlW(hInternet, url.c_str(), nullptr, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_SECURE, 0); if (!hUrl) { resultat.erreur = "InternetOpenUrlW a echoue (reseau indisponible ou serveur inaccessible)"; InternetCloseHandle(hInternet); return resultat; } char tampon[2048]; DWORD octetsLus = 0; std::string corps; while (InternetReadFile(hUrl, tampon, sizeof(tampon) - 1, &octetsLus) && octetsLus > 0) { tampon[octetsLus] = '\0'; corps += tampon; } InternetCloseHandle(hUrl); InternetCloseHandle(hInternet); if (corps.empty()) { resultat.erreur = "Reponse vide du serveur de licence"; return resultat; } resultat.succesReseau = true; resultat.reponseBrute = corps; resultat.valide = ContientChampBooleenVrai(corps, "active") || ContientChampBooleenVrai(corps, "valid"); resultat.offlineTimeoutHeures = LireChampEntier(corps, "offline_timeout_hours", 24); return resultat; } // ------------------------------------------------------------------- // Registre Windows // ------------------------------------------------------------------- void EcrireVerdictRegistre(bool valide, int dureeToleranceHeures) { HKEY hCle; if (RegCreateKeyExW(HKEY_CURRENT_USER, CLE_REGISTRE, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &hCle, nullptr) != ERROR_SUCCESS) { return; } long long expiration = (long long)time(nullptr) + (long long)dureeToleranceHeures * 3600LL; wchar_t valeurOK[2]; swprintf_s(valeurOK, 2, L"%d", valide ? 1 : 0); wchar_t valeurExpiration[32]; swprintf_s(valeurExpiration, 32, L"%lld", expiration); RegSetValueExW(hCle, L"LicenceOK", 0, REG_SZ, (const BYTE*)valeurOK, (DWORD)((wcslen(valeurOK) + 1) * sizeof(wchar_t))); RegSetValueExW(hCle, L"LicenceValideJusqua", 0, REG_SZ, (const BYTE*)valeurExpiration, (DWORD)((wcslen(valeurExpiration) + 1) * sizeof(wchar_t))); RegCloseKey(hCle); } bool LireVerdictRegistre(bool& valideOut, long long& expirationUnixOut) { HKEY hCle; if (RegOpenKeyExW(HKEY_CURRENT_USER, CLE_REGISTRE, 0, KEY_READ, &hCle) != ERROR_SUCCESS) { return false; } wchar_t valeurOK[16] = { 0 }; DWORD tailleOK = sizeof(valeurOK); wchar_t valeurExpiration[32] = { 0 }; DWORD tailleExpiration = sizeof(valeurExpiration); bool ok1 = RegQueryValueExW(hCle, L"LicenceOK", nullptr, nullptr, (LPBYTE)valeurOK, &tailleOK) == ERROR_SUCCESS; bool ok2 = RegQueryValueExW(hCle, L"LicenceValideJusqua", nullptr, nullptr, (LPBYTE)valeurExpiration, &tailleExpiration) == ERROR_SUCCESS; RegCloseKey(hCle); if (!ok1 || !ok2) { return false; } valideOut = (_wtoi(valeurOK) == 1); expirationUnixOut = _wtoi64(valeurExpiration); return true; } } // namespace CDImpLicence