Libkleo

gnupg.cpp
1 /* -*- mode: c++; c-basic-offset:4 -*-
2  utils/gnupg.cpp
3 
4  This file is part of Kleopatra, the KDE keymanager
5  SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
6 
7  SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
8  SPDX-FileContributor: Intevation GmbH
9 
10  SPDX-FileCopyrightText: 2020-2022 g10 Code GmbH
11  SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
12 
13  SPDX-License-Identifier: GPL-2.0-or-later
14 */
15 
16 #include <config-libkleo.h>
17 
18 #include "gnupg.h"
19 
20 #include "assuan.h"
21 #include "compat.h"
22 #include "compliance.h"
23 #include "cryptoconfig.h"
24 #include "hex.h"
25 
26 #include <libkleo_debug.h>
27 
28 #include <KLocalizedString>
29 
30 #include <QGpgME/CryptoConfig>
31 #include <QGpgME/Protocol>
32 
33 #include <QByteArray>
34 #include <QCoreApplication>
35 #include <QDateTime>
36 #include <QDir>
37 #include <QFile>
38 #include <QPointer>
39 #include <QProcess>
40 #include <QRegularExpression>
41 #include <QStandardPaths>
42 #include <QString>
43 
44 #include <gpgme++/engineinfo.h>
45 #include <gpgme++/error.h>
46 #include <gpgme++/key.h>
47 
48 #include <gpg-error.h>
49 
50 #ifdef Q_OS_WIN
51 #include "gnupg-registry.h"
52 #endif // Q_OS_WIN
53 
54 #include <algorithm>
55 #include <array>
56 
57 using namespace GpgME;
58 
59 QString Kleo::gnupgHomeDirectory()
60 {
61  static const QString homeDir = QString::fromUtf8(GpgME::dirInfo("homedir"));
62  return homeDir;
63 }
64 
65 QString Kleo::gnupgPrivateKeysDirectory()
66 {
67  static const QString dir = QDir{gnupgHomeDirectory()}.filePath(QStringLiteral("private-keys-v1.d"));
68  return dir;
69 }
70 
71 int Kleo::makeGnuPGError(int code)
72 {
73  return gpg_error(static_cast<gpg_err_code_t>(code));
74 }
75 
76 static QString findGpgExe(GpgME::Engine engine, const QString &exe)
77 {
78  const GpgME::EngineInfo info = GpgME::engineInfo(engine);
79  return info.fileName() ? QFile::decodeName(info.fileName()) : QStandardPaths::findExecutable(exe);
80 }
81 
82 QString Kleo::gpgConfPath()
83 {
84  static const auto path = findGpgExe(GpgME::GpgConfEngine, QStringLiteral("gpgconf"));
85  return path;
86 }
87 
88 QString Kleo::gpgSmPath()
89 {
90  static const auto path = findGpgExe(GpgME::GpgSMEngine, QStringLiteral("gpgsm"));
91  return path;
92 }
93 
94 QString Kleo::gpgPath()
95 {
96  static const auto path = findGpgExe(GpgME::GpgEngine, QStringLiteral("gpg"));
97  return path;
98 }
99 
100 QStringList Kleo::gnupgFileWhitelist()
101 {
102  return {
103  // The obvious pubring
104  QStringLiteral("pubring.gpg"),
105  // GnuPG 2.1 pubring
106  QStringLiteral("pubring.kbx"),
107  // Trust in X509 Certificates
108  QStringLiteral("trustlist.txt"),
109  // Trustdb controls ownertrust and thus WOT validity
110  QStringLiteral("trustdb.gpg"),
111  // We want to update when smartcard status changes
112  QStringLiteral("reader*.status"),
113  // No longer used in 2.1 but for 2.0 we want this
114  QStringLiteral("secring.gpg"),
115  // Secret keys (living under private-keys-v1.d/)
116  QStringLiteral("*.key"),
117  // Changes to the trustmodel / compliance mode might
118  // affect validity so we check this, too.
119  // Globbing for gpg.conf* here will trigger too often
120  // as gpgconf creates files like gpg.conf.bak or
121  // gpg.conf.tmp12312.gpgconf that should not trigger
122  // a change.
123  QStringLiteral("gpg.conf"),
124  QStringLiteral("gpg.conf-?"),
125  QStringLiteral("gpg.conf-?.?"),
126  };
127 }
128 
129 QStringList Kleo::gnupgFolderWhitelist()
130 {
131  static const QDir gnupgHome{gnupgHomeDirectory()};
132  return {
133  gnupgHome.path(),
134  gnupgPrivateKeysDirectory(),
135  };
136 }
137 
138 QString Kleo::gpg4winInstallPath()
139 {
140 #ifdef Q_OS_WIN
141  // QApplication::applicationDirPath is only used as a fallback
142  // to support the case where Kleopatra is not installed from
143  // Gpg4win but Gpg4win is also installed.
144  char *instDir = read_w32_registry_string("HKEY_LOCAL_MACHINE", "Software\\GPG4Win", "Install Directory");
145  if (!instDir) {
146  // Fallback to HKCU
147  instDir = read_w32_registry_string("HKEY_CURRENT_USER", "Software\\GPG4Win", "Install Directory");
148  }
149  if (instDir) {
150  QString ret = QString::fromLocal8Bit(instDir) + QStringLiteral("/bin");
151  free(instDir);
152  return ret;
153  }
154  qCDebug(LIBKLEO_LOG) << "Gpg4win not found. Falling back to Kleopatra instdir.";
155 #endif
157 }
158 
159 QString Kleo::gnupgInstallPath()
160 {
161 #ifdef Q_OS_WIN
162  // QApplication::applicationDirPath is only used as a fallback
163  // to support the case where Kleopatra is not installed from
164  // Gpg4win but Gpg4win is also installed.
165  char *instDir = read_w32_registry_string("HKEY_LOCAL_MACHINE", "Software\\GnuPG", "Install Directory");
166  if (!instDir) {
167  // Fallback to HKCU
168  instDir = read_w32_registry_string("HKEY_CURRENT_USER", "Software\\GnuPG", "Install Directory");
169  }
170  if (instDir) {
171  QString ret = QString::fromLocal8Bit(instDir) + QStringLiteral("/bin");
172  free(instDir);
173  return ret;
174  }
175  qCDebug(LIBKLEO_LOG) << "GnuPG not found. Falling back to gpgconf list dir.";
176 #endif
177  return gpgConfListDir("bindir");
178 }
179 
180 QString Kleo::gpgConfListDir(const char *which)
181 {
182  if (!which || !*which) {
183  return QString();
184  }
185  const QString gpgConfPath = Kleo::gpgConfPath();
186  if (gpgConfPath.isEmpty()) {
187  return QString();
188  }
189  QProcess gpgConf;
190  qCDebug(LIBKLEO_LOG) << "gpgConfListDir: starting " << qPrintable(gpgConfPath) << " --list-dirs";
191  gpgConf.start(gpgConfPath, QStringList() << QStringLiteral("--list-dirs"));
192  if (!gpgConf.waitForFinished()) {
193  qCDebug(LIBKLEO_LOG) << "gpgConfListDir(): failed to execute gpgconf: " << qPrintable(gpgConf.errorString());
194  qCDebug(LIBKLEO_LOG) << "output was:\n" << gpgConf.readAllStandardError().constData();
195  return QString();
196  }
197  const QList<QByteArray> lines = gpgConf.readAllStandardOutput().split('\n');
198  for (const QByteArray &line : lines) {
199  if (line.startsWith(which) && line[qstrlen(which)] == ':') {
200  const int begin = qstrlen(which) + 1;
201  int end = line.size();
202  while (end && (line[end - 1] == '\n' || line[end - 1] == '\r')) {
203  --end;
204  }
205  const QString result = QDir::fromNativeSeparators(QFile::decodeName(hexdecode(line.mid(begin, end - begin))));
206  qCDebug(LIBKLEO_LOG) << "gpgConfListDir: found " << qPrintable(result) << " for '" << which << "'entry";
207  return result;
208  }
209  }
210  qCDebug(LIBKLEO_LOG) << "gpgConfListDir(): didn't find '" << which << "'"
211  << "entry in output:\n"
212  << gpgConf.readAllStandardError().constData();
213  return QString();
214 }
215 
216 static std::array<int, 3> getVersionFromString(const char *actual, bool &ok)
217 {
218  std::array<int, 3> ret;
219  ok = false;
220 
221  if (!actual) {
222  return ret;
223  }
224 
226 
227  // Try to fix it up
228  QRegularExpression rx(QRegularExpression::anchoredPattern(QLatin1StringView(R"((\d+)\.(\d+)\.(\d+)(?:-svn\d+)?.*)")));
230  for (int i = 0; i < 3; i++) {
231  match = rx.match(versionString);
232  if (!match.hasMatch()) {
233  versionString += QStringLiteral(".0");
234  } else {
235  ok = true;
236  break;
237  }
238  }
239 
240  if (!ok) {
241  qCDebug(LIBKLEO_LOG) << "Can't parse version " << actual;
242  return ret;
243  }
244 
245  for (int i = 0; i < 3; ++i) {
246  ret[i] = match.capturedView(i + 1).toUInt(&ok);
247  if (!ok) {
248  return ret;
249  }
250  }
251 
252  ok = true;
253  return ret;
254 }
255 
256 bool Kleo::versionIsAtLeast(const char *minimum, const char *actual)
257 {
258  if (!minimum || !actual) {
259  return false;
260  }
261  bool ok;
262  const auto minimum_version = getVersionFromString(minimum, ok);
263  if (!ok) {
264  return false;
265  }
266  const auto actual_version = getVersionFromString(actual, ok);
267  if (!ok) {
268  return false;
269  }
270 
271  return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(minimum_version), std::end(minimum_version));
272 }
273 
274 bool Kleo::engineIsVersion(int major, int minor, int patch, GpgME::Engine engine)
275 {
276  static QMap<Engine, std::array<int, 3>> cachedVersions;
277  const int required_version[] = {major, minor, patch};
278  // Gpgconf means spawning processes which is expensive on windows.
279  std::array<int, 3> actual_version;
280  if (!cachedVersions.contains(engine)) {
281  const Error err = checkEngine(engine);
282  if (err.code() == GPG_ERR_INV_ENGINE) {
283  qCDebug(LIBKLEO_LOG) << "isVersion: invalid engine. '";
284  return false;
285  }
286 
287  const char *actual = GpgME::engineInfo(engine).version();
288  bool ok;
289  actual_version = getVersionFromString(actual, ok);
290 
291  qCDebug(LIBKLEO_LOG) << "Parsed" << actual << "as: " << actual_version[0] << '.' << actual_version[1] << '.' << actual_version[2] << '.';
292  if (!ok) {
293  return false;
294  }
295  cachedVersions.insert(engine, actual_version);
296  } else {
297  actual_version = cachedVersions.value(engine);
298  }
299 
300  // return ! ( actual_version < required_version )
301  return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(required_version), std::end(required_version));
302 }
303 
304 const QString &Kleo::paperKeyInstallPath()
305 {
306  static const QString pkPath = (QStandardPaths::findExecutable(QStringLiteral("paperkey"), QStringList() << QCoreApplication::applicationDirPath()).isEmpty()
307  ? QStandardPaths::findExecutable(QStringLiteral("paperkey"))
308  : QStandardPaths::findExecutable(QStringLiteral("paperkey"), QStringList() << QCoreApplication::applicationDirPath()));
309  return pkPath;
310 }
311 
312 bool Kleo::haveKeyserverConfigured()
313 {
314  if (engineIsVersion(2, 4, 4) //
315  || (engineIsVersion(2, 2, 42) && !engineIsVersion(2, 3, 0))) {
316  return Kleo::keyserver() != QLatin1StringView{"none"};
317  }
318  if (engineIsVersion(2, 1, 19)) {
319  // since 2.1.19 there is a builtin keyserver
320  return true;
321  }
322  return !Kleo::keyserver().isEmpty();
323 }
324 
325 QString Kleo::keyserver()
326 {
327  QString result = getCryptoConfigStringValue("gpg", "keyserver");
328  if (result.isEmpty()) {
329  result = getCryptoConfigStringValue("dirmngr", "keyserver");
330  }
331  if (result.endsWith(QLatin1StringView{"://none"})) {
332  // map hkps://none, etc., to "none"; see https://dev.gnupg.org/T6708
333  result = QStringLiteral("none");
334  }
335  return result;
336 }
337 
338 bool Kleo::haveX509DirectoryServerConfigured()
339 {
340  return !getCryptoConfigUrlList("dirmngr", "ldapserver").empty() //
341  || !getCryptoConfigUrlList("dirmngr", "LDAP Server").empty() //
342  || !getCryptoConfigUrlList("gpgsm", "keyserver").empty();
343 }
344 
345 bool Kleo::gpgComplianceP(const char *mode)
346 {
347  const auto conf = QGpgME::cryptoConfig();
348  const auto entry = getCryptoConfigEntry(conf, "gpg", "compliance");
349  return entry && entry->stringValue() == QString::fromLatin1(mode);
350 }
351 
352 bool Kleo::gnupgUsesDeVsCompliance()
353 {
354  return DeVSCompliance::isActive();
355 }
356 
357 bool Kleo::gnupgIsDeVsCompliant()
358 {
359  return DeVSCompliance::isCompliant();
360 }
361 
362 #ifdef Q_OS_WIN
363 static unsigned int guessConsoleOutputCodePage()
364 {
365  /* Qt on Windows uses GetACP while GnuPG prefers
366  * GetConsoleOutputCP.
367  *
368  * As we are not a console application GetConsoleOutputCP
369  * usually returns 0.
370  * From experience the closest thing that let's us guess
371  * what GetConsoleOutputCP returns for a console application
372  * it appears to be the OEMCP.
373  */
374  unsigned int cpno = GetConsoleOutputCP();
375  if (!cpno) {
376  cpno = GetOEMCP();
377  }
378  if (!cpno) {
379  cpno = GetACP();
380  }
381  if (!cpno) {
382  qCDebug(LIBKLEO_LOG) << __func__ << "Failed to find native codepage";
383  }
384 
385  qCDebug(LIBKLEO_LOG) << __func__ << "returns" << cpno;
386  return cpno;
387 }
388 
389 static QString fromEncoding(unsigned int src_encoding, const char *data)
390 {
391  if (!data || !*data) {
392  return {};
393  }
394 
395  // returns necessary buffer size including the terminating null character
396  int n = MultiByteToWideChar(src_encoding, 0, data, -1, NULL, 0);
397  if (n <= 0) {
398  qCDebug(LIBKLEO_LOG) << __func__ << "determining necessary buffer size failed with error code" << GetLastError();
399  return QString();
400  }
401 
402  wchar_t *result = (wchar_t *)malloc((n + 1) * sizeof *result);
403 
404  n = MultiByteToWideChar(src_encoding, 0, data, -1, result, n);
405  if (n <= 0) {
406  free(result);
407  qCDebug(LIBKLEO_LOG) << __func__ << "conversion failed with error code" << GetLastError();
408  return QString();
409  }
410  const auto ret = QString::fromWCharArray(result, n - 1);
411  free(result);
412  return ret;
413 }
414 
415 static QString stringFromGpgOutput_legacy(const QByteArray &ba)
416 {
417  static const unsigned int cpno = guessConsoleOutputCodePage();
418 
419  if (cpno) {
420  qCDebug(LIBKLEO_LOG) << __func__ << "trying to decode" << ba << "using codepage" << cpno;
421  const auto rawData = QByteArray{ba}.replace("\r\n", "\n");
422  const auto s = fromEncoding(cpno, rawData.constData());
423  if (!s.isEmpty() || ba.isEmpty()) {
424  return s;
425  }
426  qCDebug(LIBKLEO_LOG) << __func__ << "decoding output failed; falling back to QString::fromLocal8Bit()";
427  }
428  qCDebug(LIBKLEO_LOG) << __func__ << "decoding from local encoding:" << ba;
429  return QString::fromLocal8Bit(ba);
430 }
431 #endif
432 
433 QString Kleo::stringFromGpgOutput(const QByteArray &ba)
434 {
435 #ifdef Q_OS_WIN
436  // since 2.2.28, GnuPG always uses UTF-8 for console output (and input)
437  if (Kleo::engineIsVersion(2, 2, 28, GpgME::GpgEngine)) {
438  return QString::fromUtf8(ba);
439  } else {
440  return stringFromGpgOutput_legacy(ba);
441  }
442 #else
443  return QString::fromLocal8Bit(ba);
444 #endif
445 }
446 
447 QStringList Kleo::backendVersionInfo()
448 {
449  QStringList versions;
450  if (Kleo::engineIsVersion(2, 2, 24, GpgME::GpgConfEngine)) {
451  QProcess p;
452  qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions ...";
453  p.start(Kleo::gpgConfPath(), {QStringLiteral("--show-versions")});
454  // wait at most 1 second
455  if (!p.waitForFinished(1000)) {
456  qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions timed out after 1 second.";
457  } else if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) {
458  qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions failed:" << p.errorString();
459  qCDebug(LIBKLEO_LOG) << "gpgconf stderr:" << p.readAllStandardError();
460  qCDebug(LIBKLEO_LOG) << "gpgconf stdout:" << p.readAllStandardOutput();
461  } else {
462  const QByteArray output = p.readAllStandardOutput().replace("\r\n", "\n");
463  qCDebug(LIBKLEO_LOG) << "gpgconf stdout:" << output;
464  const auto lines = output.split('\n');
465  for (const auto &line : lines) {
466  if (line.startsWith("* GnuPG") || line.startsWith("* Libgcrypt")) {
467  const auto components = line.split(' ');
468  versions.push_back(QString::fromLatin1(components.at(1) + ' ' + components.value(2)));
469  }
470  }
471  }
472  }
473  return versions;
474 }
475 
476 namespace
477 {
478 
479 template<typename Function1, typename Function2>
480 auto startGpgConf(const QStringList &arguments, Function1 onSuccess, Function2 onFailure)
481 {
482  auto process = new QProcess;
483  process->setProgram(Kleo::gpgConfPath());
484  process->setArguments(arguments);
485 
486  QObject::connect(process, &QProcess::started, [process]() {
487  qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") was started successfully";
488  });
489  QObject::connect(process, &QProcess::errorOccurred, [process, onFailure](auto error) {
490  qCDebug(LIBKLEO_LOG).nospace() << "Error while running gpgconf (" << process << "): " << error;
491  process->deleteLater();
492  onFailure();
493  });
494  QObject::connect(process, &QProcess::readyReadStandardError, [process]() {
495  for (const auto &line : process->readAllStandardError().trimmed().split('\n')) {
496  qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") stderr: " << line;
497  }
498  });
499  QObject::connect(process, &QProcess::readyReadStandardOutput, [process]() {
500  (void)process->readAllStandardOutput(); /* ignore stdout */
501  });
502  QObject::connect(process,
503  qOverload<int, QProcess::ExitStatus>(&QProcess::finished),
504  [process, onSuccess, onFailure](int exitCode, QProcess::ExitStatus exitStatus) {
505  if (exitStatus == QProcess::NormalExit) {
506  qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") exited (exit code: " << exitCode << ")";
507  if (exitCode == 0) {
508  onSuccess();
509  } else {
510  onFailure();
511  }
512  } else {
513  qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") crashed (exit code: " << exitCode << ")";
514  onFailure();
515  }
516  process->deleteLater();
517  });
518 
519  qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgconf (" << process << ") with arguments " << process->arguments().join(QLatin1Char(' ')) << " ...";
520  process->start();
521 
522  return process;
523 }
524 
525 static auto startGpgConf(const QStringList &arguments)
526 {
527  return startGpgConf(
528  arguments,
529  []() {},
530  []() {});
531 }
532 
533 }
534 
535 void Kleo::launchGpgAgent()
536 {
537  static QPointer<QProcess> process;
538  static qint64 mSecsSinceEpochOfLastLaunch = 0;
539  static int numberOfFailedLaunches = 0;
540 
542  qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already running";
543  return;
544  }
545  if (process) {
546  qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already being launched";
547  return;
548  }
549  const auto now = QDateTime::currentMSecsSinceEpoch();
550  if (now - mSecsSinceEpochOfLastLaunch < 1000) {
551  // reduce attempts to launch the agent to 1 attempt per second
552  return;
553  }
554  mSecsSinceEpochOfLastLaunch = now;
555  if (numberOfFailedLaunches > 5) {
556  qCWarning(LIBKLEO_LOG) << __func__ << ": Launching gpg-agent failed" << numberOfFailedLaunches << "times in a row. Giving up.";
557  return;
558  }
559 
560  process = startGpgConf(
561  {QStringLiteral("--launch"), QStringLiteral("gpg-agent")},
562  []() {
563  numberOfFailedLaunches = 0;
564  },
565  []() {
566  numberOfFailedLaunches++;
567  });
568 }
569 
570 void Kleo::killDaemons()
571 {
572  static QPointer<QProcess> process;
573 
574  if (process) {
575  qCDebug(LIBKLEO_LOG) << __func__ << ": The daemons are already being shut down";
576  return;
577  }
578 
579  process = startGpgConf({QStringLiteral("--kill"), QStringLiteral("all")});
580 }
581 
582 const std::vector<std::string> &Kleo::availableAlgorithms()
583 {
584  static const std::vector<std::string> algos = {
585  "brainpoolP256r1",
586  "brainpoolP384r1",
587  "brainpoolP512r1",
588  "curve25519",
589  "curve448",
590  "nistp256",
591  "nistp384",
592  "nistp521",
593  "rsa2048",
594  "rsa3072",
595  "rsa4096",
596  // "secp256k1", // Curve secp256k1 is explicitly ignored
597  };
598  return algos;
599 }
600 
601 const std::vector<std::string> &Kleo::preferredAlgorithms()
602 {
603  static const std::vector<std::string> algos = {
604  "curve25519",
605  "brainpoolP256r1",
606  "rsa3072",
607  "rsa2048",
608  };
609  return algos;
610 }
611 
612 const std::vector<std::string> &Kleo::ignoredAlgorithms()
613 {
614  static const std::vector<std::string> algos = {
615  "secp256k1", // Curve secp256k1 is not useful
616  };
617  return algos;
618 }
619 
620 bool Kleo::gpgvVerify(const QString &filePath, const QString &sigPath, const QString &keyring, const QStringList &additionalSearchPaths)
621 {
622  const QFileInfo verifyFi(filePath);
623  if (!verifyFi.isReadable()) {
624  return false;
625  } else {
626  qCDebug(LIBKLEO_LOG) << "Verifying" << filePath;
627  }
628 
629  const auto gpgvPath = QStandardPaths::findExecutable(QStringLiteral("gpgv"), additionalSearchPaths);
630  if (gpgvPath.isEmpty()) {
631  qCDebug(LIBKLEO_LOG) << "Could not find gpgv";
632  return false;
633  }
634 
635  QFileInfo sigFi;
636  if (!sigPath.isEmpty()) {
637  sigFi.setFile(sigPath);
638  } else {
639  sigFi.setFile(filePath + QStringLiteral(".sig"));
640  }
641 
642  if (!sigFi.isReadable()) {
643  qCDebug(LIBKLEO_LOG) << "No signature found at" << sigFi.absoluteFilePath();
644  return false;
645  }
646 
647  auto process = QProcess();
648  process.setProgram(gpgvPath);
649  QStringList args;
650  if (!keyring.isEmpty()) {
651  args << QStringLiteral("--keyring") << keyring;
652  }
653  args << QStringLiteral("--") << sigFi.absoluteFilePath() << verifyFi.absoluteFilePath();
654  process.setArguments(args);
655  qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgv (" << gpgvPath << ") with arguments " << args.join(QLatin1Char(' ')) << " ...";
656  process.start();
657 
658  if (!process.waitForFinished(-1)) {
659  qCDebug(LIBKLEO_LOG) << "Failed to execute gpgv" << process.errorString();
660  }
661  bool ret = (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0);
662 
663  if (!ret) {
664  qCDebug(LIBKLEO_LOG) << "Failed to verify file";
665  qCDebug(LIBKLEO_LOG) << "gpgv stdout:" << QString::fromUtf8(process.readAllStandardOutput());
666  qCDebug(LIBKLEO_LOG) << "gpgv stderr:" << QString::fromUtf8(process.readAllStandardError());
667  }
668  return ret;
669 }
void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode)
QString anchoredPattern(const QString &expression)
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString errorString() const const
bool contains(const Key &key) const const
QString fromUtf8(const char *str, int size)
bool waitForFinished(int msecs)
const T value(const Key &key, const T &defaultValue) const const
void setProgram(const QString &program)
qint64 currentMSecsSinceEpoch()
QByteArray readAllStandardOutput()
QList< QByteArray > split(char sep) const const
QString applicationDirPath()
void push_back(const T &value)
const QList< QKeySequence > & begin()
void finished(int exitCode)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setFile(const QString &file)
void readyReadStandardOutput()
QString findExecutable(const QString &executableName, const QStringList &paths)
QMap::iterator insert(const Key &key, const T &value)
bool empty() const const
QString fromLocal8Bit(const char *str, int size)
QString absoluteFilePath() const const
QString fromWCharArray(const wchar_t *string, int size)
QProcess::ExitStatus exitStatus() const const
bool isEmpty() const const
QString fromNativeSeparators(const QString &pathName)
void readyReadStandardError()
QString join(const QString &separator) const const
QByteArray & replace(int pos, int len, const char *after)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
void started()
QByteArray readAllStandardError()
bool isEmpty() const const
KIOCORE_EXPORT QString dir(const QString &fileClass)
const char * constData() const const
QString path(const QString &relativePath)
QString fromLatin1(const char *str, int size)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
KLEO_EXPORT bool agentIsRunning()
Checks if the GnuPG agent is running and accepts connections.
Definition: assuan.cpp:49
KCOREADDONS_EXPORT QString versionString()
void errorOccurred(QProcess::ProcessError error)
const QList< QKeySequence > & end()
bool isReadable() const const
int exitCode() const const
QString decodeName(const QByteArray &localFileName)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 03:56:14 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.