#pragma once #include #include "HttpHelper.h" #include "FileHelper.h" #include #include #include #include #include #include #include struct FileDownloadItem { QString path; QString url; QString sha256; qint64 size; }; class UpdaterLogic : public QObject { Q_OBJECT public: explicit UpdaterLogic(QObject* parent = nullptr); void getManifest(const QString& appId, const QString& channel, const QString& targetVer, int versionId); bool verifyManifestSignature(const QString& publicKeyPath = ":/simcae/update-client/manifest-public-key.pem") const; bool validateLocalFiles(const QString& stagingDir, const QString& installedDir = QString()) const; bool saveManifestCache(const QString& cacheDir) const; bool loadManifestCache(const QString& cacheDir, const QString& version); bool loadOfflinePackage(const QString& packagePath, const QString& stagingDir = QString()); QString offlineError() const { return m_offlineError; } void getDownloadUrl(const QString& appId, const QString& channel, const QString& targetVer, int versionId); void reportResult(const QString& deviceId, const QString& fromVer, const QString& toVer, bool success); void reportDownloadResult(const QString& appId, const QString& channel, const QString& version, bool success); bool downloadAllFiles(const QString& tempDir, const QString& targetDir); qint64 estimateAdditionalDiskBytes(const QString& targetDir, const QStringList& obsoletePaths) const; QString calcLocalFileSha256(const QString& filePath) const; QList getFileList() const; QJsonObject getManifest() const { return m_manifest; } QStringList obsoleteFilesComparedTo(const QJsonObject& oldManifest) const; QString getManifestText() const { return m_manifestText; } bool allDownloadSuccess() const { return m_downloadAllOk; } signals: void fetchUrlFinished(); void downloadProgress(qint64 receivedBytes, qint64 totalBytes, const QString& currentPath, double bytesPerSecond); private: bool downloadSingleFile(const QString& url, const QString& savePath, const QString& expectSha256, qint64 expectedSize, const QString& resumePartPath); bool validateOnlineManifest(const QJsonObject& manifest, const QString& appId, const QString& channel, const QString& targetVer, int versionId, QList* fileItems) const; bool isSafeRelativePath(const QString& path) const; bool isRuntimeProtectedPath(const QString& path) const; QByteArray canonicalManifestBytes(const QJsonObject& manifest) const; bool verifySignature(const QByteArray& payload, const QString& signatureBase64, const QString& publicKeyPath) const; HttpHelper m_http; QString m_serverAddr; QJsonObject m_manifest; QString m_manifestText; QList m_fileItems; mutable bool m_manifestSignatureVerified = false; bool m_downloadAllOk = false; qint64 m_downloadTotalBytes = 0; qint64 m_downloadCompletedBytes = 0; qint64 m_sessionDownloadedBytes = 0; QString m_currentDownloadPath; QString m_offlineError; QElapsedTimer m_downloadTimer; };