KPackage

packagejob.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Sebastian Kügler <sebas@kde.org>
3 SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "packagejob.h"
9
10#include "config-package.h"
11#include "packageloader.h"
12#include "packagestructure.h"
13#include "private/package_p.h"
14#include "private/packagejobthread_p.h"
15#include "private/utils.h"
16
17#include "kpackage_debug.h"
18
19#include <QDBusConnection>
20#include <QDBusMessage>
21#include <QDebug>
22#include <QStandardPaths>
23#include <QThreadPool>
24#include <QTimer>
25
26namespace KPackage
27{
28struct StructureOrErrorJob {
29 PackageStructure *structure = nullptr;
30 PackageJob *errorJob = nullptr;
31};
32class PackageJobPrivate
33{
34public:
35 static StructureOrErrorJob loadStructure(const QString &packageFormat)
36 {
37 if (auto structure = PackageLoader::self()->loadPackageStructure(packageFormat)) {
38 return StructureOrErrorJob{structure, nullptr};
39 } else {
40 auto job = new PackageJob(PackageJob::Install, Package(), QString(), QString());
41 job->setErrorText(QStringLiteral("Could not load package structure ") + packageFormat);
43 QTimer::singleShot(0, job, [job]() {
44 job->emitResult();
45 });
46 return StructureOrErrorJob{nullptr, job};
47 }
48 }
49 PackageJobThread *thread = nullptr;
50 Package package;
51 QString installPath;
52};
53
54PackageJob::PackageJob(OperationType type, const Package &package, const QString &src, const QString &dest)
55 : KJob()
56 , d(new PackageJobPrivate)
57{
58 d->thread = new PackageJobThread(type, src, dest, package);
59 d->package = package;
60
61 if (type == Install) {
62 setupNotificationsOnJobFinished(QStringLiteral("packageInstalled"));
63 } else if (type == Update) {
64 setupNotificationsOnJobFinished(QStringLiteral("packageUpdated"));
65 d->thread->update(src, dest, package);
66 } else if (type == Uninstall) {
67 setupNotificationsOnJobFinished(QStringLiteral("packageUninstalled"));
68 } else {
69 Q_UNREACHABLE();
70 }
71 connect(d->thread, &PackageJobThread::installPathChanged, this, [this](const QString &installPath) {
72 d->package.setPath(installPath);
73 });
74 connect(d->thread, &PackageJobThread::jobThreadFinished, this, [this]() {
75 emitResult();
76 });
77}
78
79PackageJob::~PackageJob() = default;
80
81void PackageJob::start()
82{
83 if (d->thread) {
85 d->thread = nullptr;
86 } else {
87 qCWarning(KPACKAGE_LOG) << "The KPackage::PackageJob was already started";
88 }
89}
90
91PackageJob *PackageJob::install(const QString &packageFormat, const QString &sourcePackage, const QString &packageRoot)
92{
93 auto structOrErr = PackageJobPrivate::loadStructure(packageFormat);
94 if (auto structure = structOrErr.structure) {
95 Package package(structure);
96 package.setPath(sourcePackage);
97 QString dest = packageRoot.isEmpty() ? package.defaultPackageRoot() : packageRoot;
98 PackageLoader::invalidateCache();
99
100 // use absolute paths if passed, otherwise go under share
101 if (!QDir::isAbsolutePath(dest)) {
103 }
104 auto job = new PackageJob(Install, package, sourcePackage, dest);
105 job->start();
106 return job;
107 } else {
108 return structOrErr.errorJob;
109 }
110}
111
112PackageJob *PackageJob::update(const QString &packageFormat, const QString &sourcePackage, const QString &packageRoot)
113{
114 auto structOrErr = PackageJobPrivate::loadStructure(packageFormat);
115 if (auto structure = structOrErr.structure) {
116 Package package(structure);
117 package.setPath(sourcePackage);
118 QString dest = packageRoot.isEmpty() ? package.defaultPackageRoot() : packageRoot;
119 PackageLoader::invalidateCache();
120
121 // use absolute paths if passed, otherwise go under share
122 if (!QDir::isAbsolutePath(dest)) {
124 }
125 auto job = new PackageJob(Update, package, sourcePackage, dest);
126 job->start();
127 return job;
128 } else {
129 return structOrErr.errorJob;
130 }
131}
132
133PackageJob *PackageJob::uninstall(const QString &packageFormat, const QString &pluginId, const QString &packageRoot)
134{
135 auto structOrErr = PackageJobPrivate::loadStructure(packageFormat);
136 if (auto structure = structOrErr.structure) {
137 Package package(structure);
138 QString uninstallPath;
139 // We handle the empty path when uninstalling the package
140 // If the dir already got deleted the pluginId is an empty string, without this
141 // check we would delete the package root, BUG: 410682
142 if (!pluginId.isEmpty()) {
143 uninstallPath = packageRoot + QLatin1Char('/') + pluginId;
144 }
145 package.setPath(uninstallPath);
146
147 PackageLoader::invalidateCache();
148 auto job = new PackageJob(Uninstall, package, QString(), QString());
149 job->start();
150 return job;
151 } else {
152 return structOrErr.errorJob;
153 }
154}
155
156KPackage::Package PackageJob::package() const
157{
158 return d->package;
159}
160void PackageJob::setupNotificationsOnJobFinished(const QString &messageName)
161{
162 // capture first as uninstalling wipes d->package
163 // or d-package can become dangling during the job if deleted externally
164 const QString pluginId = d->package.metadata().pluginId();
165 const QString kpackageType = readKPackageType(d->package.metadata());
166
167 auto onJobFinished = [=](bool ok, JobError errorCode, const QString &error) {
168 if (ok) {
169 auto msg = QDBusMessage::createSignal(QStringLiteral("/KPackage/") + kpackageType, QStringLiteral("org.kde.plasma.kpackage"), messageName);
170 msg.setArguments({pluginId});
172 }
173
174 if (ok) {
175 setError(NoError);
176 } else {
177 setError(errorCode);
178 setErrorText(error);
179 }
180 emitResult();
181 };
182 connect(d->thread, &PackageJobThread::jobThreadFinished, this, onJobFinished, Qt::QueuedConnection);
183}
184
185} // namespace KPackage
186
187#include "moc_packagejob.cpp"
KJob subclass that allows async install/update/uninstall operations for packages.
Definition packagejob.h:27
@ InvalidPackageStructure
Could not find/load the given package structure.
Definition packagejob.h:35
static PackageLoader * self()
Return the active plugin loader.
object representing an installed package
Definition package.h:63
void setPath(const QString &path)
Sets the path to the root of this package.
Definition package.cpp:439
QString defaultPackageRoot() const
Definition package.cpp:128
bool send(const QDBusMessage &message) const const
QDBusConnection sessionBus()
QDBusMessage createSignal(const QString &path, const QString &interface, const QString &name)
bool isAbsolutePath(const QString &path)
QThread * thread() const const
QString writableLocation(StandardLocation type)
bool isEmpty() const const
QueuedConnection
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QThreadPool * globalInstance()
void start(Callable &&callableToRun, int priority)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:48 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.