#include "services/gitea_releases.h" #include #include "platform/https_get.h" #include "update_check.h" namespace roro { namespace { struct Guard { explicit Guard(SemaphoreHandle_t l) : l_(l) { xSemaphoreTake(l_, portMAX_DELAY); } ~Guard() { xSemaphoreGive(l_); } SemaphoreHandle_t l_; }; std::string api(const std::string& what) { return std::string("/api/v1/repos/") + release::kGiteaRepo + "/releases" + what; } } // namespace bool GiteaReleases::fetch(const std::string& path, size_t max, std::vector& out) { HttpsGet get(net::User::Updates); std::string why = get.open(release::kGiteaHost, path, "application/json"); if (!why.empty()) { finish(false, why); return false; } release::ReleaseReader reader(max); std::unique_ptr buf(new (std::nothrow) uint8_t[512]); if (!buf) { finish(false, "Not enough memory"); return false; } for (;;) { int n = get.read(buf.get(), 512); if (n < 0) { finish(false, "The connection broke off"); return false; } if (n == 0) break; reader.feed(reinterpret_cast(buf.get()), n); if (!reader.ok()) break; } reader.end(); if (!reader.ok() || !reader.complete()) { finish(false, "Gitea's answer isn't what was expected"); return false; } out = reader.releases(); return true; } void GiteaReleases::finish(bool ok, const std::string& error) { Guard g(lock_); status_ = ok ? Status::Done : Status::Failed; error_ = error; seq_++; } bool GiteaReleases::fetchLatest() { { Guard g(lock_); status_ = Status::Busy; error_.clear(); } std::vector got; if (!fetch(api("/latest"), 1, got)) return false; if (got.empty() || !got[0].usable()) { finish(false, "No release to install on the server"); return false; } { Guard g(lock_); latest_ = got[0]; haveLatest_ = true; } finish(true, ""); return true; } bool GiteaReleases::fetchList() { { Guard g(lock_); status_ = Status::Busy; error_.clear(); } std::vector got; if (!fetch(api("?limit=" + std::to_string(kListSize) + "&draft=false&pre-release=false"), kListSize, got)) return false; std::vector usable; for (auto& r : got) if (r.usable()) usable.push_back(std::move(r)); { Guard g(lock_); list_ = std::move(usable); if (!list_.empty() && (!haveLatest_ || release::versionNewer(list_[0].tag, latest_.tag))) { latest_ = list_[0]; haveLatest_ = true; } } finish(true, ""); return true; } GiteaReleases::Status GiteaReleases::status() const { Guard g(lock_); return status_; } std::string GiteaReleases::error() const { Guard g(lock_); return error_; } uint32_t GiteaReleases::seq() const { Guard g(lock_); return seq_; } bool GiteaReleases::latest(release::Release& out) const { Guard g(lock_); if (haveLatest_) out = latest_; return haveLatest_; } std::vector GiteaReleases::list() const { Guard g(lock_); return list_; } bool GiteaReleases::find(const std::string& tag, release::Release& out) const { Guard g(lock_); for (const auto& r : list_) if (r.tag == tag) return out = r, true; if (haveLatest_ && latest_.tag == tag) return out = latest_, true; return false; } } // namespace roro