File indexing completed on 2025-01-05 04:55:51
0001 /* 0002 utils/assuan.cpp 0003 0004 This file is part of libkleopatra 0005 SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH 0006 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include <config-libkleo.h> 0012 0013 #include "assuan.h" 0014 0015 #include <libkleo_debug.h> 0016 0017 #if __has_include(<QGpgME/Debug>) 0018 #include <QGpgME/Debug> 0019 #endif 0020 0021 #include <QThread> 0022 0023 #include <gpgme++/context.h> 0024 #include <gpgme++/defaultassuantransaction.h> 0025 #include <gpgme++/error.h> 0026 0027 using namespace GpgME; 0028 using namespace Kleo; 0029 using namespace Kleo::Assuan; 0030 using namespace std::chrono_literals; 0031 0032 static const auto initialRetryDelay = 125ms; 0033 static const auto maxRetryDelay = 1000ms; 0034 static const auto maxConnectionAttempts = 10; 0035 0036 namespace 0037 { 0038 static QDebug operator<<(QDebug s, const std::vector<std::pair<std::string, std::string>> &v) 0039 { 0040 using pair = std::pair<std::string, std::string>; 0041 s << '('; 0042 for (const pair &p : v) { 0043 s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << '\n'; 0044 } 0045 return s << ')'; 0046 } 0047 } 0048 0049 bool Kleo::Assuan::agentIsRunning() 0050 { 0051 Error err; 0052 const std::unique_ptr<Context> ctx = Context::createForEngine(AssuanEngine, &err); 0053 if (err) { 0054 qCWarning(LIBKLEO_LOG) << __func__ << ": Creating context for Assuan engine failed:" << err; 0055 return false; 0056 } 0057 static const char *command = "GETINFO version"; 0058 err = ctx->assuanTransact(command); 0059 if (!err) { 0060 // all good 0061 } else if (err.code() == GPG_ERR_ASS_CONNECT_FAILED) { 0062 qCDebug(LIBKLEO_LOG) << __func__ << ": Connecting to the agent failed."; 0063 } else { 0064 qCWarning(LIBKLEO_LOG) << __func__ << ": Starting Assuan transaction for" << command << "failed:" << err; 0065 } 0066 return !err; 0067 } 0068 0069 std::unique_ptr<GpgME::AssuanTransaction> Kleo::Assuan::sendCommand(std::shared_ptr<GpgME::Context> &context, 0070 const std::string &command, 0071 std::unique_ptr<GpgME::AssuanTransaction> transaction, 0072 GpgME::Error &err) 0073 { 0074 qCDebug(LIBKLEO_LOG) << __func__ << command; 0075 int connectionAttempts = 1; 0076 err = context->assuanTransact(command.c_str(), std::move(transaction)); 0077 0078 auto retryDelay = initialRetryDelay; 0079 while (err.code() == GPG_ERR_ASS_CONNECT_FAILED && connectionAttempts < maxConnectionAttempts) { 0080 // Esp. on Windows the agent processes may take their time so we try 0081 // in increasing waits for them to start up 0082 qCDebug(LIBKLEO_LOG) << "Connecting to the agent failed. Retrying in" << retryDelay.count() << "ms"; 0083 QThread::msleep(retryDelay.count()); 0084 retryDelay = std::min(retryDelay * 2, maxRetryDelay); 0085 connectionAttempts++; 0086 err = context->assuanTransact(command.c_str(), context->takeLastAssuanTransaction()); 0087 } 0088 if (err.code()) { 0089 qCDebug(LIBKLEO_LOG) << __func__ << command << "failed:" << err; 0090 if (err.code() >= GPG_ERR_ASS_GENERAL && err.code() <= GPG_ERR_ASS_UNKNOWN_INQUIRE) { 0091 qCDebug(LIBKLEO_LOG) << "Assuan problem, killing context"; 0092 context.reset(); 0093 } 0094 return {}; 0095 } 0096 return context->takeLastAssuanTransaction(); 0097 } 0098 0099 std::unique_ptr<DefaultAssuanTransaction> Kleo::Assuan::sendCommand(std::shared_ptr<Context> &context, const std::string &command, Error &err) 0100 { 0101 std::unique_ptr<AssuanTransaction> t = sendCommand(context, command, std::make_unique<DefaultAssuanTransaction>(), err); 0102 return std::unique_ptr<DefaultAssuanTransaction>(dynamic_cast<DefaultAssuanTransaction *>(t.release())); 0103 } 0104 0105 std::string Kleo::Assuan::sendDataCommand(std::shared_ptr<Context> context, const std::string &command, Error &err) 0106 { 0107 std::string data; 0108 const std::unique_ptr<DefaultAssuanTransaction> t = sendCommand(context, command, err); 0109 if (t.get()) { 0110 data = t->data(); 0111 qCDebug(LIBKLEO_LOG) << __func__ << command << ": got" << QString::fromStdString(data); 0112 } else { 0113 qCDebug(LIBKLEO_LOG) << __func__ << command << ": t == NULL"; 0114 } 0115 return data; 0116 } 0117 0118 std::vector<std::pair<std::string, std::string>> Kleo::Assuan::sendStatusLinesCommand(std::shared_ptr<Context> context, const std::string &command, Error &err) 0119 { 0120 std::vector<std::pair<std::string, std::string>> statusLines; 0121 const std::unique_ptr<DefaultAssuanTransaction> t = sendCommand(context, command, err); 0122 if (t.get()) { 0123 statusLines = t->statusLines(); 0124 qCDebug(LIBKLEO_LOG) << __func__ << command << ": got" << statusLines; 0125 } else { 0126 qCDebug(LIBKLEO_LOG) << __func__ << command << ": t == NULL"; 0127 } 0128 return statusLines; 0129 } 0130 0131 std::string Kleo::Assuan::sendStatusCommand(const std::shared_ptr<Context> &context, const std::string &command, Error &err) 0132 { 0133 const auto lines = sendStatusLinesCommand(context, command, err); 0134 // The status is only the last attribute 0135 // e.g. for SCD SERIALNO it would only be "SERIALNO" and for SCD GETATTR FOO 0136 // it would only be FOO 0137 const auto lastSpace = command.rfind(' '); 0138 const auto needle = lastSpace == std::string::npos ? command : command.substr(lastSpace + 1); 0139 for (const auto &pair : lines) { 0140 if (pair.first == needle) { 0141 return pair.second; 0142 } 0143 } 0144 return {}; 0145 }