File indexing completed on 2024-06-23 05:13:45

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     commands/selftestcommand.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "selftestcommand.h"
0013 
0014 #include "command_p.h"
0015 
0016 #include <dialogs/selftestdialog.h>
0017 
0018 #include "kleopatra_debug.h"
0019 
0020 #ifdef Q_OS_WIN
0021 #include <selftest/registrycheck.h>
0022 #endif
0023 #include <selftest/compliancecheck.h>
0024 #include <selftest/enginecheck.h>
0025 #include <selftest/gpgagentcheck.h>
0026 #include <selftest/gpgconfcheck.h>
0027 #include <selftest/libkleopatrarccheck.h>
0028 #include <selftest/uiservercheck.h>
0029 
0030 #include <Libkleo/Stl_Util>
0031 
0032 #include <KConfigGroup>
0033 #include <KSharedConfig>
0034 
0035 #include <vector>
0036 
0037 #include <QGpgME/CryptoConfig>
0038 #include <QGpgME/Protocol>
0039 
0040 using namespace Kleo;
0041 using namespace Kleo::Commands;
0042 using namespace Kleo::Dialogs;
0043 
0044 #define CURRENT_SELFTEST_VERSION 1
0045 
0046 static const char *const components[] = {
0047     nullptr, // gpgconf
0048     "gpg",
0049     "gpg-agent",
0050     "scdaemon",
0051     "gpgsm",
0052     "dirmngr",
0053 };
0054 static const unsigned int numComponents = sizeof components / sizeof *components;
0055 
0056 class SelfTestCommand::Private : Command::Private
0057 {
0058     friend class ::Kleo::Commands::SelfTestCommand;
0059     SelfTestCommand *q_func() const
0060     {
0061         return static_cast<SelfTestCommand *>(q);
0062     }
0063 
0064 public:
0065     explicit Private(SelfTestCommand *qq, KeyListController *c);
0066     ~Private() override;
0067 
0068 private:
0069     void init();
0070 
0071     void ensureDialogCreated()
0072     {
0073         if (dialog) {
0074             return;
0075         }
0076         dialog = new SelfTestDialog;
0077         applyWindowID(dialog);
0078         dialog->setAttribute(Qt::WA_DeleteOnClose);
0079 
0080         connect(dialog, &SelfTestDialog::updateRequested, q_func(), [this]() {
0081             slotUpdateRequested();
0082         });
0083         connect(dialog, &QDialog::accepted, q_func(), [this]() {
0084             slotDialogAccepted();
0085         });
0086         connect(dialog, &QDialog::rejected, q_func(), [this]() {
0087             slotDialogRejected();
0088         });
0089 
0090         dialog->setRunAtStartUp(runAtStartUp());
0091         dialog->setAutomaticMode(automatic);
0092     }
0093 
0094     void ensureDialogShown()
0095     {
0096         ensureDialogCreated();
0097         if (dialog->isVisible()) {
0098             dialog->raise();
0099         } else {
0100             dialog->show();
0101         }
0102     }
0103 
0104     bool runAtStartUp() const
0105     {
0106         const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("Self-Test"));
0107 
0108         if (config.readEntry("run-at-startup", false)) {
0109             qCDebug(KLEOPATRA_LOG) << "Selftest forced";
0110             return true;
0111         }
0112 #ifdef Q_OS_WIN
0113         /* On Windows the selftest only needs to run once as we control
0114          * the distribution of both GnuPG and Kleopatra together. While
0115          * under Linux it is more important to check for installation
0116          * incositencies. Under Windows it is also more rarely that
0117          * multiple versions of GnuPG run in the same home directory and
0118          * might interfer with their config files. */
0119         const int lastVersionRun = config.readEntry("last-selftest-version", 0);
0120         if (lastVersionRun < CURRENT_SELFTEST_VERSION) {
0121             qCDebug(KLEOPATRA_LOG) << "Last successful selftest:" << lastVersionRun << "starting it.";
0122             return true;
0123         }
0124         return false;
0125 #endif
0126         return config.readEntry("run-at-startup", true);
0127     }
0128 
0129     void setRunAtStartUp(bool on)
0130     {
0131         KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("Self-Test"));
0132         config.writeEntry("run-at-startup", on);
0133     }
0134 
0135     void runTests()
0136     {
0137         std::vector<std::shared_ptr<Kleo::SelfTest>> tests;
0138 
0139 #if defined(Q_OS_WIN)
0140         qCDebug(KLEOPATRA_LOG) << "Checking Windows Registry...";
0141         tests.push_back(makeGpgProgramRegistryCheckSelfTest());
0142         qCDebug(KLEOPATRA_LOG) << "Checking Ui Server connectivity...";
0143         tests.push_back(makeUiServerConnectivitySelfTest());
0144 #endif
0145         qCDebug(KLEOPATRA_LOG) << "Checking gpg installation...";
0146         tests.push_back(makeGpgEngineCheckSelfTest());
0147         qCDebug(KLEOPATRA_LOG) << "Checking gpgsm installation...";
0148         tests.push_back(makeGpgSmEngineCheckSelfTest());
0149         qCDebug(KLEOPATRA_LOG) << "Checking gpgconf installation...";
0150         tests.push_back(makeGpgConfEngineCheckSelfTest());
0151         for (unsigned int i = 0; i < numComponents; ++i) {
0152             qCDebug(KLEOPATRA_LOG) << "Checking configuration of:" << components[i];
0153             tests.push_back(makeGpgConfCheckConfigurationSelfTest(components[i]));
0154         }
0155 #ifndef Q_OS_WIN
0156         tests.push_back(makeGpgAgentConnectivitySelfTest());
0157 #endif
0158         tests.push_back(makeDeVSComplianceCheckSelfTest());
0159         tests.push_back(makeLibKleopatraRcSelfTest());
0160 
0161         if (!dialog && std::none_of(tests.cbegin(), tests.cend(), [](const std::shared_ptr<SelfTest> &test) {
0162                 return test->failed();
0163             })) {
0164             finished();
0165             KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("Self-Test"));
0166             config.writeEntry("last-selftest-version", CURRENT_SELFTEST_VERSION);
0167             return;
0168         }
0169 
0170         ensureDialogCreated();
0171 
0172         dialog->setTests(tests);
0173 
0174         ensureDialogShown();
0175     }
0176 
0177 private:
0178     void slotDialogAccepted()
0179     {
0180         setRunAtStartUp(dialog->runAtStartUp());
0181         finished();
0182     }
0183     void slotDialogRejected()
0184     {
0185         if (automatic) {
0186             canceled = true;
0187             Command::Private::canceled();
0188         } else {
0189             slotDialogAccepted();
0190         }
0191     }
0192     void slotUpdateRequested()
0193     {
0194         const auto conf = QGpgME::cryptoConfig();
0195         if (conf) {
0196             conf->clear();
0197         }
0198         runTests();
0199     }
0200 
0201 private:
0202     QPointer<SelfTestDialog> dialog;
0203     bool canceled;
0204     bool automatic;
0205 };
0206 
0207 SelfTestCommand::Private *SelfTestCommand::d_func()
0208 {
0209     return static_cast<Private *>(d.get());
0210 }
0211 const SelfTestCommand::Private *SelfTestCommand::d_func() const
0212 {
0213     return static_cast<const Private *>(d.get());
0214 }
0215 
0216 #define d d_func()
0217 #define q q_func()
0218 
0219 SelfTestCommand::Private::Private(SelfTestCommand *qq, KeyListController *c)
0220     : Command::Private(qq, c)
0221     , dialog()
0222     , canceled(false)
0223     , automatic(false)
0224 {
0225 }
0226 
0227 SelfTestCommand::Private::~Private()
0228 {
0229 }
0230 
0231 SelfTestCommand::SelfTestCommand(KeyListController *c)
0232     : Command(new Private(this, c))
0233 {
0234     d->init();
0235 }
0236 
0237 SelfTestCommand::SelfTestCommand(QAbstractItemView *v, KeyListController *c)
0238     : Command(v, new Private(this, c))
0239 {
0240     d->init();
0241 }
0242 
0243 void SelfTestCommand::Private::init()
0244 {
0245 }
0246 
0247 SelfTestCommand::~SelfTestCommand()
0248 {
0249 }
0250 
0251 void SelfTestCommand::setAutomaticMode(bool on)
0252 {
0253     d->automatic = on;
0254     if (d->dialog) {
0255         d->dialog->setAutomaticMode(on);
0256     }
0257 }
0258 
0259 bool SelfTestCommand::isCanceled() const
0260 {
0261     return d->canceled;
0262 }
0263 
0264 void SelfTestCommand::doStart()
0265 {
0266     if (d->automatic) {
0267         if (!d->runAtStartUp()) {
0268             d->finished();
0269             return;
0270         }
0271     } else {
0272         d->ensureDialogCreated();
0273     }
0274 
0275     d->runTests();
0276 }
0277 
0278 void SelfTestCommand::doCancel()
0279 {
0280     d->canceled = true;
0281     if (d->dialog) {
0282         d->dialog->close();
0283     }
0284     d->dialog = nullptr;
0285 }
0286 
0287 #undef d
0288 #undef q
0289 
0290 #include "moc_selftestcommand.cpp"