diff --git a/src/smartcard/readerstatus.cpp b/src/smartcard/readerstatus.cpp
index dce80e3c4..ef6973d56 100644
--- a/src/smartcard/readerstatus.cpp
+++ b/src/smartcard/readerstatus.cpp
@@ -1,1049 +1,1055 @@
 /* -*- mode: c++; c-basic-offset:4 -*-
     smartcard/readerstatus.cpp
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
     SPDX-FileCopyrightText: 2020 g10 Code GmbH
     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include <config-kleopatra.h>
 
 #include "readerstatus.h"
 
 #include "deviceinfowatcher.h"
 
 #include <Libkleo/Assuan>
 #include <Libkleo/GnuPG>
 #include <Libkleo/FileSystemWatcher>
+#include <Libkleo/KeyCache>
 
 #include <QGpgME/Debug>
 
 #include <gpgme++/context.h>
 #include <gpgme++/defaultassuantransaction.h>
 #include <gpgme++/engineinfo.h>
 
 #include <gpg-error.h>
 
 #include "openpgpcard.h"
 #include "netkeycard.h"
 #include "pivcard.h"
 #include "p15card.h"
 
 #include <QMutex>
 #include <QWaitCondition>
 #include <QThread>
 #include <QPointer>
 #include <QRegularExpression>
 
 #include "utils/kdtoolsglobal.h"
 
 #include "kleopatra_debug.h"
 
 using namespace Kleo;
 using namespace Kleo::SmartCard;
 using namespace GpgME;
 
 static ReaderStatus *self = nullptr;
 
 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
 
 static const char *flags[] = {
     "NOCARD",
     "PRESENT",
     "ACTIVE",
     "USABLE",
 };
 static_assert(sizeof flags / sizeof * flags == Card::_NumScdStates, "");
 
 static const char *prettyFlags[] = {
     "NoCard",
     "CardPresent",
     "CardActive",
     "CardUsable",
     "CardError",
 };
 static_assert(sizeof prettyFlags / sizeof * prettyFlags == Card::NumStates, "");
 
 Q_DECLARE_METATYPE(GpgME::Error)
 
 namespace
 {
 static bool gpgHasMultiCardMultiAppSupport()
 {
     return !(engineInfo(GpgME::GpgEngine).engineVersion() < "2.3.0");
 }
 
 static QDebug operator<<(QDebug s, const std::string &string)
 {
     return s << QString::fromStdString(string);
 }
 
 static QDebug operator<<(QDebug s, const std::vector< std::pair<std::string, std::string> > &v)
 {
     using pair = std::pair<std::string, std::string>;
     s << '(';
     for (const pair &p : v) {
         s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << '\n';
     }
     return s << ')';
 }
 
 struct CardApp {
     std::string serialNumber;
     std::string appName;
 };
 
 static void logUnexpectedStatusLine(const std::pair<std::string, std::string> &line,
                                     const std::string &prefix = std::string(),
                                     const std::string &command = std::string())
 {
     qCWarning(KLEOPATRA_LOG) << (!prefix.empty() ? QString::fromStdString(prefix + ": ") : QString())
             << "Unexpected status line"
             << (!command.empty() ? QString::fromStdString(" on " + command + ":") : QLatin1String(":"))
             << QString::fromStdString(line.first)
             << QString::fromStdString(line.second);
 }
 
 static int parse_app_version(const std::string &s)
 {
     return std::atoi(s.c_str());
 }
 
 static Card::PinState parse_pin_state(const QString &s)
 {
     bool ok;
     int i = s.toInt(&ok);
     if (!ok) {
         qCDebug(KLEOPATRA_LOG) << "Failed to parse pin state" << s;
         return Card::UnknownPinState;
     }
     switch (i) {
     case -4: return Card::NullPin;
     case -3: return Card::PinBlocked;
     case -2: return Card::NoPin;
     case -1: return Card::UnknownPinState;
     default:
         if (i < 0) {
             return Card::UnknownPinState;
         } else {
             return Card::PinOk;
         }
     }
 }
 
 static const std::string scd_getattr_status(std::shared_ptr<Context> &gpgAgent, const char *what, Error &err)
 {
     std::string cmd = "SCD GETATTR ";
     cmd += what;
     return Assuan::sendStatusCommand(gpgAgent, cmd.c_str(), err);
 }
 
 static const std::string getAttribute(std::shared_ptr<Context> &gpgAgent, const char *attribute, const char *versionHint)
 {
     Error err;
     const auto result = scd_getattr_status(gpgAgent, attribute, err);
     if (err) {
         if (err.code() == GPG_ERR_INV_NAME) {
             qCDebug(KLEOPATRA_LOG) << "Querying for attribute" << attribute << "not yet supported; needs GnuPG" << versionHint;
         } else {
             qCWarning(KLEOPATRA_LOG) << "Running SCD GETATTR " << attribute << " failed:" << err;
         }
         return std::string();
     }
     return result;
 }
 
 static std::vector<CardApp> getCardsAndApps(std::shared_ptr<Context> &gpgAgent, Error &err)
 {
     std::vector<CardApp> result;
     if (gpgHasMultiCardMultiAppSupport()) {
         const std::string command = "SCD GETINFO all_active_apps";
         const auto statusLines = Assuan::sendStatusLinesCommand(gpgAgent, command.c_str(), err);
         if (err) {
             return result;
         }
         for (const auto &statusLine: statusLines) {
             if (statusLine.first == "SERIALNO") {
                 const auto serialNumberAndApps = QByteArray::fromStdString(statusLine.second).split(' ');
                 if (serialNumberAndApps.size() >= 2) {
                     const auto serialNumber = serialNumberAndApps[0];
                     auto apps = serialNumberAndApps.mid(1);
                     // sort the apps to get a stable order independently of the currently selected application
                     std::sort(apps.begin(), apps.end());
                     for (const auto &app: apps) {
                         qCDebug(KLEOPATRA_LOG) << "getCardsAndApps(): Found card" << serialNumber << "with app" << app;
                         result.push_back({ serialNumber.toStdString(), app.toStdString() });
                     }
                 } else {
                     logUnexpectedStatusLine(statusLine, "getCardsAndApps()", command);
                 }
             } else {
                 logUnexpectedStatusLine(statusLine, "getCardsAndApps()", command);
             }
         }
     } else {
         // use SCD SERIALNO to get the currently active card
         const auto serialNumber = Assuan::sendStatusCommand(gpgAgent, "SCD SERIALNO", err);
         if (err) {
             return result;
         }
         // use SCD GETATTR APPTYPE to find out which app is active
         auto appName = scd_getattr_status(gpgAgent, "APPTYPE", err);
         std::transform(appName.begin(), appName.end(), appName.begin(),
                        [](unsigned char c){ return std::tolower(c); });
         if (err) {
             return result;
         }
         result.push_back({ serialNumber, appName });
     }
     return result;
 }
 
 static std::string switchCard(std::shared_ptr<Context> &gpgAgent, const std::string &serialNumber, Error &err)
 {
     const std::string command = "SCD SWITCHCARD " + serialNumber;
     const auto statusLines = Assuan::sendStatusLinesCommand(gpgAgent, command.c_str(), err);
     if (err) {
         return std::string();
     }
     if (statusLines.size() == 1 && statusLines[0].first == "SERIALNO" && statusLines[0].second == serialNumber) {
         return serialNumber;
     }
     qCWarning(KLEOPATRA_LOG) << "switchCard():" << command << "returned" << statusLines
             << "(expected:" << "SERIALNO " + serialNumber << ")";
     return std::string();
 }
 
 static std::string switchApp(std::shared_ptr<Context> &gpgAgent, const std::string &serialNumber,
                              const std::string &appName, Error &err)
 {
     const std::string command = "SCD SWITCHAPP " + appName;
     const auto statusLines = Assuan::sendStatusLinesCommand(gpgAgent, command.c_str(), err);
     if (err) {
         return std::string();
     }
     if (statusLines.size() == 1 && statusLines[0].first == "SERIALNO" &&
         statusLines[0].second.find(serialNumber + ' ' + appName) == 0) {
         return appName;
     }
     qCWarning(KLEOPATRA_LOG) << "switchApp():" << command << "returned" << statusLines
             << "(expected:" << "SERIALNO " + serialNumber + ' ' + appName + "..." << ")";
     return std::string();
 }
 
 static const char * get_openpgp_card_manufacturer_from_serial_number(const std::string &serialno)
 {
     qCDebug(KLEOPATRA_LOG) << "get_openpgp_card_manufacturer_from_serial_number(" << serialno.c_str() << ")";
 
     const bool isProperOpenPGPCardSerialNumber =
         serialno.size() == 32 && serialno.substr(0, 12) == "D27600012401";
     if (isProperOpenPGPCardSerialNumber) {
         const char *sn = serialno.c_str();
         const int manufacturerId = xtoi_2(sn + 16)*256 + xtoi_2(sn + 18);
         switch (manufacturerId) {
             case 0x0001: return "PPC Card Systems";
             case 0x0002: return "Prism";
             case 0x0003: return "OpenFortress";
             case 0x0004: return "Wewid";
             case 0x0005: return "ZeitControl";
             case 0x0006: return "Yubico";
             case 0x0007: return "OpenKMS";
             case 0x0008: return "LogoEmail";
 
             case 0x002A: return "Magrathea";
 
             case 0x1337: return "Warsaw Hackerspace";
 
             case 0xF517: return "FSIJ";
 
             /* 0x0000 and 0xFFFF are defined as test cards per spec,
                0xFF00 to 0xFFFE are assigned for use with randomly created
                serial numbers.  */
             case 0x0000:
             case 0xffff: return "test card";
             default: return (manufacturerId & 0xff00) == 0xff00 ? "unmanaged S/N range" : "unknown";
         }
     } else {
         return "unknown";
     }
 }
 
 static bool isOpenPGPCardSerialNumber(const std::string &serialNumber)
 {
     return serialNumber.size() == 32 && serialNumber.substr(0, 12) == "D27600012401";
 }
 
 static const std::string getDisplaySerialNumber(std::shared_ptr<Context> &gpgAgent, Error &err)
 {
     const auto displaySerialNumber = scd_getattr_status(gpgAgent, "$DISPSERIALNO", err);
     if (err && err.code() != GPG_ERR_INV_NAME) {
         qCWarning(KLEOPATRA_LOG) << "Running SCD GETATTR $DISPSERIALNO failed:" << err;
     }
     return displaySerialNumber;
 }
 
 static void setDisplaySerialNumber(Card *card, std::shared_ptr<Context> &gpgAgent)
 {
     static const QRegularExpression leadingZeros(QStringLiteral("^0*"));
 
     Error err;
     const QString displaySerialNumber = QString::fromStdString(getDisplaySerialNumber(gpgAgent, err));
     if (err) {
         card->setDisplaySerialNumber(QString::fromStdString(card->serialNumber()));
         return;
     }
     if (isOpenPGPCardSerialNumber(card->serialNumber()) && displaySerialNumber.size() == 12) {
         // add a space between manufacturer id and card id for OpenPGP cards
         card->setDisplaySerialNumber(displaySerialNumber.left(4) + QLatin1Char(' ') + displaySerialNumber.right(8));
     } else {
         card->setDisplaySerialNumber(displaySerialNumber);
     }
     return;
 }
 
 static void learnCardKeyStubs(const Card *card, std::shared_ptr<Context> &gpg_agent)
 {
     for (const KeyPairInfo &keyInfo : card->keyInfos()) {
         if (!keyInfo.grip.empty()) {
             Error err;
             const auto command = std::string("READKEY --card --no-data -- ") + keyInfo.keyRef;
             (void)Assuan::sendStatusLinesCommand(gpg_agent, command.c_str(), err);
             if (err) {
                 qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err;
             }
         }
     }
 }
 
 static void handle_openpgp_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
 {
     Error err;
     auto pgpCard = new OpenPGPCard(*ci);
 
     const auto info = Assuan::sendStatusLinesCommand(gpg_agent, "SCD LEARN --force", err);
     if (err.code()) {
         ci->setStatus(Card::CardError);
         return;
     }
     pgpCard->setCardInfo(info);
 
     if (pgpCard->manufacturer().empty()) {
         // fallback in case MANUFACTURER is not yet included in the card info
         pgpCard->setManufacturer(get_openpgp_card_manufacturer_from_serial_number(ci->serialNumber()));
     }
 
     setDisplaySerialNumber(pgpCard, gpg_agent);
 
     learnCardKeyStubs(pgpCard, gpg_agent);
 
     ci.reset(pgpCard);
 }
 
 static void readKeyPairInfoFromPIVCard(const std::string &keyRef, PIVCard *pivCard, const std::shared_ptr<Context> &gpg_agent)
 {
     Error err;
     const std::string command = std::string("SCD READKEY --info-only -- ") + keyRef;
     const auto keyPairInfoLines = Assuan::sendStatusLinesCommand(gpg_agent, command.c_str(), err);
     if (err) {
         qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err;
         return;
     }
     for (const auto &pair: keyPairInfoLines) {
         if (pair.first == "KEYPAIRINFO") {
             const KeyPairInfo info = KeyPairInfo::fromStatusLine(pair.second);
             if (info.grip.empty()) {
                 qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO status line"
                         << QString::fromStdString(pair.second);
                 continue;
             }
             pivCard->setKeyAlgorithm(keyRef, info.algorithm);
         } else {
             logUnexpectedStatusLine(pair, "readKeyPairInfoFromPIVCard()", command);
         }
     }
 }
 
 static void readCertificateFromPIVCard(const std::string &keyRef, PIVCard *pivCard, const std::shared_ptr<Context> &gpg_agent)
 {
     Error err;
     const std::string command = std::string("SCD READCERT ") + keyRef;
     const std::string certificateData = Assuan::sendDataCommand(gpg_agent, command.c_str(), err);
     if (err && err.code() != GPG_ERR_NOT_FOUND) {
         qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err;
         return;
     }
     if (certificateData.empty()) {
         qCDebug(KLEOPATRA_LOG) << "readCertificateFromPIVCard(" << QString::fromStdString(keyRef) << "): No certificate stored on card";
         return;
     }
     qCDebug(KLEOPATRA_LOG) << "readCertificateFromPIVCard(" << QString::fromStdString(keyRef) << "): Found certificate stored on card";
     pivCard->setCertificateData(keyRef, certificateData);
 }
 
 static void handle_piv_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
 {
     Error err;
     auto pivCard = new PIVCard(*ci);
 
     const auto info = Assuan::sendStatusLinesCommand(gpg_agent, "SCD LEARN --force", err);
     if (err) {
         ci->setStatus(Card::CardError);
         return;
     }
     pivCard->setCardInfo(info);
 
     setDisplaySerialNumber(pivCard, gpg_agent);
 
     for (const KeyPairInfo &keyInfo : pivCard->keyInfos()) {
         if (!keyInfo.grip.empty()) {
             readKeyPairInfoFromPIVCard(keyInfo.keyRef, pivCard, gpg_agent);
             readCertificateFromPIVCard(keyInfo.keyRef, pivCard, gpg_agent);
         }
     }
 
     learnCardKeyStubs(pivCard, gpg_agent);
 
     ci.reset(pivCard);
 }
 
 static void handle_p15_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
 {
     Error err;
     auto p15Card = new P15Card(*ci);
 
     auto info = Assuan::sendStatusLinesCommand(gpg_agent, "SCD LEARN --force", err);
     if (err) {
         ci->setStatus(Card::CardError);
         return;
     }
     const auto fprs = Assuan::sendStatusLinesCommand(gpg_agent, "SCD GETATTR KEY-FPR", err);
     if (!err) {
         info.insert(info.end(), fprs.begin(), fprs.end());
     }
 
     p15Card->setCardInfo(info);
 
     learnCardKeyStubs(p15Card, gpg_agent);
 
     setDisplaySerialNumber(p15Card, gpg_agent);
 
     ci.reset(p15Card);
 }
 
 static void handle_netkey_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
 {
     Error err;
     auto nkCard = new NetKeyCard(*ci);
     ci.reset(nkCard);
 
     ci->setAppVersion(parse_app_version(scd_getattr_status(gpg_agent, "NKS-VERSION", err)));
 
     if (err.code()) {
         qCWarning(KLEOPATRA_LOG) << "Running SCD GETATTR NKS-VERSION failed:" << err;
         ci->setErrorMsg(QStringLiteral ("NKS-VERSION failed: ") + QString::fromUtf8(err.asString()));
         return;
     }
 
     if (ci->appVersion() != 3) {
         qCDebug(KLEOPATRA_LOG) << "not a NetKey v3 card, giving up. Version:" << ci->appVersion();
         ci->setErrorMsg(QStringLiteral("NetKey v%1 cards are not supported.").arg(ci->appVersion()));
         return;
     }
 
     setDisplaySerialNumber(nkCard, gpg_agent);
 
     // the following only works for NKS v3...
     const auto chvStatus = QString::fromStdString(
             scd_getattr_status(gpg_agent, "CHV-STATUS", err)).split(QLatin1Char(' '));
     if (err.code()) {
         qCDebug(KLEOPATRA_LOG) << "Running SCD GETATTR CHV-STATUS failed:" << err;
         ci->setErrorMsg(QStringLiteral ("CHV-Status failed: ") + QString::fromUtf8(err.asString()));
         return;
     }
 
     std::vector<Card::PinState> states;
     states.reserve(chvStatus.count());
     // CHV Status for NKS v3 is
     // Pin1 (Normal pin) Pin2 (Normal PUK)
     // SigG1 SigG PUK.
     int num = 0;
     for (const auto &state: chvStatus) {
         const auto parsed = parse_pin_state (state);
         states.push_back(parsed);
         if (parsed == Card::NullPin) {
             if (num == 0) {
                 ci->setHasNullPin(true);
             }
         }
         ++num;
     }
     nkCard->setPinStates(states);
 
     const auto info = Assuan::sendStatusLinesCommand(gpg_agent, "SCD LEARN --force", err);
     if (err) {
         ci->setStatus(Card::CardError);
         return;
     }
     nkCard->setCardInfo(info);
 
     learnCardKeyStubs(nkCard, gpg_agent);
 }
 
 static std::shared_ptr<Card> get_card_status(const std::string &serialNumber, const std::string &appName, std::shared_ptr<Context> &gpg_agent)
 {
     qCDebug(KLEOPATRA_LOG) << "get_card_status(" << serialNumber << ',' << appName << ',' << gpg_agent.get() << ')';
     auto ci = std::shared_ptr<Card>(new Card());
 
     if (gpgHasMultiCardMultiAppSupport()) {
         // select card
         Error err;
         const auto result = switchCard(gpg_agent, serialNumber, err);
         if (err) {
             if (err.code() == GPG_ERR_CARD_NOT_PRESENT || err.code() == GPG_ERR_CARD_REMOVED) {
                 ci->setStatus(Card::NoCard);
             } else {
                 ci->setStatus(Card::CardError);
             }
             return ci;
         }
         if (result.empty()) {
             qCWarning(KLEOPATRA_LOG) << "get_card_status: switching card failed";
             ci->setStatus(Card::CardError);
             return ci;
         }
         ci->setStatus(Card::CardPresent);
     } else {
         ci->setStatus(Card::CardPresent);
     }
 
     if (gpgHasMultiCardMultiAppSupport()) {
         // select app
         Error err;
         const auto result = switchApp(gpg_agent, serialNumber, appName, err);
         if (err) {
             if (err.code() == GPG_ERR_CARD_NOT_PRESENT || err.code() == GPG_ERR_CARD_REMOVED) {
                 ci->setStatus(Card::NoCard);
             } else {
                 ci->setStatus(Card::CardError);
             }
             return ci;
         }
         if (result.empty()) {
             qCWarning(KLEOPATRA_LOG) << "get_card_status: switching app failed";
             ci->setStatus(Card::CardError);
             return ci;
         }
     }
 
     ci->setSerialNumber(serialNumber);
 
     ci->setSigningKeyRef(getAttribute(gpg_agent, "$SIGNKEYID", "2.2.18"));
     ci->setEncryptionKeyRef(getAttribute(gpg_agent, "$ENCRKEYID", "2.2.18"));
 
     // Handle different card types
     if (appName == NetKeyCard::AppName) {
         qCDebug(KLEOPATRA_LOG) << "get_card_status: found Netkey card" << ci->serialNumber().c_str() << "end";
         handle_netkey_card(ci, gpg_agent);
         return ci;
     } else if (appName == OpenPGPCard::AppName) {
         qCDebug(KLEOPATRA_LOG) << "get_card_status: found OpenPGP card" << ci->serialNumber().c_str() << "end";
         ci->setAuthenticationKeyRef(OpenPGPCard::pgpAuthKeyRef());
         handle_openpgp_card(ci, gpg_agent);
         return ci;
     } else if (appName == PIVCard::AppName) {
         qCDebug(KLEOPATRA_LOG) << "get_card_status: found PIV card" << ci->serialNumber().c_str() << "end";
         handle_piv_card(ci, gpg_agent);
         return ci;
     } else if (appName == P15Card::AppName) {
         qCDebug(KLEOPATRA_LOG) << "get_card_status: found P15 card" << ci->serialNumber().c_str() << "end";
         handle_p15_card(ci, gpg_agent);
         return ci;
     } else {
         qCDebug(KLEOPATRA_LOG) << "get_card_status: unhandled application:" << appName;
         return ci;
     }
 
     return ci;
 }
 
 static bool isCardNotPresentError(const GpgME::Error &err)
 {
     // see fixup_scd_errors() in gpg-card.c
     return err && ((err.code() == GPG_ERR_CARD_NOT_PRESENT) ||
                    ((err.code() == GPG_ERR_ENODEV || err.code() == GPG_ERR_CARD_REMOVED) &&
                     (err.sourceID() == GPG_ERR_SOURCE_SCD)));
 }
 
 static std::vector<std::shared_ptr<Card> > update_cardinfo(std::shared_ptr<Context> &gpgAgent)
 {
     qCDebug(KLEOPATRA_LOG) << "update_cardinfo()";
 
     // ensure that a card is present and that all cards are properly set up
     {
         Error err;
         const char *command = (gpgHasMultiCardMultiAppSupport()) ? "SCD SERIALNO --all" : "SCD SERIALNO";
         const std::string serialno = Assuan::sendStatusCommand(gpgAgent, command, err);
         if (err) {
             if (isCardNotPresentError(err)) {
                 qCDebug(KLEOPATRA_LOG) << "update_cardinfo: No card present";
                 return std::vector<std::shared_ptr<Card> >();
             } else {
                 qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err;
                 auto ci = std::shared_ptr<Card>(new Card());
                 ci->setStatus(Card::CardError);
                 return std::vector<std::shared_ptr<Card> >(1, ci);
             }
         }
     }
 
     Error err;
     const std::vector<CardApp> cardApps = getCardsAndApps(gpgAgent, err);
     if (err) {
         if (isCardNotPresentError(err)) {
             qCDebug(KLEOPATRA_LOG) << "update_cardinfo: No card present";
             return std::vector<std::shared_ptr<Card> >();
         } else {
             qCWarning(KLEOPATRA_LOG) << "Getting active apps on all inserted cards failed:" << err;
             auto ci = std::shared_ptr<Card>(new Card());
             ci->setStatus(Card::CardError);
             return std::vector<std::shared_ptr<Card> >(1, ci);
         }
     }
 
     std::vector<std::shared_ptr<Card> > cards;
     for (const auto &cardApp: cardApps) {
         const auto card = get_card_status(cardApp.serialNumber, cardApp.appName, gpgAgent);
         cards.push_back(card);
     }
     return cards;
 }
 } // namespace
 
 struct Transaction {
     CardApp cardApp;
     QByteArray command;
     QPointer<QObject> receiver;
     const char *slot;
     AssuanTransaction* assuanTransaction;
 };
 
 static const Transaction updateTransaction = { { "__all__", "__all__" }, "__update__", nullptr, nullptr, nullptr };
 static const Transaction quitTransaction   = { { "__all__", "__all__" }, "__quit__",   nullptr, nullptr, nullptr };
 
 namespace
 {
 class ReaderStatusThread : public QThread
 {
     Q_OBJECT
 public:
     explicit ReaderStatusThread(QObject *parent = nullptr)
         : QThread(parent),
           m_gnupgHomePath(Kleo::gnupgHomeDirectory()),
           m_transactions(1, updateTransaction)   // force initial scan
     {
         connect(this, &ReaderStatusThread::oneTransactionFinished,
                 this, &ReaderStatusThread::slotOneTransactionFinished);
     }
 
     std::vector<std::shared_ptr<Card> > cardInfos() const
     {
         const QMutexLocker locker(&m_mutex);
         return m_cardInfos;
     }
 
     Card::Status cardStatus(unsigned int slot) const
     {
         const QMutexLocker locker(&m_mutex);
         if (slot < m_cardInfos.size()) {
             return m_cardInfos[slot]->status();
         } else {
             return Card::NoCard;
         }
     }
 
     void addTransaction(const Transaction &t)
     {
         const QMutexLocker locker(&m_mutex);
         m_transactions.push_back(t);
         m_waitForTransactions.wakeOne();
     }
 
 Q_SIGNALS:
     void firstCardWithNullPinChanged(const std::string &serialNumber);
     void anyCardCanLearnKeysChanged(bool);
     void cardAdded(const std::string &serialNumber, const std::string &appName);
     void cardChanged(const std::string &serialNumber, const std::string &appName);
     void cardRemoved(const std::string &serialNumber, const std::string &appName);
     void oneTransactionFinished(const GpgME::Error &err);
 
 public Q_SLOTS:
     void deviceStatusChanged(const QByteArray &details)
     {
         qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[GUI]::deviceStatusChanged(" << details << ")";
         addTransaction(updateTransaction);
     }
 
     void ping()
     {
         qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[GUI]::ping()";
         addTransaction(updateTransaction);
     }
 
     void stop()
     {
         const QMutexLocker locker(&m_mutex);
         m_transactions.push_front(quitTransaction);
         m_waitForTransactions.wakeOne();
     }
 
 private Q_SLOTS:
     void slotOneTransactionFinished(const GpgME::Error &err)
     {
         std::list<Transaction> ft;
         KDAB_SYNCHRONIZED(m_mutex)
         ft.splice(ft.begin(), m_finishedTransactions);
         for (const Transaction &t : std::as_const(ft))
             if (t.receiver && t.slot && *t.slot) {
                 QMetaObject::invokeMethod(t.receiver, t.slot, Qt::DirectConnection, Q_ARG(GpgME::Error, err));
             }
     }
 
 private:
     void run() override {
         while (true) {
             std::shared_ptr<Context> gpgAgent;
 
             CardApp cardApp;
             QByteArray command;
             bool nullSlot = false;
             AssuanTransaction* assuanTransaction = nullptr;
             std::list<Transaction> item;
             std::vector<std::shared_ptr<Card> > oldCards;
 
+            while (!KeyCache::instance()->initialized()) {
+                qCDebug(KLEOPATRA_LOG) << "Waiting for Keycache to be initialized.";
+                sleep(1);
+            }
+
             Error err;
             std::unique_ptr<Context> c = Context::createForEngine(AssuanEngine, &err);
             if (err.code() == GPG_ERR_NOT_SUPPORTED) {
                 return;
             }
             gpgAgent = std::shared_ptr<Context>(c.release());
 
             KDAB_SYNCHRONIZED(m_mutex) {
 
                 while (m_transactions.empty()) {
                     // go to sleep waiting for more work:
                     qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: waiting for commands";
                     m_waitForTransactions.wait(&m_mutex);
                 }
 
                 // splice off the first transaction without
                 // copying, so we own it without really importing
                 // it into this thread (the QPointer isn't
                 // thread-safe):
                 item.splice(item.end(),
                             m_transactions, m_transactions.begin());
 
                 // make local copies of the interesting stuff so
                 // we can release the mutex again:
                 cardApp = item.front().cardApp;
                 command = item.front().command;
                 nullSlot = !item.front().slot;
                 // we take ownership of the assuan transaction
                 std::swap(assuanTransaction, item.front().assuanTransaction);
                 oldCards = m_cardInfos;
             }
 
             qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: new iteration command=" << command << " ; nullSlot=" << nullSlot;
             // now, let's see what we got:
             if (nullSlot && command == quitTransaction.command) {
                 return;    // quit
             }
 
             if ((nullSlot && command == updateTransaction.command)) {
 
                 std::vector<std::shared_ptr<Card> > newCards = update_cardinfo(gpgAgent);
 
                 KDAB_SYNCHRONIZED(m_mutex)
                 m_cardInfos = newCards;
 
                 bool anyLC = false;
                 std::string firstCardWithNullPin;
                 bool anyError = false;
                 for (const auto &newCard: newCards) {
                     const auto serialNumber = newCard->serialNumber();
                     const auto appName = newCard->appName();
                     const auto matchingOldCard = std::find_if(oldCards.cbegin(), oldCards.cend(),
                         [serialNumber, appName] (const std::shared_ptr<Card> &card) {
                             return card->serialNumber() == serialNumber && card->appName() == appName;
                         });
                     if (matchingOldCard == oldCards.cend()) {
                         qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread: Card" << serialNumber << "with app" << appName << "was added";
                         Q_EMIT cardAdded(serialNumber, appName);
                     } else {
                         if (*newCard != **matchingOldCard) {
                             qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread: Card" << serialNumber << "with app" << appName << "changed";
                             Q_EMIT cardChanged(serialNumber, appName);
                         }
                         oldCards.erase(matchingOldCard);
                     }
                     if (newCard->canLearnKeys()) {
                         anyLC = true;
                     }
                     if (newCard->hasNullPin() && firstCardWithNullPin.empty()) {
                         firstCardWithNullPin = newCard->serialNumber();
                     }
                     if (newCard->status() == Card::CardError) {
                         anyError = true;
                     }
                 }
                 for (const auto &oldCard: oldCards) {
                     qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread: Card" << oldCard->serialNumber() << "with app" << oldCard->appName() << "was removed";
                     Q_EMIT cardRemoved(oldCard->serialNumber(), oldCard->appName());
                 }
 
                 Q_EMIT firstCardWithNullPinChanged(firstCardWithNullPin);
                 Q_EMIT anyCardCanLearnKeysChanged(anyLC);
 
                 if (anyError) {
                     gpgAgent.reset();
                 }
             } else {
                 GpgME::Error err;
                 if (gpgHasMultiCardMultiAppSupport()) {
                     switchCard(gpgAgent, cardApp.serialNumber, err);
                     if (!err) {
                         switchApp(gpgAgent, cardApp.serialNumber, cardApp.appName, err);
                     }
                 }
                 if (!err) {
                     if (assuanTransaction) {
                         (void)Assuan::sendCommand(gpgAgent, command.constData(), std::unique_ptr<AssuanTransaction>(assuanTransaction), err);
                     } else {
                         (void)Assuan::sendCommand(gpgAgent, command.constData(), err);
                     }
                 }
 
                 KDAB_SYNCHRONIZED(m_mutex)
                 // splice 'item' into m_finishedTransactions:
                 m_finishedTransactions.splice(m_finishedTransactions.end(), item);
 
                 Q_EMIT oneTransactionFinished(err);
             }
         }
     }
 
 private:
     mutable QMutex m_mutex;
     QWaitCondition m_waitForTransactions;
     const QString m_gnupgHomePath;
     // protected by m_mutex:
     std::vector<std::shared_ptr<Card> > m_cardInfos;
     std::list<Transaction> m_transactions, m_finishedTransactions;
 };
 
 }
 
 class ReaderStatus::Private : ReaderStatusThread
 {
     friend class Kleo::SmartCard::ReaderStatus;
     ReaderStatus *const q;
 public:
     explicit Private(ReaderStatus *qq)
         : ReaderStatusThread(qq),
           q(qq),
           watcher()
     {
         KDAB_SET_OBJECT_NAME(watcher);
 
         qRegisterMetaType<Card::Status>("Kleo::SmartCard::Card::Status");
         qRegisterMetaType<GpgME::Error>("GpgME::Error");
 
         connect(this, &::ReaderStatusThread::cardAdded,
                 q, &ReaderStatus::cardAdded);
         connect(this, &::ReaderStatusThread::cardChanged,
                 q, &ReaderStatus::cardChanged);
         connect(this, &::ReaderStatusThread::cardRemoved,
                 q, &ReaderStatus::cardRemoved);
         connect(this, &::ReaderStatusThread::firstCardWithNullPinChanged,
                 q, &ReaderStatus::firstCardWithNullPinChanged);
         connect(this, &::ReaderStatusThread::anyCardCanLearnKeysChanged,
                 q, &ReaderStatus::anyCardCanLearnKeysChanged);
 
         if (DeviceInfoWatcher::isSupported()) {
             qCDebug(KLEOPATRA_LOG) << "ReaderStatus::Private: Using new DeviceInfoWatcher";
             connect(&devInfoWatcher, &DeviceInfoWatcher::statusChanged, this, &::ReaderStatusThread::deviceStatusChanged);
         } else {
             qCDebug(KLEOPATRA_LOG) << "ReaderStatus::Private: Using deprecated FileSystemWatcher";
 
             watcher.whitelistFiles(QStringList(QStringLiteral("reader_*.status")));
             watcher.addPath(Kleo::gnupgHomeDirectory());
             watcher.setDelay(100);
 
             connect(&watcher, &FileSystemWatcher::triggered, this, &::ReaderStatusThread::ping);
         }
     }
     ~Private() override
     {
         stop();
         if (!wait(100)) {
             terminate();
             wait();
         }
     }
 
 private:
     std::string firstCardWithNullPinImpl() const
     {
         const auto cis = cardInfos();
         const auto firstWithNullPin = std::find_if(cis.cbegin(), cis.cend(),
                                                    [](const std::shared_ptr<Card> &ci) { return ci->hasNullPin(); });
         return firstWithNullPin != cis.cend() ? (*firstWithNullPin)->serialNumber() : std::string();
     }
 
     bool anyCardCanLearnKeysImpl() const
     {
         const auto cis = cardInfos();
         return std::any_of(cis.cbegin(), cis.cend(),
                            [](const std::shared_ptr<Card> &ci) { return ci->canLearnKeys(); });
     }
 
 private:
     FileSystemWatcher watcher;
     DeviceInfoWatcher devInfoWatcher;
 };
 
 ReaderStatus::ReaderStatus(QObject *parent)
     : QObject(parent), d(new Private(this))
 {
     self = this;
 
     qRegisterMetaType<std::string>("std::string");
 }
 
 ReaderStatus::~ReaderStatus()
 {
     self = nullptr;
 }
 
 // slot
 void ReaderStatus::startMonitoring()
 {
     d->start();
     if (DeviceInfoWatcher::isSupported()) {
         connect(&d->devInfoWatcher, &DeviceInfoWatcher::startOfGpgAgentRequested,
                 this, &ReaderStatus::startOfGpgAgentRequested);
         d->devInfoWatcher.start();
     }
 }
 
 // static
 ReaderStatus *ReaderStatus::mutableInstance()
 {
     return self;
 }
 
 // static
 const ReaderStatus *ReaderStatus::instance()
 {
     return self;
 }
 
 Card::Status ReaderStatus::cardStatus(unsigned int slot) const
 {
     return d->cardStatus(slot);
 }
 
 std::string ReaderStatus::firstCardWithNullPin() const
 {
     return d->firstCardWithNullPinImpl();
 }
 
 bool ReaderStatus::anyCardCanLearnKeys() const
 {
     return d->anyCardCanLearnKeysImpl();
 }
 
 void ReaderStatus::startSimpleTransaction(const std::shared_ptr<Card> &card, const QByteArray &command, QObject *receiver, const char *slot)
 {
     const CardApp cardApp = { card->serialNumber(), card->appName() };
     const Transaction t = { cardApp, command, receiver, slot, nullptr };
     d->addTransaction(t);
 }
 
 void ReaderStatus::startTransaction(const std::shared_ptr<Card> &card, const QByteArray &command, QObject *receiver, const char *slot,
                                     std::unique_ptr<AssuanTransaction> transaction)
 {
     const CardApp cardApp = { card->serialNumber(), card->appName() };
     const Transaction t = { cardApp, command, receiver, slot, transaction.release() };
     d->addTransaction(t);
 }
 
 void ReaderStatus::updateStatus()
 {
     d->ping();
 }
 
 std::vector <std::shared_ptr<Card> > ReaderStatus::getCards() const
 {
     return d->cardInfos();
 }
 
 std::shared_ptr<Card> ReaderStatus::getCard(const std::string &serialNumber, const std::string &appName) const
 {
     for (const auto &card: d->cardInfos()) {
         if (card->serialNumber() == serialNumber && card->appName() == appName) {
             qCDebug(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Found card with serial number" << serialNumber << "and app" << appName;
             return card;
         }
     }
     qCWarning(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Did not find card with serial number" << serialNumber << "and app" << appName;
     return std::shared_ptr<Card>();
 }
 
 // static
 std::string ReaderStatus::switchCard(std::shared_ptr<Context>& ctx, const std::string& serialNumber, Error& err)
 {
     return ::switchCard(ctx, serialNumber, err);
 }
 
 // static
 std::string ReaderStatus::switchApp(std::shared_ptr<Context>& ctx, const std::string& serialNumber, const std::string& appName, Error& err)
 {
     return ::switchApp(ctx, serialNumber, appName, err);
 }
 
 // static
 Error ReaderStatus::switchCardAndApp(const std::string &serialNumber, const std::string &appName)
 {
     Error err;
     if (!(engineInfo(GpgEngine).engineVersion() < "2.3.0")) {
         std::unique_ptr<Context> c = Context::createForEngine(AssuanEngine, &err);
         if (err.code() == GPG_ERR_NOT_SUPPORTED) {
             return err;
         }
         auto assuanContext = std::shared_ptr<Context>(c.release());
         const auto resultSerialNumber = switchCard(assuanContext, serialNumber, err);
         if (err || resultSerialNumber != serialNumber) {
             qCWarning(KLEOPATRA_LOG) << "Switching to card" << QString::fromStdString(serialNumber) << "failed";
             if (!err) {
                 err = Error::fromCode(GPG_ERR_UNEXPECTED);
             }
             return err;
         }
         const auto resultAppName = switchApp(assuanContext, serialNumber, appName, err);
         if (err || resultAppName != appName) {
             qCWarning(KLEOPATRA_LOG) << "Switching card to" << QString::fromStdString(appName) << "app failed";
             if (!err) {
                 err = Error::fromCode(GPG_ERR_UNEXPECTED);
             }
             return err;
         }
     }
     return err;
 }
 
 #include "readerstatus.moc"