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 // 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
129QStringList Kleo::gnupgFolderWhitelist()
130{
131 static const QDir gnupgHome{gnupgHomeDirectory()};
132 return {
133 gnupgHome.path(),
134 gnupgPrivateKeysDirectory(),
135 };
136}
137
138QString 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
159QString 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
180QString 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
216static std::array<int, 3> getVersionFromString(const char *actual, bool &ok)
217{
218 std::array<int, 3> ret{-1, -1, -1};
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
256bool 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
274bool 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
304const 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
312bool 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
325QString 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
338bool Kleo::haveX509DirectoryServerConfigured()
339{
340 return !getCryptoConfigUrlList("dirmngr", "ldapserver").empty() //
341 || !getCryptoConfigUrlList("dirmngr", "LDAP Server").empty() //
342 || !getCryptoConfigUrlList("gpgsm", "keyserver").empty();
343}
344
345bool 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
352bool Kleo::gnupgUsesDeVsCompliance()
353{
354 return DeVSCompliance::isActive();
355}
356
357bool Kleo::gnupgIsDeVsCompliant()
358{
359 return DeVSCompliance::isCompliant();
360}
361
362#ifdef Q_OS_WIN
363static 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
389static 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
415static 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
433QString 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
447QStringList 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
476namespace
477{
478
479template<typename Function1, typename Function2>
480auto 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 });
495 for (const auto &line : process->readAllStandardError().trimmed().split('\n')) {
496 qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") stderr: " << line;
497 }
498 });
500 (void)process->readAllStandardOutput(); /* ignore stdout */
501 });
502 QObject::connect(process, &QProcess::finished, [process, onSuccess, onFailure](int exitCode, QProcess::ExitStatus exitStatus) {
503 if (exitStatus == QProcess::NormalExit) {
504 qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") exited (exit code: " << exitCode << ")";
505 if (exitCode == 0) {
506 onSuccess();
507 } else {
508 onFailure();
509 }
510 } else {
511 qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") crashed (exit code: " << exitCode << ")";
512 onFailure();
513 }
514 process->deleteLater();
515 });
516
517 qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgconf (" << process << ") with arguments " << process->arguments().join(QLatin1Char(' ')) << " ...";
518 process->start();
519
520 return process;
521}
522}
523
524void Kleo::launchGpgAgent(Kleo::LaunchGpgAgentOptions options)
525{
526 static thread_local QPointer<QProcess> process;
527 static thread_local qint64 mSecsSinceEpochOfLastLaunch = 0;
528 static thread_local int numberOfFailedLaunches = 0;
529
530 if ((options == CheckForRunningAgent) && Kleo::Assuan::agentIsRunning()) {
531 qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already running";
532 return;
533 }
534 if (process) {
535 qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already being launched";
536 return;
537 }
538 const auto now = QDateTime::currentMSecsSinceEpoch();
539 if (now - mSecsSinceEpochOfLastLaunch < 1000) {
540 // reduce attempts to launch the agent to 1 attempt per second
541 return;
542 }
543 mSecsSinceEpochOfLastLaunch = now;
544 if (numberOfFailedLaunches > 5) {
545 qCWarning(LIBKLEO_LOG) << __func__ << ": Launching gpg-agent failed" << numberOfFailedLaunches << "times in a row. Giving up.";
546 return;
547 }
548
549 process = startGpgConf(
550 {QStringLiteral("--launch"), QStringLiteral("gpg-agent")},
551 []() {
552 numberOfFailedLaunches = 0;
553 },
554 []() {
555 numberOfFailedLaunches++;
556 });
557}
558
559void Kleo::restartGpgAgent()
560{
561 static QPointer<QProcess> process;
562
563 if (process) {
564 qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already being restarted";
565 return;
566 }
567
568 auto startAgent = []() {
569 Kleo::launchGpgAgent(SkipCheckForRunningAgent);
570 };
571 process = startGpgConf({QStringLiteral("--kill"), QStringLiteral("all")}, startAgent, startAgent);
572}
573
574const std::vector<std::string> &Kleo::availableAlgorithms()
575{
576 static const std::vector<std::string> algos = {
577 "brainpoolP256r1",
578 "brainpoolP384r1",
579 "brainpoolP512r1",
580 "curve25519",
581 "curve448",
582 "nistp256",
583 "nistp384",
584 "nistp521",
585 "rsa2048",
586 "rsa3072",
587 "rsa4096",
588 // "secp256k1", // Curve secp256k1 is explicitly ignored
589 };
590 return algos;
591}
592
593const std::vector<std::string> &Kleo::preferredAlgorithms()
594{
595 static const std::vector<std::string> algos = {
596 "curve25519",
597 "brainpoolP256r1",
598 "rsa3072",
599 "rsa2048",
600 };
601 return algos;
602}
603
604const std::vector<std::string> &Kleo::ignoredAlgorithms()
605{
606 static const std::vector<std::string> algos = {
607 "secp256k1", // Curve secp256k1 is not useful
608 };
609 return algos;
610}
611
612bool Kleo::gpgvVerify(const QString &filePath, const QString &sigPath, const QString &keyring, const QStringList &additionalSearchPaths)
613{
614 const QFileInfo verifyFi(filePath);
615 if (!verifyFi.isReadable()) {
616 return false;
617 } else {
618 qCDebug(LIBKLEO_LOG) << "Verifying" << filePath;
619 }
620
621 const auto gpgvPath = QStandardPaths::findExecutable(QStringLiteral("gpgv"), additionalSearchPaths);
622 if (gpgvPath.isEmpty()) {
623 qCDebug(LIBKLEO_LOG) << "Could not find gpgv";
624 return false;
625 }
626
627 QFileInfo sigFi;
628 if (!sigPath.isEmpty()) {
629 sigFi.setFile(sigPath);
630 } else {
631 sigFi.setFile(filePath + QStringLiteral(".sig"));
632 }
633
634 if (!sigFi.isReadable()) {
635 qCDebug(LIBKLEO_LOG) << "No signature found at" << sigFi.absoluteFilePath();
636 return false;
637 }
638
639 auto process = QProcess();
640 process.setProgram(gpgvPath);
641 QStringList args;
642 if (!keyring.isEmpty()) {
643 args << QStringLiteral("--keyring") << keyring;
644 }
645 args << QStringLiteral("--") << sigFi.absoluteFilePath() << verifyFi.absoluteFilePath();
646 process.setArguments(args);
647 qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgv (" << gpgvPath << ") with arguments " << args.join(QLatin1Char(' ')) << " ...";
648 process.start();
649
650 if (!process.waitForFinished(-1)) {
651 qCDebug(LIBKLEO_LOG) << "Failed to execute gpgv" << process.errorString();
652 }
653 bool ret = (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0);
654
655 if (!ret) {
656 qCDebug(LIBKLEO_LOG) << "Failed to verify file";
657 qCDebug(LIBKLEO_LOG) << "gpgv stdout:" << QString::fromUtf8(process.readAllStandardOutput());
658 qCDebug(LIBKLEO_LOG) << "gpgv stderr:" << QString::fromUtf8(process.readAllStandardError());
659 }
660 return ret;
661}
662
663std::vector<QByteArray> Kleo::readSecretKeyFile(const QString &keyGrip)
664{
665 const auto filename = QStringLiteral("%1.key").arg(keyGrip);
666 const auto path = QDir{Kleo::gnupgPrivateKeysDirectory()}.filePath(filename);
667
668 QFile file{path};
669 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
670 qCDebug(LIBKLEO_LOG) << "Cannot open the private key file" << path << "for reading";
671 return {};
672 }
673
674 std::vector<QByteArray> lines;
675 while (!file.atEnd()) {
676 lines.push_back(file.readLine());
677 }
678 if (lines.empty()) {
679 qCDebug(LIBKLEO_LOG) << "The private key file" << path << "is empty";
680 }
681 return lines;
682}
KCOREADDONS_EXPORT QString versionString()
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
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()
const QList< QKeySequence > & end()
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 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 Fri Jun 14 2024 11:51:59 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.