packages can now generate a hash of their contents; inc unit test

svn path=/trunk/KDE/kdelibs/; revision=1026489
This commit is contained in:
Aaron J. Seigo 2009-09-21 21:37:44 +00:00
parent 72c4534396
commit 19f55f804b
5 changed files with 109 additions and 4 deletions

View File

@ -1,2 +1,3 @@
#cmakedefine ENABLE_REMOTE_WIDGETS
#cmakedefine QCA2_FOUND

View File

@ -19,11 +19,16 @@
*******************************************************************************/
#include "package.h"
#include "config-plasma.h"
#include <QDir>
#include <QFile>
#include <QRegExp>
#ifdef QCA2_FOUND
#include <QtCrypto>
#endif
#include <karchive.h>
#include <kcomponentdata.h>
#include <kdesktopfile.h>
@ -165,6 +170,85 @@ const PackageStructure::Ptr Package::structure() const
return d->structure;
}
#ifdef QCA2_FOUND
void PackagePrivate::updateHash(const QString &basePath, const QString &subPath, const QDir &dir, QCA::Hash &hash)
{
// hash is calculated as a function of:
// * files ordered alphabetically by name, with each file's:
// * path relative to the content root
// * file data
// * directories ordered alphabetically by name, with each dir's:
// * path relative to the content root
// * file listing (recursing)
// symlinks (in both the file and dir case) are handled by adding
// the name of the symlink itself and the abs path of what it points to
const QDir::SortFlags sorting = QDir::Name | QDir::IgnoreCase;
const QDir::Filters filters = QDir::Hidden | QDir::System | QDir::NoDotAndDotDot;
foreach (const QString &file, dir.entryList(QDir::Files | filters, sorting)) {
if (!subPath.isEmpty()) {
hash.update(subPath.toUtf8());
}
hash.update(file.toUtf8());
QFileInfo info(dir.path() + '/' + file);
if (info.isSymLink()) {
hash.update(info.symLinkTarget().toUtf8());
} else {
QFile f(info.filePath());
if (f.open(QIODevice::ReadOnly)) {
while (!f.atEnd()) {
hash.update(f.read(1024));
}
} else {
kWarning() << "permissions fail?" << info.permissions() << info.isFile();
kWarning() << "could not add" << f.fileName() << "to the hash; file could not be opened for reading";
}
}
}
foreach (const QString &subDirPath, dir.entryList(QDir::Dirs | filters, sorting)) {
const QString relativePath = subPath + subDirPath + '/';
hash.update(relativePath.toUtf8());
QDir subDir(dir.path());
subDir.cd(subDirPath);
if (subDir.path() != subDir.canonicalPath()) {
hash.update(subDir.canonicalPath().toUtf8());
} else {
updateHash(basePath, relativePath, subDir, hash);
}
}
}
#endif
QString Package::hash() const
{
#ifdef QCA2_FOUND
if (!QCA::isSupported("sha1")) {
kWarning() << "can not create hash for" << path() << "due to no SHA1 support in QCA2";
return QString();
}
const QString basePath = d->structure->path() + d->structure->contentsPrefix();
QDir dir(basePath);
if (!dir.exists()) {
return QString();
}
QCA::Hash hash("sha1");
d->updateHash(basePath, QString(), dir, hash);
return QCA::arrayToHex(hash.final().toByteArray());
#else
// no QCA2!
kWarning() << "can not create hash for" << path() << "due to no cryptographic support (QCA2)";
return QString();
#endif
}
//TODO: provide a version of this that allows one to ask for certain types of packages, etc?
// should we be using KService here instead/as well?
QStringList Package::listInstalled(const QString &packageRoot) // static

View File

@ -135,6 +135,11 @@ class PLASMA_EXPORT Package
*/
const PackageStructure::Ptr structure() const;
/**
* @return a SHA1 hash digest of the contents of the package in hexadecimal form
*/
QString hash() const;
/**
* Returns a list of all installed packages by name
*

View File

@ -35,6 +35,10 @@ public:
void unpublish();
bool isPublished() const;
#ifdef QCA2_FOUND
void updateHash(const QString &basePath, const QString &subPath, const QDir &dir, QCA::Hash &hash);
#endif
PackageStructure::Ptr structure;
Service *service;
bool valid;

View File

@ -18,6 +18,7 @@
*******************************************************************************/
#include "plasmoidpackagetest.h"
#include "../config-plasma.h"
#include <QDir>
#include <QFile>
@ -26,8 +27,15 @@
#include "plasma/applet.h"
#include "plasma/packagemetadata.h"
#ifdef QCA2_FOUND
#include <QtCrypto>
#endif
void PlasmoidPackageTest::init()
{
#ifdef QCA2_FOUND
QCA::Initializer *cryptoInit = new QCA::Initializer;
#endif
mPackage = QString("Package");
mPackageRoot = QDir::homePath() + "/.kde-unit-test/packageRoot";
ps = Plasma::Applet::packageStructure();
@ -42,7 +50,7 @@ void PlasmoidPackageTest::cleanup()
// Clean things up.
QDir local = QDir::homePath() + QLatin1String("/.kde-unit-test/packageRoot");
foreach(const QString &dir, local.entryList(QDir::Dirs)) {
foreach (const QString &dir, local.entryList(QDir::Dirs)) {
removeDir(QLatin1String("packageRoot/" + dir.toLatin1() + "/contents/code"));
removeDir(QLatin1String("packageRoot/" + dir.toLatin1() + "/contents/images"));
removeDir(QLatin1String("packageRoot/" + dir.toLatin1() + "/contents"));
@ -70,8 +78,7 @@ void PlasmoidPackageTest::createTestPackage(const QString &packageName)
{
QDir pRoot(mPackageRoot);
// Create the root and package dir.
if(!pRoot.exists())
{
if (!pRoot.exists()) {
QVERIFY(QDir().mkpath(mPackageRoot));
}
@ -166,14 +173,18 @@ void PlasmoidPackageTest::isValid()
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
out.setDevice(&file);
out << "THIS IS A PLASMOID SCRIPT.....";
out << "THIS IS A PLASMOID SCRIPT.....\n";
file.flush();
file.close();
file.setPermissions(QFile::ReadUser | QFile::WriteUser);
// Main file exists so should be valid now.
delete p;
p = new Plasma::Package(mPackageRoot, mPackage, ps);
QVERIFY(p->isValid());
#ifdef QCA2_FOUND
QCOMPARE(QString("0b8c7de4bee1ac6f373276ac2b5776c9194b2c56"), p->hash());
#endif
}
void PlasmoidPackageTest::filePath()