#include "guncelleme_kontrol.hpp" #include #include #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/cekirdek/guncelleme.hpp" #include "guncelleme_indir.hpp" // WinHTTP (Windows yerleşik) — winhttp.lib linklenir. #define WIN32_LEAN_AND_MEAN #include #include #include "simdjson.h" namespace fiko { namespace { namespace sj = simdjson; // ASCII host/yol → geniş dizge (manifest yolları ASCII). std::wstring genislet(const std::string& s) { return std::wstring(s.begin(), s.end()); } // WinHTTP GET → gövde metni. Doner: başarılı mı (+hata mesajı). HTTP 200 dışı = hata. // sertifikaYoksay: dev self-signed cert için TLS doğrulamasını atla (varsayılan false). bool httpGet(const std::string& host, u16 port, bool https, const std::string& yol, bool sertifikaYoksay, std::string& out, std::string& hata) { out.clear(); HINTERNET oturum = WinHttpOpen(L"FikoYT-Guncelleme/1.0", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!oturum) { hata = "WinHttpOpen başarısız"; return false; } // Zaman aşımları (ms): çözümleme/bağlan/gönder/al — ağ takılırsa UI worker'ı bloke etmesin. WinHttpSetTimeouts(oturum, 8000, 8000, 12000, 12000); HINTERNET baglanti = WinHttpConnect(oturum, genislet(host).c_str(), port, 0); if (!baglanti) { hata = "WinHttpConnect başarısız"; WinHttpCloseHandle(oturum); return false; } HINTERNET istek = WinHttpOpenRequest(baglanti, L"GET", genislet(yol).c_str(), nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, https ? WINHTTP_FLAG_SECURE : 0); if (!istek) { hata = "WinHttpOpenRequest başarısız"; WinHttpCloseHandle(baglanti); WinHttpCloseHandle(oturum); return false; } if (https && sertifikaYoksay) { // dev self-signed: TLS doğrulama bayraklarını gevşet DWORD bayrak = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; WinHttpSetOption(istek, WINHTTP_OPTION_SECURITY_FLAGS, &bayrak, sizeof(bayrak)); } bool ok = false; if (WinHttpSendRequest(istek, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) && WinHttpReceiveResponse(istek, nullptr)) { // HTTP durum kodu 200 mü? DWORD kod = 0, boyut = sizeof(kod); WinHttpQueryHeaders(istek, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &kod, &boyut, WINHTTP_NO_HEADER_INDEX); if (kod == 200) { DWORD var = 0; do { var = 0; if (!WinHttpQueryDataAvailable(istek, &var)) break; if (var == 0) break; if (out.size() + var > 32u * 1024 * 1024) { hata = "manifest çok büyük"; ok = false; break; } std::string parca(var, '\0'); DWORD okunan = 0; if (!WinHttpReadData(istek, parca.data(), var, &okunan)) break; parca.resize(okunan); out += parca; ok = true; } while (var > 0); } else { hata = "HTTP durum kodu " + std::to_string(kod); } } else { hata = "istek gönderilemedi (ağ/sunucu erişilemiyor)"; } WinHttpCloseHandle(istek); WinHttpCloseHandle(baglanti); WinHttpCloseHandle(oturum); if (out.empty() && hata.empty()) hata = "boş yanıt"; return ok && !out.empty(); } // String alan oku (yoksa vars). std::string sStr(sj::ondemand::object& o, const char* alan, const char* vars = "") { std::string_view sv; if (o[alan].get_string().get(sv) == sj::SUCCESS) return std::string(sv); return vars; } bool sBool(sj::ondemand::object& o, const char* alan, bool vars = false) { bool b; if (o[alan].get_bool().get(b) == sj::SUCCESS) return b; return vars; } u64 sU64(sj::ondemand::object& o, const char* alan, u64 vars = 0) { uint64_t v; if (o[alan].get_uint64().get(v) == sj::SUCCESS) return v; return vars; } // Manifest JSON → ManifestBilgi. gecerli=parse başarısı. bool manifestCoz(const std::string& json, ManifestBilgi& m) { sj::ondemand::parser parser; sj::padded_string dolu(json); sj::ondemand::document doc; if (parser.iterate(dolu).get(doc) != sj::SUCCESS) return false; sj::ondemand::object o; if (doc.get_object().get(o) != sj::SUCCESS) return false; m.surum = surumAyristir(sStr(o, "surum").c_str()); m.kanal = sStr(o, "kanal", "kararli"); m.zorunlu = sBool(o, "zorunlu", false); m.minDestekli = surumAyristir(sStr(o, "minDestekli").c_str()); m.cikisTarihi = sStr(o, "cikisTarihi"); m.degisiklikler = sStr(o, "degisiklikler"); m.paketUrl = sStr(o, "paketUrl"); m.paketBoyut = sU64(o, "paketBoyut", 0); m.paketSha256 = sStr(o, "paketSha256"); m.paketImza = sStr(o, "paketImza"); m.gecerli = (m.surum.buyuk || m.surum.kucuk || m.surum.yama); // en az bir sürüm alanı return m.gecerli; } } // namespace GuncellemeServisi& guncellemeServisi() { static GuncellemeServisi s; // app-global tek örnek (main.cpp g_guncelleme buna bağlanır) return s; } void guncellemeKontrolBaslat(GuncellemeServisi& svc, bool sessiz) { Cekirdek* c = svc.cekirdek(); if (!c) return; const auto& su = svc.sunucu(); const std::string host = su.host; const u16 port = su.port; const bool https = su.https; const std::string yol = svc.manifestYolu(); // Dev self-signed sertifikayı yoksay (kendi Go backend geliştirme sertifikası). const bool sertYoksay = true; svc.kontrolBasladi(); // Worker'da paylaşılan sonuç (bitince main-thread'de okunur — happens-before iş bitişi). struct Sonuc { bool ok = false; std::string govde; std::string hata; }; auto sonuc = std::make_shared(); c->gorevler().kuyrukla( "Güncelleme kontrolü", [sonuc, host, port, https, yol, sertYoksay](GorevBaglam&) { sonuc->ok = httpGet(host, port, https, yol, sertYoksay, sonuc->govde, sonuc->hata); }, [sonuc, &svc, sessiz]() { // main-thread: parse + değerlendir + olay if (!sonuc->ok) { svc.hataAyarla(sonuc->hata.empty() ? "bilinmeyen ağ hatası" : sonuc->hata); return; } ManifestBilgi m; if (!manifestCoz(sonuc->govde, m)) { svc.hataAyarla("manifest çözümlenemedi"); return; } svc.manifestDegerlendir(m); // Guncel/Mevcut → GuncellemeMevcut olayı yayınlar // Otomatik mod: yeni sürüm bulunursa arka planda hemen indir + doğrula // (kullanıcı hiç uğraşmaz). Kurulum Dalga 3'te uygun anda yapılır. if (svc.durum() == GuncellemeServisi::Durum::Mevcut && svc.mod() == GuncellemeServisi::Mod::Otomatik) guncellemeIndirBaslat(svc); (void)sessiz; }); } } // namespace fiko