Changeset View
Standalone View
src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp
Show All 14 Lines | |||||
15 | * GNU General Public License for more details. * | 15 | * GNU General Public License for more details. * | ||
16 | * * | 16 | * * | ||
17 | * You should have received a copy of the GNU General Public License * | 17 | * You should have received a copy of the GNU General Public License * | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. * | 18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. * | ||
19 | ***************************************************************************/ | 19 | ***************************************************************************/ | ||
20 | 20 | | |||
21 | #include <QDebug> | 21 | #include <QDebug> | ||
22 | #include <QProcess> | 22 | #include <QProcess> | ||
23 | #include <QTimer> | ||||
23 | #include <QStandardPaths> | 24 | #include <QStandardPaths> | ||
24 | #include <QDir> | 25 | #include <QDir> | ||
25 | #include <QDirIterator> | 26 | #include <QDirIterator> | ||
26 | #include <QCommandLineParser> | 27 | #include <QCommandLineParser> | ||
27 | #include <QMimeDatabase> | 28 | #include <QMimeDatabase> | ||
28 | #include <QUrl> | 29 | #include <QUrl> | ||
29 | #include <QDesktopServices> | | |||
30 | #include <QGuiApplication> | 30 | #include <QGuiApplication> | ||
31 | | ||||
32 | #include <KLocalizedString> | 31 | #include <KLocalizedString> | ||
33 | #include <KShell> | 32 | #include <KShell> | ||
34 | 33 | | |||
34 | #include "../../../config-packagekit.h" | ||||
anthonyfieroni: `const`, `QStringLiteral` to strings. | |||||
This should include application/x-bzip2 as well. Also, it would probably be optimal to query what PackageKit backend is currently being used and set associations based on that. cblack: This should include `application/x-bzip2` as well. Also, it would probably be optimal to query… | |||||
qWarning() << "Mime types:" << PackageKit::Daemon::mimeTypes(); returns an empty list, I don't know why. And application/x-bzip2 is already in the list of file formats used for decompression. (Line 150). alex: `qWarning() << "Mime types:" << PackageKit::Daemon::mimeTypes();` returns an empty list, I… | |||||
35 | | ||||
36 | const static QStringList binaryPackages = {QStringLiteral("application/vnd.debian.binary-package"), | ||||
37 | QStringLiteral("application/x-rpm"), | ||||
38 | QStringLiteral("application/x-xz"), | ||||
39 | QStringLiteral("application/zstd")}; | ||||
40 | enum PackageOperation { | ||||
41 | Install, | ||||
42 | Uninstall | ||||
43 | }; | ||||
44 | | ||||
45 | #ifdef HAVE_PACKAGEKIT | ||||
46 | #include <PackageKit/Daemon> | ||||
47 | #include <PackageKit/Details> | ||||
48 | #include <PackageKit/Transaction> | ||||
49 | #else | ||||
50 | #include <QDesktopServices> | ||||
51 | #endif | ||||
52 | | ||||
35 | // @param msg Error that gets logged to CLI | 53 | // @param msg Error that gets logged to CLI | ||
36 | Q_NORETURN void fail(const QString &str) | 54 | Q_NORETURN void fail(const QString &str) | ||
37 | { | 55 | { | ||
38 | qCritical() << str; | 56 | qCritical() << str; | ||
39 | 57 | const QStringList args = {"--detailederror" ,i18n("Dolphin service menu installation failed"), str}; | |||
40 | QProcess process; | 58 | QProcess::startDetached("kdialog", args); | ||
41 | const QStringList args = {"--passivepopup", i18n("Dolphin service menu installation failed"), "15"}; | | |||
42 | process.start("kdialog", args, QIODevice::ReadOnly); | | |||
43 | if (!process.waitForStarted()) { | | |||
44 | qFatal("Failed to run kdialog"); | | |||
45 | } | | |||
46 | 59 | | |||
47 | exit(1); | 60 | exit(1); | ||
48 | } | 61 | } | ||
49 | 62 | | |||
50 | QString getServiceMenusDir() | 63 | QString getServiceMenusDir() | ||
51 | { | 64 | { | ||
52 | const QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); | 65 | const QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); | ||
53 | return QDir(dataLocation).absoluteFilePath("kservices5/ServiceMenus"); | 66 | return QDir(dataLocation).absoluteFilePath("kservices5/ServiceMenus"); | ||
54 | } | 67 | } | ||
55 | 68 | | |||
69 | #ifdef HAVE_PACKAGEKIT | ||||
70 | void packageKitInstall(const QString &fileName) | ||||
71 | { | ||||
72 | PackageKit::Transaction *transaction = PackageKit::Daemon::installFile(fileName); | ||||
73 | | ||||
74 | const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) { | ||||
Please call it exitWithError() or similar instead, since this is what this lambda actually does. elvisangelaccio: Please call it `exitWithError()` or similar instead, since this is what this lambda actually… | |||||
75 | fail(details); | ||||
76 | }; | ||||
77 | | ||||
78 | QObject::connect(transaction, &PackageKit::Transaction::finished, | ||||
meven: Mention the fileName in the error message. | |||||
79 | [=](PackageKit::Transaction::Exit status, uint) { | ||||
Not sure calling QFileInfo on fileName is necessary : it adds a stat, if fileName does not exist anymore it will break. meven: Not sure calling QFileInfo on fileName is necessary : it adds a stat, if fileName does not… | |||||
80 | if (status == PackageKit::Transaction::ExitSuccess) { | ||||
Installation process may fail due to unsatisfied depends can we get more info about? anthonyfieroni: Installation process may fail due to unsatisfied depends can we get more info about? | |||||
81 | exit(0); | ||||
82 | } | ||||
83 | // Fallback error handling | ||||
84 | QTimer::singleShot(500, [=](){ | ||||
85 | fail(i18n("Failed to install \"%1\", exited with status \"%2\"", | ||||
86 | fileName, QVariant::fromValue(status).toString())); | ||||
87 | }); | ||||
88 | }); | ||||
89 | QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); | ||||
elvisangelaccio: Same here. | |||||
90 | } | ||||
meven: Indentation seems of :/ way soo spacey | |||||
alex: How would it be better? Maybe define the lambdas before? | |||||
meven: That's a solution, or adding a function. | |||||
91 | | ||||
92 | void packageKitUninstall(const QString &fileName) | ||||
93 | { | ||||
94 | const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) { | ||||
meven: Mention the fileName in the error message. | |||||
95 | fail(details); | ||||
96 | }; | ||||
97 | const auto uninstallLambda = [=](PackageKit::Transaction::Exit status, uint) { | ||||
98 | if (status == PackageKit::Transaction::ExitSuccess) { | ||||
99 | exit(0); | ||||
100 | } | ||||
alex: Or should there be an exit(1) in the next line? | |||||
101 | }; | ||||
102 | | ||||
meven: Mention the fileName in the error message. | |||||
103 | PackageKit::Transaction *transaction = PackageKit::Daemon::getDetailsLocal(fileName); | ||||
104 | QObject::connect(transaction, &PackageKit::Transaction::details, | ||||
105 | [=](const PackageKit::Details &details) { | ||||
106 | PackageKit::Transaction *transaction = PackageKit::Daemon::removePackage(details.packageId()); | ||||
107 | QObject::connect(transaction, &PackageKit::Transaction::finished, uninstallLambda); | ||||
108 | QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); | ||||
109 | }); | ||||
110 | | ||||
111 | QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); | ||||
112 | // Fallback error handling | ||||
elvisangelaccio: Same here: please don't use booleans as arguments. | |||||
113 | QObject::connect(transaction, &PackageKit::Transaction::finished, | ||||
114 | [=](PackageKit::Transaction::Exit status, uint) { | ||||
115 | if (status != PackageKit::Transaction::ExitSuccess) { | ||||
116 | QTimer::singleShot(500, [=]() { | ||||
elvisangelaccio: Please use `QFileInfo::exists()` instead. | |||||
117 | fail(i18n("Failed to uninstall \"%1\", exited with status \"%2\"", | ||||
118 | fileName, QVariant::fromValue(status).toString())); | ||||
119 | }); | ||||
120 | } | ||||
121 | }); | ||||
122 | } | ||||
123 | #endif | ||||
124 | | ||||
125 | Q_NORETURN void packageKit(PackageOperation operation, const QString &fileName) | ||||
126 | { | ||||
127 | #ifdef HAVE_PACKAGEKIT | ||||
128 | QFileInfo fileInfo(fileName); | ||||
129 | if (!fileInfo.exists()) { | ||||
130 | fail(i18n("The file does not exist!")); | ||||
131 | } | ||||
132 | const QString absPath = fileInfo.absoluteFilePath(); | ||||
133 | if (operation == PackageOperation::Install) { | ||||
134 | packageKitInstall(absPath); | ||||
135 | } else { | ||||
136 | packageKitUninstall(absPath); | ||||
137 | } | ||||
138 | QGuiApplication::exec(); // For event handling, no return after signals finish | ||||
139 | fail(i18n("Unknown error when installing package")); | ||||
140 | #else | ||||
141 | Q_UNUSED(operation) | ||||
142 | QDesktopServices::openUrl(QUrl(fileName)); | ||||
143 | exit(0); | ||||
144 | #endif | ||||
145 | } | ||||
146 | | ||||
56 | struct UncompressCommand | 147 | struct UncompressCommand | ||
57 | { | 148 | { | ||
58 | QString command; | 149 | QString command; | ||
59 | QStringList args1; | 150 | QStringList args1; | ||
60 | QStringList args2; | 151 | QStringList args2; | ||
61 | }; | 152 | }; | ||
62 | 153 | | |||
63 | enum ScriptExecution{ | 154 | enum ScriptExecution{ | ||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Line(s) | 303 | if (archive.endsWith(QLatin1String(".desktop"))) { | |||
215 | qInfo() << "Single-File Service-Menu" << archive << dest; | 306 | qInfo() << "Single-File Service-Menu" << archive << dest; | ||
216 | 307 | | |||
217 | QFile source(archive); | 308 | QFile source(archive); | ||
218 | if (!source.copy(dest)) { | 309 | if (!source.copy(dest)) { | ||
219 | errorText = i18n("Failed to copy .desktop file %1 to %2: %3", archive, dest, source.errorString()); | 310 | errorText = i18n("Failed to copy .desktop file %1 to %2: %3", archive, dest, source.errorString()); | ||
220 | return false; | 311 | return false; | ||
221 | } | 312 | } | ||
222 | } else { | 313 | } else { | ||
223 | const QStringList binaryPackages = {"application/vnd.debian.binary-package", "application/x-rpm"}; | | |||
224 | if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) { | 314 | if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) { | ||
225 | return QDesktopServices::openUrl(QUrl(archive)); | 315 | packageKit(PackageOperation::Install, archive); | ||
226 | } | 316 | } | ||
227 | const QString dir = generateDirPath(archive); | 317 | const QString dir = generateDirPath(archive); | ||
228 | if (QFile::exists(dir)) { | 318 | if (QFile::exists(dir)) { | ||
229 | if (!QDir(dir).removeRecursively()) { | 319 | if (!QDir(dir).removeRecursively()) { | ||
230 | errorText = i18n("Failed to remove directory %1", dir); | 320 | errorText = i18n("Failed to remove directory %1", dir); | ||
231 | return false; | 321 | return false; | ||
232 | } | 322 | } | ||
233 | } | 323 | } | ||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Line(s) | 374 | if (archive.endsWith(QLatin1String(".desktop"))) { | |||
285 | // Append basename to destination directory | 375 | // Append basename to destination directory | ||
286 | const auto dest = QDir(serviceDir).absoluteFilePath(QFileInfo(archive).fileName()); | 376 | const auto dest = QDir(serviceDir).absoluteFilePath(QFileInfo(archive).fileName()); | ||
287 | QFile file(dest); | 377 | QFile file(dest); | ||
288 | if (!file.remove()) { | 378 | if (!file.remove()) { | ||
289 | errorText = i18n("Failed to remove .desktop file %1: %2", dest, file.errorString()); | 379 | errorText = i18n("Failed to remove .desktop file %1: %2", dest, file.errorString()); | ||
290 | return false; | 380 | return false; | ||
291 | } | 381 | } | ||
292 | } else { | 382 | } else { | ||
383 | if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) { | ||||
anthonyfieroni: Why limited to these 2? Pacman packages should be included as well. | |||||
I choose only these two because they are very common package formats and quite a few services in the store already have deb/rpm packages available. But I am unsure on how to check the mimetype for pacman packages, would it be enough to check for application/x-xz? alex: I choose only these two because they are very common package formats and quite a few services… | |||||
https://en.wikipedia.org/wiki/List_of_archive_formats#Software_packaging_and_distribution anthonyfieroni: https://en.wikipedia.org/wiki/List_of_archive_formats#Software_packaging_and_distribution… | |||||
384 | packageKit(PackageOperation::Uninstall, archive); | ||||
385 | } | ||||
293 | const QString dir = generateDirPath(archive); | 386 | const QString dir = generateDirPath(archive); | ||
294 | 387 | | |||
295 | // Try "deinstall" first | 388 | // Try "deinstall" first | ||
296 | QString deinstallPath; | 389 | QString deinstallPath; | ||
297 | const QStringList basenames1 = {"uninstall.sh", "uninstal", "deinstall.sh", "deinstall"}; | 390 | const QStringList basenames1 = {"uninstall.sh", "uninstal", "deinstall.sh", "deinstall"}; | ||
298 | for (const auto &basename : basenames1) { | 391 | for (const auto &basename : basenames1) { | ||
299 | const auto path = findRecursive(dir, basename); | 392 | const auto path = findRecursive(dir, basename); | ||
300 | if (!path.isEmpty()) { | 393 | if (!path.isEmpty()) { | ||
▲ Show 20 Lines • Show All 79 Lines • Show Last 20 Lines |
const, QStringLiteral to strings.