File indexing completed on 2024-12-15 03:44:58

0001 /*
0002     SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include <kuserfeedback_version.h>
0008 
0009 #include <jobs/handshakejob.h>
0010 #include <jobs/productexportjob.h>
0011 #include <jobs/productimportjob.h>
0012 #include <jobs/securityscanjob.h>
0013 #include <rest/restapi.h>
0014 #include <rest/restclient.h>
0015 #include <rest/serverinfo.h>
0016 #include <core/product.h>
0017 
0018 #include <QCommandLineParser>
0019 #include <QCoreApplication>
0020 #include <QDir>
0021 #include <QNetworkReply>
0022 
0023 #include <iostream>
0024 
0025 using namespace KUserFeedback::Console;
0026 
0027 static int jobCount = 0;
0028 
0029 int main(int argc, char **argv)
0030 {
0031     QCoreApplication app(argc, argv);
0032     QCoreApplication::setApplicationName(QStringLiteral("userfeedbackctl"));
0033     QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
0034     QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
0035     QCoreApplication::setApplicationVersion(QStringLiteral(KUSERFEEDBACK_VERSION_STRING));
0036 
0037     QCommandLineParser parser;
0038     parser.setApplicationDescription(QStringLiteral("UserFeedback management tool"));
0039     parser.addHelpOption();
0040     parser.addVersionOption();
0041     QCommandLineOption serverOpt({ QStringLiteral("server"), QStringLiteral("s") }, QStringLiteral("Server Name"), QStringLiteral("name"));
0042     parser.addOption(serverOpt);
0043     QCommandLineOption outputOpt({ QStringLiteral("output"), QStringLiteral("o") }, QStringLiteral("Output path"), QStringLiteral("path"));
0044     parser.addOption(outputOpt);
0045     QCommandLineOption forceOpt({ QStringLiteral("force"), QStringLiteral("f") }, QStringLiteral("Force destructive operations"));
0046     parser.addOption(forceOpt);
0047     QCommandLineOption urlOpt({ QStringLiteral("url"), QStringLiteral("u") }, QStringLiteral("Server URL"), QStringLiteral("url"));
0048     parser.addOption(urlOpt);
0049     QCommandLineOption userOpt({ QStringLiteral("user") }, QStringLiteral("User name"), QStringLiteral("name"));
0050     parser.addOption(userOpt);
0051     QCommandLineOption passOpt({ QStringLiteral("password") }, QStringLiteral("Password"), QStringLiteral("pass"));
0052     parser.addOption(passOpt);
0053     parser.addPositionalArgument(QStringLiteral("command"), QStringLiteral("Command: add-server, delete-product, delete-server, export-all, export-product, import-product, list-products, list-servers, scan-server"));
0054 
0055     parser.process(app);
0056 
0057     if (parser.positionalArguments().isEmpty())
0058         parser.showHelp(1);
0059     const auto cmd = parser.positionalArguments().at(0);
0060     if (cmd == QLatin1String("list-servers")) {
0061         const auto servers = ServerInfo::allServerInfoNames();
0062         for (const auto &n : servers)
0063             std::cout << qPrintable(n) << std::endl;
0064         return 0;
0065     }
0066     if (!parser.isSet(serverOpt))
0067         parser.showHelp(1);
0068 
0069     if (cmd == QLatin1String("add-server")) {
0070         if (!parser.isSet(urlOpt))
0071             parser.showHelp(1);
0072         ServerInfo s;
0073         s.setName(parser.value(serverOpt));
0074         s.setUrl(QUrl::fromUserInput(parser.value(urlOpt)));
0075         s.setUserName(parser.value(userOpt));
0076         s.setPassword(parser.value(passOpt));
0077         s.save();
0078         return 0;
0079     } else if (cmd == QLatin1String("delete-server")) {
0080         ServerInfo::remove(parser.value(serverOpt));
0081         return 0;
0082     }
0083 
0084     const auto server = ServerInfo::load(parser.value(serverOpt));
0085     if (!server.isValid()) {
0086         std::cerr << "Invalid server information." << std::endl;
0087         return 1;
0088     }
0089 
0090     QFileInfo fi(parser.value(outputOpt));
0091     if (parser.isSet(outputOpt) && (!fi.isDir() || !fi.isWritable())) {
0092         std::cerr << "Output path does exist or is not accessible." << std::endl;
0093         return 1;
0094     }
0095 
0096     RESTClient restClient;
0097     QObject::connect(&restClient, &RESTClient::errorMessage, [](const auto &msg) {
0098         std::cerr << qPrintable(msg) << std::endl;
0099         QCoreApplication::exit(1);
0100     });
0101     restClient.setServerInfo(server);
0102 
0103     if (cmd == QLatin1String("delete-product")) {
0104         jobCount = parser.positionalArguments().size() - 1;
0105         if (jobCount <= 0 || !parser.isSet(forceOpt))
0106             parser.showHelp(1);
0107         QObject::connect(&restClient, &RESTClient::clientConnected, [&parser, &restClient]() {
0108             for (int i = 0; i < jobCount; ++i) {
0109                 Product p;
0110                 p.setName(parser.positionalArguments().at(i+1));
0111                 auto reply = RESTApi::deleteProduct(&restClient, p);
0112                 QObject::connect(reply, &QNetworkReply::finished, [reply]() {
0113                     if (reply->error() != QNetworkReply::NoError)
0114                         return;
0115                     --jobCount;
0116                     if (jobCount == 0)
0117                         qApp->quit();
0118                 });
0119             }
0120         });
0121     } else if (cmd == QLatin1String("export-all")) {
0122         if (parser.positionalArguments().size() != 1)
0123             parser.showHelp(1);
0124         QObject::connect(&restClient, &RESTClient::clientConnected, [&]() {
0125             auto reply = RESTApi::listProducts(&restClient);
0126             QObject::connect(reply, &QNetworkReply::finished, [reply, &parser, &outputOpt, &restClient]() {
0127                 if (reply->error() != QNetworkReply::NoError)
0128                     return;
0129                 const auto products = Product::fromJson(reply->readAll());
0130                 for (const auto &p : products) {
0131                     ++jobCount;
0132                     auto job = new ProductExportJob(p, parser.value(outputOpt), &restClient);
0133                     QObject::connect(job, &Job::destroyed, []() {
0134                         --jobCount;
0135                         if (jobCount == 0)
0136                             qApp->quit();
0137                     });
0138                     QObject::connect(job, &Job::error, [](const auto &msg) {
0139                         std::cerr << qPrintable(msg) << std::endl;
0140                     });
0141                 }
0142             });
0143         });
0144     } else if (cmd == QLatin1String("export-product")) {
0145         if (parser.positionalArguments().size() != 2)
0146             parser.showHelp(1);
0147         QObject::connect(&restClient, &RESTClient::clientConnected, [&parser, &outputOpt, &restClient]() {
0148             auto job = new ProductExportJob(parser.positionalArguments().at(1), parser.value(outputOpt), &restClient);
0149             QObject::connect(job, &Job::destroyed, qApp, &QCoreApplication::quit);
0150             QObject::connect(job, &Job::error, [](const auto &msg) {
0151                 std::cerr << qPrintable(msg) << std::endl;
0152             });
0153         });
0154     } else if (cmd == QLatin1String("import-product")) {
0155         if (parser.positionalArguments().size() != 2)
0156             parser.showHelp(1);
0157         QObject::connect(&restClient, &RESTClient::clientConnected, [&parser, &restClient]() {
0158             auto job = new ProductImportJob(parser.positionalArguments().at(1), &restClient);
0159             QObject::connect(job, &Job::destroyed, qApp, &QCoreApplication::quit);
0160             QObject::connect(job, &Job::error, [](const auto &msg) {
0161                 std::cerr << qPrintable(msg) << std::endl;
0162             });
0163         });
0164     } else if (cmd == QLatin1String("list-products")) {
0165         if (parser.positionalArguments().size() != 1)
0166             parser.showHelp(1);
0167         QObject::connect(&restClient, &RESTClient::clientConnected, [&restClient]() {
0168             auto reply = RESTApi::listProducts(&restClient);
0169             QObject::connect(reply, &QNetworkReply::finished, [reply]() {
0170                 if (reply->error() != QNetworkReply::NoError)
0171                     return;
0172                 const auto products = Product::fromJson(reply->readAll());
0173                 for (const auto &p : products)
0174                     std::cout << qPrintable(p.name()) << std::endl;
0175                 qApp->quit();
0176             });
0177         });
0178     } else if (cmd == QLatin1String("scan-server")) {
0179         QObject::connect(&restClient, &RESTClient::clientConnected, [&restClient]() {
0180             auto job = new SecurityScanJob(&restClient);
0181             QObject::connect(job, &Job::destroyed, qApp, &QCoreApplication::quit);
0182             QObject::connect(job, &Job::error, [](const auto &msg) {
0183                 std::cerr << qPrintable(msg) << std::endl;
0184             });
0185             QObject::connect(job, &Job::info, [](const auto &msg) {
0186                 std::cout << qPrintable(msg) << std::endl;
0187             });
0188         });
0189     } else {
0190         parser.showHelp(1);
0191     }
0192 
0193     auto job = new HandshakeJob(&restClient);
0194     QObject::connect(job, &Job::error, [](const QString &msg) {
0195         std::cerr << qPrintable(msg) << std::endl;
0196         QCoreApplication::exit(1);
0197     });
0198 
0199     return app.exec();
0200 }