diff --git a/src/kpackage/packageloader.cpp b/src/kpackage/packageloader.cpp index abcfea9..be13dab 100644 --- a/src/kpackage/packageloader.cpp +++ b/src/kpackage/packageloader.cpp @@ -1,435 +1,435 @@ /* * Copyright 2010 Ryan Rix * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "packageloader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config-package.h" #include "private/packages_p.h" #include "package.h" #include "packagestructure.h" namespace KPackage { static PackageLoader *s_packageTrader = nullptr; static const int s_maxCacheAge = 60; static QHash s_pluginCacheAge; static QHash> s_pluginCache; class PackageLoaderPrivate { public: PackageLoaderPrivate() : isDefaultLoader(false), packageStructurePluginDir(QStringLiteral("kpackage/packagestructure")) { } static QSet knownCategories(); static QString parentAppConstraint(const QString &parentApp = QString()); static QSet s_customCategories; QHash > structures; bool isDefaultLoader; QString packageStructurePluginDir; }; QSet PackageLoaderPrivate::s_customCategories; QSet PackageLoaderPrivate::knownCategories() { // this is to trick the tranlsation tools into making the correct // strings for translation QSet categories = s_customCategories; categories << QStringLiteral(I18N_NOOP("Accessibility")).toLower() << QStringLiteral(I18N_NOOP("Application Launchers")).toLower() << QStringLiteral(I18N_NOOP("Astronomy")).toLower() << QStringLiteral(I18N_NOOP("Date and Time")).toLower() << QStringLiteral(I18N_NOOP("Development Tools")).toLower() << QStringLiteral(I18N_NOOP("Education")).toLower() << QStringLiteral(I18N_NOOP("Environment and Weather")).toLower() << QStringLiteral(I18N_NOOP("Examples")).toLower() << QStringLiteral(I18N_NOOP("File System")).toLower() << QStringLiteral(I18N_NOOP("Fun and Games")).toLower() << QStringLiteral(I18N_NOOP("Graphics")).toLower() << QStringLiteral(I18N_NOOP("Language")).toLower() << QStringLiteral(I18N_NOOP("Mapping")).toLower() << QStringLiteral(I18N_NOOP("Miscellaneous")).toLower() << QStringLiteral(I18N_NOOP("Multimedia")).toLower() << QStringLiteral(I18N_NOOP("Online Services")).toLower() << QStringLiteral(I18N_NOOP("Productivity")).toLower() << QStringLiteral(I18N_NOOP("System Information")).toLower() << QStringLiteral(I18N_NOOP("Utilities")).toLower() << QStringLiteral(I18N_NOOP("Windows and Tasks")).toLower(); return categories; } QString PackageLoaderPrivate::parentAppConstraint(const QString &parentApp) { if (parentApp.isEmpty()) { const QCoreApplication *app = QCoreApplication::instance(); if (!app) { return QString(); } return QStringLiteral("((not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '') or [X-KDE-ParentApp] == '%1')") .arg(app->applicationName()); } return QStringLiteral("[X-KDE-ParentApp] == '%1'").arg(parentApp); } PackageLoader::PackageLoader() : d(new PackageLoaderPrivate) { } PackageLoader::~PackageLoader() { foreach (auto wp, d->structures) { delete wp.data(); } delete d; } void PackageLoader::setPackageLoader(PackageLoader *loader) { if (!s_packageTrader) { s_packageTrader = loader; } else { #ifndef NDEBUG // qDebug() << "Cannot set packageTrader, already set!" << s_packageTrader; #endif } } PackageLoader *PackageLoader::self() { if (!s_packageTrader) { // we have been called before any PackageLoader was set, so just use the default // implementation. this prevents plugins from nefariously injecting their own // plugin loader if the app doesn't s_packageTrader = new PackageLoader; s_packageTrader->d->isDefaultLoader = true; } return s_packageTrader; } Package PackageLoader::loadPackage(const QString &packageFormat, const QString &packagePath) { if (!d->isDefaultLoader) { Package p = internalLoadPackage(packageFormat); if (p.hasValidStructure()) { if (!packagePath.isEmpty()) { p.setPath(packagePath); } return p; } } if (packageFormat.isEmpty()) { return Package(); } PackageStructure *structure = loadPackageStructure(packageFormat); if (structure) { Package p(structure); if (!packagePath.isEmpty()) { p.setPath(packagePath); } return p; } #ifndef NDEBUG // qDebug() << "Couldn't load Package for" << packageFormat << "! reason given: " << error; #endif return Package(); } QList PackageLoader::listPackages(const QString &packageFormat, const QString &packageRoot) { QElapsedTimer tmr; tmr.restart(); const bool useRuntimeCache = QFile::exists(QStringLiteral("/home/pine/USECACHE")); QString cacheKey = QString(QStringLiteral("%1.%2")).arg(packageFormat, packageRoot); qint64 now = QDateTime::currentSecsSinceEpoch(); if (useRuntimeCache && s_pluginCache.contains(cacheKey)) { if (now - s_pluginCacheAge.value(cacheKey) > s_maxCacheAge) { // cache is too old, ditch and compute s_pluginCache.remove(cacheKey); s_pluginCacheAge.remove(cacheKey); } else { qWarning() << "TMR CC Returning list from cache" << cacheKey << tmr.elapsed(); return s_pluginCache.value(cacheKey); } } QList lst; //has been a root specified? QString actualRoot = packageRoot; //try to take it from the package structure if (actualRoot.isEmpty()) { PackageStructure *structure = d->structures.value(packageFormat).data(); if (!structure) { if (packageFormat == QStringLiteral("KPackage/Generic")) { structure = new GenericPackage(); } else if (packageFormat == QStringLiteral("KPackage/GenericQML")) { structure = new GenericQMLPackage(); } } if (!structure) { structure = loadPackageStructure(packageFormat); } if (structure) { d->structures.insert(packageFormat, structure); Package p(structure); actualRoot = p.defaultPackageRoot(); } } if (actualRoot.isEmpty()) { actualRoot = packageFormat; } QSet uniqueIds; QStringList paths; if (QDir::isAbsolutePath(actualRoot)) { paths = QStringList(actualRoot); } else { foreach(const QString &path, QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) { paths += path + QLatin1Char('/') + actualRoot; } } Q_FOREACH(auto const &plugindir, paths) { const QString &_ixfile = plugindir + QStringLiteral("kpluginindex.json"); // QFile indexFile(_ixfile); - KCompressionDevice indexFile(_ixfile, KCompressionDevice::None); + KCompressionDevice indexFile(_ixfile, KCompressionDevice::BZip2); if (QFile::exists(_ixfile)) { qDebug() << "kpluginindex: Using indexfile: " << _ixfile; indexFile.open(QIODevice::ReadOnly); QJsonDocument jdoc = QJsonDocument::fromBinaryData(indexFile.readAll()); indexFile.close(); QJsonArray plugins = jdoc.array(); for (QJsonArray::const_iterator iter = plugins.constBegin(); iter != plugins.constEnd(); ++iter) { const QJsonObject &obj = QJsonValue(*iter).toObject(); const QString &pluginFileName = obj.value(QStringLiteral("FileName")).toString(); const KPluginMetaData m(obj, QString(), pluginFileName); if (m.isValid() && !uniqueIds.contains(m.pluginId())) { uniqueIds << m.pluginId(); lst << m; } } } else { qDebug() << "kpluginindex: Not cached" << plugindir; // If there's no cache file, fall back to listing the directory const QDirIterator::IteratorFlags flags = QDirIterator::Subdirectories; const QStringList nameFilters = { QStringLiteral("metadata.json"), QStringLiteral("metadata.desktop") }; QDirIterator it(plugindir, nameFilters, QDir::Files, flags); QSet dirs; while (it.hasNext()) { it.next(); const QString dir = it.fileInfo().absoluteDir().path(); if (dirs.contains(dir)) { continue; } dirs << dir; const QString metadataPath = it.fileInfo().absoluteFilePath(); const KPluginMetaData info(metadataPath); if (!info.isValid() || uniqueIds.contains(info.pluginId())) { continue; } if (packageFormat.isEmpty() || info.serviceTypes().isEmpty() || info.serviceTypes().contains(packageFormat)) { uniqueIds << info.pluginId(); lst << info; } } } } qWarning() << "TMR spent" << tmr.elapsed() << "in" << packageFormat << packageRoot; if (useRuntimeCache) { s_pluginCache.insert(cacheKey, lst); s_pluginCacheAge.insert(cacheKey, now); qWarning() << "TMR CC Cache populated for " << cacheKey; } else { qWarning() << "TMR CC Not using cache" << cacheKey; } return lst; } QList PackageLoader::findPackages(const QString &packageFormat, const QString &packageRoot, std::function filter) { QList lst; Q_FOREACH(auto const &plugin, listPackages(packageFormat, packageRoot)) { if (!filter || filter(plugin)) { lst << plugin; } } return lst; } KPackage::PackageStructure *PackageLoader::loadPackageStructure(const QString &packageFormat) { QElapsedTimer tmr; tmr.restart(); PackageStructure *structure = d->structures.value(packageFormat).data(); if (!structure) { if (packageFormat == QStringLiteral("KPackage/Generic")) { structure = new GenericPackage(); d->structures.insert(packageFormat, structure); } else if (packageFormat == QStringLiteral("KPackage/GenericQML")) { structure = new GenericQMLPackage(); d->structures.insert(packageFormat, structure); } } if (structure) { return structure; } QStringList libraryPaths; const QString subDirectory = QStringLiteral("kpackage/packagestructure"); Q_FOREACH (const QString &dir, QCoreApplication::libraryPaths()) { QString d = dir + QDir::separator() + subDirectory; if (!d.endsWith(QDir::separator())) { d += QDir::separator(); } libraryPaths << d; } QString pluginFileName; Q_FOREACH (const QString &plugindir, libraryPaths) { const QString &_ixfile = plugindir + QStringLiteral("kpluginindex.json"); // QFile indexFile(_ixfile); KCompressionDevice indexFile(_ixfile, KCompressionDevice::BZip2); if (QFile::exists(_ixfile)) { indexFile.open(QIODevice::ReadOnly); QJsonDocument jdoc = QJsonDocument::fromBinaryData(indexFile.readAll()); indexFile.close(); QJsonArray plugins = jdoc.array(); for (QJsonArray::const_iterator iter = plugins.constBegin(); iter != plugins.constEnd(); ++iter) { const QJsonObject &obj = QJsonValue(*iter).toObject(); const QString &candidate = obj.value(QStringLiteral("FileName")).toString(); const KPluginMetaData m(obj, candidate); if (m.isValid() && m.pluginId() == packageFormat) { pluginFileName = candidate; break; } } } else { QVector plugins = KPluginLoader::findPlugins(plugindir); QVectorIterator iter(plugins); while (iter.hasNext()) { auto md = iter.next(); if (md.isValid() && md.pluginId() == packageFormat) { pluginFileName = md.fileName(); break; } } } } QString error; if (!pluginFileName.isEmpty()) { KPluginLoader loader(pluginFileName); const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); KPluginFactory *factory = loader.factory(); if (factory) { structure = factory->create(nullptr, argsWithMetaData); if (!structure) { error = QCoreApplication::translate("", "No service matching the requirements was found"); } } } if (structure && !error.isEmpty()) { qWarning() << i18n("Could not load installer for package of type %1. Error reported was: %2", packageFormat, error); } if (structure) d->structures.insert(packageFormat, structure); //qDebug() << "TMR structure spent" << tmr.elapsed() << "in" << packageFormat; return structure; } void PackageLoader::addKnownPackageStructure(const QString &packageFormat, KPackage::PackageStructure *structure) { d->structures.insert(packageFormat, structure); } Package PackageLoader::internalLoadPackage(const QString &name) { Q_UNUSED(name); return Package(); } } // KPackage Namespace diff --git a/src/kpackage/private/packagejobthread.cpp b/src/kpackage/private/packagejobthread.cpp index f801f18..729f7be 100644 --- a/src/kpackage/private/packagejobthread.cpp +++ b/src/kpackage/private/packagejobthread.cpp @@ -1,477 +1,477 @@ /****************************************************************************** * Copyright 2007-2009 by Aaron Seigo * * Copyright 2012 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "private/packagejobthread_p.h" #include "package.h" #include "config-package.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPackage { bool copyFolder(QString sourcePath, QString targetPath) { QDir source(sourcePath); if (!source.exists()) { return false; } QDir target(targetPath); if (!target.exists()) { QString targetName = target.dirName(); target.cdUp(); target.mkdir(targetName); target = QDir(targetPath); } foreach (const QString &fileName, source.entryList(QDir::Files)) { QString sourceFilePath = sourcePath + QDir::separator() + fileName; QString targetFilePath = targetPath + QDir::separator() + fileName; if (!QFile::copy(sourceFilePath, targetFilePath)) { return false; } } foreach (const QString &subFolderName, source.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { QString sourceSubFolderPath = sourcePath + QDir::separator() + subFolderName; QString targetSubFolderPath = targetPath + QDir::separator() + subFolderName; if (!copyFolder(sourceSubFolderPath, targetSubFolderPath)) { return false; } } return true; } bool removeFolder(QString folderPath) { QDir folder(folderPath); return folder.removeRecursively(); } bool removeIndex(const QString& dir) { bool ok = true; QFileInfo fileInfo(dir, QStringLiteral("kpluginindex.json")); if (fileInfo.exists()) { if (fileInfo.isWritable()) { QFile f(fileInfo.absoluteFilePath()); if (!f.remove()) { ok = false; qWarning() << "Cannot remove kplugin index file: " << fileInfo.absoluteFilePath(); } else { qDebug() << "Deleted index: " << fileInfo.absoluteFilePath(); } } else { qWarning() << "Cannot remove kplugin index file (not writable): " << fileInfo.absoluteFilePath(); ok = false; } } return ok; } Q_GLOBAL_STATIC_WITH_ARGS(QStringList, metaDataFiles, (QStringList(QLatin1String("metadata.desktop")) << QLatin1String("metadata.json"))) bool indexDirectory(const QString& dir, const QString& dest) { QVariantMap vm; QVariantMap pluginsVm; vm[QStringLiteral("Version")] = QStringLiteral("1.0"); vm[QStringLiteral("Timestamp")] = QDateTime::currentMSecsSinceEpoch(); QJsonArray plugins; QDirIterator it(dir, *metaDataFiles, QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) { it.next(); const QString path = it.fileInfo().absoluteFilePath(); QJsonObject obj = KPluginMetaData(path).rawData(); obj.insert(QStringLiteral("FileName"), path); plugins.append(obj); } // Less than two plugin means it's not worth indexing if (plugins.count() < 2) { removeIndex(dir); return true; } QString destfile = dest; if (!QDir::isAbsolutePath(dest)) { destfile = dir + QLatin1Char('/') + dest; } QDir().mkpath(QFileInfo(destfile).dir().absolutePath()); //QFile file(destfile); - KCompressionDevice file(destfile, KCompressionDevice::None); + KCompressionDevice file(destfile, KCompressionDevice::BZip2); if (!file.open(QIODevice::WriteOnly)) { qWarning() << "Failed to open " << destfile; return false; } QJsonDocument jdoc; jdoc.setArray(plugins); // file.write(jdoc.toJson()); file.write(jdoc.toBinaryData()); qWarning() << "Generated " << destfile << " (" << plugins.count() << " plugins)"; return true; } class PackageJobThreadPrivate { public: QString installPath; QString errorMessage; int errorCode; }; PackageJobThread::PackageJobThread(QObject *parent) : QThread(parent) { d = new PackageJobThreadPrivate; d->errorCode = KJob::NoError; } PackageJobThread::~PackageJobThread() { delete d; } bool PackageJobThread::install(const QString &src, const QString &dest) { bool ok = installPackage(src, dest, Install); emit installPathChanged(d->installPath); emit finished(ok, d->errorMessage); return ok; } static QString resolveHandler(const QString &scheme) { QString candidatePath = QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "/kpackagehandlers/%1handler").arg(scheme); if (qEnvironmentVariableIsSet("KPACKAGE_DEP_RESOLVERS_PATH")) { candidatePath = QStringLiteral("%1/%2handler").arg(QString::fromUtf8(qgetenv("KPACKAGE_DEP_RESOLVERS_PATH")), scheme); } return QFile::exists(candidatePath) ? candidatePath : QString(); } bool PackageJobThread::installDependency(const QUrl &destUrl) { auto handler = resolveHandler(destUrl.scheme()); if (handler.isEmpty()) return false; QProcess process; process.setProgram(handler); process.setArguments({ destUrl.toString() }); process.setProcessChannelMode(QProcess::ForwardedChannels); process.start(); process.waitForFinished(); return process.exitCode() == 0; } bool PackageJobThread::installPackage(const QString &src, const QString &dest, OperationType operation) { QDir root(dest); if (!root.exists()) { QDir().mkpath(dest); if (!root.exists()) { d->errorMessage = i18n("Could not create package root directory: %1", dest); d->errorCode = Package::JobError::RootCreationError; //qWarning() << "Could not create package root directory: " << dest; return false; } } QFileInfo fileInfo(src); if (!fileInfo.exists()) { d->errorMessage = i18n("No such file: %1", src); d->errorCode = Package::JobError::PackageFileNotFoundError; return false; } QString path; QTemporaryDir tempdir; bool archivedPackage = false; if (fileInfo.isDir()) { // we have a directory, so let's just install what is in there path = src; // make sure we end in a slash! if (!path.endsWith(QLatin1Char('/'))) { path.append(QLatin1Char('/')); } } else { KArchive *archive = nullptr; QMimeDatabase db; QMimeType mimetype = db.mimeTypeForFile(src); if (mimetype.inherits(QStringLiteral("application/zip"))) { archive = new KZip(src); } else if (mimetype.inherits(QStringLiteral("application/x-compressed-tar")) || mimetype.inherits(QStringLiteral("application/x-tar")) || mimetype.inherits(QStringLiteral("application/x-bzip-compressed-tar")) || mimetype.inherits(QStringLiteral("application/x-xz")) || mimetype.inherits(QStringLiteral("application/x-lzma"))) { archive = new KTar(src); } else { //qWarning() << "Could not open package file, unsupported archive format:" << src << mimetype.name(); d->errorMessage = i18n("Could not open package file, unsupported archive format: %1 %2", src, mimetype.name()); d->errorCode = Package::JobError::UnsupportedArchiveFormatError; return false; } if (!archive->open(QIODevice::ReadOnly)) { //qWarning() << "Could not open package file:" << src; delete archive; d->errorMessage = i18n("Could not open package file: %1", src); d->errorCode = Package::JobError::PackageOpenError; return false; } archivedPackage = true; path = tempdir.path() + QLatin1Char('/'); d->installPath = path; const KArchiveDirectory *source = archive->directory(); source->copyTo(path); QStringList entries = source->entries(); if (entries.count() == 1) { const KArchiveEntry *entry = source->entry(entries[0]); if (entry->isDirectory()) { path = path + entry->name() + QLatin1Char('/'); } } delete archive; } QDir packageDir(path); QFileInfoList entries = packageDir.entryInfoList(*metaDataFiles); KPluginMetaData meta; if (!entries.isEmpty()) { const QString metadataFilePath = entries.first().filePath(); if (metadataFilePath.endsWith(QLatin1String(".desktop"))) { meta = KPluginMetaData::fromDesktopFile(metadataFilePath, {QStringLiteral(":/kservicetypes5/kpackage-generic.desktop")}); } else { QFile f(metadataFilePath); if(!f.open(QIODevice::ReadOnly)){ qWarning() << "Couldn't open metadata file" << src << path; d->errorMessage = i18n("Could not open metadata file: %1", src); d->errorCode = Package::JobError::MetadataFileMissingError; return false; } QJsonObject metadataObject = QJsonDocument::fromJson(f.readAll()).object(); meta = KPluginMetaData(metadataObject, QString(), metadataFilePath); } } if (!meta.isValid()) { qDebug() << "No metadata file in package" << src << path; d->errorMessage = i18n("No metadata file in package: %1", src); d->errorCode = Package::JobError::MetadataFileMissingError; return false; } QString pluginName = meta.pluginId(); qDebug() << "pluginname: " << meta.pluginId(); if (pluginName.isEmpty()) { //qWarning() << "Package plugin name not specified"; d->errorMessage = i18n("Package plugin name not specified: %1", src); d->errorCode = Package::JobError::PluginNameMissingError; return false; } // Ensure that package names are safe so package uninstall can't inject // bad characters into the paths used for removal. QRegExp validatePluginName(QStringLiteral("^[\\w-\\.]+$")); // Only allow letters, numbers, underscore and period. if (!validatePluginName.exactMatch(pluginName)) { //qDebug() << "Package plugin name " << pluginName << "contains invalid characters"; d->errorMessage = i18n("Package plugin name %1 contains invalid characters", pluginName); d->errorCode = Package::JobError::PluginNameInvalidError; return false; } QString targetName = dest; if (targetName[targetName.size() - 1] != QLatin1Char('/')) { targetName.append(QLatin1Char('/')); } targetName.append(pluginName); if (QFile::exists(targetName)) { if (operation == Update) { KPluginMetaData oldMeta(targetName + QLatin1String("/metadata.desktop")); if (oldMeta.serviceTypes() != meta.serviceTypes()) { d->errorMessage = i18n("The new package has a different type from the old version already installed.", meta.version(), meta.pluginId(), oldMeta.version()); d->errorCode = Package::JobError::UpdatePackageTypeMismatchError; } else if (isVersionNewer(oldMeta.version(), meta.version())) { const bool ok = uninstallPackage(targetName); if (!ok) { d->errorMessage = i18n("Impossible to remove the old installation of %1 located at %2. error: %3", pluginName, targetName, d->errorMessage); d->errorCode = Package::JobError::OldVersionRemovalError; } } else { d->errorMessage = i18n("Not installing version %1 of %2. Version %3 already installed.", meta.version(), meta.pluginId(), oldMeta.version()); d->errorCode = Package::JobError::NewerVersionAlreadyInstalledError; } } else { d->errorMessage = i18n("%1 already exists", targetName); d->errorCode = Package::JobError::PackageAlreadyInstalledError; } if (d->errorCode != KJob::NoError) { d->installPath = targetName; return false; } } //install dependencies const QStringList dependencies = KPluginMetaData::readStringList(meta.rawData(), QStringLiteral("X-KPackage-Dependencies")); for(const QString &dep : dependencies) { QUrl depUrl(dep); if (!installDependency(depUrl)) { d->errorMessage = i18n("Could not install dependency: '%1'", dep); d->errorCode = Package::JobError::PackageCopyError; return false; } } if (archivedPackage) { // it's in a temp dir, so just move it over. const bool ok = copyFolder(path, targetName); removeFolder(path); if (!ok) { //qWarning() << "Could not move package to destination:" << targetName; d->errorMessage = i18n("Could not move package to destination: %1", targetName); d->errorCode = Package::JobError::PackageMoveError; return false; } } else { // it's a directory containing the stuff, so copy the contents rather // than move them const bool ok = copyFolder(path, targetName); if (!ok) { //qWarning() << "Could not copy package to destination:" << targetName; d->errorMessage = i18n("Could not copy package to destination: %1", targetName); d->errorCode = Package::JobError::PackageCopyError; return false; } } if (archivedPackage) { // no need to remove the temp dir (which has been successfully moved if it's an archive) tempdir.setAutoRemove(false); } indexDirectory(dest, QStringLiteral("kpluginindex.json")); d->installPath = targetName; return true; } bool PackageJobThread::update(const QString &src, const QString &dest) { bool ok = installPackage(src, dest, Update); emit installPathChanged(d->installPath); emit finished(ok, d->errorMessage); return ok; } bool PackageJobThread::uninstall(const QString &packagePath) { bool ok = uninstallPackage(packagePath); //qDebug() << "emit installPathChanged " << d->installPath; emit installPathChanged(QString()); //qDebug() << "Thread: installFinished" << ok; emit finished(ok, d->errorMessage); return ok; } bool PackageJobThread::uninstallPackage(const QString &packagePath) { if (!QFile::exists(packagePath)) { d->errorMessage = i18n("%1 does not exist", packagePath); d->errorCode = Package::JobError::PackageFileNotFoundError; return false; } QString pkg; QString root; { // FIXME: remove, pass in packageroot, type and pluginName separately? QStringList ps = packagePath.split(QLatin1Char('/')); int ix = ps.count() - 1; if (packagePath.endsWith(QLatin1Char('/'))) { ix = ps.count() - 2; } pkg = ps[ix]; ps.pop_back(); root = ps.join(QLatin1Char('/')); } bool ok = removeFolder(packagePath); if (!ok) { d->errorMessage = i18n("Could not delete package from: %1", packagePath); d->errorCode = Package::JobError::PackageUninstallError; return false; } indexDirectory(root, QStringLiteral("kpluginindex.json")); return true; } Package::JobError PackageJobThread::errorCode() const { return (Package::JobError)d->errorCode; } } // namespace KPackage #include "moc_packagejobthread_p.cpp"