16#include <config-libkleo.h> 
   22#include "compliance.h" 
   23#include "cryptoconfig.h" 
   26#include <libkleo_debug.h> 
   28#include <KAboutComponent> 
   29#include <KLocalizedString> 
   31#include <QGpgME/CryptoConfig> 
   32#include <QGpgME/Protocol> 
   35#include <QCoreApplication> 
   41#include <QRegularExpression> 
   42#include <QStandardPaths> 
   46#include <gpgme++/engineinfo.h> 
   47#include <gpgme++/error.h> 
   48#include <gpgme++/key.h> 
   53#include "gnupg-registry.h" 
   61QString Kleo::gnupgHomeDirectory()
 
   67QString Kleo::gnupgPrivateKeysDirectory()
 
   73int Kleo::makeGnuPGError(
int code)
 
   75    return gpg_error(
static_cast<gpg_err_code_t
>(code));
 
   80    const GpgME::EngineInfo info = GpgME::engineInfo(engine);
 
   86    static const auto path = findGpgExe(GpgME::GpgConfEngine, QStringLiteral(
"gpgconf"));
 
   92    static const auto path = findGpgExe(GpgME::GpgSMEngine, QStringLiteral(
"gpgsm"));
 
   98    static const auto path = findGpgExe(GpgME::GpgEngine, QStringLiteral(
"gpg"));
 
  106        QStringLiteral(
"pubring.gpg"),
 
  108        QStringLiteral(
"pubring.kbx"),
 
  110        QStringLiteral(
"trustlist.txt"),
 
  112        QStringLiteral(
"trustdb.gpg"),
 
  114        QStringLiteral(
"reader*.status"),
 
  116        QStringLiteral(
"secring.gpg"),
 
  118        QStringLiteral(
"*.key"),
 
  120        QStringLiteral(
"pubring.db"),
 
  127        QStringLiteral(
"gpg.conf"),
 
  128        QStringLiteral(
"gpg.conf-?"),
 
  129        QStringLiteral(
"gpg.conf-?.?"),
 
  135    static const QDir gnupgHome{gnupgHomeDirectory()};
 
  138        gnupgPrivateKeysDirectory(),
 
  140        gnupgHome.filePath(QStringLiteral(
"public-keys.d")),
 
  144QString Kleo::gpg4winInstallPath()
 
  150    char *instDir = read_w32_registry_string(
"HKEY_LOCAL_MACHINE", 
"Software\\GPG4Win", 
"Install Directory");
 
  153        instDir = read_w32_registry_string(
"HKEY_CURRENT_USER", 
"Software\\GPG4Win", 
"Install Directory");
 
  160    qCDebug(LIBKLEO_LOG) << 
"Gpg4win not found. Falling back to Kleopatra instdir.";
 
  165QString Kleo::gnupgInstallPath()
 
  171    char *instDir = read_w32_registry_string(
"HKEY_LOCAL_MACHINE", 
"Software\\GnuPG", 
"Install Directory");
 
  174        instDir = read_w32_registry_string(
"HKEY_CURRENT_USER", 
"Software\\GnuPG", 
"Install Directory");
 
  181    qCDebug(LIBKLEO_LOG) << 
