summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libs/installer/metadata.cpp145
-rw-r--r--src/libs/installer/metadata.h5
-rw-r--r--src/libs/installer/metadatajob.cpp3
-rw-r--r--src/libs/installer/metadatajob_p.h39
4 files changed, 184 insertions, 8 deletions
diff --git a/src/libs/installer/metadata.cpp b/src/libs/installer/metadata.cpp
index 9ae817127..e0ff7f614 100644
--- a/src/libs/installer/metadata.cpp
+++ b/src/libs/installer/metadata.cpp
@@ -46,6 +46,60 @@ namespace QInstaller {
\brief The Metadata class represents fetched metadata from a repository.
*/
+
+/*!
+ \internal
+*/
+static bool verifyFileIntegrityFromElement(const QDomElement &element, const QString &childNodeName,
+ const QString &attribute, const QString &metaDirectory, bool testChecksum)
+{
+ const QDomNodeList nodes = element.childNodes();
+ for (int i = 0; i < nodes.count(); ++i) {
+ const QDomNode node = nodes.at(i);
+ if (node.nodeName() != childNodeName)
+ continue;
+
+ const QDir dir(metaDirectory);
+ const QString filename = attribute.isEmpty()
+ ? node.toElement().text()
+ : node.toElement().attribute(attribute);
+
+ if (filename.isEmpty())
+ continue;
+
+ QFile file(dir.absolutePath() + QDir::separator() + filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << file.fileName()
+ << "for reading:" << file.errorString();
+ return false;
+ }
+
+ if (!testChecksum)
+ continue;
+
+ QFile hashFile(file.fileName() + QLatin1String(".sha1"));
+ if (!hashFile.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << hashFile.fileName()
+ << "for reading:" << hashFile.errorString();
+ return false;
+ }
+
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+
+ const QByteArray checksum = hash.result().toHex();
+ const QByteArray expectedChecksum = hashFile.readAll();
+ if (checksum != expectedChecksum) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Unexpected checksum for file" << file.fileName();
+ return false;
+ }
+ }
+ return true;
+}
+
/*!
Constructs a new metadata object.
*/
@@ -122,18 +176,23 @@ QDomDocument Metadata::updatesDocument() const
}
/*!
- Returns \c true if the \c Updates.xml document of this metadata
- exists, \c false otherwise.
+ Returns \c true if the \c Updates.xml document of this metadata exists, and that all
+ meta files referenced in the document exist. If the \c Updates.xml contains a \c Checksum
+ element with a value of \c true, the integrity of the files is also verified.
+
+ Returns \c false otherwise.
*/
bool Metadata::isValid() const
{
- const QString updateFile(path() + QLatin1String("/Updates.xml"));
- if (!QFileInfo::exists(updateFile)) {
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
qCWarning(QInstaller::lcInstallerInstallLog)
- << "File" << updateFile << "does not exist.";
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
return false;
}
- return true;
+
+ return verifyMetaFiles(&updateFile);
}
/*!
@@ -282,4 +341,78 @@ bool Metadata::containsRepositoryUpdates() const
return false;
}
+/*!
+ Verifies that the files referenced in \a updateFile document exist
+ on disk. If the document contains a \c Checksum element with a value
+ of \c true, the integrity of the files is also verified.
+
+ Returns \c true if the meta files are valid, \c false otherwise.
+*/
+bool Metadata::verifyMetaFiles(QFile *updateFile) const
+{
+ QDomDocument doc;
+ QString errorString;
+ if (!doc.setContent(updateFile, &errorString)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot set document content:" << errorString;
+ return false;
+ }
+
+ const QDomElement rootElement = doc.documentElement();
+ const QDomNodeList childNodes = rootElement.childNodes();
+
+ bool testChecksum = true;
+ const QDomElement checksumElement = rootElement.firstChildElement(QLatin1String("Checksum"));
+ if (!checksumElement.isNull())
+ testChecksum = (checksumElement.text().toLower() == scTrue);
+
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomElement element = childNodes.at(i).toElement();
+ if (element.isNull() || element.tagName() != QLatin1String("PackageUpdate"))
+ continue;
+
+ const QDomNodeList c2 = element.childNodes();
+ QString packageName;
+ QString unused1;
+ QString unused2;
+
+ // Only need the package name, so values for "online" and "testCheckSum" do not matter
+ if (!MetadataJob::parsePackageUpdate(c2, packageName, unused1, unused2, true, true))
+ continue; // nothing to check for this package
+
+ const QString packagePath = QString::fromLatin1("%1/%2/").arg(path(), packageName);
+ for (auto &metaTagName : qAsConst(*scMetaElements)) {
+ const QDomElement metaElement = element.firstChildElement(metaTagName);
+ if (metaElement.isNull())
+ continue;
+
+ if (metaElement.tagName() == QLatin1String("Licenses")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("License"),
+ QLatin1String("file"), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("UserInterfaces")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("UserInterface"),
+ QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("Translations")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("Translation"),
+ QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("Script")) {
+ if (!verifyFileIntegrityFromElement(metaElement.parentNode().toElement(),
+ QLatin1String("Script"), QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else {
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown meta element.");
+ }
+ }
+ }
+
+ return true;
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/metadata.h b/src/libs/installer/metadata.h
index 3063be829..7312d106b 100644
--- a/src/libs/installer/metadata.h
+++ b/src/libs/installer/metadata.h
@@ -35,6 +35,8 @@
#include <QDomDocument>
+class QFile;
+
namespace QInstaller {
class INSTALLER_EXPORT Metadata : public CacheableItem
@@ -64,6 +66,9 @@ public:
bool containsRepositoryUpdates() const;
private:
+ bool verifyMetaFiles(QFile *updateFile) const;
+
+private:
Repository m_repository;
QString m_persistentRepositoryPath;
mutable QByteArray m_checksum;
diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp
index ff15d7b0d..663b209f7 100644
--- a/src/libs/installer/metadatajob.cpp
+++ b/src/libs/installer/metadatajob.cpp
@@ -193,7 +193,7 @@ bool MetadataJob::resetCache(bool init)
m_metaFromCache.setPath(m_core->settings().localCachePath());
m_metaFromCache.setType(QLatin1String("Metadata"));
- m_metaFromCache.setVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION)));
+ m_metaFromCache.setVersion(QLatin1String(QUOTE(IFW_CACHE_FORMAT_VERSION)));
if (!init)
return true;
@@ -630,6 +630,7 @@ void MetadataJob::metadataTaskFinished()
UnzipArchiveTask *task = new UnzipArchiveTask(result.target(),
item.value(TaskRole::UserRole).toString());
task->setRemoveArchive(true);
+ task->setStoreChecksums(true);
QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
m_unzipTasks.insert(watcher, qobject_cast<QObject*> (task));
diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h
index 5340e5d1c..8ab2d9e7d 100644
--- a/src/libs/installer/metadatajob_p.h
+++ b/src/libs/installer/metadatajob_p.h
@@ -62,11 +62,16 @@ class UnzipArchiveTask : public AbstractTask<void>
public:
UnzipArchiveTask(const QString &arcive, const QString &target)
- : m_archive(arcive), m_targetDir(target), m_removeArchive(false)
+ : m_archive(arcive)
+ , m_targetDir(target)
+ , m_removeArchive(false)
+ , m_storeChecksums(false)
{}
+
QString target() { return m_targetDir; }
QString archive() { return m_archive; }
void setRemoveArchive(bool remove) { m_removeArchive = remove; }
+ void setStoreChecksums(bool store) { m_storeChecksums = store; }
void doTask(QFutureInterface<void> &fi) override
{
@@ -82,12 +87,43 @@ public:
if (!archive) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Unsupported archive \"%1\": no handler "
"registered for file suffix \"%2\".").arg(m_archive, QFileInfo(m_archive).suffix())));
+ return;
} else if (!archive->open(QIODevice::ReadOnly)) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for "
"reading: %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString())));
+ return;
} else if (!archive->extract(m_targetDir)) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting "
"archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString())));
+ return;
+ }
+
+ if (m_storeChecksums) {
+ // Calculate and store checksums of extracted files for later use
+ const QVector<ArchiveEntry> entries = archive->list();
+ for (auto &entry : entries) {
+ if (entry.isDirectory)
+ continue;
+
+ QFile file(m_targetDir + QDir::separator() + entry.path);
+ if (!file.open(QIODevice::ReadOnly)) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open extracted file \"%1\" for "
+ "reading: %2").arg(QDir::toNativeSeparators(file.fileName()), file.errorString())));
+ break;
+ }
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+
+ const QByteArray hexChecksum = hash.result().toHex();
+ QFile hashFile(file.fileName() + QLatin1String(".sha1"));
+ if (!hashFile.open(QIODevice::WriteOnly)) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for "
+ "writing: %2").arg(QDir::toNativeSeparators(hashFile.fileName()), hashFile.errorString())));
+ break;
+ }
+ QTextStream stream(&hashFile);
+ stream << hexChecksum;
+ }
}
archive->close();
@@ -101,6 +137,7 @@ private:
QString m_archive;
QString m_targetDir;
bool m_removeArchive;
+ bool m_storeChecksums;
};
class CacheTaskException : public QException