File indexing completed on 2025-04-27 06:49:48
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/EngineBase> 0023 #include <KNSCore/Question> 0024 #include <KNSCore/QuestionManager> 0025 #include <KNSCore/ResultsStream> 0026 #include <KNSCore/Transaction> 0027 0028 #include "knshandlerversion.h" 0029 0030 /** 0031 * Unfortunately there are two knsrc files for the window decorations, but only one is used in the KCM. 0032 * But both are used by third parties, consequently we can not remove one. To solve this we create a symlink 0033 * which links the old cache file to the new cache file, which is exposed on the GUI. 0034 * This way users can again remove window decorations that are installed as a dependency of a global theme. 0035 * BUG: 414570 0036 */ 0037 void createSymlinkForWindowDecorations() 0038 { 0039 QFileInfo info(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/knewstuff3/aurorae.knsregistry")); 0040 // If we have created the symbolic link already we can exit the function here 0041 if (info.isSymbolicLink()) { 0042 return; 0043 } 0044 // Delete this file, it the KNS entries are not exposed in any GUI 0045 if (info.exists()) { 0046 QFile::remove(info.absoluteFilePath()); 0047 } 0048 QFileInfo newFileInfo(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/knewstuff3/window-decorations.knsregistry")); 0049 QFile file(newFileInfo.absoluteFilePath()); 0050 // Make sure that the file exists 0051 if (!newFileInfo.exists()) { 0052 file.open(QFile::WriteOnly); 0053 file.close(); 0054 } 0055 file.link(info.absoluteFilePath()); 0056 } 0057 0058 int main(int argc, char **argv) 0059 { 0060 createSymlinkForWindowDecorations(); 0061 QCoreApplication app(argc, argv); 0062 app.setApplicationName(QStringLiteral("kpackage-knshandler")); 0063 app.setApplicationVersion(knshandlerversion); 0064 app.setQuitLockEnabled(false); 0065 Q_ASSERT(app.arguments().count() == 2); 0066 0067 #ifdef TEST 0068 QStandardPaths::setTestModeEnabled(true); 0069 #endif 0070 0071 const QUrl url(app.arguments().last()); 0072 Q_ASSERT(url.isValid()); 0073 Q_ASSERT(url.scheme() == QLatin1String("kns")); 0074 0075 QString knsname; 0076 const QStringList availableConfigFiles = KNSCore::EngineBase::availableConfigFiles(); 0077 auto knsNameIt = std::find_if(availableConfigFiles.begin(), availableConfigFiles.end(), [&url](const QString &availableFile) { 0078 return availableFile.endsWith(QLatin1String("/") + url.host()); 0079 }); 0080 0081 if (knsNameIt == availableConfigFiles.end()) { 0082 qWarning() << "couldn't find knsrc file for" << url.host(); 0083 return 1; 0084 } else { 0085 knsname = *knsNameIt; 0086 } 0087 0088 const auto pathParts = url.path().split(QLatin1Char('/'), Qt::SkipEmptyParts); 0089 if (pathParts.size() != 2) { 0090 qWarning() << "wrong format in the url path" << url << pathParts; 0091 return 1; 0092 } 0093 const auto providerid = pathParts.at(0); 0094 const auto entryid = pathParts.at(1); 0095 int linkid = 1; 0096 if (url.hasQuery()) { 0097 QUrlQuery query(url); 0098 if (query.hasQueryItem(QStringLiteral("linkid"))) { 0099 bool ok; 0100 linkid = query.queryItemValue(QStringLiteral("linkid")).toInt(&ok); 0101 if (!ok) { 0102 qWarning() << "linkid is not an integer" << url << pathParts; 0103 return 1; 0104 } 0105 } 0106 } 0107 0108 KNSCore::EngineBase engine; 0109 int installedCount = 0; 0110 QObject::connect(KNSCore::QuestionManager::instance(), &KNSCore::QuestionManager::askQuestion, &engine, [](KNSCore::Question *question) { 0111 auto discardQuestion = [question]() { 0112 question->setResponse(KNSCore::Question::InvalidResponse); 0113 }; 0114 switch (question->questionType()) { 0115 case KNSCore::Question::YesNoQuestion: { 0116 auto f = KNotification::event(KNotification::StandardEvent::Notification, question->title(), question->question()); 0117 0118 auto *yes = f->addAction(i18n("Yes")); 0119 QObject::connect(yes, &KNotificationAction::activated, question, [question] { 0120 question->setResponse(KNSCore::Question::YesResponse); 0121 }); 0122 0123 auto *no = f->addAction(i18n("Yes")); 0124 QObject::connect(no, &KNotificationAction::activated, question, [question] { 0125 question->setResponse(KNSCore::Question::NoResponse); 0126 }); 0127 0128 QObject::connect(f, &KNotification::closed, question, discardQuestion); 0129 } break; 0130 case KNSCore::Question::ContinueCancelQuestion: { 0131 auto f = KNotification::event(KNotification::StandardEvent::Notification, question->title(), question->question()); 0132 0133 auto *continueAction = f->addAction(i18n("Continue")); 0134 QObject::connect(continueAction, &KNotificationAction::activated, question, [question]() { 0135 question->setResponse(KNSCore::Question::ContinueResponse); 0136 }); 0137 0138 auto *cancelAction = f->addAction(i18n("Cancel")); 0139 QObject::connect(cancelAction, &KNotificationAction::activated, question, [question]() { 0140 question->setResponse(KNSCore::Question::CancelResponse); 0141 }); 0142 0143 QObject::connect(f, &KNotification::closed, question, discardQuestion); 0144 } break; 0145 case KNSCore::Question::InputTextQuestion: 0146 case KNSCore::Question::SelectFromListQuestion: 0147 case KNSCore::Question::PasswordQuestion: 0148 discardQuestion(); 0149 break; 0150 } 0151 }); 0152 0153 const auto onError = [](KNSCore::ErrorCode::ErrorCode errorCode, const QString &message, const QVariant &metadata) { 0154 qWarning() << "kns error:" << errorCode << message << metadata; 0155 QCoreApplication::exit(1); 0156 }; 0157 0158 bool entryWasFound = false; 0159 const auto onEntriesLoded = [providerid, linkid, &installedCount, &engine, &entryWasFound, onError](const KNSCore::Entry::List list) { 0160 Q_ASSERT(list.size() == 1); 0161 entryWasFound = true; 0162 const auto entry = list.first(); 0163 if (providerid != entry.providerId()) { 0164 qWarning() << "Wrong provider" << providerid << "instead of" << entry.providerId(); 0165 QCoreApplication::exit(1); 0166 } else if (entry.status() == KNSCore::Entry::Downloadable) { 0167 qDebug() << "installing..."; 0168 installedCount++; 0169 auto transaction = KNSCore::Transaction::install(&engine, entry, linkid); 0170 QObject::connect(transaction, &KNSCore::Transaction::signalErrorCode, onError); 0171 QObject::connect(transaction, &KNSCore::Transaction::signalEntryEvent, [&installedCount](auto entry, auto event) { 0172 if (event == KNSCore::Entry::StatusChangedEvent) { 0173 if (entry.status() == KNSCore::Entry::Installed) { 0174 installedCount--; 0175 } 0176 if (installedCount == 0) { 0177 QCoreApplication::exit(0); 0178 } 0179 } 0180 }); 0181 } else if (installedCount == 0) { 0182 qDebug() << "already installed."; 0183 QCoreApplication::exit(0); 0184 } 0185 }; 0186 QObject::connect(&engine, &KNSCore::EngineBase::signalProvidersLoaded, &engine, [&engine, &entryid, onEntriesLoded, &entryWasFound]() { 0187 qWarning() << "providers are loaded"; 0188 KNSCore::Provider::SearchRequest request(KNSCore::Provider::Newest, KNSCore::Provider::ExactEntryId, entryid, QStringList{}, 0); 0189 KNSCore::ResultsStream *results = engine.search(request); 0190 QObject::connect(results, &KNSCore::ResultsStream::entriesFound, &engine, onEntriesLoded); 0191 QObject::connect(results, &KNSCore::ResultsStream::finished, &engine, [&entryWasFound, &entryid]() { 0192 if (!entryWasFound) { 0193 qWarning() << "Entry with id" << entryid << "could not be found"; 0194 QCoreApplication::exit(1); 0195 } 0196 }); 0197 results->fetch(); 0198 }); 0199 0200 QObject::connect(&engine, &KNSCore::EngineBase::signalErrorCode, &engine, onError); 0201 if (!engine.init(knsname)) { 0202 qWarning() << "couldn't initialize" << knsname; 0203 return 1; 0204 } 0205 return app.exec(); 0206 }