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#if HAVE_QTDBUS
20#include <QDBusConnection>
21#include <QDBusMessage>
22#endif
23
24#include <QDebug>
25#include <QStandardPaths>
26#include <QThreadPool>
27#include <QTimer>
28
29namespace KPackage
30{
31struct StructureOrErrorJob {
32 PackageStructure *structure = nullptr;
33 PackageJob *errorJob = nullptr;
34};
35class PackageJobPrivate
36{
37public:
38 static StructureOrErrorJob loadStructure(const QString &packageFormat)
39 {
40 if (auto structure = PackageLoader::self()->loadPackageStructure(packageFormat)) {
41 return StructureOrErrorJob{structure, nullptr};
42 } else {
43 auto job = new PackageJob(PackageJob::Install, Package(), QString(), QString());
44 job->setErrorText(QStringLiteral("Could not load package structure ") + packageFormat);
46 QTimer::singleShot(0, job, [job]() {
47 job->emitResult();
48 });
49 return StructureOrErrorJob{nullptr, job};
50 }
51 }
52 PackageJobThread *thread = nullptr;
53 Package package;
54 QString installPath;
55};
56
57PackageJob::PackageJob(OperationType type, const Package &package, const QString &src, const QString &dest)
58 : KJob()
59 , d(new PackageJobPrivate)
60{
61 d->thread = new PackageJobThread(type, src, dest, package);
62 d->package = package;
63
64 connect(d->thread, &PackageJobThread::installPathChanged, this, [this](const QString &installPath) {
65 d->package.setPath(installPath);
66 });
67
68 // setupNotificationsOnJobFinished connects to jobThreadFinished,
69 // don't connect to it again
70 if (type == Install) {
71 setupNotificationsOnJobFinished(QStringLiteral("packageInstalled"));
72 } else if (type == Update) {
73 setupNotificationsOnJobFinished(QStringLiteral("packageUpdated"));
74 d->thread->update(src, dest, package);
75 } else if (type == Uninstall) {
76 setupNotificationsOnJobFinished(QStringLiteral("packageUninstalled"));
77 } else {
78 Q_UNREACHABLE();
79 }
80}
81
82PackageJob::~PackageJob() = default;
83
84void PackageJob::start()
85{
86 if (d->thread) {
88 d->thread = nullptr;
89 } else {
90 qCWarning(KPACKAGE_LOG) << "The KPackage::PackageJob was already started";
91 }
92}
93
94PackageJob *PackageJob::install(const QString &packageFormat, const QString &sourcePackage, const QString &packageRoot)
95{
96 auto structOrErr = PackageJobPrivate::loadStructure(packageFormat);
97 if (auto structure = structOrErr.structure) {
98 Package package(structure);
99 package.setPath(sourcePackage);
100 QString dest = packageRoot.isEmpty() ? package.defaultPackageRoot() : packageRoot;
101 PackageLoader::invalidateCache();
102
103 // use absolute paths if passed, otherwise go under share
104 if (!QDir::isAbsolutePath(dest)) {
106 }
107 auto job = new PackageJob(Install, package, sourcePackage, dest);
108 job->start();
109 return job;
110 } else {
111 return structOrErr.errorJob;
112 }
113}
114
115PackageJob *PackageJob::update(const QString &packageFormat, const QString &sourcePackage, const QString &packageRoot)
116{
117 auto structOrErr = PackageJobPrivate::loadStructure(packageFormat);
118 if (auto structure = structOrErr.structure) {
119 Package package(structure);
120 package.setPath(sourcePackage);
121 QString dest = packageRoot.isEmpty() ? package.defaultPackageRoot() : packageRoot;
122 PackageLoader::invalidateCache();
123
124 // use absolute paths if passed, otherwise go under share
125 if (!QDir::isAbsolutePath(dest)) {
127 }
128 auto job = new PackageJob(Update, package, sourcePackage, dest);
129 job->start();
130 return job;
131 } else {
132 return structOrErr.errorJob;
133 }
134}
135
136PackageJob *PackageJob::uninstall(const QString &packageFormat, const QString &pluginId, const QString &packageRoot)
137{
138 auto structOrErr = PackageJobPrivate::loadStructure(packageFormat);
139 if (auto structure = structOrErr.structure) {
140 Package package(structure);
141 QString uninstallPath;
142 // We handle the empty path when uninstalling the package
143 // If the dir already got deleted the pluginId is an empty string, without this
144 // check we would delete the package root, BUG: 410682
145 if (!pluginId.isEmpty()) {
146 uninstallPath = packageRoot + QLatin1Char('/') + pluginId;
147 }
148 package.setPath(uninstallPath);
149
150 PackageLoader::invalidateCache();
151 auto job = new PackageJob(Uninstall, package, QString(), QString());
152 job->start();
153 return job;
154 } else {
155 return structOrErr.errorJob;
156 }
157}
158
159KPackage::Package PackageJob::package() const
160{
161 return d->package;
162}
163void PackageJob::setupNotificationsOnJobFinished(const QString &messageName)
164{
165 // capture first as uninstalling wipes d->package
166 // or d-package can become dangling during the job if deleted externally
167 const QString pluginId = d->package.metadata().pluginId();
168 const QString kpackageType = readKPackageType(d->package.metadata());
169
170 auto onJobFinished = [=, this](bool ok, JobError errorCode, const QString &error) {
171#if HAVE_QTDBUS
172 if (ok) {
173 auto msg = QDBusMessage::createSignal(QStringLiteral("/KPackage/") + kpackageType, QStringLiteral("org.kde.plasma.kpackage"), messageName);
174 msg.setArguments({pluginId});
176 }
177#endif
178
179 if (ok) {
180 setError(NoError);
181 } else {
182 setError(errorCode);
183 setErrorText(error);
184 }
185 emitResult();
186 };
187 connect(d->thread, &PackageJobThread::jobThreadFinished, this, onJobFinished, Qt::QueuedConnection);
188}
189
190} // namespace KPackage
191
192#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 Sat Dec 21 2024 17:00:55 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.