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

KDE's Doxygen guidelines are available online.