"GnuPG not found. Falling back to gpgconf list dir.";
 
  183    return gpgConfListDir(
"bindir");
 
  186QString Kleo::gpgConfListDir(
const char *which)
 
  188    if (!which || !*which) {
 
  191    const QString gpgConfPath = Kleo::gpgConfPath();
 
  196    qCDebug(LIBKLEO_LOG) << 
"gpgConfListDir: starting " << qPrintable(gpgConfPath) << 
" --list-dirs";
 
  199        qCDebug(LIBKLEO_LOG) << 
"gpgConfListDir(): failed to execute gpgconf: " << qPrintable(gpgConf.
errorString());
 
  205        if (line.startsWith(which) && line[qstrlen(which)] == 
':') {
 
  206            const int begin = qstrlen(which) + 1;
 
  207            int end = line.size();
 
  208            while (end && (line[end - 1] == 
'\n' || line[end - 1] == 
'\r')) {
 
  212            qCDebug(LIBKLEO_LOG) << 
"gpgConfListDir: found " << qPrintable(result) << 
" for '" << which << 
"'entry";
 
  216    qCDebug(LIBKLEO_LOG) << 
"gpgConfListDir(): didn't find '" << which << 
"'" 
  217                         << 
"entry in output:\n" 
  222static std::array<int, 3> getVersionFromString(
const char *actual, 
bool &ok)
 
  224    std::array<int, 3> ret{-1, -1, -1};
 
  236    for (
int i = 0; i < 3; i++) {
 
  237        match = rx.match(versionString);
 
  238        if (!
match.hasMatch()) {
 
  247        qCDebug(LIBKLEO_LOG) << 
"Can't parse version " << actual;
 
  251    for (
int i = 0; i < 3; ++i) {
 
  252        ret[i] = 
match.capturedView(i + 1).toUInt(&ok);
 
  262bool Kleo::versionIsAtLeast(
const char *minimum, 
const char *actual)
 
  264    if (!minimum || !actual) {
 
  268    const auto minimum_version = getVersionFromString(minimum, ok);
 
  272    const auto actual_version = getVersionFromString(actual, ok);
 
  277    return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(minimum_version), std::end(minimum_version));
 
  280bool Kleo::engineIsVersion(
int major, 
int minor, 
int patch, GpgME::Engine engine)
 
  283    const int required_version[] = {major, minor, patch};
 
  285    std::array<int, 3> actual_version;
 
  286    if (!cachedVersions.
contains(engine)) {
 
  287        const Error err = checkEngine(engine);
 
  288        if (err.code() == GPG_ERR_INV_ENGINE) {
 
  289            qCDebug(LIBKLEO_LOG) << 
"isVersion: invalid engine. '";
 
  293        const char *actual = GpgME::engineInfo(engine).version();
 
  295        actual_version = getVersionFromString(actual, ok);
 
  297        qCDebug(LIBKLEO_LOG) << 
"Parsed" << actual << 
"as: " << actual_version[0] << 
'.' << actual_version[1] << 
'.' << actual_version[2] << 
'.';
 
  301        cachedVersions.
insert(engine, actual_version);
 
  303        actual_version = cachedVersions.
value(engine);
 
  307    return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(required_version), std::end(required_version));
 
  310const QString &Kleo::paperKeyInstallPath()
 
  318bool Kleo::haveKeyserverConfigured()
 
  320    if (engineIsVersion(2, 4, 4) 
 
  321        || (engineIsVersion(2, 2, 42) && !engineIsVersion(2, 3, 0))) {
 
  324    if (engineIsVersion(2, 1, 19)) {
 
  328    return !Kleo::keyserver().
isEmpty();
 
  333    QString result = getCryptoConfigStringValue(
"gpg", 
"keyserver");
 
  335        result = getCryptoConfigStringValue(
"dirmngr", 
"keyserver");
 
  339        result = QStringLiteral(
"none");
 
  344bool Kleo::haveX509DirectoryServerConfigured()
 
  346    return !getCryptoConfigUrlList(
"dirmngr", 
"ldapserver").empty() 
 
  347        || !getCryptoConfigUrlList(
"dirmngr", 
"LDAP Server").empty() 
 
  348        || !getCryptoConfigUrlList(
"gpgsm", 
"keyserver").empty();
 
  351bool Kleo::gpgComplianceP(
const char *mode)
 
  353    const auto conf = QGpgME::cryptoConfig();
 
  354    const auto entry = getCryptoConfigEntry(conf, 
"gpg", 
"compliance");
 
  358bool Kleo::gnupgUsesDeVsCompliance()
 
  360    return DeVSCompliance::isActive();
 
  363bool Kleo::gnupgIsDeVsCompliant()
 
  365    return DeVSCompliance::isCompliant();
 
  369static unsigned int guessConsoleOutputCodePage()
 
  380    unsigned int cpno = GetConsoleOutputCP();
 
  388        qCDebug(LIBKLEO_LOG) << __func__ << 
"Failed to find native codepage";
 
  391    qCDebug(LIBKLEO_LOG) << __func__ << 
"returns" << cpno;
 
  395static QString fromEncoding(
unsigned int src_encoding, 
const char *data)
 
  397    if (!data || !*data) {
 
  402    int n = MultiByteToWideChar(src_encoding, 0, data, -1, NULL, 0);
 
  404        qCDebug(LIBKLEO_LOG) << __func__ << 
"determining necessary buffer size failed with error code" << GetLastError();
 
  408    wchar_t *result = (
wchar_t *)malloc((n + 1) * 
sizeof *result);
 
  410    n = MultiByteToWideChar(src_encoding, 0, data, -1, result, n);
 
  413        qCDebug(LIBKLEO_LOG) << __func__ << 
"conversion failed with error code" << GetLastError();
 
  423    static const unsigned int cpno = guessConsoleOutputCodePage();
 
  426        qCDebug(LIBKLEO_LOG) << __func__ << 
"trying to decode" << ba << 
"using codepage" << cpno;
 
  428        const auto s = fromEncoding(cpno, rawData.constData());
 
  429        if (!s.isEmpty() || ba.
isEmpty()) {
 
  432        qCDebug(LIBKLEO_LOG) << __func__ << 
"decoding output failed; falling back to QString::fromLocal8Bit()";
 
  434    qCDebug(LIBKLEO_LOG) << __func__ << 
"decoding from local encoding:" << ba;
 
  443    if (Kleo::engineIsVersion(2, 2, 28, GpgME::GpgEngine)) {
 
  446        return stringFromGpgOutput_legacy(ba);
 
  455    const auto components = backendComponents();
 
  458    for (
const auto &component : components) {
 
  459        versions.
push_back(component.name() + u
' ' + component.version());
 
  467    if (Kleo::engineIsVersion(2, 2, 24, GpgME::GpgConfEngine)) {
 
  469        qCDebug(LIBKLEO_LOG) << 
"Running gpgconf --show-versions ...";
 
  470        p.
start(Kleo::gpgConfPath(), {QStringLiteral(
"--show-versions")});
 
  473            qCDebug(LIBKLEO_LOG) << 
"Running gpgconf --show-versions timed out after 1 second.";
 
  475            qCDebug(LIBKLEO_LOG) << 
"Running gpgconf --show-versions failed:" << p.
errorString();
 
  480            qCDebug(LIBKLEO_LOG) << 
"gpgconf stdout:" << output;
 
  481            const auto lines = output.
split(
'\n');
 
  482            for (
const auto &line : lines) {
 
  483                if (line.startsWith(
"* GnuPG")) {
 
  484                    const auto componentsLine = line.split(
' ');
 
  486                                                      i18nc(
"@info", 
"GnuPG provides support for OpenPGP/LibrePGP and S/MIME."),
 
  488                                                      QStringLiteral(
"https://gnupg.org"),
 
  492                if (line.startsWith(
"* Libgcrypt")) {
 
  493                    const auto componentsLine = line.split(
' ');
 
  495                                                      i18nc(
"@info", 
"Libgcrypt is a general purpose cryptographic library."),
 
  497                                                      QStringLiteral(
"https://www.gnupg.org/software/libgcrypt/index.html"),
 
  509void startGpgConfDetached(
const QStringList &arguments)
 
  511    const QString gpgconf = Kleo::gpgConfPath();
 
  512    qCDebug(LIBKLEO_LOG) << 
"Starting" << gpgconf << arguments.
join(
QLatin1Char(
' ')) << 
" ...";
 
  514        qCDebug(LIBKLEO_LOG) << 
"gpgconf was started successfully";
 
  516        qCDebug(LIBKLEO_LOG) << 
"gpgconf failed to start";
 
  520template<
typename Function1, 
typename Function2>
 
  521auto startGpgConf(
const QStringList &arguments, Function1 onSuccess, Function2 onFailure)
 
  525    process->setArguments(arguments);
 
  528        qCDebug(LIBKLEO_LOG).nospace() << 
"gpgconf (" << process << 
") was started successfully";
 
  531        qCDebug(LIBKLEO_LOG).nospace() << 
"Error while running gpgconf (" << process << 
"): " << 
error;
 
  532        process->deleteLater();
 
  536        for (
const auto &line : process->readAllStandardError().trimmed().split(
'\n')) {
 
  537            qCDebug(LIBKLEO_LOG).nospace() << 
"gpgconf (" << process << 
") stderr: " << line;
 
  541        (void)process->readAllStandardOutput(); 
 
  545            qCDebug(LIBKLEO_LOG).nospace() << 
"gpgconf (" << process << 
") exited (exit code: " << exitCode << 
")";
 
  552            qCDebug(LIBKLEO_LOG).nospace() << 
"gpgconf (" << process << 
") crashed (exit code: " << exitCode << 
")";
 
  555        process->deleteLater();
 
  558    qCDebug(LIBKLEO_LOG).nospace() << 
"Starting gpgconf (" << process << 
") with arguments " << process->arguments().join(
QLatin1Char(
' ')) << 
" ...";
 
  564static void launchGpgAgentWithEventLoop()
 
  566    static thread_local QProcess *process = 
nullptr;
 
  567    static thread_local qint64 mSecsSinceEpochOfLastLaunch = 0;
 
  568    static thread_local int numberOfFailedLaunches = 0;
 
  571        qCDebug(LIBKLEO_LOG) << __func__ << 
": gpg-agent is already being launched";
 
  575    if (now - mSecsSinceEpochOfLastLaunch < 1000) {
 
  579    mSecsSinceEpochOfLastLaunch = now;
 
  580    if (numberOfFailedLaunches > 5) {
 
  581        qCWarning(LIBKLEO_LOG) << __func__ << 
": Launching gpg-agent failed" << numberOfFailedLaunches << 
"times in a row. Giving up.";
 
  585    process = startGpgConf(
 
  586        {QStringLiteral(
"--launch"), QStringLiteral(
"gpg-agent")},
 
  588            numberOfFailedLaunches = 0;
 
  592            numberOfFailedLaunches++;
 
  598void Kleo::launchGpgAgent(Kleo::LaunchGpgAgentOptions options)
 
  601        qCDebug(LIBKLEO_LOG) << __func__ << 
": gpg-agent is already running";
 
  606        launchGpgAgentWithEventLoop();
 
  608        startGpgConfDetached({QStringLiteral(
"--launch"), QStringLiteral(
"gpg-agent")});
 
  612void Kleo::restartGpgAgent()
 
  617        qCDebug(LIBKLEO_LOG) << __func__ << 
": gpg-agent is already being restarted";
 
  621    auto startAgent = []() {
 
  622        Kleo::launchGpgAgent(SkipCheckForRunningAgent);
 
  624    process = startGpgConf({QStringLiteral(
"--kill"), QStringLiteral(
"all")}, startAgent, startAgent);
 
  627const std::vector<std::string> &Kleo::availableAlgorithms()
 
  629    static std::vector<std::string> algos;
 
  646#if GPGMEPP_SUPPORTS_KYBER 
  647        if (engineIsVersion(2, 5, 2)) {
 
  648            algos.insert(algos.end(),
 
  659const std::vector<std::string> &Kleo::preferredAlgorithms()
 
  661    static const std::vector<std::string> algos = {
 
  670const std::vector<std::string> &Kleo::ignoredAlgorithms()
 
  672    static const std::vector<std::string> algos = {
 
  681    if (!verifyFi.isReadable()) {
 
  684        qCDebug(LIBKLEO_LOG) << 
"Verifying" << filePath;
 
  688    if (gpgvPath.isEmpty()) {
 
  689        qCDebug(LIBKLEO_LOG) << 
"Could not find gpgv";
 
  697        sigFi.
setFile(filePath + QStringLiteral(
".sig"));
 
  701        qCDebug(LIBKLEO_LOG) << 
"No signature found at" << sigFi.
absoluteFilePath();
 
  706    process.setProgram(gpgvPath);
 
  709        args << QStringLiteral(
"--keyring") << keyring;
 
  711    args << QStringLiteral(
"--") << sigFi.
absoluteFilePath() << verifyFi.absoluteFilePath();
 
  712    process.setArguments(args);
 
  713    qCDebug(LIBKLEO_LOG).nospace() << 
"Starting gpgv (" << gpgvPath << 
") with arguments " << args.
join(
QLatin1Char(
' ')) << 
" ...";
 
  716    if (!process.waitForFinished(-1)) {
 
  717        qCDebug(LIBKLEO_LOG) << 
"Failed to execute gpgv" << process.errorString();
 
  722        qCDebug(LIBKLEO_LOG) << 
"Failed to verify file";
 
  723        qCDebug(LIBKLEO_LOG) << 
"gpgv stdout:" << 
QString::fromUtf8(process.readAllStandardOutput());
 
  724        qCDebug(LIBKLEO_LOG) << 
"gpgv stderr:" << 
QString::fromUtf8(process.readAllStandardError());
 
  729std::vector<QByteArray> Kleo::readSecretKeyFile(
const QString &keyGrip)
 
  731    const auto filename = QStringLiteral(
"%1.key").arg(keyGrip);
 
  732    const auto path = 
QDir{Kleo::gnupgPrivateKeysDirectory()}.
filePath(filename);
 
  736        qCDebug(LIBKLEO_LOG) << 
"Cannot open the private key file" << 
path << 
"for reading";
 
  740    std::vector<QByteArray> lines;
 
  741    while (!file.atEnd()) {
 
  742        lines.push_back(file.readLine());
 
  745        qCDebug(LIBKLEO_LOG) << 
"The private key file" << 
path << 
"is empty";
 
QString i18nc(const char *context, const char *text, const TYPE &arg...)
 
const char * versionString()
 
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
 
QString path(const QString &relativePath)
 
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
 
KIOCORE_EXPORT QString dir(const QString &fileClass)
 
const QList< QKeySequence > & begin()
 
const QList< QKeySequence > & end()
 
KLEO_EXPORT bool agentIsRunning()
Checks if the GnuPG agent is running and accepts connections.
 
const char * constData() const const
 
bool isEmpty() const const
 
QByteArray & replace(QByteArrayView before, QByteArrayView after)
 
QList< QByteArray > split(char sep) const const
 
QString applicationDirPath()
 
qint64 currentMSecsSinceEpoch()
 
QString filePath(const QString &fileName) const const
 
QString fromNativeSeparators(const QString &pathName)
 
QString decodeName(const QByteArray &localFileName)
 
QString absoluteFilePath() const const
 
bool isReadable() const const
 
void setFile(const QDir &dir, const QString &path)
 
QString errorString() const const
 
void append(QList< T > &&value)
 
void push_back(parameter_type value)
 
bool contains(const Key &key) const const
 
iterator insert(const Key &key, const T &value)
 
T value(const Key &key, const T &defaultValue) const const
 
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
 
void errorOccurred(QProcess::ProcessError error)
 
int exitCode() const const
 
QProcess::ExitStatus exitStatus() const const
 
void finished(int exitCode, QProcess::ExitStatus exitStatus)
 
QByteArray readAllStandardError()
 
QByteArray readAllStandardOutput()
 
void readyReadStandardError()
 
void readyReadStandardOutput()
 
void setProgram(const QString &program)
 
void start(OpenMode mode)
 
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
 
bool waitForFinished(int msecs)
 
QString anchoredPattern(QStringView expression)
 
QString findExecutable(const QString &executableName, const QStringList &paths)
 
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
 
QString fromLatin1(QByteArrayView str)
 
QString fromLocal8Bit(QByteArrayView str)
 
QString fromUtf8(QByteArrayView str)
 
QString fromWCharArray(const wchar_t *string, qsizetype size)
 
bool isEmpty() const const
 
QString join(QChar separator) const const
 
QThread * currentThread()