#include "LocalStateHelper.h" #include "ConfigHelper.h" #include #include #include LocalStateHelper::LocalStateHelper(const QString& baseDir) : m_baseDir(baseDir) , m_filePath(ConfigHelper::instance().localStatePath()) { } bool LocalStateHelper::loadState(const QString& relativePath) { const QString defaultState = QStringLiteral("config/local_state.json"); if (relativePath == defaultState) m_filePath = ConfigHelper::instance().localStatePath(); else if (QDir::isAbsolutePath(relativePath)) m_filePath = relativePath; else m_filePath = m_baseDir + "/" + relativePath; QFile file(m_filePath); if (!file.exists()) { m_state = QJsonObject{ {"max_policy_seq", 0}, {"last_success_run_at", QString()}, {"last_online_verified_at", QString()}, {"last_success_version", QString()} }; m_loaded = true; if (!saveState()) { m_error = QString("Cannot write new state file: %1").arg(m_filePath); return false; } return true; } if (!file.open(QIODevice::ReadOnly)) { m_error = QString("Cannot open state file: %1").arg(m_filePath); return false; } QByteArray raw = file.readAll(); file.close(); QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(raw, &parseError); if (parseError.error != QJsonParseError::NoError || !doc.isObject()) { m_error = QString("Invalid state JSON: %1").arg(parseError.errorString()); return false; } m_state = doc.object(); m_loaded = true; return true; } bool LocalStateHelper::saveState() const { if (!m_loaded) { m_error = "State is not loaded"; return false; } QJsonDocument doc(m_state); QString writeError; if (!ConfigHelper::writeFileWithElevationIfNeeded(m_filePath, doc.toJson(QJsonDocument::Indented), &writeError)) { m_error = QString("Cannot save state file: %1").arg(writeError); return false; } m_error.clear(); return true; } bool LocalStateHelper::isLoaded() const { return m_loaded; } QString LocalStateHelper::errorString() const { return m_error; } qint64 LocalStateHelper::maxPolicySeq() const { return m_state.value("max_policy_seq").toVariant().toLongLong(); } QDateTime LocalStateHelper::lastSuccessRunAt() const { return QDateTime::fromString(m_state.value("last_success_run_at").toString(), Qt::ISODate); } QDateTime LocalStateHelper::lastOnlineVerifiedAt() const { return QDateTime::fromString(m_state.value("last_online_verified_at").toString(), Qt::ISODate); } QString LocalStateHelper::lastSuccessVersion() const { return m_state.value("last_success_version").toString(); } bool LocalStateHelper::isPolicySeqRolledBack(qint64 policySeq) const { if (!m_loaded) return false; return policySeq < maxPolicySeq(); } bool LocalStateHelper::isSystemTimeRewound() const { if (!m_loaded) return false; const QDateTime now = QDateTime::currentDateTimeUtc(); const QDateTime lastRun = lastSuccessRunAt(); const QDateTime lastOnline = lastOnlineVerifiedAt(); return (lastRun.isValid() && now < lastRun) || (lastOnline.isValid() && now < lastOnline); } void LocalStateHelper::updateAfterSuccessfulRun(const QString& currentVersion, qint64 policySeq) { if (!m_loaded) m_loaded = true; m_state["last_success_run_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate); m_state["last_success_version"] = currentVersion; if (policySeq > maxPolicySeq()) m_state["max_policy_seq"] = policySeq; } void LocalStateHelper::updateOnlineVerified(qint64 policySeq) { if (!m_loaded) m_loaded = true; m_state["last_online_verified_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate); if (policySeq > maxPolicySeq()) m_state["max_policy_seq"] = policySeq; }