Libkleo

assuan.cpp
1/*
2 utils/assuan.cpp
3
4 This file is part of libkleopatra
5 SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH
6 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include <config-libkleo.h>
12
13#include "assuan.h"
14
15#include "gnupg.h"
16
17#include <libkleo_debug.h>
18
19#if __has_include(<QGpgME/Debug>)
20#include <QGpgME/Debug>
21#endif
22
23#include <QThread>
24
25#include <gpgme++/context.h>
26#include <gpgme++/defaultassuantransaction.h>
27#include <gpgme++/error.h>
28
29using namespace GpgME;
30using namespace Kleo;
31using namespace Kleo::Assuan;
32using namespace std::chrono_literals;
33
34static const auto initialRetryDelay = 125ms;
35static const auto maxRetryDelay = 1000ms;
36static const auto maxConnectionAttempts = 10;
37
38namespace
39{
40static QDebug operator<<(QDebug s, const std::vector<std::pair<std::string, std::string>> &v)
41{
42 using pair = std::pair<std::string, std::string>;
43 s << '(';
44 for (const pair &p : v) {
45 s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << '\n';
46 }
47 return s << ')';
48}
49}
50
52{
53 Error err;
54 const std::unique_ptr<Context> ctx = Context::createForEngine(AssuanEngine, &err);
55 if (err) {
56 qCWarning(LIBKLEO_LOG) << __func__ << ": Creating context for Assuan engine failed:" << err;
57 return false;
58 }
59 static const char *command = "GETINFO version";
60 err = ctx->assuanTransact(command);
61 if (!err) {
62 // all good
63 } else if (err.code() == GPG_ERR_ASS_CONNECT_FAILED) {
64 qCDebug(LIBKLEO_LOG) << __func__ << ": Connecting to the agent failed.";
65 } else {
66 qCWarning(LIBKLEO_LOG) << __func__ << ": Starting Assuan transaction for" << command << "failed:" << err;
67 }
68 return !err;
69}
70
71std::unique_ptr<GpgME::AssuanTransaction> Kleo::Assuan::sendCommand(std::shared_ptr<GpgME::Context> &context,
72 const std::string &command,
73 std::unique_ptr<GpgME::AssuanTransaction> transaction,
74 GpgME::Error &err)
75{
76 qCDebug(LIBKLEO_LOG) << __func__ << command;
77 int connectionAttempts = 1;
78 err = context->assuanTransact(command.c_str(), std::move(transaction));
79
80 auto retryDelay = initialRetryDelay;
81 while (err.code() == GPG_ERR_ASS_CONNECT_FAILED && connectionAttempts < maxConnectionAttempts) {
82 if (connectionAttempts == 1) {
83 Kleo::launchGpgAgent(Kleo::SkipCheckForRunningAgent);
84 }
85 // Esp. on Windows the agent processes may take their time so we try
86 // in increasing waits for them to start up
87 qCDebug(LIBKLEO_LOG) << "Connecting to the agent failed. Retrying in" << retryDelay.count() << "ms";
88 QThread::msleep(retryDelay.count());
89 retryDelay = std::min(retryDelay * 2, maxRetryDelay);
90 connectionAttempts++;
91 err = context->assuanTransact(command.c_str(), context->takeLastAssuanTransaction());
92 }
93 if (err.code()) {
94 qCDebug(LIBKLEO_LOG) << __func__ << command << "failed:" << err;
95 if (err.code() >= GPG_ERR_ASS_GENERAL && err.code() <= GPG_ERR_ASS_UNKNOWN_INQUIRE) {
96 qCDebug(LIBKLEO_LOG) << "Assuan problem, killing context";
97 context.reset();
98 }
99 return {};
100 }
101 return context->takeLastAssuanTransaction();
102}
103
104std::unique_ptr<DefaultAssuanTransaction> Kleo::Assuan::sendCommand(std::shared_ptr<Context> &context, const std::string &command, Error &err)
105{
106 std::unique_ptr<AssuanTransaction> t = sendCommand(context, command, std::make_unique<DefaultAssuanTransaction>(), err);
107 return std::unique_ptr<DefaultAssuanTransaction>(dynamic_cast<DefaultAssuanTransaction *>(t.release()));
108}
109
110std::string Kleo::Assuan::sendDataCommand(std::shared_ptr<Context> context, const std::string &command, Error &err)
111{
112 std::string data;
113 const std::unique_ptr<DefaultAssuanTransaction> t = sendCommand(context, command, err);
114 if (t.get()) {
115 data = t->data();
116 qCDebug(LIBKLEO_LOG) << __func__ << command << ": got" << QString::fromStdString(data);
117 } else {
118 qCDebug(LIBKLEO_LOG) << __func__ << command << ": t == NULL";
119 }
120 return data;
121}
122
123std::vector<std::pair<std::string, std::string>> Kleo::Assuan::sendStatusLinesCommand(std::shared_ptr<Context> context, const std::string &command, Error &err)
124{
125 std::vector<std::pair<std::string, std::string>> statusLines;
126 const std::unique_ptr<DefaultAssuanTransaction> t = sendCommand(context, command, err);
127 if (t.get()) {
128 statusLines = t->statusLines();
129 qCDebug(LIBKLEO_LOG) << __func__ << command << ": got" << statusLines;
130 } else {
131 qCDebug(LIBKLEO_LOG) << __func__ << command << ": t == NULL";
132 }
133 return statusLines;
134}
135
136std::string Kleo::Assuan::sendStatusCommand(const std::shared_ptr<Context> &context, const std::string &command, Error &err)
137{
138 const auto lines = sendStatusLinesCommand(context, command, err);
139 // The status is only the last attribute
140 // e.g. for SCD SERIALNO it would only be "SERIALNO" and for SCD GETATTR FOO
141 // it would only be FOO
142 const auto lastSpace = command.rfind(' ');
143 const auto needle = lastSpace == std::string::npos ? command : command.substr(lastSpace + 1);
144 for (const auto &pair : lines) {
145 if (pair.first == needle) {
146 return pair.second;
147 }
148 }
149 return {};
150}
The Assuan namespace collects functions for communicating with the GnuPG agent via the Assuan protoco...
Definition assuan.h:32
KLEO_EXPORT std::unique_ptr< GpgME::AssuanTransaction > sendCommand(std::shared_ptr< GpgME::Context > &assuanContext, const std::string &command, std::unique_ptr< GpgME::AssuanTransaction > transaction, GpgME::Error &err)
Sends the Assuan command using the transaction and the assuanContext to the GnuPG agent and waits for...
Definition assuan.cpp:71
KLEO_EXPORT std::vector< std::pair< std::string, std::string > > sendStatusLinesCommand(std::shared_ptr< GpgME::Context > assuanContext, const std::string &command, GpgME::Error &err)
Sends the Assuan command using a default Assuan transaction and the assuanContext to the GnuPG agent ...
KLEO_EXPORT std::string sendStatusCommand(const std::shared_ptr< GpgME::Context > &assuanContext, const std::string &command, GpgME::Error &err)
Sends the Assuan command using a default Assuan transaction and the assuanContext to the GnuPG agent ...
KLEO_EXPORT bool agentIsRunning()
Checks if the GnuPG agent is running and accepts connections.
Definition assuan.cpp:51
KLEO_EXPORT std::string sendDataCommand(std::shared_ptr< GpgME::Context > assuanContext, const std::string &command, GpgME::Error &err)
Sends the Assuan command using a default Assuan transaction and the assuanContext to the GnuPG agent ...
QDebug operator<<(QDebug dbg, const PerceptualColor::LchaDouble &value)
QString fromStdString(const std::string &str)
void msleep(unsigned long msecs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:12 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.