1/*
2 * Copyright (C) 2003-2008 Justin Karneges <justin@affinix.com>
3 * Copyright (C) 2004,2005 Brad Hards <bradh@frogmouth.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 *
20 */
21
22#include "qca_keystore.h"
23
24#include <QAbstractEventDispatcher>
25#include <QCoreApplication>
26#include <QMutex>
27#include <QPointer>
28#include <QSet>
29#include <QWaitCondition>
30
31#include <cstdio> // fprintf
32#include <cstdlib> // abort
33
34#include "qcaprovider.h"
35
36Q_DECLARE_METATYPE(QCA::KeyStoreEntry)
37Q_DECLARE_METATYPE(QList<QCA::KeyStoreEntry>)
38Q_DECLARE_METATYPE(QList<QCA::KeyStoreEntry::Type>)
39Q_DECLARE_METATYPE(QCA::KeyBundle)
40Q_DECLARE_METATYPE(QCA::Certificate)
41Q_DECLARE_METATYPE(QCA::CRL)
42Q_DECLARE_METATYPE(QCA::PGPKey)
43
44namespace QCA {
45
46Provider::Context *getContext(const QString &type, Provider *p);
47
48// from qca_plugin.cpp
49QString truncate_log(const QString &in, int size);
50
51/*
52 How this stuff works:
53
54 KeyStoreListContext is queried for a list of store context ids. A signal
55 is used to indicate when the list may have changed, so polling for changes
56 is not necessary. Context ids change for every new presence of a store.
57 Even if a user removes and inserts the same smart card device, which has
58 the same storeId, the context id will ALWAYS be different. If a previously
59 known context id is missing from a later queried list, then it means the
60 associated store is unavailable. It is recommended that the provider just
61 use a counter for the contextId, incrementing the value anytime a new
62 context is made.
63
64 KeyStoreTracker manages all of the keystore stuff, and exists in its own
65 thread (called the tracker thread). All of the KeyStoreListContext
66 objects exist in the tracker thread.
67*/
68
69/*
70 scenarios to handle:
71 - ksm.start shouldn't block
72 - keystore in available list, but gone by the time it is requested
73 - keystore is unavailable during a call to a keystoreentry method
74 - keystore/keystoreentry methods called simultaneously from different threads
75 - and of course, objects from keystores should work, despite being created
76 in the keystore thread
77*/
78
79//----------------------------------------------------------------------------
80// KeyStoreTracker
81//----------------------------------------------------------------------------
82static int tracker_id_at = 0;
83
84class KeyStoreTracker : public QObject
85{
86 Q_OBJECT
87public:
88 static KeyStoreTracker *self;
89
90 class Item
91 {
92 public:
93 // combine keystore owner and contextid into a single id
94 int trackerId;
95
96 // number of times the keystore has been updated
97 int updateCount;
98
99 // keystore context
100 KeyStoreListContext *owner;
101 int storeContextId;
102
103 // properties
104 QString storeId;
105 QString name;
106 KeyStore::Type type;
107 bool isReadOnly;
108
109 Item()
110 : trackerId(-1)
111 , updateCount(0)
112 , owner(nullptr)
113 , storeContextId(-1)
114 , storeId(QLatin1String(""))
115 , name(QLatin1String(""))
116 , type(KeyStore::System)
117 , isReadOnly(false)
118 {
119 }
120 };
121
122 QMutex m;
123 QSet<KeyStoreListContext *> sources;
124 QSet<KeyStoreListContext *> busySources;
125 QList<Item> items;
126 QString dtext;
127 bool startedAll;
128 bool busy;
129
130 QMutex updateMutex;
131
132 KeyStoreTracker()
133 {
134 self = this;
135
136 qRegisterMetaType<KeyStoreEntry>();
137 qRegisterMetaType<QList<KeyStoreEntry>>();
138 qRegisterMetaType<QList<KeyStoreEntry::Type>>();
139 qRegisterMetaType<KeyBundle>();
140 qRegisterMetaType<Certificate>();
141 qRegisterMetaType<CRL>();
142 qRegisterMetaType<PGPKey>();
143
144 connect(sender: this, signal: &KeyStoreTracker::updated_p, context: this, slot: &KeyStoreTracker::updated_locked, type: Qt::QueuedConnection);
145
146 startedAll = false;
147 busy = true; // we start out busy
148 }
149
150 ~KeyStoreTracker() override
151 {
152 qDeleteAll(c: sources);
153 self = nullptr;
154 }
155
156 static KeyStoreTracker *instance()
157 {
158 return self;
159 }
160
161 // thread-safe
162 bool isBusy()
163 {
164 QMutexLocker locker(&m);
165 return busy;
166 }
167
168 // thread-safe
169 QList<Item> getItems()
170 {
171 QMutexLocker locker(&m);
172 return items;
173 }
174
175 // thread-safe
176 QString getDText()
177 {
178 QMutexLocker locker(&m);
179 return dtext;
180 }
181
182 // thread-safe
183 void clearDText()
184 {
185 QMutexLocker locker(&m);
186 dtext.clear();
187 }
188
189 // thread-safe
190 void addTarget(KeyStoreManagerPrivate *ksm);
191
192 // thread-safe
193 void removeTarget(QObject *ksm)
194 {
195 QMutexLocker locker(&updateMutex);
196 disconnect(receiver: ksm);
197 }
198
199public Q_SLOTS:
200 void spinEventLoop()
201 {
202 QAbstractEventDispatcher::instance()->processEvents(flags: QEventLoop::AllEvents);
203 }
204
205 void start()
206 {
207 // grab providers (and default)
208 ProviderList list = providers();
209 list.append(t: defaultProvider());
210
211 for (int n = 0; n < list.count(); ++n) {
212 Provider *p = list[n];
213 if (p->features().contains(QStringLiteral("keystorelist")) && !haveProviderSource(p))
214 startProvider(p);
215 }
216
217 startedAll = true;
218 }
219
220 void start(const QString &provider)
221 {
222 // grab providers (and default)
223 ProviderList list = providers();
224 list.append(t: defaultProvider());
225
226 Provider *p = nullptr;
227 for (int n = 0; n < list.count(); ++n) {
228 if (list[n]->name() == provider) {
229 p = list[n];
230 break;
231 }
232 }
233
234 if (p && p->features().contains(QStringLiteral("keystorelist")) && !haveProviderSource(p))
235 startProvider(p);
236 }
237
238 void scan()
239 {
240 if (startedAll)
241 start();
242 }
243
244 QList<QCA::KeyStoreEntry> entryList(int trackerId)
245 {
246 QList<KeyStoreEntry> out;
247 int at = findItem(trackerId);
248 if (at == -1)
249 return out;
250 Item &i = items[at];
251 const QList<KeyStoreEntryContext *> list = i.owner->entryList(id: i.storeContextId);
252 for (int n = 0; n < list.count(); ++n) {
253 KeyStoreEntry entry;
254 entry.change(c: list[n]);
255 out.append(t: entry);
256 }
257 return out;
258 }
259
260 QList<QCA::KeyStoreEntry::Type> entryTypes(int trackerId)
261 {
262 QList<KeyStoreEntry::Type> out;
263 int at = findItem(trackerId);
264 if (at == -1)
265 return out;
266 Item &i = items[at];
267 return i.owner->entryTypes(id: i.storeContextId);
268 }
269
270 // hack with void *
271 void *entry(const QString &storeId, const QString &entryId)
272 {
273 KeyStoreListContext *c = nullptr;
274 int contextId = -1;
275 m.lock();
276 foreach (const Item &i, items) {
277 if (i.storeId == storeId) {
278 c = i.owner;
279 contextId = i.storeContextId;
280 break;
281 }
282 }
283 m.unlock();
284 if (!c)
285 return nullptr;
286
287 return c->entry(id: contextId, entryId);
288 }
289
290 // hack with void *
291 void *entryPassive(const QString &serialized)
292 {
293 foreach (KeyStoreListContext *ksl, sources) {
294 // "is this yours?"
295 KeyStoreEntryContext *e = ksl->entryPassive(serialized);
296 if (e)
297 return e;
298 }
299 return nullptr;
300 }
301
302 QString writeEntry(int trackerId, const QVariant &v)
303 {
304 int at = findItem(trackerId);
305 if (at == -1)
306 return QString();
307 Item &i = items[at];
308 if (v.canConvert<KeyBundle>())
309 return i.owner->writeEntry(id: i.storeContextId, kb: v.value<KeyBundle>());
310 else if (v.canConvert<Certificate>())
311 return i.owner->writeEntry(id: i.storeContextId, cert: v.value<Certificate>());
312 else if (v.canConvert<CRL>())
313 return i.owner->writeEntry(id: i.storeContextId, crl: v.value<CRL>());
314 else if (v.canConvert<PGPKey>())
315 return i.owner->writeEntry(id: i.storeContextId, key: v.value<PGPKey>());
316 else
317 return QString();
318 }
319
320 QString writeEntry(int trackerId, const QCA::KeyBundle &v)
321 {
322 int at = findItem(trackerId);
323 if (at == -1)
324 return QString();
325 Item &i = items[at];
326
327 return i.owner->writeEntry(id: i.storeContextId, kb: v);
328 }
329
330 QString writeEntry(int trackerId, const QCA::Certificate &v)
331 {
332 int at = findItem(trackerId);
333 if (at == -1)
334 return QString();
335 Item &i = items[at];
336
337 return i.owner->writeEntry(id: i.storeContextId, cert: v);
338 }
339
340 QString writeEntry(int trackerId, const QCA::CRL &v)
341 {
342 int at = findItem(trackerId);
343 if (at == -1)
344 return QString();
345 Item &i = items[at];
346
347 return i.owner->writeEntry(id: i.storeContextId, crl: v);
348 }
349
350 QString writeEntry(int trackerId, const QCA::PGPKey &v)
351 {
352 int at = findItem(trackerId);
353 if (at == -1)
354 return QString();
355 Item &i = items[at];
356
357 return i.owner->writeEntry(id: i.storeContextId, key: v);
358 }
359
360 bool removeEntry(int trackerId, const QString &entryId)
361 {
362 int at = findItem(trackerId);
363 if (at == -1)
364 return false;
365 Item &i = items[at];
366 return i.owner->removeEntry(id: i.storeContextId, entryId);
367 }
368
369Q_SIGNALS:
370 // emit this when items or busy state changes
371 void updated();
372 void updated_p();
373
374private Q_SLOTS:
375 void updated_locked()
376 {
377 QMutexLocker locker(&updateMutex);
378 emit updated();
379 }
380
381private:
382 bool haveProviderSource(Provider *p) const
383 {
384 foreach (KeyStoreListContext *ksl, sources) {
385 if (ksl->provider() == p)
386 return true;
387 }
388 return false;
389 }
390
391 int findItem(int trackerId)
392 {
393 for (int n = 0; n < items.count(); ++n) {
394 if (items[n].trackerId == trackerId)
395 return n;
396 }
397 return -1;
398 }
399
400 void startProvider(Provider *p)
401 {
402 KeyStoreListContext *c = static_cast<KeyStoreListContext *>(getContext(QStringLiteral("keystorelist"), p));
403 if (!c)
404 return;
405
406 sources += c;
407 busySources += c;
408 connect(sender: c, signal: &KeyStoreListContext::busyStart, context: this, slot: &KeyStoreTracker::ksl_busyStart);
409 connect(sender: c, signal: &KeyStoreListContext::busyEnd, context: this, slot: &KeyStoreTracker::ksl_busyEnd);
410 connect(sender: c, signal: &KeyStoreListContext::updated, context: this, slot: &KeyStoreTracker::ksl_updated);
411 connect(sender: c, signal: &KeyStoreListContext::diagnosticText, context: this, slot: &KeyStoreTracker::ksl_diagnosticText);
412 connect(sender: c, signal: &KeyStoreListContext::storeUpdated, context: this, slot: &KeyStoreTracker::ksl_storeUpdated);
413 c->start();
414 c->setUpdatesEnabled(true);
415
416 QCA_logTextMessage(QStringLiteral("keystore: startProvider %1").arg(p->name()), Logger::Information);
417 }
418
419 bool updateStores(KeyStoreListContext *c)
420 {
421 bool changed = false;
422
423 QMutexLocker locker(&m);
424
425 const QList<int> keyStores = c->keyStores();
426
427 // remove any contexts that are gone
428 for (int n = 0; n < items.count(); ++n) {
429 if (items[n].owner == c && !keyStores.contains(t: items[n].storeContextId)) {
430 QCA_logTextMessage(QStringLiteral("keystore: updateStores remove %1").arg(items[n].storeContextId),
431 Logger::Information);
432
433 items.removeAt(i: n);
434 --n; // adjust position
435
436 changed = true;
437 }
438 }
439
440 // handle add/updates
441 foreach (int id, keyStores) {
442 // do we have it already?
443 int at = -1;
444 for (int n = 0; n < items.count(); ++n) {
445 if (items[n].owner == c && items[n].storeContextId == id) {
446 at = n;
447 break;
448 }
449 }
450
451 // if so, update it
452 if (at != -1) {
453 Item &i = items[at];
454
455 QString name = c->name(id);
456 bool isReadOnly = c->isReadOnly(id);
457 if (i.name != name || i.isReadOnly != isReadOnly) {
458 QCA_logTextMessage(QStringLiteral("keystore: updateStores update %1").arg(id), Logger::Information);
459 i.name = name;
460 i.isReadOnly = isReadOnly;
461 changed = true;
462 }
463 }
464 // otherwise, add it
465 else {
466 QCA_logTextMessage(QStringLiteral("keystore: updateStores add %1").arg(id), Logger::Information);
467
468 Item i;
469 i.trackerId = tracker_id_at++;
470 i.updateCount = 0;
471 i.owner = c;
472 i.storeContextId = id;
473 i.storeId = c->storeId(id);
474 i.name = c->name(id);
475 i.type = c->type(id);
476 i.isReadOnly = c->isReadOnly(id);
477 items += i;
478
479 changed = true;
480 }
481 }
482
483 return changed;
484 }
485
486private Q_SLOTS:
487 void ksl_busyStart()
488 {
489 KeyStoreListContext *c = (KeyStoreListContext *)sender();
490
491 QCA_logTextMessage(QStringLiteral("keystore: ksl_busyStart %1").arg(c->provider()->name()),
492 Logger::Information);
493
494 if (!busySources.contains(value: c)) {
495 busySources += c;
496
497 QCA_logTextMessage(QStringLiteral("keystore: emitting updated"), Logger::Information);
498 emit updated_p();
499 }
500 }
501
502 void ksl_busyEnd()
503 {
504 KeyStoreListContext *c = (KeyStoreListContext *)sender();
505
506 QCA_logTextMessage(QStringLiteral("keystore: ksl_busyEnd %1").arg(c->provider()->name()), Logger::Information);
507
508 busySources.remove(value: c);
509 bool changed = updateStores(c);
510 const bool any_busy = !busySources.isEmpty();
511
512 if (!any_busy) {
513 m.lock();
514 busy = false;
515 m.unlock();
516 }
517
518 if (!any_busy || changed) {
519 QCA_logTextMessage(QStringLiteral("keystore: emitting updated"), Logger::Information);
520 emit updated_p();
521 }
522 }
523
524 void ksl_updated()
525 {
526 KeyStoreListContext *c = (KeyStoreListContext *)sender();
527
528 QCA_logTextMessage(QStringLiteral("keystore: ksl_updated %1").arg(c->provider()->name()), Logger::Information);
529
530 bool changed = updateStores(c);
531 if (changed) {
532 QCA_logTextMessage(QStringLiteral("keystore: emitting updated"), Logger::Information);
533 emit updated_p();
534 }
535 }
536
537 void ksl_diagnosticText(const QString &str)
538 {
539 QMutexLocker locker(&m);
540 dtext += str;
541 dtext = truncate_log(in: dtext, size: 100000);
542 }
543
544 void ksl_storeUpdated(int id)
545 {
546 KeyStoreListContext *c = (KeyStoreListContext *)sender();
547
548 QCA_logTextMessage(
549 QStringLiteral("keystore: ksl_storeUpdated %1 %2").arg(c->provider()->name(), QString::number(id)),
550 Logger::Information);
551
552 QMutexLocker locker(&m);
553 for (int n = 0; n < items.count(); ++n) {
554 Item &i = items[n];
555 if (i.owner == c && i.storeContextId == id) {
556 ++i.updateCount;
557
558 QCA_logTextMessage(
559 QStringLiteral("keystore: %1 updateCount = %2").arg(i.name, QString::number(i.updateCount)),
560 Logger::Information);
561
562 QCA_logTextMessage(QStringLiteral("keystore: emitting updated"), Logger::Information);
563 emit updated_p();
564 return;
565 }
566 }
567 }
568};
569
570KeyStoreTracker *KeyStoreTracker::self = nullptr;
571
572//----------------------------------------------------------------------------
573// KeyStoreThread
574//----------------------------------------------------------------------------
575class KeyStoreThread : public SyncThread
576{
577 Q_OBJECT
578public:
579 KeyStoreTracker *tracker;
580 QMutex call_mutex;
581
582 KeyStoreThread(QObject *parent = nullptr)
583 : SyncThread(parent)
584 {
585 }
586
587 ~KeyStoreThread() override
588 {
589 stop();
590 }
591
592 void atStart() override
593 {
594 tracker = new KeyStoreTracker;
595 }
596
597 void atEnd() override
598 {
599 delete tracker;
600 }
601};
602
603//----------------------------------------------------------------------------
604// KeyStoreGlobal
605//----------------------------------------------------------------------------
606class KeyStoreManagerGlobal;
607
608Q_GLOBAL_STATIC(QMutex, ksm_mutex)
609static KeyStoreManagerGlobal *g_ksm = nullptr;
610
611class KeyStoreManagerGlobal
612{
613public:
614 KeyStoreThread *thread;
615
616 KeyStoreManagerGlobal()
617 {
618 thread = new KeyStoreThread;
619 thread->moveToThread(thread: QCoreApplication::instance()->thread());
620 thread->start();
621 }
622
623 ~KeyStoreManagerGlobal()
624 {
625 delete thread;
626 }
627
628 KeyStoreManagerGlobal(const KeyStoreManagerGlobal &) = delete;
629 KeyStoreManagerGlobal &operator=(const KeyStoreManagerGlobal &) = delete;
630};
631
632// this function is thread-safe
633static QVariant trackercall(const char *method, const QVariantList &args = QVariantList())
634{
635 QVariant ret;
636 bool ok;
637
638 g_ksm->thread->call_mutex.lock();
639 ret = g_ksm->thread->call(obj: KeyStoreTracker::instance(), method, args, ok: &ok);
640 g_ksm->thread->call_mutex.unlock();
641
642 Q_ASSERT(ok);
643 if (!ok) {
644 fprintf(stderr, format: "QCA: KeyStoreTracker call [%s] failed.\n", method);
645 abort();
646 return QVariant();
647 }
648 return ret;
649}
650
651//----------------------------------------------------------------------------
652// KeyStoreEntry
653//----------------------------------------------------------------------------
654class KeyStoreEntry::Private
655{
656public:
657 bool accessible;
658
659 Private()
660 {
661 accessible = false;
662 }
663};
664
665KeyStoreEntry::KeyStoreEntry()
666 : d(new Private)
667{
668}
669
670KeyStoreEntry::KeyStoreEntry(const QString &serialized)
671 : d(new Private)
672{
673 *this = fromString(serialized);
674}
675
676KeyStoreEntry::KeyStoreEntry(const KeyStoreEntry &from)
677 : Algorithm(from)
678 , d(new Private(*from.d))
679{
680}
681
682KeyStoreEntry::~KeyStoreEntry()
683{
684 delete d;
685}
686
687KeyStoreEntry &KeyStoreEntry::operator=(const KeyStoreEntry &from)
688{
689 Algorithm::operator=(from);
690 *d = *from.d;
691 return *this;
692}
693
694bool KeyStoreEntry::isNull() const
695{
696 return (!context() ? true : false);
697}
698
699bool KeyStoreEntry::isAvailable() const
700{
701 return static_cast<const KeyStoreEntryContext *>(context())->isAvailable();
702}
703
704bool KeyStoreEntry::isAccessible() const
705{
706 return d->accessible;
707}
708
709KeyStoreEntry::Type KeyStoreEntry::type() const
710{
711 return static_cast<const KeyStoreEntryContext *>(context())->type();
712}
713
714QString KeyStoreEntry::name() const
715{
716 return static_cast<const KeyStoreEntryContext *>(context())->name();
717}
718
719QString KeyStoreEntry::id() const
720{
721 return static_cast<const KeyStoreEntryContext *>(context())->id();
722}
723
724QString KeyStoreEntry::storeName() const
725{
726 return static_cast<const KeyStoreEntryContext *>(context())->storeName();
727}
728
729QString KeyStoreEntry::storeId() const
730{
731 return static_cast<const KeyStoreEntryContext *>(context())->storeId();
732}
733
734QString KeyStoreEntry::toString() const
735{
736 return static_cast<const KeyStoreEntryContext *>(context())->serialize();
737}
738
739KeyStoreEntry KeyStoreEntry::fromString(const QString &serialized)
740{
741 KeyStoreEntry e;
742 KeyStoreEntryContext *c = (KeyStoreEntryContext *)KeyStoreTracker::instance()->entryPassive(serialized);
743 if (c)
744 e.change(c);
745 return e;
746}
747
748KeyBundle KeyStoreEntry::keyBundle() const
749{
750 return static_cast<const KeyStoreEntryContext *>(context())->keyBundle();
751}
752
753Certificate KeyStoreEntry::certificate() const
754{
755 return static_cast<const KeyStoreEntryContext *>(context())->certificate();
756}
757
758CRL KeyStoreEntry::crl() const
759{
760 return static_cast<const KeyStoreEntryContext *>(context())->crl();
761}
762
763PGPKey KeyStoreEntry::pgpSecretKey() const
764{
765 return static_cast<const KeyStoreEntryContext *>(context())->pgpSecretKey();
766}
767
768PGPKey KeyStoreEntry::pgpPublicKey() const
769{
770 return static_cast<const KeyStoreEntryContext *>(context())->pgpPublicKey();
771}
772
773bool KeyStoreEntry::ensureAvailable()
774{
775 const QString storeId = this->storeId();
776 const QString entryId = id();
777 KeyStoreEntryContext *c =
778 (KeyStoreEntryContext *)trackercall(method: "entry", args: QVariantList() << storeId << entryId).value<void *>();
779 if (c)
780 change(c);
781 return isAvailable();
782}
783
784bool KeyStoreEntry::ensureAccess()
785{
786 if (!ensureAvailable()) {
787 d->accessible = false;
788 return false;
789 }
790 const bool ok = static_cast<KeyStoreEntryContext *>(context())->ensureAccess();
791 d->accessible = ok;
792 return d->accessible;
793}
794
795//----------------------------------------------------------------------------
796// KeyStoreEntryWatcher
797//----------------------------------------------------------------------------
798class KeyStoreEntryWatcher::Private : public QObject
799{
800 Q_OBJECT
801public:
802 KeyStoreEntryWatcher *q;
803 KeyStoreManager ksm;
804 KeyStoreEntry entry;
805 QString storeId, entryId;
806 KeyStore *ks;
807 bool avail;
808
809 Private(KeyStoreEntryWatcher *_q)
810 : QObject(_q)
811 , q(_q)
812 , ksm(this)
813 {
814 ks = nullptr;
815 avail = false;
816 connect(sender: &ksm, signal: &KeyStoreManager::keyStoreAvailable, context: this, slot: &KeyStoreEntryWatcher::Private::ksm_available);
817 }
818
819 ~Private() override
820 {
821 delete ks;
822 }
823
824 void start()
825 {
826 const QStringList list = ksm.keyStores();
827 foreach (const QString &storeId, list)
828 ksm_available(storeId: storeId);
829 }
830
831private Q_SLOTS:
832 void ksm_available(const QString &_storeId)
833 {
834 // we only care about one store
835 if (_storeId == storeId) {
836 ks = new KeyStore(storeId, &ksm);
837 connect(sender: ks, signal: &KeyStore::updated, context: this, slot: &Private::ks_updated);
838 ks->startAsynchronousMode();
839 }
840 }
841
842 void ks_updated()
843 {
844 bool found = false;
845 const QList<KeyStoreEntry> list = ks->entryList();
846 foreach (const KeyStoreEntry &e, list) {
847 if (e.id() == entryId && e.isAvailable()) {
848 found = true;
849 if (!avail)
850 entry = e;
851 break;
852 }
853 }
854
855 if (found && !avail) {
856 avail = true;
857 emit q->available();
858 } else if (!found && avail) {
859 avail = false;
860 emit q->unavailable();
861 }
862 }
863
864 void ks_unavailable()
865 {
866 delete ks;
867 ks = nullptr;
868
869 if (avail) {
870 avail = false;
871 emit q->unavailable();
872 }
873 }
874};
875
876KeyStoreEntryWatcher::KeyStoreEntryWatcher(const KeyStoreEntry &e, QObject *parent)
877 : QObject(parent)
878{
879 d = new Private(this);
880 if (!e.isNull()) {
881 d->entry = e;
882 d->storeId = e.storeId();
883 d->entryId = e.id();
884 d->start();
885 }
886}
887
888KeyStoreEntryWatcher::~KeyStoreEntryWatcher()
889{
890 delete d;
891}
892
893KeyStoreEntry KeyStoreEntryWatcher::entry() const
894{
895 return d->entry;
896}
897
898//----------------------------------------------------------------------------
899// KeyStore
900//----------------------------------------------------------------------------
901// union thingy
902class KeyStoreWriteEntry
903{
904public:
905 enum Type
906 {
907 TypeKeyBundle,
908 TypeCertificate,
909 TypeCRL,
910 TypePGPKey
911 };
912
913 Type type;
914 KeyBundle keyBundle;
915 Certificate cert;
916 CRL crl;
917 PGPKey pgpKey;
918
919 KeyStoreWriteEntry()
920 {
921 }
922
923 KeyStoreWriteEntry(const KeyBundle &_keyBundle)
924 : type(TypeKeyBundle)
925 , keyBundle(_keyBundle)
926 {
927 }
928
929 KeyStoreWriteEntry(const Certificate &_cert)
930 : type(TypeCertificate)
931 , cert(_cert)
932 {
933 }
934
935 KeyStoreWriteEntry(const CRL &_crl)
936 : type(TypeCRL)
937 , crl(_crl)
938 {
939 }
940
941 KeyStoreWriteEntry(const PGPKey &_pgpKey)
942 : type(TypePGPKey)
943 , pgpKey(_pgpKey)
944 {
945 }
946};
947
948class KeyStoreOperation : public QThread
949{
950 Q_OBJECT
951public:
952 enum Type
953 {
954 EntryList,
955 WriteEntry,
956 RemoveEntry
957 };
958
959 Type type;
960 int trackerId;
961
962 KeyStoreWriteEntry wentry; // in: WriteEntry
963 QList<KeyStoreEntry> entryList; // out: EntryList
964 QString entryId; // in: RemoveEntry, out: WriteEntry
965 bool success; // out: RemoveEntry
966
967 KeyStoreOperation(QObject *parent = nullptr)
968 : QThread(parent)
969 {
970 }
971
972 ~KeyStoreOperation() override
973 {
974 wait();
975 }
976
977protected:
978 void run() override
979 {
980 if (type == EntryList)
981 entryList = trackercall(method: "entryList", args: QVariantList() << trackerId).value<QList<KeyStoreEntry>>();
982 else if (type == WriteEntry) {
983 QVariant arg;
984 if (wentry.type == KeyStoreWriteEntry::TypeKeyBundle)
985 arg = QVariant::fromValue<KeyBundle>(value: wentry.keyBundle);
986 else if (wentry.type == KeyStoreWriteEntry::TypeCertificate)
987 arg = QVariant::fromValue<Certificate>(value: wentry.cert);
988 else if (wentry.type == KeyStoreWriteEntry::TypeCRL)
989 arg = QVariant::fromValue<CRL>(value: wentry.crl);
990 else if (wentry.type == KeyStoreWriteEntry::TypePGPKey)
991 arg = QVariant::fromValue<PGPKey>(value: wentry.pgpKey);
992
993 // note: each variant in the argument list is resolved
994 // to its native type. so even though it looks like
995 // we're attempting to call a method named
996 // writeEntry(QString,QVariant), we're actually
997 // calling one of many possible methods, such as
998 // writeEntry(QString,PGPKey) or
999 // writeEntry(QString,Certificate), etc, depending
1000 // on the type of object we put in the variant.
1001 entryId = trackercall(method: "writeEntry", args: QVariantList() << trackerId << arg).toString();
1002 } else // RemoveEntry
1003 {
1004 success = trackercall(method: "removeEntry", args: QVariantList() << trackerId << entryId).toBool();
1005 }
1006 }
1007};
1008
1009class KeyStorePrivate : public QObject
1010{
1011 Q_OBJECT
1012public:
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(c: 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(sender: op, signal: &KeyStoreOperation::finished, context: this, slot: &KeyStorePrivate::op_finished, type: 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(sender: op, signal: &KeyStoreOperation::finished, context: this, slot: &KeyStorePrivate::op_finished, type: 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(sender: op, signal: &KeyStoreOperation::finished, context: this, slot: &KeyStorePrivate::op_finished, type: Qt::QueuedConnection);
1094 op->type = KeyStoreOperation::RemoveEntry;
1095 op->trackerId = trackerId;
1096 op->entryId = entryId;
1097 ops += op;
1098 op->start();
1099 }
1100
1101private 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(t: 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(t: op);
1120 delete op;
1121
1122 emit q->entryWritten(entryId);
1123 } else // RemoveEntry
1124 {
1125 bool success = op->success;
1126 ops.removeAll(t: op);
1127 delete op;
1128
1129 emit q->entryRemoved(success);
1130 }
1131 }
1132};
1133
1134KeyStore::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(storeId: id);
1141 if (i) {
1142 d->trackerId = i->trackerId;
1143 d->item = *i;
1144 d->reg();
1145 } else
1146 d->trackerId = -1;
1147}
1148
1149KeyStore::~KeyStore()
1150{
1151 if (d->trackerId != -1)
1152 d->unreg();
1153 delete d;
1154}
1155
1156bool KeyStore::isValid() const
1157{
1158 return (d->getItem(trackerId: d->trackerId) ? true : false);
1159}
1160
1161KeyStore::Type KeyStore::type() const
1162{
1163 return d->item.type;
1164}
1165
1166QString KeyStore::name() const
1167{
1168 return d->item.name;
1169}
1170
1171QString KeyStore::id() const
1172{
1173 return d->item.storeId;
1174}
1175
1176bool KeyStore::isReadOnly() const
1177{
1178 return d->item.isReadOnly;
1179}
1180
1181void 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
1193QList<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(method: "entryList", args: QVariantList() << d->trackerId).value<QList<KeyStoreEntry>>();
1201}
1202
1203bool KeyStore::holdsTrustedCertificates() const
1204{
1205 QList<KeyStoreEntry::Type> list;
1206 if (d->trackerId == -1)
1207 return false;
1208 list = trackercall(method: "entryTypes", args: QVariantList() << d->trackerId).value<QList<KeyStoreEntry::Type>>();
1209 if (list.contains(t: KeyStoreEntry::TypeCertificate) || list.contains(t: KeyStoreEntry::TypeCRL))
1210 return true;
1211 return false;
1212}
1213
1214bool KeyStore::holdsIdentities() const
1215{
1216 QList<KeyStoreEntry::Type> list;
1217 if (d->trackerId == -1)
1218 return false;
1219 list = trackercall(method: "entryTypes", args: QVariantList() << d->trackerId).value<QList<KeyStoreEntry::Type>>();
1220 if (list.contains(t: KeyStoreEntry::TypeKeyBundle) || list.contains(t: KeyStoreEntry::TypePGPSecretKey))
1221 return true;
1222 return false;
1223}
1224
1225bool KeyStore::holdsPGPPublicKeys() const
1226{
1227 QList<KeyStoreEntry::Type> list;
1228 if (d->trackerId == -1)
1229 return false;
1230 list = trackercall(method: "entryTypes", args: QVariantList() << d->trackerId).value<QList<KeyStoreEntry::Type>>();
1231 if (list.contains(t: KeyStoreEntry::TypePGPPublicKey))
1232 return true;
1233 return false;
1234}
1235
1236QString KeyStore::writeEntry(const KeyBundle &kb)
1237{
1238 if (d->async) {
1239 d->async_writeEntry(wentry: KeyStoreWriteEntry(kb));
1240 return QString();
1241 } else {
1242 const auto arg = QVariant::fromValue<KeyBundle>(value: kb);
1243 return trackercall(method: "writeEntry", args: QVariantList() << d->trackerId << arg).toString();
1244 }
1245}
1246
1247QString KeyStore::writeEntry(const Certificate &cert)
1248{
1249 if (d->async) {
1250 d->async_writeEntry(wentry: KeyStoreWriteEntry(cert));
1251 return QString();
1252 } else {
1253 const auto arg = QVariant::fromValue<Certificate>(value: cert);
1254 return trackercall(method: "writeEntry", args: QVariantList() << d->trackerId << arg).toString();
1255 }
1256}
1257
1258QString KeyStore::writeEntry(const CRL &crl)
1259{
1260 if (d->async) {
1261 d->async_writeEntry(wentry: KeyStoreWriteEntry(crl));
1262 return QString();
1263 } else {
1264 const auto arg = QVariant::fromValue<CRL>(value: crl);
1265 return trackercall(method: "writeEntry", args: QVariantList() << d->trackerId << arg).toString();
1266 }
1267}
1268
1269QString KeyStore::writeEntry(const PGPKey &key)
1270{
1271 if (d->async) {
1272 d->async_writeEntry(wentry: KeyStoreWriteEntry(key));
1273 return QString();
1274 } else {
1275 const auto arg = QVariant::fromValue<PGPKey>(value: key);
1276 return trackercall(method: "writeEntry", args: QVariantList() << d->trackerId << arg).toString();
1277 }
1278}
1279
1280bool KeyStore::removeEntry(const QString &id)
1281{
1282 if (d->async) {
1283 d->async_removeEntry(entryId: id);
1284 return false;
1285 } else {
1286 return trackercall(method: "removeEntry", args: QVariantList() << d->trackerId << id).toBool();
1287 }
1288}
1289
1290//----------------------------------------------------------------------------
1291// KeyStoreManager
1292//----------------------------------------------------------------------------
1293static void ensure_init()
1294{
1295 QMutexLocker locker(ksm_mutex());
1296 if (!g_ksm)
1297 g_ksm = new KeyStoreManagerGlobal;
1298}
1299
1300// static functions
1301void KeyStoreManager::start()
1302{
1303 ensure_init();
1304 QMetaObject::invokeMethod(obj: KeyStoreTracker::instance(), member: "start", c: Qt::QueuedConnection);
1305 trackercall(method: "spinEventLoop");
1306}
1307
1308void KeyStoreManager::start(const QString &provider)
1309{
1310 ensure_init();
1311 QMetaObject::invokeMethod(obj: KeyStoreTracker::instance(), member: "start", c: Qt::QueuedConnection, Q_ARG(QString, provider));
1312 trackercall(method: "spinEventLoop");
1313}
1314
1315QString 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(method: "spinEventLoop");
1323
1324 return KeyStoreTracker::instance()->getDText();
1325}
1326
1327void KeyStoreManager::clearDiagnosticText()
1328{
1329 ensure_init();
1330 KeyStoreTracker::instance()->clearDText();
1331}
1332
1333void KeyStoreManager::scan()
1334{
1335 ensure_init();
1336 QMetaObject::invokeMethod(obj: KeyStoreTracker::instance(), member: "scan", c: Qt::QueuedConnection);
1337}
1338
1339void KeyStoreManager::shutdown()
1340{
1341 QMutexLocker locker(ksm_mutex());
1342 delete g_ksm;
1343 g_ksm = nullptr;
1344}
1345
1346// object
1347class KeyStoreManagerPrivate : public QObject
1348{
1349 Q_OBJECT
1350public:
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(key: trackerId, value: ks);
1387 trackerIdForKeyStore.insert(key: ks, value: trackerId);
1388 }
1389
1390 void unreg(KeyStore *ks)
1391 {
1392 int trackerId = trackerIdForKeyStore.take(key: ks);
1393
1394 // this is the only way I know to remove one item from a multihash
1395 QList<KeyStore *> vals = keyStoreForTrackerId.values(key: trackerId);
1396 keyStoreForTrackerId.remove(key: trackerId);
1397 vals.removeAll(t: ks);
1398 foreach (KeyStore *i, vals)
1399 keyStoreForTrackerId.insert(key: trackerId, value: 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(key: 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(key: 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(id: storeId);
1511 if (!self)
1512 return;
1513 }
1514 }
1515
1516public 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(obj: this, member: "update", c: 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
1546void KeyStoreTracker::addTarget(KeyStoreManagerPrivate *ksm)
1547{
1548 QMutexLocker locker(&updateMutex);
1549 connect(sender: this, signal: &KeyStoreTracker::updated, context: ksm, slot: &KeyStoreManagerPrivate::tracker_updated, type: Qt::DirectConnection);
1550}
1551
1552// from KeyStorePrivate
1553void KeyStorePrivate::reg()
1554{
1555 ksm->d->reg(ks: q, trackerId);
1556}
1557
1558void KeyStorePrivate::unreg()
1559{
1560 ksm->d->unreg(ks: q);
1561}
1562
1563KeyStoreTracker::Item *KeyStorePrivate::getItem(const QString &storeId)
1564{
1565 return ksm->d->getItem(storeId);
1566}
1567
1568KeyStoreTracker::Item *KeyStorePrivate::getItem(int trackerId)
1569{
1570 return ksm->d->getItem(trackerId);
1571}
1572
1573KeyStoreManager::KeyStoreManager(QObject *parent)
1574 : QObject(parent)
1575{
1576 ensure_init();
1577 d = new KeyStoreManagerPrivate(this);
1578 KeyStoreTracker::instance()->addTarget(ksm: d);
1579 sync();
1580}
1581
1582KeyStoreManager::~KeyStoreManager()
1583{
1584 Q_ASSERT(KeyStoreTracker::instance());
1585 KeyStoreTracker::instance()->removeTarget(ksm: d);
1586 delete d;
1587}
1588
1589bool KeyStoreManager::isBusy() const
1590{
1591 return d->busy;
1592}
1593
1594void 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(lockedMutex: &d->m);
1601 d->waiting = false;
1602 }
1603 d->m.unlock();
1604}
1605
1606QStringList 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
1614void KeyStoreManager::sync()
1615{
1616 d->busy = KeyStoreTracker::instance()->isBusy();
1617 d->items = KeyStoreTracker::instance()->getItems();
1618}
1619
1620//----------------------------------------------------------------------------
1621// KeyStoreInfo
1622//----------------------------------------------------------------------------
1623class KeyStoreInfo::Private : public QSharedData
1624{
1625public:
1626 KeyStore::Type type;
1627 QString id, name;
1628};
1629
1630KeyStoreInfo::KeyStoreInfo()
1631{
1632}
1633
1634KeyStoreInfo::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
1642KeyStoreInfo::KeyStoreInfo(const KeyStoreInfo &from)
1643 : d(from.d)
1644{
1645}
1646
1647KeyStoreInfo::~KeyStoreInfo()
1648{
1649}
1650
1651KeyStoreInfo &KeyStoreInfo::operator=(const KeyStoreInfo &from)
1652{
1653 d = from.d;
1654 return *this;
1655}
1656
1657bool KeyStoreInfo::isNull() const
1658{
1659 return (d ? false : true);
1660}
1661
1662KeyStore::Type KeyStoreInfo::type() const
1663{
1664 return d->type;
1665}
1666
1667QString KeyStoreInfo::id() const
1668{
1669 return d->id;
1670}
1671
1672QString KeyStoreInfo::name() const
1673{
1674 return d->name;
1675}
1676
1677}
1678
1679#include "qca_keystore.moc"
1680

source code of qca/src/qca_keystore.cpp