diff --git a/src/selftest/enginecheck.cpp b/src/selftest/enginecheck.cpp index cb21c0521..b3399f412 100644 --- a/src/selftest/enginecheck.cpp +++ b/src/selftest/enginecheck.cpp @@ -1,197 +1,216 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/enginecheck.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "enginecheck.h" #include "utils/gnupg-helper.h" #include "implementation_p.h" #include #include #include #include #include #include "kleopatra_debug.h" +#include +#include + #include #include using namespace Kleo; using namespace Kleo::_detail; using namespace GpgME; static QString engine_name(GpgME::Engine eng) { static const char *engines[] = { "gpg", "gpgsm", "gpgconf" }; return QString::fromLatin1(engines[eng]); } static QString test_name(GpgME::Engine eng) { static const char *names[] = { I18N_NOOP2("@title", "GPG (OpenPGP Backend) installation"), I18N_NOOP2("@title", "GpgSM (S/MIME Backend) installation"), I18N_NOOP2("@title", "GpgConf (Configuration) installation"), }; return i18nc("@title", names[eng]); } namespace { class EngineCheck : public SelfTestImplementation { public: explicit EngineCheck(GpgME::Engine eng) : SelfTestImplementation(test_name(eng)) { runTest(eng); } void runTest(GpgME::Engine eng) { + // First use the crypto config which is much faster because it is only + // created once and then kept in memory. Only if the crypoconfig is + // bad we check into the engine info. + const auto conf = QGpgME::cryptoConfig(); + if (conf && eng == GpgME::GpgEngine) { + m_passed = true; + return; + } else if (conf) { + const auto comp = conf->component(engine_name(eng)); + if (comp) { + m_passed = true; + return; + } + } + + // Problem with the config. Try to get more details: const Error err = GpgME::checkEngine(eng); Q_ASSERT(!err.code() || err.code() == GPG_ERR_INV_ENGINE); m_passed = !err; if (m_passed) { return; } m_explaination = xi18nc("@info", "A problem was detected with the %1 backend.", engine_name(eng)); const EngineInfo ei = engineInfo(eng); if (ei.isNull()) { m_error = i18n("not supported"); m_explaination += xi18nc("@info", "It seems that the gpgme library was compiled without " "support for this backend."); m_proposedFix += xi18nc("@info", "Replace the gpgme library with a version compiled " "with %1 support.", engine_name(eng)); } else if (ei.fileName() && (!ei.version() || !strcmp(ei.version(), "1.0.0"))) { // GPGSM only got the ei.version() working with 1.0.0 so 1.0.0 is returned as // a fallback if the version could not be checked. We assume that it's not properly // installed in that case. m_error = i18n("not properly installed"); m_explaination += xi18nc("@info", "Backend %1 is not installed properly.", QFile::decodeName(ei.fileName())); m_proposedFix += xi18nc("@info", "Please check the output of %1 --version manually.", QFile::decodeName(ei.fileName())); } else if (ei.fileName() && ei.version() && ei.requiredVersion()) { m_error = i18n("too old"); m_explaination += xi18nc("@info", "Backend %1 is installed in version %2, " "but at least version %3 is required.", QFile::decodeName(ei.fileName()), QString::fromUtf8(ei.version()), QString::fromUtf8(ei.requiredVersion())); m_proposedFix += xi18nc("@info", "Install %1 version %2 or higher.", engine_name(eng), QString::fromUtf8(ei.requiredVersion())); } else { m_error = m_explaination = i18n("unknown problem"); m_proposedFix += xi18nc("@info", "Make sure %1 is installed and " "in PATH.", engine_name(eng)); } } }; } std::shared_ptr Kleo::makeGpgEngineCheckSelfTest() { return std::shared_ptr(new EngineCheck(GpgME::GpgEngine)); } std::shared_ptr Kleo::makeGpgSmEngineCheckSelfTest() { return std::shared_ptr(new EngineCheck(GpgME::GpgSMEngine)); } std::shared_ptr Kleo::makeGpgConfEngineCheckSelfTest() { return std::shared_ptr(new EngineCheck(GpgME::GpgConfEngine)); } // // SelfTestImplementation (parts) // bool SelfTestImplementation::ensureEngineVersion(GpgME::Engine engine, int major, int minor, int patch) { const Error err = GpgME::checkEngine(engine); Q_ASSERT(!err || err.code() == GPG_ERR_INV_ENGINE); m_skipped = err || !engineIsVersion(major, minor, patch, engine); if (!m_skipped) { return true; } const char *version = GpgME::engineInfo(engine).version(); if (!err && version) { // properly installed, but too old m_explaination = xi18nc("@info", "%1 v%2.%3.%4 is required for this test, but only %5 is installed.", engine_name(engine), major, minor, patch, QString::fromUtf8(version)); m_proposedFix += xi18nc("@info", "Install %1 version %2 or higher.", engine_name(engine), QStringLiteral("%1.%2.%3").arg(major).arg(minor).arg(patch)); } else { // not properly installed m_explaination = xi18nc("@info", "%1 is required for this test, but does not seem available." "See tests further up for more information.", engine_name(engine)); m_proposedFix = xi18nc("@info %1: test name", "See \"%1\" above.", test_name(engine)); } return false; } diff --git a/src/selftest/gpgconfcheck.cpp b/src/selftest/gpgconfcheck.cpp index 7e20d24e9..be43b92be 100644 --- a/src/selftest/gpgconfcheck.cpp +++ b/src/selftest/gpgconfcheck.cpp @@ -1,163 +1,114 @@ /* -*- mode: c++; c-basic-offset:4 -*- selftest/gpgconfcheck.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "gpgconfcheck.h" #include "implementation_p.h" #include #include #include "kleopatra_debug.h" #include #include #include +#include +#include + using namespace Kleo; using namespace Kleo::_detail; namespace { class GpgConfCheck : public SelfTestImplementation { QString m_component; public: explicit GpgConfCheck(const char *component) : SelfTestImplementation(i18nc("@title", "%1 Configuration Check", component && * component ? QLatin1String(component) : QLatin1String("gpgconf"))), m_component(QLatin1String(component)) { runTest(); } - QStringList arguments() const - { - if (m_component.isEmpty()) { - return QStringList() << QStringLiteral("--check-config"); - } else { - return QStringList() << QStringLiteral("--check-options") << m_component; - } - } - - bool canRun() - { - if (!ensureEngineVersion(GpgME::GpgConfEngine, 2, 0, 10)) { - return false; - } - - if (!m_component.isEmpty()) { - return true; - } - - QProcess gpgconf; - gpgconf.setReadChannel(QProcess::StandardOutput); - gpgconf.start(gpgConfPath(), QStringList() << QStringLiteral("--list-dirs"), QIODevice::ReadOnly); - gpgconf.waitForFinished(); - if (gpgconf.exitStatus() != QProcess::NormalExit || gpgconf.exitCode() != 0) { - qCDebug(KLEOPATRA_LOG) << "GpgConfCheck: \"gpgconf --list-dirs\" gives error, disabling"; - return false; - } - const QList lines = gpgconf.readAll().split('\n'); - for (const QByteArray &line : lines) - if (line.startsWith("sysconfdir:")) //krazy:exclude=strings - try { - return QDir(QFile::decodeName(hexdecode(line.mid(strlen("sysconfdir:"))))).exists(QStringLiteral("gpgconf.conf")); - } catch (...) { - return false; - } - qCDebug(KLEOPATRA_LOG) << "GpgConfCheck: \"gpgconf --list-dirs\" has no sysconfdir entry"; - return false; - } - void runTest() { + const auto conf = QGpgME::cryptoConfig(); + QString message; + m_passed = true; - if (!canRun()) { - if (!m_skipped) { - m_passed = true; + if (!conf) { + message = QStringLiteral ("Could not be started."); + m_passed = false; + } else if (m_component.isEmpty() && conf->componentList().empty()) { + message = QStringLiteral ("Could not list components."); + m_passed = false; + } else if (!m_component.isEmpty()) { + const auto comp = conf->component (m_component); + if (!comp) { + message = QStringLiteral ("Binary could not be found."); + m_passed = false; + } else if (comp->groupList().empty()) { + // If we don't have any group it means that list-options + // for this component failed. + message = QStringLiteral ("The configuration file is invalid."); + m_passed = false; } - return; } - QProcess process; - process.setProcessChannelMode(QProcess::MergedChannels); - - process.start(gpgConfPath(), arguments(), QIODevice::ReadOnly); - - process.waitForFinished(); - - const QString output = QString::fromUtf8(process.readAll()); - const QString message = process.exitStatus() == QProcess::CrashExit ? i18n("The process terminated prematurely") : process.errorString(); - - if (process.exitStatus() != QProcess::NormalExit || - process.error() != QProcess::UnknownError) { - m_passed = false; + if (!m_passed) { m_error = i18nc("self-test did not pass", "Failed"); m_explaination = i18n("There was an error executing the GnuPG configuration self-check for %2:\n" " %1\n" "You might want to execute \"gpgconf %3\" on the command line.\n", - message, m_component.isEmpty() ? QStringLiteral("GnuPG") : m_component, arguments().join(QLatin1Char(' '))); - if (!output.trimmed().isEmpty()) { - m_explaination += QLatin1Char('\n') + i18n("Diagnostics:") + QLatin1Char('\n') + output; - } + message, m_component.isEmpty() ? QStringLiteral("GnuPG") : m_component, + QStringLiteral("--check-options ") + (m_component.isEmpty() ? QStringLiteral("") : m_component)); - m_proposedFix.clear(); - } else if (process.exitCode()) { - m_passed = false; - m_error = i18nc("self-check did not pass", "Failed"); - m_explaination = !output.trimmed().isEmpty() - ? i18nc("Self-test did not pass", - "The GnuPG configuration self-check failed.\n" - "\n" - "Error code: %1\n" - "Diagnostics:", process.exitCode()) + QLatin1Char('\n') + output - : i18nc("self-check did not pass", - "The GnuPG configuration self-check failed with error code %1.\n" - "No output was received.", process.exitCode()); - m_proposedFix.clear(); - } else { - m_passed = true; + // To avoid modifying the l10n + m_explaination.replace(QLatin1Char('\n'), QStringLiteral("
")); } } }; } std::shared_ptr Kleo::makeGpgConfCheckConfigurationSelfTest(const char *component) { return std::shared_ptr(new GpgConfCheck(component)); }