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

KDE's Doxygen guidelines are available online.