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

KDE's Doxygen guidelines are available online.