File indexing completed on 2024-04-21 04:43:51

0001 /*
0002  * Copyright (C) 2003-2008  Justin Karneges <justin@affinix.com>
0003  * Copyright (C) 2004,2005  Brad Hards <bradh@frogmouth.net>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Lesser General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2.1 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Lesser General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Lesser General Public
0016  * License along with this library; if not, write to the Free Software
0017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0018  * 02110-1301  USA
0019  *
0020  */
0021 
0022 #include "qca_keystore.h"
0023 
0024 #include <QAbstractEventDispatcher>
0025 #include <QCoreApplication>
0026 #include <QMutex>
0027 #include <QPointer>
0028 #include <QSet>
0029 #include <QWaitCondition>
0030 
0031 #include <cstdio>  // fprintf
0032 #include <cstdlib> // abort
0033 
0034 #include "qcaprovider.h"
0035 
0036 Q_DECLARE_METATYPE(QCA::KeyStoreEntry)
0037 Q_DECLARE_METATYPE(QList<QCA::KeyStoreEntry>)
0038 Q_DECLARE_METATYPE(QList<QCA::KeyStoreEntry::Type>)
0039 Q_DECLARE_METATYPE(QCA::KeyBundle)
0040 Q_DECLARE_METATYPE(QCA::Certificate)
0041 Q_DECLARE_METATYPE(QCA::CRL)
0042 Q_DECLARE_METATYPE(QCA::PGPKey)
0043 
0044 namespace QCA {
0045 
0046 Provider::Context *getContext(const QString &type, Provider *p);
0047 
0048 // from qca_plugin.cpp
0049 QString truncate_log(const QString &in, int size);
0050 
0051 /*
0052   How this stuff works:
0053 
0054   KeyStoreListContext is queried for a list of store context ids.  A signal
0055   is used to indicate when the list may have changed, so polling for changes
0056   is not necessary.  Context ids change for every new presence of a store.
0057   Even if a user removes and inserts the same smart card device, which has
0058   the same storeId, the context id will ALWAYS be different.  If a previously
0059   known context id is missing from a later queried list, then it means the
0060   associated store is unavailable.  It is recommended that the provider just
0061   use a counter for the contextId, incrementing the value anytime a new
0062   context is made.
0063 
0064   KeyStoreTracker manages all of the keystore stuff, and exists in its own
0065   thread (called the tracker thread).  All of the KeyStoreListContext
0066   objects exist in the tracker thread.
0067 */
0068 
0069 /*
0070   scenarios to handle:
0071   - ksm.start shouldn't block
0072   - keystore in available list, but gone by the time it is requested
0073   - keystore is unavailable during a call to a keystoreentry method
0074   - keystore/keystoreentry methods called simultaneously from different threads
0075   - and of course, objects from keystores should work, despite being created
0076     in the keystore thread
0077 */
0078 
0079 //----------------------------------------------------------------------------
0080 // KeyStoreTracker
0081 //----------------------------------------------------------------------------
0082 static int tracker_id_at = 0;
0083 
0084 class KeyStoreTracker : public QObject
0085 {
0086     Q_OBJECT
0087 public:
0088     static KeyStoreTracker *self;
0089 
0090     class Item
0091     {
0092     public:
0093         // combine keystore owner and contextid into a single id
0094         int trackerId;
0095 
0096         // number of times the keystore has been updated
0097         int updateCount;
0098 
0099         // keystore context
0100         KeyStoreListContext *owner;
0101         int                  storeContextId;
0102 
0103         // properties
0104         QString        storeId;
0105         QString        name;
0106         KeyStore::Type type;
0107         bool           isReadOnly;
0108 
0109         Item()
0110             : trackerId(-1)
0111             , updateCount(0)
0112             , owner(nullptr)
0113             , storeContextId(-1)
0114             , storeId(QLatin1String(""))
0115             , name(QLatin1String(""))
0116             , type(KeyStore::System)
0117             , isReadOnly(false)
0118         {
0119         }
0120     };
0121 
0122     QMutex                      m;
0123     QSet<KeyStoreListContext *> sources;
0124     QSet<KeyStoreListContext *> busySources;
0125     QList<Item>                 items;
0126     QString                     dtext;
0127     bool                        startedAll;
0128     bool                        busy;
0129 
0130     QMutex updateMutex;
0131 
0132     KeyStoreTracker()
0133     {
0134         self = this;
0135 
0136         qRegisterMetaType<KeyStoreEntry>();
0137         qRegisterMetaType<QList<KeyStoreEntry>>();
0138         qRegisterMetaType<QList<KeyStoreEntry::Type>>();
0139         qRegisterMetaType<KeyBundle>();
0140         qRegisterMetaType<Certificate>();
0141         qRegisterMetaType<CRL>();
0142         qRegisterMetaType<PGPKey>();
0143 
0144         connect(this, &KeyStoreTracker::updated_p, this, &KeyStoreTracker::updated_locked, Qt::QueuedConnection);
0145 
0146         startedAll = false;
0147         busy       = true; // we start out busy
0148     }
0149 
0150     ~KeyStoreTracker() override
0151     {
0152         qDeleteAll(sources);
0153         self = nullptr;
0154     }
0155 
0156     static KeyStoreTracker *instance()
0157     {
0158         return self;
0159     }
0160 
0161     // thread-safe
0162     bool isBusy()
0163     {
0164         QMutexLocker locker(&m);
0165         return busy;
0166     }
0167 
0168     // thread-safe
0169     QList<Item> getItems()
0170     {
0171         QMutexLocker locker(&m);
0172         return items;
0173     }
0174 
0175     // thread-safe
0176     QString getDText()
0177     {
0178         QMutexLocker locker(&m);
0179         return dtext;
0180     }
0181 
0182     // thread-safe
0183     void clearDText()
0184     {
0185         QMutexLocker locker(&m);
0186         dtext.clear();
0187     }
0188 
0189     // thread-safe
0190     void addTarget(KeyStoreManagerPrivate *ksm);
0191 
0192     // thread-safe
0193     void removeTarget(QObject *ksm)
0194     {
0195         QMutexLocker locker(&updateMutex);
0196         disconnect(ksm);
0197     }
0198 
0199 public Q_SLOTS:
0200     void spinEventLoop()
0201     {
0202         QAbstractEventDispatcher::instance()->processEvents(QEventLoop::AllEvents);
0203     }
0204 
0205     void start()
0206     {
0207         // grab providers (and default)
0208         ProviderList list = providers();
0209         list.append(defaultProvider());
0210 
0211         for (int n = 0; n < list.count(); ++n) {
0212             Provider *p = list[n];
0213             if (p->features().contains(QStringLiteral("keystorelist")) && !haveProviderSource(p))
0214                 startProvider(p);
0215         }
0216 
0217         startedAll = true;
0218     }
0219 
0220     void start(const QString &provider)
0221     {
0222         // grab providers (and default)
0223         ProviderList list = providers();
0224         list.append(defaultProvider());
0225 
0226         Provider *p = nullptr;
0227         for (int n = 0; n < list.count(); ++n) {
0228             if (list[n]->name() == provider) {
0229                 p = list[n];
0230                 break;
0231             }
0232         }
0233 
0234         if (p && p->features().contains(QStringLiteral("keystorelist")) && !haveProviderSource(p))
0235             startProvider(p);
0236     }
0237 
0238     void scan()
0239     {
0240         if (startedAll)
0241             start();
0242     }
0243 
0244     QList<QCA::KeyStoreEntry> entryList(int trackerId)
0245     {
0246         QList<KeyStoreEntry> out;
0247         int                  at = findItem(trackerId);
0248         if (at == -1)
0249             return out;
0250         Item                               &i    = items[at];
0251         const QList<KeyStoreEntryContext *> list = i.owner->entryList(i.storeContextId);
0252         for (int n = 0; n < list.count(); ++n) {
0253             KeyStoreEntry entry;
0254             entry.change(list[n]);
0255             out.append(entry);
0256         }
0257         return out;
0258     }
0259 
0260     QList<QCA::KeyStoreEntry::Type> entryTypes(int trackerId)
0261     {
0262         QList<KeyStoreEntry::Type> out;
0263         int                        at = findItem(trackerId);
0264         if (at == -1)
0265             return out;
0266         Item &i = items[at];
0267         return i.owner->entryTypes(i.storeContextId);
0268     }
0269 
0270     // hack with void *
0271     void *entry(const QString &storeId, const QString &entryId)
0272     {
0273         KeyStoreListContext *c         = nullptr;
0274         int                  contextId = -1;
0275         m.lock();
0276         foreach (const Item &i, items) {
0277             if (i.storeId == storeId) {
0278                 c         = i.owner;
0279                 contextId = i.storeContextId;
0280                 break;
0281             }
0282         }
0283         m.unlock();
0284         if (!c)
0285             return nullptr;
0286 
0287         return c->entry(contextId, entryId);
0288     }
0289 
0290     // hack with void *
0291     void *entryPassive(const QString &serialized)
0292     {
0293         foreach (KeyStoreListContext *ksl, sources) {
0294             // "is this yours?"
0295             KeyStoreEntryContext *e = ksl->entryPassive(serialized);
0296             if (e)
0297                 return e;
0298         }
0299         return nullptr;
0300     }
0301 
0302     QString writeEntry(int trackerId, const QVariant &v)
0303     {
0304         int at = findItem(trackerId);
0305         if (at == -1)
0306             return QString();
0307         Item &i = items[at];
0308         if (v.canConvert<KeyBundle>())
0309             return i.owner->writeEntry(i.storeContextId, v.value<KeyBundle>());
0310         else if (v.canConvert<Certificate>())
0311             return i.owner->writeEntry(i.storeContextId, v.value<Certificate>());
0312         else if (v.canConvert<CRL>())
0313             return i.owner->writeEntry(i.storeContextId, v.value<CRL>());
0314         else if (v.canConvert<PGPKey>())
0315             return i.owner->writeEntry(i.storeContextId, v.value<PGPKey>());
0316         else
0317             return QString();
0318     }
0319 
0320     QString writeEntry(int trackerId, const QCA::KeyBundle &v)
0321     {
0322         int at = findItem(trackerId);
0323         if (at == -1)
0324             return QString();
0325         Item &i = items[at];
0326 
0327         return i.owner->writeEntry(i.storeContextId, v);
0328     }
0329 
0330     QString writeEntry(int trackerId, const QCA::Certificate &v)
0331     {
0332         int at = findItem(trackerId);
0333         if (at == -1)
0334             return QString();
0335         Item &i = items[at];
0336 
0337         return i.owner->writeEntry(i.storeContextId, v);
0338     }
0339 
0340     QString writeEntry(int trackerId, const QCA::CRL &v)
0341     {
0342         int at = findItem(trackerId);
0343         if (at == -1)
0344             return QString();
0345         Item &i = items[at];
0346 
0347         return i.owner->writeEntry(i.storeContextId, v);
0348     }
0349 
0350     QString writeEntry(int trackerId, const QCA::PGPKey &v)
0351     {
0352         int at = findItem(trackerId);
0353         if (at == -1)
0354             return QString();
0355         Item &i = items[at];
0356 
0357         return i.owner->writeEntry(i.storeContextId, v);
0358     }
0359 
0360     bool removeEntry(int trackerId, const QString &entryId)
0361     {
0362         int at = findItem(trackerId);
0363         if (at == -1)
0364             return false;
0365         Item &i = items[at];
0366         return i.owner->removeEntry(i.storeContextId, entryId);
0367     }
0368 
0369 Q_SIGNALS:
0370     // emit this when items or busy state changes
0371     void updated();
0372     void updated_p();
0373 
0374 private Q_SLOTS:
0375     void updated_locked()
0376     {
0377         QMutexLocker locker(&updateMutex);
0378         emit         updated();
0379     }
0380 
0381 private:
0382     bool haveProviderSource(Provider *p) const
0383     {
0384         foreach (KeyStoreListContext *ksl, sources) {
0385             if (ksl->provider() == p)
0386                 return true;
0387         }
0388         return false;
0389     }
0390 
0391     int findItem(int trackerId)
0392     {
0393         for (int n = 0; n < items.count(); ++n) {
0394             if (items[n].trackerId == trackerId)
0395                 return n;
0396         }
0397         return -1;
0398     }
0399 
0400     void startProvider(Provider *p)
0401     {
0402         KeyStoreListContext *c = static_cast<KeyStoreListContext *>(getContext(QStringLiteral("keystorelist"), p));
0403         if (!c)
0404             return;
0405 
0406         sources += c;
0407         busySources += c;
0408         connect(c, &KeyStoreListContext::busyStart, this, &KeyStoreTracker::ksl_busyStart);
0409         connect(c, &KeyStoreListContext::busyEnd, this, &KeyStoreTracker::ksl_busyEnd);
0410         connect(c, &KeyStoreListContext::updated, this, &KeyStoreTracker::ksl_updated);
0411         connect(c, &KeyStoreListContext::diagnosticText, this, &KeyStoreTracker::ksl_diagnosticText);
0412         connect(c, &KeyStoreListContext::storeUpdated, this, &KeyStoreTracker::ksl_storeUpdated);
0413         c->start();
0414         c->setUpdatesEnabled(true);
0415 
0416         QCA_logTextMessage(QStringLiteral("keystore: startProvider %1").arg(p->name()), Logger::Information);
0417     }
0418 
0419     bool updateStores(KeyStoreListContext *c)
0420     {
0421         bool changed = false;
0422 
0423         QMutexLocker locker(&m);
0424 
0425         const QList<int> keyStores = c->keyStores();
0426 
0427         // remove any contexts that are gone
0428         for (int n = 0; n < items.count(); ++n) {
0429             if (items[n].owner == c && !keyStores.contains(items[n].storeContextId)) {
0430                 QCA_logTextMessage(QStringLiteral("keystore: updateStores remove %1").arg(items[n].storeContextId),
0431                                    Logger::Information);
0432 
0433                 items.removeAt(n);
0434                 --n; // adjust position
0435 
0436                 changed = true;
0437             }
0438         }
0439 
0440         // handle add/updates
0441         foreach (int id, keyStores) {
0442             // do we have it already?
0443             int at = -1;
0444             for (int n = 0; n < items.count(); ++n) {
0445                 if (items[n].owner == c && items[n].storeContextId == id) {
0446                     at = n;
0447                     break;
0448                 }
0449             }
0450 
0451             // if so, update it
0452             if (at != -1) {
0453                 Item &i = items[at];
0454 
0455                 QString name       = c->name(id);
0456                 bool    isReadOnly = c->isReadOnly(id);
0457                 if (i.name != name || i.isReadOnly != isReadOnly) {
0458                     QCA_logTextMessage(QStringLiteral("keystore: updateStores update %1").arg(id), Logger::Information);
0459                     i.name       = name;
0460                     i.isReadOnly = isReadOnly;
0461                     changed      = true;
0462                 }
0463             }
0464             // otherwise, add it
0465             else {
0466                 QCA_logTextMessage(QStringLiteral("keystore: updateStores add %1").arg(id), Logger::Information);
0467 
0468                 Item i;
0469                 i.trackerId      = tracker_id_at++;
0470                 i.updateCount    = 0;
0471                 i.owner          = c;
0472                 i.storeContextId = id;
0473                 i.storeId        = c->storeId(id);
0474                 i.name           = c->name(id);
0475                 i.type           = c->type(id);
0476                 i.isReadOnly     = c->isReadOnly(id);
0477                 items += i;
0478 
0479                 changed = true;
0480             }
0481         }
0482 
0483         return changed;
0484     }
0485 
0486 private Q_SLOTS:
0487     void ksl_busyStart()
0488     {
0489         KeyStoreListContext *c = (KeyStoreListContext *)sender();
0490 
0491         QCA_logTextMessage(QStringLiteral("keystore: ksl_busyStart %1").arg(c->provider()->name()),
0492                            Logger::Information);
0493 
0494         if (!busySources.contains(c)) {
0495             busySources += c;
0496 
0497             QCA_logTextMessage(QStringLiteral("keystore: emitting updated"), Logger::Information);
0498             emit updated_p();
0499         }
0500     }
0501 
0502     void ksl_busyEnd()
0503     {
0504         KeyStoreListContext *c = (KeyStoreListContext *)sender();
0505 
0506         QCA_logTextMessage(QStringLiteral("keystore: ksl_busyEnd %1").arg(c->provider()->name()), Logger::Information);
0507 
0508         busySources.remove(c);
0509         bool       changed  = updateStores(c);
0510         const bool any_busy = !busySources.isEmpty();
0511 
0512         if (!any_busy) {
0513             m.lock();
0514             busy = false;
0515             m.unlock();
0516         }
0517 
0518         if (!any_busy || changed) {
0519             QCA_logTextMessage(QStringLiteral("keystore: emitting updated"), Logger::Information);
0520             emit updated_p();
0521         }
0522     }
0523 
0524     void ksl_updated()
0525     {
0526         KeyStoreListContext *c = (KeyStoreListContext *)sender();
0527 
0528         QCA_logTextMessage(QStringLiteral("keystore: ksl_updated %1").arg(c->provider()->name()), Logger::Information);
0529 
0530         bool changed = updateStores(c);
0531         if (changed) {
0532             QCA_logTextMessage(QStringLiteral("keystore: emitting updated"), Logger::Information);
0533             emit updated_p();
0534         }
0535     }
0536 
0537     void ksl_diagnosticText(const QString &str)
0538     {
0539         QMutexLocker locker(&m);
0540         dtext += str;
0541         dtext = truncate_log(dtext, 100000);
0542     }
0543 
0544     void ksl_storeUpdated(int id)
0545     {
0546         KeyStoreListContext *c = (KeyStoreListContext *)sender();
0547 
0548         QCA_logTextMessage(
0549             QStringLiteral("keystore: ksl_storeUpdated %1 %2").arg(c->provider()->name(), QString::number(id)),
0550             Logger::Information);
0551 
0552         QMutexLocker locker(&m);
0553         for (int n = 0; n < items.count(); ++n) {
0554             Item &i = items[n];
0555             if (i.owner == c && i.storeContextId == id) {
0556                 ++i.updateCount;
0557 
0558                 QCA_logTextMessage(
0559                     QStringLiteral("keystore: %1 updateCount = %2").arg(i.name, QString::number(i.updateCount)),
0560                     Logger::Information);
0561 
0562                 QCA_logTextMessage(QStringLiteral("keystore: emitting updated"), Logger::Information);
0563                 emit updated_p();
0564                 return;
0565             }
0566         }
0567     }
0568 };
0569 
0570 KeyStoreTracker *KeyStoreTracker::self = nullptr;
0571 
0572 //----------------------------------------------------------------------------
0573 // KeyStoreThread
0574 //----------------------------------------------------------------------------
0575 class KeyStoreThread : public SyncThread
0576 {
0577     Q_OBJECT
0578 public:
0579     KeyStoreTracker *tracker;
0580     QMutex           call_mutex;
0581 
0582     KeyStoreThread(QObject *parent = nullptr)
0583         : SyncThread(parent)
0584     {
0585     }
0586 
0587     ~KeyStoreThread() override
0588     {
0589         stop();
0590     }
0591 
0592     void atStart() override
0593     {
0594         tracker = new KeyStoreTracker;
0595     }
0596 
0597     void atEnd() override
0598     {
0599         delete tracker;
0600     }
0601 };
0602 
0603 //----------------------------------------------------------------------------
0604 // KeyStoreGlobal
0605 //----------------------------------------------------------------------------
0606 class KeyStoreManagerGlobal;
0607 
0608 Q_GLOBAL_STATIC(QMutex, ksm_mutex)
0609 static KeyStoreManagerGlobal *g_ksm = nullptr;
0610 
0611 class KeyStoreManagerGlobal
0612 {
0613 public:
0614     KeyStoreThread *thread;
0615 
0616     KeyStoreManagerGlobal()
0617     {
0618         thread = new KeyStoreThread;
0619         thread->moveToThread(QCoreApplication::instance()->thread());
0620         thread->start();
0621     }
0622 
0623     ~KeyStoreManagerGlobal()
0624     {
0625         delete thread;
0626     }
0627 
0628     KeyStoreManagerGlobal(const KeyStoreManagerGlobal &)            = delete;
0629     KeyStoreManagerGlobal &operator=(const KeyStoreManagerGlobal &) = delete;
0630 };
0631 
0632 // this function is thread-safe
0633 static QVariant trackercall(const char *method, const QVariantList &args = QVariantList())
0634 {
0635     QVariant ret;
0636     bool     ok;
0637 
0638     g_ksm->thread->call_mutex.lock();
0639     ret = g_ksm->thread->call(KeyStoreTracker::instance(), method, args, &ok);
0640     g_ksm->thread->call_mutex.unlock();
0641 
0642     Q_ASSERT(ok);
0643     if (!ok) {
0644         fprintf(stderr, "QCA: KeyStoreTracker call [%s] failed.\n", method);
0645         abort();
0646         return QVariant();
0647     }
0648     return ret;
0649 }
0650 
0651 //----------------------------------------------------------------------------
0652 // KeyStoreEntry
0653 //----------------------------------------------------------------------------
0654 class KeyStoreEntry::Private
0655 {
0656 public:
0657     bool accessible;
0658 
0659     Private()
0660     {
0661         accessible = false;
0662     }
0663 };
0664 
0665 KeyStoreEntry::KeyStoreEntry()
0666     : d(new Private)
0667 {
0668 }
0669 
0670 KeyStoreEntry::KeyStoreEntry(const QString &serialized)
0671     : d(new Private)
0672 {
0673     *this = fromString(serialized);
0674 }
0675 
0676 KeyStoreEntry::KeyStoreEntry(const KeyStoreEntry &from)
0677     : Algorithm(from)
0678     , d(new Private(*from.d))
0679 {
0680 }
0681 
0682 KeyStoreEntry::~KeyStoreEntry()
0683 {
0684     delete d;
0685 }
0686 
0687 KeyStoreEntry &KeyStoreEntry::operator=(const KeyStoreEntry &from)
0688 {
0689     Algorithm::operator=(from);
0690     *d = *from.d;
0691     return *this;
0692 }
0693 
0694 bool KeyStoreEntry::isNull() const
0695 {
0696     return (!context() ? true : false);
0697 }
0698 
0699 bool KeyStoreEntry::isAvailable() const
0700 {
0701     return static_cast<const KeyStoreEntryContext *>(context())->isAvailable();
0702 }
0703 
0704 bool KeyStoreEntry::isAccessible() const
0705 {
0706     return d->accessible;
0707 }
0708 
0709 KeyStoreEntry::Type KeyStoreEntry::type() const
0710 {
0711     return static_cast<const KeyStoreEntryContext *>(context())->type();
0712 }
0713 
0714 QString KeyStoreEntry::name() const
0715 {
0716     return static_cast<const KeyStoreEntryContext *>(context())->name();
0717 }
0718 
0719 QString KeyStoreEntry::id() const
0720 {
0721     return static_cast<const KeyStoreEntryContext *>(context())->id();
0722 }
0723 
0724 QString KeyStoreEntry::storeName() const
0725 {
0726     return static_cast<const KeyStoreEntryContext *>(context())->storeName();
0727 }
0728 
0729 QString KeyStoreEntry::storeId() const
0730 {
0731     return static_cast<const KeyStoreEntryContext *>(context())->storeId();
0732 }
0733 
0734 QString KeyStoreEntry::toString() const
0735 {
0736     return static_cast<const KeyStoreEntryContext *>(context())->serialize();
0737 }
0738 
0739 KeyStoreEntry KeyStoreEntry::fromString(const QString &serialized)
0740 {
0741     KeyStoreEntry         e;
0742     KeyStoreEntryContext *c = (KeyStoreEntryContext *)KeyStoreTracker::instance()->entryPassive(serialized);
0743     if (c)
0744         e.change(c);
0745     return e;
0746 }
0747 
0748 KeyBundle KeyStoreEntry::keyBundle() const
0749 {
0750     return static_cast<const KeyStoreEntryContext *>(context())->keyBundle();
0751 }
0752 
0753 Certificate KeyStoreEntry::certificate() const
0754 {
0755     return static_cast<const KeyStoreEntryContext *>(context())->certificate();
0756 }
0757 
0758 CRL KeyStoreEntry::crl() const
0759 {
0760     return static_cast<const KeyStoreEntryContext *>(context())->crl();
0761 }
0762 
0763 PGPKey KeyStoreEntry::pgpSecretKey() const
0764 {
0765     return static_cast<const KeyStoreEntryContext *>(context())->pgpSecretKey();
0766 }
0767 
0768 PGPKey KeyStoreEntry::pgpPublicKey() const
0769 {
0770     return static_cast<const KeyStoreEntryContext *>(context())->pgpPublicKey();
0771 }
0772 
0773 bool KeyStoreEntry::ensureAvailable()
0774 {
0775     const QString         storeId = this->storeId();
0776     const QString         entryId = id();
0777     KeyStoreEntryContext *c =
0778         (KeyStoreEntryContext *)trackercall("entry", QVariantList() << storeId << entryId).value<void *>();
0779     if (c)
0780         change(c);
0781     return isAvailable();
0782 }
0783 
0784 bool KeyStoreEntry::ensureAccess()
0785 {
0786     if (!ensureAvailable()) {
0787         d->accessible = false;
0788         return false;
0789     }
0790     const bool ok = static_cast<KeyStoreEntryContext *>(context())->ensureAccess();
0791     d->accessible = ok;
0792     return d->accessible;
0793 }
0794 
0795 //----------------------------------------------------------------------------
0796 // KeyStoreEntryWatcher
0797 //----------------------------------------------------------------------------
0798 class KeyStoreEntryWatcher::Private : public QObject
0799 {
0800     Q_OBJECT
0801 public:
0802     KeyStoreEntryWatcher *q;
0803     KeyStoreManager       ksm;
0804     KeyStoreEntry         entry;
0805     QString               storeId, entryId;
0806     KeyStore             *ks;
0807     bool                  avail;
0808 
0809     Private(KeyStoreEntryWatcher *_q)
0810         : QObject(_q)
0811         , q(_q)
0812         , ksm(this)
0813     {
0814         ks    = nullptr;
0815         avail = false;
0816         connect(&ksm, &KeyStoreManager::keyStoreAvailable, this, &KeyStoreEntryWatcher::Private::ksm_available);
0817     }
0818 
0819     ~Private() override
0820     {
0821         delete ks;
0822     }
0823 
0824     void start()
0825     {
0826         const QStringList list = ksm.keyStores();
0827         foreach (const QString &storeId, list)
0828             ksm_available(storeId);
0829     }
0830 
0831 private Q_SLOTS:
0832     void ksm_available(const QString &_storeId)
0833     {
0834         // we only care about one store
0835         if (_storeId == storeId) {
0836             ks = new KeyStore(storeId, &ksm);
0837             connect(ks, &KeyStore::updated, this, &Private::ks_updated);
0838             ks->startAsynchronousMode();
0839         }
0840     }
0841 
0842     void ks_updated()
0843     {
0844         bool                       found = false;
0845         const QList<KeyStoreEntry> list  = ks->entryList();
0846         foreach (const KeyStoreEntry &e, list) {
0847             if (e.id() == entryId && e.isAvailable()) {
0848                 found = true;
0849                 if (!avail)
0850                     entry = e;
0851                 break;
0852             }
0853         }
0854 
0855         if (found && !avail) {
0856             avail = true;
0857             emit q->available();
0858         } else if (!found && avail) {
0859             avail = false;
0860             emit q->unavailable();
0861         }
0862     }
0863 
0864     void ks_unavailable()
0865     {
0866         delete ks;
0867         ks = nullptr;
0868 
0869         if (avail) {
0870             avail = false;
0871             emit q->unavailable();
0872         }
0873     }
0874 };
0875 
0876 KeyStoreEntryWatcher::KeyStoreEntryWatcher(const KeyStoreEntry &e, QObject *parent)
0877     : QObject(parent)
0878 {
0879     d = new Private(this);
0880     if (!e.isNull()) {
0881         d->entry   = e;
0882         d->storeId = e.storeId();
0883         d->entryId = e.id();
0884         d->start();
0885     }
0886 }
0887 
0888 KeyStoreEntryWatcher::~KeyStoreEntryWatcher()
0889 {
0890     delete d;
0891 }
0892 
0893 KeyStoreEntry KeyStoreEntryWatcher::entry() const
0894 {
0895     return d->entry;
0896 }
0897 
0898 //----------------------------------------------------------------------------
0899 // KeyStore
0900 //----------------------------------------------------------------------------
0901 // union thingy
0902 class KeyStoreWriteEntry
0903 {
0904 public:
0905     enum Type
0906     {
0907         TypeKeyBundle,
0908         TypeCertificate,
0909         TypeCRL,
0910         TypePGPKey
0911     };
0912 
0913     Type        type;
0914     KeyBundle   keyBundle;
0915     Certificate cert;
0916     CRL         crl;
0917     PGPKey      pgpKey;
0918 
0919     KeyStoreWriteEntry()
0920     {
0921     }
0922 
0923     KeyStoreWriteEntry(const KeyBundle &_keyBundle)
0924         : type(TypeKeyBundle)
0925         , keyBundle(_keyBundle)
0926     {
0927     }
0928 
0929     KeyStoreWriteEntry(const Certificate &_cert)
0930         : type(TypeCertificate)
0931         , cert(_cert)
0932     {
0933     }
0934 
0935     KeyStoreWriteEntry(const CRL &_crl)
0936         : type(TypeCRL)
0937         , crl(_crl)
0938     {
0939     }
0940 
0941     KeyStoreWriteEntry(const PGPKey &_pgpKey)
0942         : type(TypePGPKey)
0943         , pgpKey(_pgpKey)
0944     {
0945     }
0946 };
0947 
0948 class KeyStoreOperation : public QThread
0949 {
0950     Q_OBJECT
0951 public:
0952     enum Type
0953     {
0954         EntryList,
0955         WriteEntry,
0956         RemoveEntry
0957     };
0958 
0959     Type type;
0960     int  trackerId;
0961 
0962     KeyStoreWriteEntry   wentry;    // in: WriteEntry
0963     QList<KeyStoreEntry> entryList; // out: EntryList
0964     QString              entryId;   // in: RemoveEntry, out: WriteEntry
0965     bool                 success;   // out: RemoveEntry
0966 
0967     KeyStoreOperation(QObject *parent = nullptr)
0968         : QThread(parent)
0969     {
0970     }
0971 
0972     ~KeyStoreOperation() override
0973     {
0974         wait();
0975     }
0976 
0977 protected:
0978     void run() override
0979     {
0980         if (type == EntryList)
0981             entryList = trackercall("entryList", QVariantList() << trackerId).value<QList<KeyStoreEntry>>();
0982         else if (type == WriteEntry) {
0983             QVariant arg;
0984             if (wentry.type == KeyStoreWriteEntry::TypeKeyBundle)
0985                 arg = QVariant::fromValue<KeyBundle>(wentry.keyBundle);
0986             else if (wentry.type == KeyStoreWriteEntry::TypeCertificate)
0987                 arg = QVariant::fromValue<Certificate>(wentry.cert);
0988             else if (wentry.type == KeyStoreWriteEntry::TypeCRL)
0989                 arg = QVariant::fromValue<CRL>(wentry.crl);
0990             else if (wentry.type == KeyStoreWriteEntry::TypePGPKey)
0991                 arg = QVariant::fromValue<PGPKey>(wentry.pgpKey);
0992 
0993             // note: each variant in the argument list is resolved
0994             //   to its native type.  so even though it looks like
0995             //   we're attempting to call a method named
0996             //   writeEntry(QString,QVariant), we're actually
0997             //   calling one of many possible methods, such as
0998             //   writeEntry(QString,PGPKey) or
0999             //   writeEntry(QString,Certificate), etc, depending
1000             //   on the type of object we put in the variant.
1001             entryId = trackercall("writeEntry", QVariantList() << trackerId << arg).toString();
1002         } else // RemoveEntry
1003         {
1004             success = trackercall("removeEntry", QVariantList() << trackerId << entryId).toBool();
1005         }
1006     }
1007 };
1008 
1009 class KeyStorePrivate : public QObject
1010 {
1011     Q_OBJECT
1012 public:
1013     KeyStore                  *q;
1014     KeyStoreManager           *ksm;
1015     int                        trackerId;
1016     KeyStoreTracker::Item      item;
1017     bool                       async;
1018     bool                       need_update;
1019     QList<KeyStoreEntry>       latestEntryList;
1020     QList<KeyStoreOperation *> ops;
1021 
1022     KeyStorePrivate(KeyStore *_q)
1023         : QObject(_q)
1024         , q(_q)
1025         , async(false)
1026     {
1027     }
1028 
1029     ~KeyStorePrivate() override
1030     {
1031         qDeleteAll(ops);
1032     }
1033 
1034     // implemented below, after KeyStorePrivate is declared
1035     void                   reg();
1036     void                   unreg();
1037     KeyStoreTracker::Item *getItem(const QString &storeId);
1038     KeyStoreTracker::Item *getItem(int trackerId);
1039 
1040     void invalidate()
1041     {
1042         trackerId = -1;
1043         unreg();
1044     }
1045 
1046     bool have_entryList_op() const
1047     {
1048         foreach (KeyStoreOperation *op, ops) {
1049             if (op->type == KeyStoreOperation::EntryList)
1050                 return true;
1051         }
1052         return false;
1053     }
1054 
1055     void handle_updated()
1056     {
1057         if (async) {
1058             if (!have_entryList_op())
1059                 async_entryList();
1060             else
1061                 need_update = true;
1062         } else
1063             emit q->updated();
1064     }
1065 
1066     void async_entryList()
1067     {
1068         KeyStoreOperation *op = new KeyStoreOperation(this);
1069         // use queued for signal-safety
1070         connect(op, &KeyStoreOperation::finished, this, &KeyStorePrivate::op_finished, Qt::QueuedConnection);
1071         op->type      = KeyStoreOperation::EntryList;
1072         op->trackerId = trackerId;
1073         ops += op;
1074         op->start();
1075     }
1076 
1077     void async_writeEntry(const KeyStoreWriteEntry &wentry)
1078     {
1079         KeyStoreOperation *op = new KeyStoreOperation(this);
1080         // use queued for signal-safety
1081         connect(op, &KeyStoreOperation::finished, this, &KeyStorePrivate::op_finished, Qt::QueuedConnection);
1082         op->type      = KeyStoreOperation::WriteEntry;
1083         op->trackerId = trackerId;
1084         op->wentry    = wentry;
1085         ops += op;
1086         op->start();
1087     }
1088 
1089     void async_removeEntry(const QString &entryId)
1090     {
1091         KeyStoreOperation *op = new KeyStoreOperation(this);
1092         // use queued for signal-safety
1093         connect(op, &KeyStoreOperation::finished, this, &KeyStorePrivate::op_finished, Qt::QueuedConnection);
1094         op->type      = KeyStoreOperation::RemoveEntry;
1095         op->trackerId = trackerId;
1096         op->entryId   = entryId;
1097         ops += op;
1098         op->start();
1099     }
1100 
1101 private Q_SLOTS:
1102     void op_finished()
1103     {
1104         KeyStoreOperation *op = (KeyStoreOperation *)sender();
1105 
1106         if (op->type == KeyStoreOperation::EntryList) {
1107             latestEntryList = op->entryList;
1108             ops.removeAll(op);
1109             delete op;
1110 
1111             if (need_update) {
1112                 need_update = false;
1113                 async_entryList();
1114             }
1115 
1116             emit q->updated();
1117         } else if (op->type == KeyStoreOperation::WriteEntry) {
1118             QString entryId = op->entryId;
1119             ops.removeAll(op);
1120             delete op;
1121 
1122             emit q->entryWritten(entryId);
1123         } else // RemoveEntry
1124         {
1125             bool success = op->success;
1126             ops.removeAll(op);
1127             delete op;
1128 
1129             emit q->entryRemoved(success);
1130         }
1131     }
1132 };
1133 
1134 KeyStore::KeyStore(const QString &id, KeyStoreManager *keyStoreManager)
1135     : QObject(keyStoreManager)
1136 {
1137     d      = new KeyStorePrivate(this);
1138     d->ksm = keyStoreManager;
1139 
1140     KeyStoreTracker::Item *i = d->getItem(id);
1141     if (i) {
1142         d->trackerId = i->trackerId;
1143         d->item      = *i;
1144         d->reg();
1145     } else
1146         d->trackerId = -1;
1147 }
1148 
1149 KeyStore::~KeyStore()
1150 {
1151     if (d->trackerId != -1)
1152         d->unreg();
1153     delete d;
1154 }
1155 
1156 bool KeyStore::isValid() const
1157 {
1158     return (d->getItem(d->trackerId) ? true : false);
1159 }
1160 
1161 KeyStore::Type KeyStore::type() const
1162 {
1163     return d->item.type;
1164 }
1165 
1166 QString KeyStore::name() const
1167 {
1168     return d->item.name;
1169 }
1170 
1171 QString KeyStore::id() const
1172 {
1173     return d->item.storeId;
1174 }
1175 
1176 bool KeyStore::isReadOnly() const
1177 {
1178     return d->item.isReadOnly;
1179 }
1180 
1181 void KeyStore::startAsynchronousMode()
1182 {
1183     if (d->async)
1184         return;
1185 
1186     d->async = true;
1187 
1188     // initial entrylist
1189     d->need_update = false;
1190     d->async_entryList();
1191 }
1192 
1193 QList<KeyStoreEntry> KeyStore::entryList() const
1194 {
1195     if (d->async)
1196         return d->latestEntryList;
1197 
1198     if (d->trackerId == -1)
1199         return QList<KeyStoreEntry>();
1200     return trackercall("entryList", QVariantList() << d->trackerId).value<QList<KeyStoreEntry>>();
1201 }
1202 
1203 bool KeyStore::holdsTrustedCertificates() const
1204 {
1205     QList<KeyStoreEntry::Type> list;
1206     if (d->trackerId == -1)
1207         return false;
1208     list = trackercall("entryTypes", QVariantList() << d->trackerId).value<QList<KeyStoreEntry::Type>>();
1209     if (list.contains(KeyStoreEntry::TypeCertificate) || list.contains(KeyStoreEntry::TypeCRL))
1210         return true;
1211     return false;
1212 }
1213 
1214 bool KeyStore::holdsIdentities() const
1215 {
1216     QList<KeyStoreEntry::Type> list;
1217     if (d->trackerId == -1)
1218         return false;
1219     list = trackercall("entryTypes", QVariantList() << d->trackerId).value<QList<KeyStoreEntry::Type>>();
1220     if (list.contains(KeyStoreEntry::TypeKeyBundle) || list.contains(KeyStoreEntry::TypePGPSecretKey))
1221         return true;
1222     return false;
1223 }
1224 
1225 bool KeyStore::holdsPGPPublicKeys() const
1226 {
1227     QList<KeyStoreEntry::Type> list;
1228     if (d->trackerId == -1)
1229         return false;
1230     list = trackercall("entryTypes", QVariantList() << d->trackerId).value<QList<KeyStoreEntry::Type>>();
1231     if (list.contains(KeyStoreEntry::TypePGPPublicKey))
1232         return true;
1233     return false;
1234 }
1235 
1236 QString KeyStore::writeEntry(const KeyBundle &kb)
1237 {
1238     if (d->async) {
1239         d->async_writeEntry(KeyStoreWriteEntry(kb));
1240         return QString();
1241     } else {
1242         const auto arg = QVariant::fromValue<KeyBundle>(kb);
1243         return trackercall("writeEntry", QVariantList() << d->trackerId << arg).toString();
1244     }
1245 }
1246 
1247 QString KeyStore::writeEntry(const Certificate &cert)
1248 {
1249     if (d->async) {
1250         d->async_writeEntry(KeyStoreWriteEntry(cert));
1251         return QString();
1252     } else {
1253         const auto arg = QVariant::fromValue<Certificate>(cert);
1254         return trackercall("writeEntry", QVariantList() << d->trackerId << arg).toString();
1255     }
1256 }
1257 
1258 QString KeyStore::writeEntry(const CRL &crl)
1259 {
1260     if (d->async) {
1261         d->async_writeEntry(KeyStoreWriteEntry(crl));
1262         return QString();
1263     } else {
1264         const auto arg = QVariant::fromValue<CRL>(crl);
1265         return trackercall("writeEntry", QVariantList() << d->trackerId << arg).toString();
1266     }
1267 }
1268 
1269 QString KeyStore::writeEntry(const PGPKey &key)
1270 {
1271     if (d->async) {
1272         d->async_writeEntry(KeyStoreWriteEntry(key));
1273         return QString();
1274     } else {
1275         const auto arg = QVariant::fromValue<PGPKey>(key);
1276         return trackercall("writeEntry", QVariantList() << d->trackerId << arg).toString();
1277     }
1278 }
1279 
1280 bool KeyStore::removeEntry(const QString &id)
1281 {
1282     if (d->async) {
1283         d->async_removeEntry(id);
1284         return false;
1285     } else {
1286         return trackercall("removeEntry", QVariantList() << d->trackerId << id).toBool();
1287     }
1288 }
1289 
1290 //----------------------------------------------------------------------------
1291 // KeyStoreManager
1292 //----------------------------------------------------------------------------
1293 static void ensure_init()
1294 {
1295     QMutexLocker locker(ksm_mutex());
1296     if (!g_ksm)
1297         g_ksm = new KeyStoreManagerGlobal;
1298 }
1299 
1300 // static functions
1301 void KeyStoreManager::start()
1302 {
1303     ensure_init();
1304     QMetaObject::invokeMethod(KeyStoreTracker::instance(), "start", Qt::QueuedConnection);
1305     trackercall("spinEventLoop");
1306 }
1307 
1308 void KeyStoreManager::start(const QString &provider)
1309 {
1310     ensure_init();
1311     QMetaObject::invokeMethod(KeyStoreTracker::instance(), "start", Qt::QueuedConnection, Q_ARG(QString, provider));
1312     trackercall("spinEventLoop");
1313 }
1314 
1315 QString KeyStoreManager::diagnosticText()
1316 {
1317     ensure_init();
1318 
1319     // spin one event cycle in the tracker, to receive any pending text.
1320     //   note that since trackercall also goes through the eventloop,
1321     //   this may end up doing two rounds.  probably no big deal.
1322     trackercall("spinEventLoop");
1323 
1324     return KeyStoreTracker::instance()->getDText();
1325 }
1326 
1327 void KeyStoreManager::clearDiagnosticText()
1328 {
1329     ensure_init();
1330     KeyStoreTracker::instance()->clearDText();
1331 }
1332 
1333 void KeyStoreManager::scan()
1334 {
1335     ensure_init();
1336     QMetaObject::invokeMethod(KeyStoreTracker::instance(), "scan", Qt::QueuedConnection);
1337 }
1338 
1339 void KeyStoreManager::shutdown()
1340 {
1341     QMutexLocker locker(ksm_mutex());
1342     delete g_ksm;
1343     g_ksm = nullptr;
1344 }
1345 
1346 // object
1347 class KeyStoreManagerPrivate : public QObject
1348 {
1349     Q_OBJECT
1350 public:
1351     KeyStoreManager *q;
1352 
1353     QMutex                       m;
1354     QWaitCondition               w;
1355     bool                         busy;
1356     QList<KeyStoreTracker::Item> items;
1357     bool                         pending, waiting;
1358 
1359     QMultiHash<int, KeyStore *> keyStoreForTrackerId;
1360     QHash<KeyStore *, int>      trackerIdForKeyStore;
1361 
1362     KeyStoreManagerPrivate(KeyStoreManager *_q)
1363         : QObject(_q)
1364         , q(_q)
1365     {
1366         pending = false;
1367         waiting = false;
1368     }
1369 
1370     ~KeyStoreManagerPrivate() override
1371     {
1372         // invalidate registered keystores
1373         QList<KeyStore *>              list;
1374         QHashIterator<KeyStore *, int> it(trackerIdForKeyStore);
1375         while (it.hasNext()) {
1376             it.next();
1377             list += it.key();
1378         }
1379         foreach (KeyStore *ks, list)
1380             ks->d->invalidate();
1381     }
1382 
1383     // for keystore
1384     void reg(KeyStore *ks, int trackerId)
1385     {
1386         keyStoreForTrackerId.insert(trackerId, ks);
1387         trackerIdForKeyStore.insert(ks, trackerId);
1388     }
1389 
1390     void unreg(KeyStore *ks)
1391     {
1392         int trackerId = trackerIdForKeyStore.take(ks);
1393 
1394         // this is the only way I know to remove one item from a multihash
1395         QList<KeyStore *> vals = keyStoreForTrackerId.values(trackerId);
1396         keyStoreForTrackerId.remove(trackerId);
1397         vals.removeAll(ks);
1398         foreach (KeyStore *i, vals)
1399             keyStoreForTrackerId.insert(trackerId, i);
1400     }
1401 
1402     KeyStoreTracker::Item *getItem(const QString &storeId)
1403     {
1404         for (int n = 0; n < items.count(); ++n) {
1405             KeyStoreTracker::Item *i = &items[n];
1406             if (i->storeId == storeId)
1407                 return i;
1408         }
1409         return nullptr;
1410     }
1411 
1412     KeyStoreTracker::Item *getItem(int trackerId)
1413     {
1414         for (int n = 0; n < items.count(); ++n) {
1415             KeyStoreTracker::Item *i = &items[n];
1416             if (i->trackerId == trackerId)
1417                 return i;
1418         }
1419         return nullptr;
1420     }
1421 
1422     void do_update()
1423     {
1424         // ksm doesn't have reset or state changes so we can
1425         //   use QPointer here for full SS.
1426         QPointer<QObject> self(this);
1427 
1428         const bool                         newbusy  = KeyStoreTracker::instance()->isBusy();
1429         const QList<KeyStoreTracker::Item> newitems = KeyStoreTracker::instance()->getItems();
1430 
1431         if (!busy && newbusy) {
1432             emit q->busyStarted();
1433             if (!self)
1434                 return;
1435         }
1436         if (busy && !newbusy) {
1437             emit q->busyFinished();
1438             if (!self)
1439                 return;
1440         }
1441 
1442         QStringList here;
1443         QList<int>  changed;
1444         QList<int>  gone;
1445 
1446         // removed
1447         for (int n = 0; n < items.count(); ++n) {
1448             KeyStoreTracker::Item &i     = items[n];
1449             bool                   found = false;
1450             for (int k = 0; k < newitems.count(); ++k) {
1451                 if (i.trackerId == newitems[k].trackerId) {
1452                     found = true;
1453                     break;
1454                 }
1455             }
1456             if (!found)
1457                 gone += i.trackerId;
1458         }
1459 
1460         // changed
1461         for (int n = 0; n < items.count(); ++n) {
1462             KeyStoreTracker::Item &i = items[n];
1463             for (int k = 0; k < newitems.count(); ++k) {
1464                 if (i.trackerId == newitems[k].trackerId) {
1465                     if (i.updateCount < newitems[k].updateCount)
1466                         changed += i.trackerId;
1467                     break;
1468                 }
1469             }
1470         }
1471 
1472         // added
1473         for (int n = 0; n < newitems.count(); ++n) {
1474             const KeyStoreTracker::Item &i     = newitems[n];
1475             bool                         found = false;
1476             for (int k = 0; k < items.count(); ++k) {
1477                 if (i.trackerId == items[k].trackerId) {
1478                     found = true;
1479                     break;
1480                 }
1481             }
1482             if (!found)
1483                 here += i.storeId;
1484         }
1485 
1486         busy  = newbusy;
1487         items = newitems;
1488 
1489         // signals
1490         foreach (int trackerId, gone) {
1491             KeyStore *ks = keyStoreForTrackerId.value(trackerId);
1492             if (ks) {
1493                 ks->d->invalidate();
1494                 emit ks->unavailable();
1495                 if (!self)
1496                     return;
1497             }
1498         }
1499 
1500         foreach (int trackerId, changed) {
1501             KeyStore *ks = keyStoreForTrackerId.value(trackerId);
1502             if (ks) {
1503                 ks->d->handle_updated();
1504                 if (!self)
1505                     return;
1506             }
1507         }
1508 
1509         foreach (const QString &storeId, here) {
1510             emit q->keyStoreAvailable(storeId);
1511             if (!self)
1512                 return;
1513         }
1514     }
1515 
1516 public Q_SLOTS:
1517     void tracker_updated()
1518     {
1519         QCA_logTextMessage(QString::asprintf("keystore: %p: tracker_updated start", q), Logger::Information);
1520 
1521         QMutexLocker locker(&m);
1522         if (!pending) {
1523             QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
1524             pending = true;
1525         }
1526         if (waiting && !KeyStoreTracker::instance()->isBusy()) {
1527             busy  = false;
1528             items = KeyStoreTracker::instance()->getItems();
1529             w.wakeOne();
1530         }
1531 
1532         QCA_logTextMessage(QString::asprintf("keystore: %p: tracker_updated end", q), Logger::Information);
1533     }
1534 
1535     void update()
1536     {
1537         m.lock();
1538         pending = false;
1539         m.unlock();
1540 
1541         do_update();
1542     }
1543 };
1544 
1545 // from KeyStoreTracker
1546 void KeyStoreTracker::addTarget(KeyStoreManagerPrivate *ksm)
1547 {
1548     QMutexLocker locker(&updateMutex);
1549     connect(this, &KeyStoreTracker::updated, ksm, &KeyStoreManagerPrivate::tracker_updated, Qt::DirectConnection);
1550 }
1551 
1552 // from KeyStorePrivate
1553 void KeyStorePrivate::reg()
1554 {
1555     ksm->d->reg(q, trackerId);
1556 }
1557 
1558 void KeyStorePrivate::unreg()
1559 {
1560     ksm->d->unreg(q);
1561 }
1562 
1563 KeyStoreTracker::Item *KeyStorePrivate::getItem(const QString &storeId)
1564 {
1565     return ksm->d->getItem(storeId);
1566 }
1567 
1568 KeyStoreTracker::Item *KeyStorePrivate::getItem(int trackerId)
1569 {
1570     return ksm->d->getItem(trackerId);
1571 }
1572 
1573 KeyStoreManager::KeyStoreManager(QObject *parent)
1574     : QObject(parent)
1575 {
1576     ensure_init();
1577     d = new KeyStoreManagerPrivate(this);
1578     KeyStoreTracker::instance()->addTarget(d);
1579     sync();
1580 }
1581 
1582 KeyStoreManager::~KeyStoreManager()
1583 {
1584     Q_ASSERT(KeyStoreTracker::instance());
1585     KeyStoreTracker::instance()->removeTarget(d);
1586     delete d;
1587 }
1588 
1589 bool KeyStoreManager::isBusy() const
1590 {
1591     return d->busy;
1592 }
1593 
1594 void KeyStoreManager::waitForBusyFinished()
1595 {
1596     d->m.lock();
1597     d->busy = KeyStoreTracker::instance()->isBusy();
1598     if (d->busy) {
1599         d->waiting = true;
1600         d->w.wait(&d->m);
1601         d->waiting = false;
1602     }
1603     d->m.unlock();
1604 }
1605 
1606 QStringList KeyStoreManager::keyStores() const
1607 {
1608     QStringList out;
1609     for (int n = 0; n < d->items.count(); ++n)
1610         out += d->items[n].storeId;
1611     return out;
1612 }
1613 
1614 void KeyStoreManager::sync()
1615 {
1616     d->busy  = KeyStoreTracker::instance()->isBusy();
1617     d->items = KeyStoreTracker::instance()->getItems();
1618 }
1619 
1620 //----------------------------------------------------------------------------
1621 // KeyStoreInfo
1622 //----------------------------------------------------------------------------
1623 class KeyStoreInfo::Private : public QSharedData
1624 {
1625 public:
1626     KeyStore::Type type;
1627     QString        id, name;
1628 };
1629 
1630 KeyStoreInfo::KeyStoreInfo()
1631 {
1632 }
1633 
1634 KeyStoreInfo::KeyStoreInfo(KeyStore::Type type, const QString &id, const QString &name)
1635     : d(new Private)
1636 {
1637     d->type = type;
1638     d->id   = id;
1639     d->name = name;
1640 }
1641 
1642 KeyStoreInfo::KeyStoreInfo(const KeyStoreInfo &from)
1643     : d(from.d)
1644 {
1645 }
1646 
1647 KeyStoreInfo::~KeyStoreInfo()
1648 {
1649 }
1650 
1651 KeyStoreInfo &KeyStoreInfo::operator=(const KeyStoreInfo &from)
1652 {
1653     d = from.d;
1654     return *this;
1655 }
1656 
1657 bool KeyStoreInfo::isNull() const
1658 {
1659     return (d ? false : true);
1660 }
1661 
1662 KeyStore::Type KeyStoreInfo::type() const
1663 {
1664     return d->type;
1665 }
1666 
1667 QString KeyStoreInfo::id() const
1668 {
1669     return d->id;
1670 }
1671 
1672 QString KeyStoreInfo::name() const
1673 {
1674     return d->name;
1675 }
1676 
1677 }
1678 
1679 #include "qca_keystore.moc"