File indexing completed on 2023-09-24 04:10:43
0001 /* 0002 SPDX-FileCopyrightText: 2008 Aaron Seigo <aseigo@kde.org> 0003 SPDX-FileCopyrightText: 2012-2017 Sebastian Kügler <sebas@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kpackagetool.h" 0009 0010 #include <KAboutData> 0011 #include <KLocalizedString> 0012 #include <KShell> 0013 #include <QDebug> 0014 0015 #include <KJob> 0016 #include <kpackage/package.h> 0017 #include <kpackage/packageloader.h> 0018 #include <kpackage/packagestructure.h> 0019 #include <kpackage/private/utils.h> 0020 0021 #include <QCommandLineParser> 0022 #include <QDir> 0023 #include <QFileInfo> 0024 #include <QMap> 0025 #include <QRegularExpression> 0026 #include <QStandardPaths> 0027 #include <QStringList> 0028 #include <QTimer> 0029 #include <QUrl> 0030 #include <QVector> 0031 #include <QXmlStreamWriter> 0032 0033 #include <iomanip> 0034 #include <iostream> 0035 0036 #include "options.h" 0037 0038 #include "../kpackage/config-package.h" 0039 // for the index creation function 0040 #include "../kpackage/package_export.h" 0041 #include "../kpackage/private/packagejobthread_p.h" 0042 0043 #include "kpackage_debug.h" 0044 0045 Q_GLOBAL_STATIC_WITH_ARGS(QTextStream, cout, (stdout)) 0046 Q_GLOBAL_STATIC_WITH_ARGS(QTextStream, cerr, (stderr)) 0047 0048 namespace KPackage 0049 { 0050 class PackageToolPrivate 0051 { 0052 public: 0053 QString packageRoot; 0054 QString packageFile; 0055 QString package; 0056 QStringList pluginTypes; 0057 KPackage::Package installer; 0058 KPluginMetaData metadata; 0059 QString installPath; 0060 void output(const QString &msg); 0061 QStringList packages(const QStringList &types, const QString &path = QString()); 0062 void renderTypeTable(const QMap<QString, QString> &plugins); 0063 void listTypes(); 0064 void coutput(const QString &msg); 0065 void cerror(const QString &msg); 0066 QCommandLineParser *parser = nullptr; 0067 }; 0068 0069 PackageTool::PackageTool(int &argc, char **argv, QCommandLineParser *parser) 0070 : QCoreApplication(argc, argv) 0071 { 0072 d = new PackageToolPrivate; 0073 d->parser = parser; 0074 QTimer::singleShot(0, this, &PackageTool::runMain); 0075 } 0076 0077 PackageTool::~PackageTool() 0078 { 0079 delete d; 0080 } 0081 0082 void PackageTool::runMain() 0083 { 0084 KPackage::PackageStructure structure; 0085 if (d->parser->isSet(Options::hash())) { 0086 const QString path = d->parser->value(Options::hash()); 0087 KPackage::Package package(&structure); 0088 package.setPath(path); 0089 const QString hash = QString::fromLocal8Bit(package.cryptographicHash(QCryptographicHash::Sha1)); 0090 if (hash.isEmpty()) { 0091 d->coutput(i18n("Failed to generate a Package hash for %1", path)); 0092 exit(9); 0093 } else { 0094 d->coutput(i18n("SHA1 hash for Package at %1: '%2'", package.path(), hash)); 0095 exit(0); 0096 } 0097 return; 0098 } 0099 0100 if (d->parser->isSet(Options::listTypes())) { 0101 d->listTypes(); 0102 exit(0); 0103 return; 0104 } 0105 0106 QString type = d->parser->value(Options::type()); 0107 d->pluginTypes.clear(); 0108 d->installer = Package(); 0109 0110 if (d->parser->isSet(Options::remove())) { 0111 d->package = d->parser->value(Options::remove()); 0112 } else if (d->parser->isSet(Options::upgrade())) { 0113 d->package = d->parser->value(Options::upgrade()); 0114 } else if (d->parser->isSet(Options::install())) { 0115 d->package = d->parser->value(Options::install()); 0116 } else if (d->parser->isSet(Options::show())) { 0117 d->package = d->parser->value(Options::show()); 0118 } else if (d->parser->isSet(Options::appstream())) { 0119 d->package = d->parser->value(Options::appstream()); 0120 } 0121 0122 if (!QDir::isAbsolutePath(d->package)) { 0123 d->packageFile = QDir(QDir::currentPath() + QLatin1Char('/') + d->package).absolutePath(); 0124 d->packageFile = QFileInfo(d->packageFile).canonicalFilePath(); 0125 if (d->parser->isSet(Options::upgrade())) { 0126 d->package = d->packageFile; 0127 } 0128 } else { 0129 d->packageFile = d->package; 0130 } 0131 0132 if (!d->packageFile.isEmpty() 0133 && (!d->parser->isSet(Options::type()) || type.compare(i18nc("package type", "wallpaper"), Qt::CaseInsensitive) == 0 0134 || type.compare(QLatin1String("wallpaper"), Qt::CaseInsensitive) == 0)) { 0135 // Check type for common plasma packages 0136 KPackage::Package package(&structure); 0137 QString serviceType; 0138 package.setPath(d->packageFile); 0139 0140 if (package.isValid() && package.metadata().isValid()) { 0141 serviceType = package.metadata().value(QStringLiteral("X-Plasma-ServiceType")); 0142 const auto serviceTypes = readKPackageTypes(package.metadata()); 0143 if (serviceType.isEmpty() && !serviceTypes.isEmpty()) { 0144 serviceType = serviceTypes.first(); 0145 } 0146 } 0147 0148 if (!serviceType.isEmpty()) { 0149 if (serviceType == QLatin1String("KPackage/Generic")) { 0150 type = QStringLiteral("KPackage/Generic"); 0151 } else { 0152 type = serviceType; 0153 // qDebug() << "fallthrough type is" << serviceType; 0154 } 0155 } 0156 } 0157 0158 { 0159 PackageStructure *structure = PackageLoader::self()->loadPackageStructure(type); 0160 0161 if (structure) { 0162 d->installer = Package(structure); 0163 } 0164 0165 if (!d->installer.hasValidStructure()) { 0166 qWarning() << "Package type" << type << "not found"; 0167 } 0168 0169 d->packageRoot = d->installer.defaultPackageRoot(); 0170 d->pluginTypes << type; 0171 } 0172 if (d->parser->isSet(Options::show())) { 0173 const QString pluginName = d->package; 0174 showPackageInfo(pluginName); 0175 return; 0176 } else if (d->parser->isSet(Options::appstream())) { 0177 const QString pluginName = d->package; 0178 showAppstreamInfo(pluginName); 0179 return; 0180 } 0181 0182 if (d->parser->isSet(Options::list())) { 0183 d->packageRoot = findPackageRoot(d->package, d->packageRoot); 0184 d->coutput(i18n("Listing service types: %1 in %2", d->pluginTypes.join(QStringLiteral(", ")), d->packageRoot)); 0185 listPackages(d->pluginTypes, d->packageRoot); 0186 exit(0); 0187 0188 } else if (d->parser->isSet(Options::generateIndex())) { 0189 // TODO KF6 Remove 0190 qWarning() << "The indexing feature is removed in KPackage 5.82"; 0191 exit(0); 0192 0193 } else if (d->parser->isSet(Options::removeIndex())) { 0194 // TODO KF6 Remove 0195 qWarning() << "The indexing feature is removed in KPackage 5.82"; 0196 exit(0); 0197 0198 } else { 0199 // install, remove or upgrade 0200 if (!d->installer.isValid()) { 0201 d->installer = KPackage::Package(new KPackage::PackageStructure()); 0202 } 0203 0204 d->packageRoot = findPackageRoot(d->package, d->packageRoot); 0205 0206 if (d->parser->isSet(Options::remove()) || d->parser->isSet(Options::upgrade())) { 0207 QString pkgPath; 0208 for (const QString &t : std::as_const(d->pluginTypes)) { 0209 KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(t); 0210 pkg.setPath(d->package); 0211 if (pkg.isValid()) { 0212 pkgPath = pkg.path(); 0213 if (pkgPath.isEmpty() && !d->packageFile.isEmpty()) { 0214 pkgPath = d->packageFile; 0215 } 0216 continue; 0217 } 0218 } 0219 if (pkgPath.isEmpty()) { 0220 pkgPath = d->package; 0221 } 0222 0223 if (d->parser->isSet(Options::upgrade())) { 0224 d->installer.setPath(d->package); 0225 } 0226 QString _p = d->packageRoot; 0227 if (!_p.endsWith(QLatin1Char('/'))) { 0228 _p.append(QLatin1Char('/')); 0229 } 0230 _p.append(d->package); 0231 d->installer.setDefaultPackageRoot(d->packageRoot); 0232 d->installer.setPath(pkgPath); 0233 0234 if (!d->parser->isSet(Options::type())) { 0235 const QStringList lst = readKPackageTypes(d->installer.metadata()); 0236 for (const QString &st : lst) { 0237 if (!d->pluginTypes.contains(st)) { 0238 d->pluginTypes << st; 0239 } 0240 } 0241 } 0242 0243 QString pluginName; 0244 if (d->installer.isValid()) { 0245 d->metadata = d->installer.metadata(); 0246 if (!d->metadata.isValid()) { 0247 pluginName = d->package; 0248 } else if (!d->metadata.isValid() && d->metadata.pluginId().isEmpty()) { 0249 // plugin name given in command line 0250 pluginName = d->package; 0251 } else { 0252 // Parameter was a plasma package, get plugin name from the package 0253 pluginName = d->metadata.pluginId(); 0254 } 0255 } 0256 QStringList installed = d->packages(d->pluginTypes); 0257 0258 if (QFile::exists(d->packageFile)) { 0259 d->installer.setPath(d->packageFile); 0260 if (d->installer.isValid()) { 0261 if (d->installer.metadata().isValid()) { 0262 pluginName = d->installer.metadata().pluginId(); 0263 } 0264 } 0265 } 0266 // Uninstalling ... 0267 if (installed.contains(pluginName)) { // Assume it's a plugin name 0268 d->installer.setPath(pluginName); 0269 KJob *uninstallJob = d->installer.uninstall(pluginName, d->packageRoot); 0270 // clang-format off 0271 connect(uninstallJob, SIGNAL(result(KJob*)), SLOT(packageUninstalled(KJob*))); 0272 // clang-format on 0273 return; 0274 } else { 0275 d->coutput(i18n("Error: Plugin %1 is not installed.", pluginName)); 0276 exit(2); 0277 } 0278 } 0279 if (d->parser->isSet(Options::install())) { 0280 if (QFileInfo::exists(d->packageFile + QLatin1String("/metadata.desktop"))) { 0281 qWarning() << "Providing a metadata.desktop file for KPackage metadata is deprecated." 0282 << "Please convert this file using the following command or contact the author:"; 0283 qWarning() << QLatin1String("desktoptojson %1; rm %1").arg(d->packageFile + QLatin1String("/metadata.desktop")); 0284 } 0285 KJob *installJob = d->installer.install(d->packageFile, d->packageRoot); 0286 // clang-format off 0287 connect(installJob, SIGNAL(result(KJob*)), SLOT(packageInstalled(KJob*))); 0288 // clang-format on 0289 return; 0290 } 0291 if (d->package.isEmpty()) { 0292 qWarning() << i18nc( 0293 "No option was given, this is the error message telling the user he needs at least one, do not translate install, remove, upgrade nor list", 0294 "One of install, remove, upgrade or list is required."); 0295 exit(6); 0296 } 0297 } 0298 } 0299 0300 void PackageToolPrivate::coutput(const QString &msg) 0301 { 0302 *cout << msg << '\n'; 0303 (*cout).flush(); 0304 } 0305 0306 void PackageToolPrivate::cerror(const QString &msg) 0307 { 0308 *cerr << msg << '\n'; 0309 (*cerr).flush(); 0310 } 0311 0312 QStringList PackageToolPrivate::packages(const QStringList &types, const QString &path) 0313 { 0314 QStringList result; 0315 0316 for (const QString &type : types) { 0317 const QList<KPluginMetaData> services = KPackage::PackageLoader::self()->listPackages(type, path); 0318 for (const KPluginMetaData &service : services) { 0319 const QString _plugin = service.pluginId(); 0320 if (!result.contains(_plugin)) { 0321 result << _plugin; 0322 } 0323 } 0324 } 0325 0326 return result; 0327 } 0328 0329 void PackageTool::showPackageInfo(const QString &pluginName) 0330 { 0331 QString type = QStringLiteral("KPackage/Generic"); 0332 if (!d->pluginTypes.contains(type) && !d->pluginTypes.isEmpty()) { 0333 type = d->pluginTypes.at(0); 0334 } 0335 KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(type); 0336 0337 pkg.setDefaultPackageRoot(d->packageRoot); 0338 0339 if (QFile::exists(d->packageFile)) { 0340 pkg.setPath(d->packageFile); 0341 } else { 0342 pkg.setPath(pluginName); 0343 } 0344 0345 KPluginMetaData i = pkg.metadata(); 0346 if (!i.isValid()) { 0347 *cerr << i18n("Error: Can't find plugin metadata: %1\n", pluginName); 0348 exit(3); 0349 return; 0350 } 0351 d->coutput(i18n("Showing info for package: %1", pluginName)); 0352 d->coutput(i18n(" Name : %1", i.name())); 0353 d->coutput(i18n(" Comment : %1", i.value(QStringLiteral("Comment")))); 0354 d->coutput(i18n(" Plugin : %1", i.pluginId())); 0355 auto const authors = i.authors(); 0356 d->coutput(i18n(" Author : %1", authors.first().name())); 0357 d->coutput(i18n(" Path : %1", pkg.path())); 0358 0359 exit(0); 0360 } 0361 0362 bool translateKPluginToAppstream(const QString &tagName, 0363 const QString &configField, 0364 const QJsonObject &configObject, 0365 QXmlStreamWriter &writer, 0366 bool canEndWithDot) 0367 { 0368 const QRegularExpression rx(QStringLiteral("%1\\[(.*)\\]").arg(configField)); 0369 const QJsonValue native = configObject.value(configField); 0370 if (native.isUndefined()) { 0371 return false; 0372 } 0373 0374 QString content = native.toString(); 0375 if (!canEndWithDot && content.endsWith(QLatin1Char('.'))) { 0376 content.chop(1); 0377 } 0378 writer.writeTextElement(tagName, content); 0379 for (auto it = configObject.begin(), itEnd = configObject.end(); it != itEnd; ++it) { 0380 const auto match = rx.match(it.key()); 0381 0382 if (match.hasMatch()) { 0383 QString content = it->toString(); 0384 if (!canEndWithDot && content.endsWith(QLatin1Char('.'))) { 0385 content.chop(1); 0386 } 0387 0388 writer.writeStartElement(tagName); 0389 writer.writeAttribute(QStringLiteral("xml:lang"), match.captured(1)); 0390 writer.writeCharacters(content); 0391 writer.writeEndElement(); 0392 } 0393 } 0394 return true; 0395 } 0396 0397 void PackageTool::showAppstreamInfo(const QString &pluginName) 0398 { 0399 KPluginMetaData i; 0400 // if the path passed is an absolute path, and a metadata file is found under it, use that metadata file to generate the appstream info. 0401 // This can happen in the case an application wanting to support kpackage based extensions includes in the same project both the packagestructure plugin and 0402 // the packages themselves. In that case at build time the packagestructure plugin wouldn't be installed yet 0403 0404 if (QFile::exists(pluginName + QStringLiteral("/metadata.json"))) { 0405 i = KPluginMetaData::fromJsonFile(pluginName + QStringLiteral("/metadata.json")); 0406 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 92) 0407 } else if (QFile::exists(pluginName + QStringLiteral("/metadata.desktop"))) { 0408 i = KPluginMetaData::fromDesktopFile(pluginName + QStringLiteral("/metadata.desktop"), {QStringLiteral(":/kservicetypes5/kpackage-generic.desktop")}); 0409 #endif 0410 } else { 0411 QString type = QStringLiteral("KPackage/Generic"); 0412 if (!d->pluginTypes.contains(type) && !d->pluginTypes.isEmpty()) { 0413 type = d->pluginTypes.at(0); 0414 } 0415 KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(type); 0416 0417 pkg.setDefaultPackageRoot(d->packageRoot); 0418 0419 if (QFile::exists(d->packageFile)) { 0420 pkg.setPath(d->packageFile); 0421 } else { 0422 pkg.setPath(pluginName); 0423 } 0424 0425 i = pkg.metadata(); 0426 } 0427 0428 if (!i.isValid()) { 0429 *cerr << i18n("Error: Can't find plugin metadata: %1\n", pluginName); 0430 std::exit(3); 0431 return; 0432 } 0433 QString parentApp = i.value(QLatin1String("X-KDE-ParentApp")); 0434 0435 #if KPACKAGE_BUILD_DEPRECATED_SINCE(5, 85) 0436 KPluginMetaData packageStructureMetaData; 0437 { 0438 const QStringList packageFormats = readKPackageTypes(i); 0439 if (!packageFormats.isEmpty()) { 0440 packageStructureMetaData = structureForKPackageType(packageFormats.first()); 0441 } 0442 } 0443 if (parentApp.isEmpty()) { 0444 parentApp = packageStructureMetaData.value(QLatin1String("X-KDE-ParentApp")); 0445 if (!parentApp.isEmpty()) { 0446 qCDebug(KPACKAGE_LOG) << "Implicitly specifying X-KDE-ParentApp by it's parent structure is deprecated and will" 0447 "be removed in KF6. Either the value should be explicitly set or the default will be used"; 0448 } 0449 } 0450 #endif 0451 0452 if (i.value(QStringLiteral("NoDisplay"), false)) { 0453 std::exit(0); 0454 } 0455 0456 QXmlStreamAttributes componentAttributes; 0457 if (!parentApp.isEmpty()) { 0458 componentAttributes << QXmlStreamAttribute(QLatin1String("type"), QLatin1String("addon")); 0459 } 0460 0461 // Compatibility: without appstream-metainfo-output argument we print the XML output to STDOUT 0462 // with the argument we'll print to the defined path. 0463 // TODO: in KF6 we should switch to argument-only. 0464 QIODevice *outputDevice = cout->device(); 0465 std::unique_ptr<QFile> outputFile; 0466 const auto outputPath = d->parser->value(Options::appstreamOutput()); 0467 if (!outputPath.isEmpty()) { 0468 auto outputUrl = QUrl::fromUserInput(outputPath); 0469 outputFile.reset(new QFile(outputUrl.toLocalFile())); 0470 if (!outputFile->open(QFile::WriteOnly | QFile::Text)) { 0471 *cerr << "Failed to open output file for writing."; 0472 exit(1); 0473 } 0474 outputDevice = outputFile.get(); 0475 } 0476 0477 if (i.description().isEmpty()) { 0478 *cerr << "Error: description missing, will result in broken appdata field as <summary/> is mandatory at " 0479 << QFileInfo(i.metaDataFileName()).absoluteFilePath(); 0480 std::exit(10); 0481 } 0482 0483 QXmlStreamWriter writer(outputDevice); 0484 writer.setAutoFormatting(true); 0485 writer.writeStartDocument(); 0486 writer.writeStartElement(QStringLiteral("component")); 0487 writer.writeAttributes(componentAttributes); 0488 0489 writer.writeTextElement(QStringLiteral("id"), i.pluginId()); 0490 if (!parentApp.isEmpty()) { 0491 writer.writeTextElement(QStringLiteral("extends"), parentApp); 0492 } 0493 0494 const QJsonObject rootObject = i.rawData()[QStringLiteral("KPlugin")].toObject(); 0495 translateKPluginToAppstream(QStringLiteral("name"), QStringLiteral("Name"), rootObject, writer, false); 0496 translateKPluginToAppstream(QStringLiteral("summary"), QStringLiteral("Description"), rootObject, writer, false); 0497 if (!i.website().isEmpty()) { 0498 writer.writeStartElement(QStringLiteral("url")); 0499 writer.writeAttribute(QStringLiteral("type"), QStringLiteral("homepage")); 0500 writer.writeCharacters(i.website()); 0501 writer.writeEndElement(); 0502 } 0503 0504 if (i.pluginId().startsWith(QLatin1String("org.kde."))) { 0505 writer.writeStartElement(QStringLiteral("url")); 0506 writer.writeAttribute(QStringLiteral("type"), QStringLiteral("donation")); 0507 writer.writeCharacters(QStringLiteral("https://www.kde.org/donate.php?app=%1").arg(i.pluginId())); 0508 writer.writeEndElement(); 0509 } 0510 0511 const auto authors = i.authors(); 0512 if (!authors.isEmpty()) { 0513 QStringList authorsText; 0514 authorsText.reserve(authors.size()); 0515 for (const auto &author : authors) { 0516 authorsText += QStringLiteral("%1 <%2>").arg(author.name(), author.emailAddress()); 0517 } 0518 writer.writeTextElement(QStringLiteral("developer_name"), authorsText.join(QStringLiteral(", "))); 0519 } 0520 0521 if (!i.iconName().isEmpty()) { 0522 writer.writeStartElement(QStringLiteral("icon")); 0523 writer.writeAttribute(QStringLiteral("type"), QStringLiteral("stock")); 0524 writer.writeCharacters(i.iconName()); 0525 writer.writeEndElement(); 0526 } 0527 writer.writeTextElement(QStringLiteral("project_license"), KAboutLicense::byKeyword(i.license()).spdx()); 0528 writer.writeTextElement(QStringLiteral("metadata_license"), QStringLiteral("CC0-1.0")); 0529 writer.writeEndElement(); 0530 writer.writeEndDocument(); 0531 0532 exit(0); 0533 } 0534 0535 QString PackageTool::findPackageRoot(const QString &pluginName, const QString &prefix) 0536 { 0537 Q_UNUSED(pluginName); 0538 Q_UNUSED(prefix); 0539 QString packageRoot; 0540 if (d->parser->isSet(Options::packageRoot()) && d->parser->isSet(Options::global()) && !d->parser->isSet(Options::generateIndex())) { 0541 qWarning() << i18nc("The user entered conflicting options packageroot and global, this is the error message telling the user he can use only one", 0542 "The packageroot and global options conflict with each other, please select only one."); 0543 ::exit(7); 0544 } else if (d->parser->isSet(Options::packageRoot())) { 0545 packageRoot = d->parser->value(Options::packageRoot()); 0546 // qDebug() << "(set via arg) d->packageRoot is: " << d->packageRoot; 0547 } else if (d->parser->isSet(Options::global())) { 0548 auto const paths = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, d->packageRoot, QStandardPaths::LocateDirectory); 0549 if (!paths.isEmpty()) { 0550 packageRoot = paths.last(); 0551 } 0552 } else { 0553 packageRoot = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + d->packageRoot; 0554 } 0555 return packageRoot; 0556 } 0557 0558 void PackageTool::listPackages(const QStringList &types, const QString &path) 0559 { 0560 QStringList list = d->packages(types, path); 0561 list.sort(); 0562 for (const QString &package : std::as_const(list)) { 0563 d->coutput(package); 0564 } 0565 exit(0); 0566 } 0567 0568 void PackageToolPrivate::renderTypeTable(const QMap<QString, QString> &plugins) 0569 { 0570 const QString nameHeader = i18n("KPackage Structure Name"); 0571 const QString pathHeader = i18n("Path"); 0572 int nameWidth = nameHeader.length(); 0573 int pathWidth = pathHeader.length(); 0574 0575 QMapIterator<QString, QString> pluginIt(plugins); 0576 while (pluginIt.hasNext()) { 0577 pluginIt.next(); 0578 if (pluginIt.key().length() > nameWidth) { 0579 nameWidth = pluginIt.key().length(); 0580 } 0581 0582 if (pluginIt.value().length() > pathWidth) { 0583 pathWidth = pluginIt.value().length(); 0584 } 0585 } 0586 0587 std::cout << nameHeader.toLocal8Bit().constData() << std::setw(nameWidth - nameHeader.length() + 2) << ' ' << pathHeader.toLocal8Bit().constData() 0588 << std::setw(pathWidth - pathHeader.length() + 2) << ' ' << std::endl; 0589 std::cout << std::setfill('-') << std::setw(nameWidth) << '-' << " " << std::setw(pathWidth) << '-' << " " << std::endl; 0590 std::cout << std::setfill(' '); 0591 0592 pluginIt.toFront(); 0593 while (pluginIt.hasNext()) { 0594 pluginIt.next(); 0595 std::cout << pluginIt.key().toLocal8Bit().constData() << std::setw(nameWidth - pluginIt.key().length() + 2) << ' ' 0596 << pluginIt.value().toLocal8Bit().constData() << std::setw(pathWidth - pluginIt.value().length() + 2) << std::endl; 0597 } 0598 } 0599 0600 void PackageToolPrivate::listTypes() 0601 { 0602 coutput(i18n("Package types that are installable with this tool:")); 0603 coutput(i18n("Built in:")); 0604 0605 QMap<QString, QString> builtIns; 0606 builtIns.insert(i18n("KPackage/Generic"), QStringLiteral(KPACKAGE_RELATIVE_DATA_INSTALL_DIR "/packages/")); 0607 builtIns.insert(i18n("KPackage/GenericQML"), QStringLiteral(KPACKAGE_RELATIVE_DATA_INSTALL_DIR "/genericqml/")); 0608 0609 renderTypeTable(builtIns); 0610 0611 const QVector<KPluginMetaData> offers = KPluginMetaData::findPlugins(QStringLiteral("kpackage/packagestructure")); 0612 0613 if (!offers.isEmpty()) { 0614 std::cout << std::endl; 0615 coutput(i18n("Provided by plugins:")); 0616 0617 QMap<QString, QString> plugins; 0618 for (const KPluginMetaData &info : offers) { 0619 const QStringList types = readKPackageTypes(info); 0620 if (types.isEmpty()) { 0621 continue; 0622 } 0623 KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(types.first()); 0624 QString path = pkg.defaultPackageRoot(); 0625 plugins.insert(types.first(), path); 0626 } 0627 0628 renderTypeTable(plugins); 0629 } 0630 } 0631 0632 void PackageTool::packageInstalled(KJob *job) 0633 { 0634 bool success = (job->error() == KJob::NoError); 0635 int exitcode = 0; 0636 if (success) { 0637 if (d->parser->isSet(Options::upgrade())) { 0638 d->coutput(i18n("Successfully upgraded %1", d->packageFile)); 0639 } else { 0640 d->coutput(i18n("Successfully installed %1", d->packageFile)); 0641 } 0642 } else { 0643 d->cerror(i18n("Error: Installation of %1 failed: %2", d->packageFile, job->errorText())); 0644 exitcode = 4; 0645 } 0646 exit(exitcode); 0647 } 0648 0649 void PackageTool::packageUninstalled(KJob *job) 0650 { 0651 bool success = (job->error() == KJob::NoError); 0652 int exitcode = 0; 0653 if (success) { 0654 if (d->parser->isSet(Options::upgrade())) { 0655 d->coutput(i18n("Upgrading package from file: %1", d->packageFile)); 0656 KJob *installJob = d->installer.install(d->packageFile, d->packageRoot); 0657 // clang-format off 0658 connect(installJob, SIGNAL(result(KJob*)), SLOT(packageInstalled(KJob*))); 0659 // clang-format on 0660 return; 0661 } 0662 d->coutput(i18n("Successfully uninstalled %1", d->packageFile)); 0663 } else { 0664 d->cerror(i18n("Error: Uninstallation of %1 failed: %2", d->packageFile, job->errorText())); 0665 exitcode = 7; 0666 } 0667 exit(exitcode); 0668 } 0669 0670 } // namespace KPackage 0671 0672 #include "moc_kpackagetool.cpp"