diff --git a/src/utils/assuan.cpp b/src/utils/assuan.cpp index 5d1676302..b51b34a81 100644 --- a/src/utils/assuan.cpp +++ b/src/utils/assuan.cpp @@ -1,150 +1,152 @@ /* utils/assuan.cpp This file is part of libkleopatra SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "assuan.h" #include #if __has_include() #include #endif #include #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Assuan; using namespace std::chrono_literals; static const auto initialRetryDelay = 125ms; static const auto maxRetryDelay = 1000ms; static const auto maxConnectionAttempts = 10; namespace { +#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) static QDebug operator<<(QDebug s, const std::string &string) { return s << QString::fromStdString(string); } +#endif static QDebug operator<<(QDebug s, const std::vector> &v) { using pair = std::pair; s << '('; for (const pair &p : v) { s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << '\n'; } return s << ')'; } } bool Kleo::Assuan::agentIsRunning() { Error err; const std::unique_ptr ctx = Context::createForEngine(AssuanEngine, &err); if (err) { qCWarning(LIBKLEO_LOG) << __func__ << ": Creating context for Assuan engine failed:" << err; return false; } static const char *command = "GETINFO version"; err = ctx->assuanTransact(command); if (!err) { // all good } else if (err.code() == GPG_ERR_ASS_CONNECT_FAILED) { qCDebug(LIBKLEO_LOG) << __func__ << ": Connecting to the agent failed."; } else { qCWarning(LIBKLEO_LOG) << __func__ << ": Starting Assuan transaction for" << command << "failed:" << err; } return !err; } std::unique_ptr Kleo::Assuan::sendCommand(std::shared_ptr &context, const std::string &command, std::unique_ptr transaction, GpgME::Error &err) { qCDebug(LIBKLEO_LOG) << __func__ << command; int connectionAttempts = 1; err = context->assuanTransact(command.c_str(), std::move(transaction)); auto retryDelay = initialRetryDelay; while (err.code() == GPG_ERR_ASS_CONNECT_FAILED && connectionAttempts < maxConnectionAttempts) { // Esp. on Windows the agent processes may take their time so we try // in increasing waits for them to start up qCDebug(LIBKLEO_LOG) << "Connecting to the agent failed. Retrying in" << retryDelay.count() << "ms"; QThread::msleep(retryDelay.count()); retryDelay = std::min(retryDelay * 2, maxRetryDelay); connectionAttempts++; err = context->assuanTransact(command.c_str(), context->takeLastAssuanTransaction()); } if (err.code()) { qCDebug(LIBKLEO_LOG) << __func__ << command << "failed:" << err; if (err.code() >= GPG_ERR_ASS_GENERAL && err.code() <= GPG_ERR_ASS_UNKNOWN_INQUIRE) { qCDebug(LIBKLEO_LOG) << "Assuan problem, killing context"; context.reset(); } return {}; } return context->takeLastAssuanTransaction(); } std::unique_ptr Kleo::Assuan::sendCommand(std::shared_ptr &context, const std::string &command, Error &err) { std::unique_ptr t = sendCommand(context, command, std::make_unique(), err); return std::unique_ptr(dynamic_cast(t.release())); } std::string Kleo::Assuan::sendDataCommand(std::shared_ptr context, const std::string &command, Error &err) { std::string data; const std::unique_ptr t = sendCommand(context, command, err); if (t.get()) { data = t->data(); qCDebug(LIBKLEO_LOG) << __func__ << command << ": got" << QString::fromStdString(data); } else { qCDebug(LIBKLEO_LOG) << __func__ << command << ": t == NULL"; } return data; } std::vector> Kleo::Assuan::sendStatusLinesCommand(std::shared_ptr context, const std::string &command, Error &err) { std::vector> statusLines; const std::unique_ptr t = sendCommand(context, command, err); if (t.get()) { statusLines = t->statusLines(); qCDebug(LIBKLEO_LOG) << __func__ << command << ": got" << statusLines; } else { qCDebug(LIBKLEO_LOG) << __func__ << command << ": t == NULL"; } return statusLines; } std::string Kleo::Assuan::sendStatusCommand(const std::shared_ptr &context, const std::string &command, Error &err) { const auto lines = sendStatusLinesCommand(context, command, err); // The status is only the last attribute // e.g. for SCD SERIALNO it would only be "SERIALNO" and for SCD GETATTR FOO // it would only be FOO const auto lastSpace = command.rfind(' '); const auto needle = lastSpace == std::string::npos ? command : command.substr(lastSpace + 1); for (const auto &pair : lines) { if (pair.first == needle) { return pair.second; } } return {}; }