3#include <QtCore/QCoreApplication>
4#include <QtCore/QEventLoop>
5#include <QtCore/QLatin1StringView>
6#include <QtCore/QSettings>
9#include <qtkeychain/keychain.h>
17constexpr QLatin1StringView kSettingsGroup{
"KeychainFallback"};
21 return QCoreApplication::applicationName();
24QSettings settingsStore()
26 return QSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(),
27 QCoreApplication::applicationName());
30bool fallbackWrite(
const QString& key,
const QByteArray& data)
32 QSettings s = settingsStore();
33 s.beginGroup(kSettingsGroup);
34 s.setValue(key, data);
37 return s.status() == QSettings::NoError;
40QByteArray fallbackRead(
const QString& key)
42 QSettings s = settingsStore();
43 s.beginGroup(kSettingsGroup);
44 const QByteArray result = s.value(key).toByteArray();
49bool fallbackRemove(
const QString& key)
51 QSettings s = settingsStore();
52 s.beginGroup(kSettingsGroup);
56 return s.status() == QSettings::NoError;
60std::atomic<bool> g_qtkeychainUnavailable{
false};
63void probeBackendOnce()
65 static std::once_flag flag;
66 std::call_once(flag, [] {
67 if (!QKeychain::isAvailable()) {
68 qCInfo(QGCKeychainLog) <<
"qtkeychain reports no available backend; using QSettings fallback";
69 g_qtkeychainUnavailable.store(
true, std::memory_order_relaxed);
74QKeychain::Error runJob(QKeychain::Job* job)
76 if (!QCoreApplication::instance()) {
77 qCWarning(QGCKeychainLog) <<
"No QCoreApplication — skipping keychain operation";
78 return QKeychain::OtherError;
81 QObject::connect(job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
87bool indicatesMissingBackend(QKeychain::Error err)
90 return err == QKeychain::NoBackendAvailable || err == QKeychain::AccessDenied;
94bool isMissingSecretService(QKeychain::Error err,
const QString&
errorString)
96 if (err != QKeychain::OtherError) {
99 return errorString.contains(QLatin1String(
"org.freedesktop.secrets")) ||
100 errorString.contains(QLatin1String(
"ServiceUnknown"));
108 if (!g_qtkeychainUnavailable.load(std::memory_order_relaxed)) {
109 QKeychain::WritePasswordJob job(serviceName());
110 job.setInsecureFallback(
false);
112 job.setBinaryData(data);
113 const auto err = runJob(&job);
114 if (err == QKeychain::NoError) {
117 if (indicatesMissingBackend(err) || isMissingSecretService(err, job.errorString())) {
118 qCInfo(QGCKeychainLog) <<
"qtkeychain unavailable, using QSettings fallback:" << job.errorString();
119 g_qtkeychainUnavailable.store(
true, std::memory_order_relaxed);
121 qCWarning(QGCKeychainLog) <<
"Keychain write error:" << job.errorString();
125 return fallbackWrite(key, data);
131 if (!g_qtkeychainUnavailable.load(std::memory_order_relaxed)) {
132 QKeychain::ReadPasswordJob job(serviceName());
133 job.setInsecureFallback(
false);
135 const auto err = runJob(&job);
136 if (err == QKeychain::NoError) {
137 return job.binaryData();
139 if (indicatesMissingBackend(err) || isMissingSecretService(err, job.errorString())) {
140 qCInfo(QGCKeychainLog) <<
"qtkeychain unavailable, using QSettings fallback:" << job.errorString();
141 g_qtkeychainUnavailable.store(
true, std::memory_order_relaxed);
143 }
else if (err == QKeychain::EntryNotFound) {
146 qCWarning(QGCKeychainLog) <<
"Keychain read error:" << job.errorString();
150 return fallbackRead(key);
156 bool keychainOk =
false;
157 if (!g_qtkeychainUnavailable.load(std::memory_order_relaxed)) {
158 QKeychain::DeletePasswordJob job(serviceName());
159 job.setInsecureFallback(
false);
161 const auto err = runJob(&job);
162 if (err == QKeychain::NoError || err == QKeychain::EntryNotFound) {
164 }
else if (indicatesMissingBackend(err) || isMissingSecretService(err, job.errorString())) {
165 qCInfo(QGCKeychainLog) <<
"qtkeychain unavailable, using QSettings fallback:" << job.errorString();
166 g_qtkeychainUnavailable.store(
true, std::memory_order_relaxed);
168 qCWarning(QGCKeychainLog) <<
"Keychain remove error:" << job.errorString();
172 const bool fallbackOk = fallbackRemove(key);
173 return keychainOk || fallbackOk;
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QByteArray readBinary(const QString &key)
bool writeBinary(const QString &key, const QByteArray &data)
bool remove(const QString &key)