File indexing completed on 2024-05-05 12:10:10

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez <aleixpol@kde.org>
0004     SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include <QCoreApplication>
0010 #include <QDebug>
0011 #include <QFile>
0012 #include <QFileInfo>
0013 #include <QStandardPaths>
0014 #include <QTimer>
0015 #include <QUrl>
0016 #include <QUrlQuery>
0017 
0018 #include <KLocalizedString>
0019 
0020 #include <KNotification>
0021 
0022 #include <KNSCore/Engine>
0023 #include <KNSCore/QuestionManager>
0024 
0025 #include "knshandlerversion.h"
0026 
0027 /**
0028  * Unfortunately there are two knsrc files for the window decorations, but only one is used in the KCM.
0029  * But both are used by third parties, consequently we can not remove one. To solve this we create a symlink
0030  * which links the old cache file to the new cache file, which is exposed on the GUI.
0031  * This way users can again remove window decorations that are installed as a dependency of a global theme.
0032  * BUG: 414570
0033  */
0034 void createSymlinkForWindowDecorations()
0035 {
0036     QFileInfo info(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/knewstuff3/aurorae.knsregistry"));
0037     // If we have created the symbolic link already we can exit the function here
0038     if (info.isSymbolicLink()) {
0039         return;
0040     }
0041     // Delete this file, it the KNS entries are not exposed in any GUI
0042     if (info.exists()) {
0043         QFile::remove(info.absoluteFilePath());
0044     }
0045     QFileInfo newFileInfo(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/knewstuff3/window-decorations.knsregistry"));
0046     QFile file(newFileInfo.absoluteFilePath());
0047     // Make sure that the file exists
0048     if (!newFileInfo.exists()) {
0049         file.open(QFile::WriteOnly);
0050         file.close();
0051     }
0052     file.link(info.absoluteFilePath());
0053 }
0054 
0055 int main(int argc, char **argv)
0056 {
0057     createSymlinkForWindowDecorations();
0058     QCoreApplication app(argc, argv);
0059     app.setApplicationName(QStringLiteral("kpackage-knshandler"));
0060     app.setApplicationVersion(knshandlerversion);
0061     app.setQuitLockEnabled(false);
0062     Q_ASSERT(app.arguments().count() == 2);
0063 
0064 #ifdef TEST
0065     QStandardPaths::setTestModeEnabled(true);
0066 #endif
0067 
0068     const QUrl url(app.arguments().last());
0069     Q_ASSERT(url.isValid());
0070     Q_ASSERT(url.scheme() == QLatin1String("kns"));
0071 
0072     QString knsname;
0073     const QStringList availableConfigFiles = KNSCore::Engine::availableConfigFiles();
0074     auto knsNameIt = std::find_if(availableConfigFiles.begin(), availableConfigFiles.end(), [&url](const QString &availableFile) {
0075         return availableFile.endsWith(QLatin1String("/") + url.host());
0076     });
0077 
0078     if (knsNameIt == availableConfigFiles.end()) {
0079         qWarning() << "couldn't find knsrc file for" << url.host();
0080         return 1;
0081     } else {
0082         knsname = *knsNameIt;
0083     }
0084 
0085     const auto pathParts = url.path().split(QLatin1Char('/'), Qt::SkipEmptyParts);
0086     if (pathParts.size() != 2) {
0087         qWarning() << "wrong format in the url path" << url << pathParts;
0088         return 1;
0089     }
0090     const auto providerid = pathParts.at(0);
0091     const auto entryid = pathParts.at(1);
0092     int linkid = 1;
0093     if (url.hasQuery()) {
0094         QUrlQuery query(url);
0095         if (query.hasQueryItem(QStringLiteral("linkid"))) {
0096             bool ok;
0097             linkid = query.queryItemValue(QStringLiteral("linkid")).toInt(&ok);
0098             if (!ok) {
0099                 qWarning() << "linkid is not an integer" << url << pathParts;
0100                 return 1;
0101             }
0102         }
0103     }
0104 
0105     KNSCore::Engine engine;
0106     int installedCount = 0;
0107     QObject::connect(KNSCore::QuestionManager::instance(), &KNSCore::QuestionManager::askQuestion, &engine, [](KNSCore::Question *question) {
0108         auto discardQuestion = [question]() {
0109             question->setResponse(KNSCore::Question::InvalidResponse);
0110         };
0111         switch (question->questionType()) {
0112         case KNSCore::Question::YesNoQuestion: {
0113             auto f = KNotification::event(KNotification::StandardEvent::Notification, question->title(), question->question());
0114             f->setActions({i18n("Yes"), i18n("No")});
0115             QObject::connect(f, &KNotification::action1Activated, question, [question]() {
0116                 question->setResponse(KNSCore::Question::YesResponse);
0117             });
0118             QObject::connect(f, &KNotification::action2Activated, question, [question]() {
0119                 question->setResponse(KNSCore::Question::NoResponse);
0120             });
0121             QObject::connect(f, &KNotification::closed, question, discardQuestion);
0122         } break;
0123         case KNSCore::Question::ContinueCancelQuestion: {
0124             auto f = KNotification::event(KNotification::StandardEvent::Notification, question->title(), question->question());
0125             f->setActions({i18n("Continue"), i18n("Cancel")});
0126             QObject::connect(f, &KNotification::action1Activated, question, [question]() {
0127                 question->setResponse(KNSCore::Question::ContinueResponse);
0128             });
0129             QObject::connect(f, &KNotification::action2Activated, question, [question]() {
0130                 question->setResponse(KNSCore::Question::CancelResponse);
0131             });
0132             QObject::connect(f, &KNotification::closed, question, discardQuestion);
0133         } break;
0134         case KNSCore::Question::InputTextQuestion:
0135         case KNSCore::Question::SelectFromListQuestion:
0136         case KNSCore::Question::PasswordQuestion:
0137             discardQuestion();
0138             break;
0139         }
0140     });
0141 
0142     QObject::connect(&engine, &KNSCore::Engine::signalProvidersLoaded, &engine, [&engine, entryid]() {
0143         engine.fetchEntryById(entryid);
0144     });
0145 
0146     QObject::connect(&engine, &KNSCore::Engine::signalErrorCode, &engine, [](KNSCore::ErrorCode errorCode, const QString &message, const QVariant &metadata) {
0147         qWarning() << "kns error:" << errorCode << message << metadata;
0148         QCoreApplication::exit(1);
0149     });
0150     QObject::connect(&engine,
0151                      &KNSCore::Engine::signalEntryEvent,
0152                      &engine,
0153                      [providerid, linkid, &engine, &installedCount](const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::EntryEvent event) {
0154                          if (event == KNSCore::EntryInternal::DetailsLoadedEvent) {
0155                              // qDebug() << "checking..." << entry.status() << entry.providerId();
0156                              if (providerid != QUrl(entry.providerId()).host()) {
0157                                  qWarning() << "Wrong provider" << providerid << "instead of" << QUrl(entry.providerId()).host();
0158                                  QCoreApplication::exit(1);
0159                              } else if (entry.status() == KNS3::Entry::Downloadable) {
0160                                  qDebug() << "installing...";
0161                                  installedCount++;
0162                                  engine.install(entry, linkid);
0163                              } else if (installedCount == 0) {
0164                                  qDebug() << "already installed.";
0165                                  QCoreApplication::exit(0);
0166                              }
0167                          } else if (event == KNSCore::EntryInternal::StatusChangedEvent) {
0168                              if (entry.status() == KNS3::Entry::Installed) {
0169                                  installedCount--;
0170                              }
0171                              if (installedCount == 0) {
0172                                  QCoreApplication::exit(0);
0173                              }
0174                          }
0175                      });
0176     if (!engine.init(knsname)) {
0177         qWarning() << "couldn't initialize" << knsname;
0178         return 1;
0179     }
0180     return app.exec();
0181 }