#include "fiko/cekirdek/lisans_sayac.hpp" #include #include #include #include #include "fiko/cekirdek/kripto.hpp" #include "fiko/cekirdek/makine_kimlik.hpp" #include "fiko/core/gunluk.hpp" #if defined(_WIN32) #include #endif namespace fs = std::filesystem; namespace fiko { namespace { struct Kovа { i64 pencere = -1; u64 sayi = 0; }; using Kovalar = std::map; // Kayıt satırı: anahtarpenceresayıhmac16 // HMAC anahtarı makine parmak izinden türer → kayıt başka makineye taşınamaz ve // elle düzenlenemez (bozuk satır YOK SAYILIR; bu düşürme değil, red demektir). std::string satirHmac(const std::string& anahtar, i64 pencere, u64 sayi) { const MakineParmakIzi& p = makineParmakIzi(); const std::string tuz = "fiko.sayac.v1|" + p.kurulum + p.cpu + "|" + anahtar + "|" + std::to_string(pencere) + "|" + std::to_string(sayi); u8 d[32]; if (!kripto::sha256(tuz.data(), tuz.size(), d)) return ""; return kripto::baytHex(d, 32).substr(0, 16); } std::string serialize(const Kovalar& k) { std::string o; for (const auto& [ad, v] : k) { o += ad; o += '\t'; o += std::to_string(v.pencere); o += '\t'; o += std::to_string(v.sayi); o += '\t'; o += satirHmac(ad, v.pencere, v.sayi); o += '\n'; } return o; } Kovalar coz(const std::string& metin) { Kovalar k; std::istringstream ss(metin); std::string satir; while (std::getline(ss, satir)) { if (!satir.empty() && satir.back() == '\r') satir.pop_back(); const std::size_t t1 = satir.find('\t'); if (t1 == std::string::npos) continue; const std::size_t t2 = satir.find('\t', t1 + 1); if (t2 == std::string::npos) continue; const std::size_t t3 = satir.find('\t', t2 + 1); if (t3 == std::string::npos) continue; const std::string ad = satir.substr(0, t1); const i64 pencere = std::strtoll(satir.c_str() + t1 + 1, nullptr, 10); const u64 sayi = std::strtoull(satir.c_str() + t2 + 1, nullptr, 10); if (satir.substr(t3 + 1) != satirHmac(ad, pencere, sayi)) continue; // kurcalanmış k[ad] = Kovа{pencere, sayi}; } return k; } // İki kaynağı birleştir: aynı pencerede BÜYÜK sayı kazanır (silme/geri alma direnci). void birlestir(Kovalar& hedef, const Kovalar& kaynak) { for (const auto& [ad, v] : kaynak) { auto it = hedef.find(ad); if (it == hedef.end()) { hedef[ad] = v; } else if (v.pencere > it->second.pencere) { it->second = v; // daha yeni dönem } else if (v.pencere == it->second.pencere && v.sayi > it->second.sayi) { it->second.sayi = v.sayi; } } } #if defined(_WIN32) // Dosyayı kilit altında oku-değiştir-yaz. islem(kovalar) → true ise geri yazılır. bool kilitliIslem(const fs::path& yol, const std::function& islem, Kovalar& sonuc) { std::error_code ec; fs::create_directories(yol.parent_path(), ec); HANDLE h = CreateFileW(yol.wstring().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (h == INVALID_HANDLE_VALUE) return false; OVERLAPPED ov{}; // Süreçler arası atomik: kilit alınana kadar bekle (kota yazımı seyrek, kısa). if (!LockFileEx(h, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &ov)) { CloseHandle(h); return false; } std::string metin; LARGE_INTEGER boyut{}; if (GetFileSizeEx(h, &boyut) && boyut.QuadPart > 0 && boyut.QuadPart < (1 << 20)) { metin.resize((std::size_t)boyut.QuadPart); DWORD okundu = 0; if (!ReadFile(h, metin.data(), (DWORD)metin.size(), &okundu, nullptr)) okundu = 0; metin.resize(okundu); } sonuc = coz(metin); const bool yaz = islem(sonuc); if (yaz) { const std::string yeni = serialize(sonuc); SetFilePointer(h, 0, nullptr, FILE_BEGIN); DWORD yazildi = 0; WriteFile(h, yeni.data(), (DWORD)yeni.size(), &yazildi, nullptr); SetEndOfFile(h); FlushFileBuffers(h); } OVERLAPPED ov2{}; UnlockFileEx(h, 0, MAXDWORD, MAXDWORD, &ov2); CloseHandle(h); return true; } #endif } // namespace SayacDepo lisansSayacDeposu(const std::string& kokDizin, std::function ikincilOku, std::function ikincilYaz) { fs::path kok = kokDizin; if (kok.empty()) { const char* pd = std::getenv("ProgramData"); kok = pd && *pd ? fs::path(pd) / "FikoCAD" : fs::temp_directory_path() / "FikoCAD"; } const fs::path yol = kok / "lisans_sayac.dat"; return [yol, ikincilOku, ikincilYaz](const std::string& anahtar, i64 pencere, u64 artis) -> u64 { u64 sonuc = 0; #if defined(_WIN32) Kovalar kovalar; const bool ok = kilitliIslem( yol, [&](Kovalar& k) { // İkincil kaynakla birleştir: ana dosya silinmişse hak tazelenmesin. if (ikincilOku) birlestir(k, coz(ikincilOku())); Kovа& kv = k[anahtar]; if (kv.pencere != pencere) { // yeni dönem → sıfırla kv.pencere = pencere; kv.sayi = 0; } kv.sayi += artis; sonuc = kv.sayi; return artis > 0 || ikincilOku; // okuma-birleştirme de yazılır (onarım) }, kovalar); if (!ok) { gunluk::uyari("Lisans sayaç dosyası açılamadı: {}", yol.string()); return 0; } if (ikincilYaz) ikincilYaz(serialize(kovalar)); #else (void)anahtar; (void)pencere; (void)artis; #endif return sonuc; }; } } // namespace fiko