File indexing completed on 2024-11-10 03:40:07
0001 /* 0002 SPDX-FileCopyrightText: 2012 Sebastian Kügler <sebas@kde.org> 0003 SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "packagejob.h" 0009 0010 #include "config-package.h" 0011 #include "packageloader.h" 0012 #include "packagestructure.h" 0013 #include "private/package_p.h" 0014 #include "private/packagejobthread_p.h" 0015 #include "private/utils.h" 0016 0017 #include "kpackage_debug.h" 0018 0019 #include <QDBusConnection> 0020 #include <QDBusMessage> 0021 #include <QDebug> 0022 #include <QStandardPaths> 0023 #include <QThreadPool> 0024 #include <QTimer> 0025 0026 namespace KPackage 0027 { 0028 struct StructureOrErrorJob { 0029 PackageStructure *structure = nullptr; 0030 PackageJob *errorJob = nullptr; 0031 }; 0032 class PackageJobPrivate 0033 { 0034 public: 0035 static StructureOrErrorJob loadStructure(const QString &packageFormat) 0036 { 0037 if (auto structure = PackageLoader::self()->loadPackageStructure(packageFormat)) { 0038 return StructureOrErrorJob{structure, nullptr}; 0039 } else { 0040 auto job = new PackageJob(PackageJob::Install, Package(), QString(), QString()); 0041 job->setErrorText(QStringLiteral("Could not load package structure ") + packageFormat); 0042 job->setError(PackageJob::JobError::InvalidPackageStructure); 0043 QTimer::singleShot(0, job, [job]() { 0044 job->emitResult(); 0045 }); 0046 return StructureOrErrorJob{nullptr, job}; 0047 } 0048 } 0049 PackageJobThread *thread = nullptr; 0050 Package package; 0051 QString installPath; 0052 }; 0053 0054 PackageJob::PackageJob(OperationType type, const Package &package, const QString &src, const QString &dest) 0055 : KJob() 0056 , d(new PackageJobPrivate) 0057 { 0058 d->thread = new PackageJobThread(type, src, dest, package); 0059 d->package = package; 0060 0061 if (type == Install) { 0062 setupNotificationsOnJobFinished(QStringLiteral("packageInstalled")); 0063 } else if (type == Update) { 0064 setupNotificationsOnJobFinished(QStringLiteral("packageUpdated")); 0065 d->thread->update(src, dest, package); 0066 } else if (type == Uninstall) { 0067 setupNotificationsOnJobFinished(QStringLiteral("packageUninstalled")); 0068 } else { 0069 Q_UNREACHABLE(); 0070 } 0071 connect(d->thread, &PackageJobThread::installPathChanged, this, [this](const QString &installPath) { 0072 d->package.setPath(installPath); 0073 }); 0074 connect(d->thread, &PackageJobThread::jobThreadFinished, this, [this]() { 0075 emitResult(); 0076 }); 0077 } 0078 0079 PackageJob::~PackageJob() = default; 0080 0081 void PackageJob::start() 0082 { 0083 if (d->thread) { 0084 QThreadPool::globalInstance()->start(d->thread); 0085 d->thread = nullptr; 0086 } else { 0087 qCWarning(KPACKAGE_LOG) << "The KPackage::PackageJob was already started"; 0088 } 0089 } 0090 0091 PackageJob *PackageJob::install(const QString &packageFormat, const QString &sourcePackage, const QString &packageRoot) 0092 { 0093 auto structOrErr = PackageJobPrivate::loadStructure(packageFormat); 0094 if (auto structure = structOrErr.structure) { 0095 Package package(structure); 0096 package.setPath(sourcePackage); 0097 QString dest = packageRoot.isEmpty() ? package.defaultPackageRoot() : packageRoot; 0098 PackageLoader::invalidateCache(); 0099 0100 // use absolute paths if passed, otherwise go under share 0101 if (!QDir::isAbsolutePath(dest)) { 0102 dest = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + dest; 0103 } 0104 auto job = new PackageJob(Install, package, sourcePackage, dest); 0105 job->start(); 0106 return job; 0107 } else { 0108 return structOrErr.errorJob; 0109 } 0110 } 0111 0112 PackageJob *PackageJob::update(const QString &packageFormat, const QString &sourcePackage, const QString &packageRoot) 0113 { 0114 auto structOrErr = PackageJobPrivate::loadStructure(packageFormat); 0115 if (auto structure = structOrErr.structure) { 0116 Package package(structure); 0117 package.setPath(sourcePackage); 0118 QString dest = packageRoot.isEmpty() ? package.defaultPackageRoot() : packageRoot; 0119 PackageLoader::invalidateCache(); 0120 0121 // use absolute paths if passed, otherwise go under share 0122 if (!QDir::isAbsolutePath(dest)) { 0123 dest = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + dest; 0124 } 0125 auto job = new PackageJob(Update, package, sourcePackage, dest); 0126 job->start(); 0127 return job; 0128 } else { 0129 return structOrErr.errorJob; 0130 } 0131 } 0132 0133 PackageJob *PackageJob::uninstall(const QString &packageFormat, const QString &pluginId, const QString &packageRoot) 0134 { 0135 auto structOrErr = PackageJobPrivate::loadStructure(packageFormat); 0136 if (auto structure = structOrErr.structure) { 0137 Package package(structure); 0138 QString uninstallPath; 0139 // We handle the empty path when uninstalling the package 0140 // If the dir already got deleted the pluginId is an empty string, without this 0141 // check we would delete the package root, BUG: 410682 0142 if (!pluginId.isEmpty()) { 0143 uninstallPath = packageRoot + QLatin1Char('/') + pluginId; 0144 } 0145 package.setPath(uninstallPath); 0146 0147 PackageLoader::invalidateCache(); 0148 auto job = new PackageJob(Uninstall, package, QString(), QString()); 0149 job->start(); 0150 return job; 0151 } else { 0152 return structOrErr.errorJob; 0153 } 0154 } 0155 0156 KPackage::Package PackageJob::package() const 0157 { 0158 return d->package; 0159 } 0160 void PackageJob::setupNotificationsOnJobFinished(const QString &messageName) 0161 { 0162 // capture first as uninstalling wipes d->package 0163 // or d-package can become dangling during the job if deleted externally 0164 const QString pluginId = d->package.metadata().pluginId(); 0165 const QString kpackageType = readKPackageType(d->package.metadata()); 0166 0167 auto onJobFinished = [=](bool ok, JobError errorCode, const QString &error) { 0168 if (ok) { 0169 auto msg = QDBusMessage::createSignal(QStringLiteral("/KPackage/") + kpackageType, QStringLiteral("org.kde.plasma.kpackage"), messageName); 0170 msg.setArguments({pluginId}); 0171 QDBusConnection::sessionBus().send(msg); 0172 } 0173 0174 if (ok) { 0175 setError(NoError); 0176 } else { 0177 setError(errorCode); 0178 setErrorText(error); 0179 } 0180 emitResult(); 0181 }; 0182 connect(d->thread, &PackageJobThread::jobThreadFinished, this, onJobFinished, Qt::QueuedConnection); 0183 } 0184 0185 } // namespace KPackage 0186 0187 #include "moc_packagejob.cpp"