diff --git a/src/smartcard/deviceinfowatcher.cpp b/src/smartcard/deviceinfowatcher.cpp index 0897e3115..21356ae9a 100644 --- a/src/smartcard/deviceinfowatcher.cpp +++ b/src/smartcard/deviceinfowatcher.cpp @@ -1,126 +1,131 @@ /* smartcard/deviceinfowatcher.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "deviceinfowatcher.h" #include "deviceinfowatcher_p.h" #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace GpgME; DeviceInfoWatcher::Worker::~Worker() { if (mContext) { mContext->cancelPendingOperationImmediately(); } } void DeviceInfoWatcher::Worker::start() { if (!mContext) { Error err; mContext = Context::createForEngine(AssuanEngine, &err); if (err) { qCWarning(KLEOPATRA_LOG) << "DeviceInfoWatcher::Worker::start: Creating context failed:" << err; return; } } // try to connect to the agent for at most ~12.8 seconds with increasing delay between retries static const int MaxRetryDelay = 100 * 64; static const char *command = "SCD DEVINFO --watch"; std::unique_ptr t(new StatusConsumerAssuanTransaction(this)); const Error err = mContext->startAssuanTransaction(command, std::move(t)); if (!err) { qCDebug(KLEOPATRA_LOG) << "DeviceInfoWatcher::Worker::start: Assuan transaction for" << command << "started"; QMetaObject::invokeMethod(this, "poll", Qt::QueuedConnection); return; } else if (err.code() == GPG_ERR_ASS_CONNECT_FAILED) { if (mRetryDelay <= MaxRetryDelay) { qCInfo(KLEOPATRA_LOG) << "DeviceInfoWatcher::Worker::start: Connecting to the agent failed. Retrying in" << mRetryDelay << "ms"; QThread::msleep(mRetryDelay); mRetryDelay *= 2; QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection); return; } qCWarning(KLEOPATRA_LOG) << "DeviceInfoWatcher::Worker::start: Connecting to the agent failed too often. Giving up."; } else { qCWarning(KLEOPATRA_LOG) << "DeviceInfoWatcher::Worker::start: Starting Assuan transaction for" << command << "failed:" << err; } } void DeviceInfoWatcher::Worker::poll() { const bool finished = mContext->poll(); if (finished) { qCDebug(KLEOPATRA_LOG) << "DeviceInfoWatcher::Worker::poll: context finished with" << mContext->lastError(); QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, "poll", Qt::QueuedConnection); } } void DeviceInfoWatcher::Worker::status(const char* status, const char* details) { qCDebug(KLEOPATRA_LOG) << "DeviceInfoWatcher::Worker::status:" << status << details; if (status && std::strcmp(status, "DEVINFO_STATUS") == 0) { Q_EMIT statusChanged(QByteArray(details)); } } DeviceInfoWatcher::Private::Private(DeviceInfoWatcher *qq) : q(qq) { } DeviceInfoWatcher::Private::~Private() { workerThread.quit(); workerThread.wait(); } void DeviceInfoWatcher::Private::start() { DeviceInfoWatcher::Worker *worker = new DeviceInfoWatcher::Worker; worker->moveToThread(&workerThread); connect(&workerThread, &QThread::started, worker, &DeviceInfoWatcher::Worker::start); connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); connect(worker, &DeviceInfoWatcher::Worker::statusChanged, q, &DeviceInfoWatcher::statusChanged); workerThread.start(); } DeviceInfoWatcher::DeviceInfoWatcher(QObject *parent) : QObject(parent) , d(new Private(this)) { } DeviceInfoWatcher::~DeviceInfoWatcher() { delete d; } // static bool DeviceInfoWatcher::isSupported() { +#ifdef Q_OS_WIN + // "SCD DEVINFO --watch" does not seem to work on Windows; see https://dev.gnupg.org/T5359 + return false; +#else return engineInfo(GpgEngine).engineVersion() >= "2.3.0"; +#endif } void DeviceInfoWatcher::start() { d->start(); }