163 lines
3.5 KiB
C++
163 lines
3.5 KiB
C++
#include "apikeystore.h"
|
|
|
|
#include <QCoroSignal>
|
|
|
|
#include <KLocalizedString>
|
|
|
|
#include <QPointer>
|
|
|
|
#include <qt6keychain/keychain.h>
|
|
|
|
#include <memory>
|
|
|
|
using namespace Qt::Literals::StringLiterals;
|
|
|
|
namespace
|
|
{
|
|
const QString Service = u"dev.trivk.qtpotato"_s;
|
|
const QString Key = u"translation-api-key"_s;
|
|
|
|
struct JobDeleter {
|
|
void operator()(QKeychain::Job *job) const
|
|
{
|
|
job->deleteLater();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
std::unique_ptr<T, JobDeleter> makeJob()
|
|
{
|
|
auto job = std::unique_ptr<T, JobDeleter>(new T(Service));
|
|
job->setAutoDelete(false);
|
|
job->setKey(Key);
|
|
return job;
|
|
}
|
|
}
|
|
|
|
ApiKeyStore::ApiKeyStore(QObject *parent)
|
|
: QObject(parent)
|
|
{
|
|
refresh();
|
|
}
|
|
|
|
bool ApiKeyStore::hasKey() const
|
|
{
|
|
return m_hasKey;
|
|
}
|
|
|
|
QString ApiKeyStore::error() const
|
|
{
|
|
return m_error;
|
|
}
|
|
|
|
bool ApiKeyStore::busy() const
|
|
{
|
|
return m_busy;
|
|
}
|
|
|
|
void ApiKeyStore::setKey(const QString &key)
|
|
{
|
|
if (m_busy) {
|
|
return;
|
|
}
|
|
m_pendingKey = key.trimmed();
|
|
m_retryStore = true;
|
|
store(m_pendingKey);
|
|
}
|
|
|
|
void ApiKeyStore::retry()
|
|
{
|
|
if (m_busy) {
|
|
return;
|
|
}
|
|
if (m_retryStore) {
|
|
store(m_pendingKey);
|
|
} else {
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
QCoro::Task<ApiKeyStore::ReadResult> ApiKeyStore::readKey()
|
|
{
|
|
auto job = makeJob<QKeychain::ReadPasswordJob>();
|
|
job->start();
|
|
co_await qCoro(job.get(), &QKeychain::Job::finished);
|
|
|
|
if (job->error() == QKeychain::EntryNotFound) {
|
|
co_return ReadResult{};
|
|
}
|
|
if (job->error() != QKeychain::NoError) {
|
|
co_return ReadResult{{}, i18nc("@info", "Could not read the API key from the system wallet.\nMake sure KWallet or Secret Service is running, then retry.")};
|
|
}
|
|
co_return ReadResult{job->textData(), {}};
|
|
}
|
|
|
|
QCoro::Task<> ApiKeyStore::refresh()
|
|
{
|
|
setBusy(true);
|
|
QPointer<ApiKeyStore> self(this);
|
|
const ReadResult result = co_await readKey();
|
|
if (!self) {
|
|
co_return;
|
|
}
|
|
setHasKey(!result.key.isEmpty());
|
|
setError(result.error);
|
|
setBusy(false);
|
|
}
|
|
|
|
QCoro::Task<> ApiKeyStore::store(QString key)
|
|
{
|
|
setBusy(true);
|
|
QPointer<ApiKeyStore> self(this);
|
|
std::unique_ptr<QKeychain::Job, JobDeleter> job;
|
|
if (key.isEmpty()) {
|
|
job = makeJob<QKeychain::DeletePasswordJob>();
|
|
} else {
|
|
auto write = makeJob<QKeychain::WritePasswordJob>();
|
|
write->setTextData(key);
|
|
job.reset(write.release());
|
|
}
|
|
job->start();
|
|
co_await qCoro(job.get(), &QKeychain::Job::finished);
|
|
if (!self) {
|
|
co_return;
|
|
}
|
|
|
|
if (job->error() != QKeychain::NoError && job->error() != QKeychain::EntryNotFound) {
|
|
setError(key.isEmpty()
|
|
? i18nc("@info", "Could not remove the API key from the system wallet.\nMake sure KWallet or Secret Service is running, then retry.")
|
|
: i18nc("@info", "Could not store the API key in the system wallet.\nMake sure KWallet or Secret Service is running, then retry."));
|
|
setBusy(false);
|
|
co_return;
|
|
}
|
|
setError({});
|
|
setHasKey(!key.isEmpty());
|
|
m_retryStore = false;
|
|
m_pendingKey.clear();
|
|
setBusy(false);
|
|
}
|
|
|
|
void ApiKeyStore::setHasKey(bool hasKey)
|
|
{
|
|
if (hasKey != m_hasKey) {
|
|
m_hasKey = hasKey;
|
|
Q_EMIT hasKeyChanged();
|
|
}
|
|
}
|
|
|
|
void ApiKeyStore::setError(const QString &error)
|
|
{
|
|
if (error != m_error) {
|
|
m_error = error;
|
|
Q_EMIT errorChanged();
|
|
}
|
|
}
|
|
|
|
void ApiKeyStore::setBusy(bool busy)
|
|
{
|
|
if (busy != m_busy) {
|
|
m_busy = busy;
|
|
Q_EMIT busyChanged();
|
|
}
|
|
}
|