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 | |
36 | Q_DECLARE_METATYPE(QCA::KeyStoreEntry) |
37 | Q_DECLARE_METATYPE(QList<QCA::KeyStoreEntry>) |
38 | Q_DECLARE_METATYPE(QList<QCA::KeyStoreEntry::Type>) |
39 | Q_DECLARE_METATYPE(QCA::KeyBundle) |
40 | Q_DECLARE_METATYPE(QCA::Certificate) |
41 | Q_DECLARE_METATYPE(QCA::CRL) |
42 | Q_DECLARE_METATYPE(QCA::PGPKey) |
43 | |
44 | namespace QCA { |
45 | |
46 | Provider::Context *getContext(const QString &type, Provider *p); |
47 | |
48 | // from qca_plugin.cpp |
49 | QString 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 | //---------------------------------------------------------------------------- |
82 | static int tracker_id_at = 0; |
83 | |
84 | class KeyStoreTracker : public QObject |
85 | { |
86 | Q_OBJECT |
87 | public: |
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 | |
199 | public 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 | |
369 | Q_SIGNALS: |
370 | // emit this when items or busy state changes |
371 | void updated(); |
372 | void updated_p(); |
373 | |
374 | private Q_SLOTS: |
375 | void updated_locked() |
376 | { |
377 | QMutexLocker locker(&updateMutex); |
378 | emit updated(); |
379 | } |
380 | |
381 | private: |
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 | |
486 | private 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 | |
570 | KeyStoreTracker *KeyStoreTracker::self = nullptr; |
571 | |
572 | //---------------------------------------------------------------------------- |
573 | // KeyStoreThread |
574 | //---------------------------------------------------------------------------- |
575 | class KeyStoreThread : public SyncThread |
576 | { |
577 | Q_OBJECT |
578 | public: |
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 | //---------------------------------------------------------------------------- |
606 | class KeyStoreManagerGlobal; |
607 | |
608 | Q_GLOBAL_STATIC(QMutex, ksm_mutex) |
609 | static KeyStoreManagerGlobal *g_ksm = nullptr; |
610 | |
611 | class KeyStoreManagerGlobal |
612 | { |
613 | public: |
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 |
633 | static 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 | //---------------------------------------------------------------------------- |
654 | class KeyStoreEntry::Private |
655 | { |
656 | public: |
657 | bool accessible; |
658 | |
659 | Private() |
660 | { |
661 | accessible = false; |
662 | } |
663 | }; |
664 | |
665 | KeyStoreEntry::KeyStoreEntry() |
666 | : d(new Private) |
667 | { |
668 | } |
669 | |
670 | KeyStoreEntry::KeyStoreEntry(const QString &serialized) |
671 | : d(new Private) |
672 | { |
673 | *this = fromString(serialized); |
674 | } |
675 | |
676 | KeyStoreEntry::KeyStoreEntry(const KeyStoreEntry &from) |
677 | : Algorithm(from) |
678 | , d(new Private(*from.d)) |
679 | { |
680 | } |
681 | |
682 | KeyStoreEntry::~KeyStoreEntry() |
683 | { |
684 | delete d; |
685 | } |
686 | |
687 | KeyStoreEntry &KeyStoreEntry::operator=(const KeyStoreEntry &from) |
688 | { |
689 | Algorithm::operator=(from); |
690 | *d = *from.d; |
691 | return *this; |
692 | } |
693 | |
694 | bool KeyStoreEntry::isNull() const |
695 | { |
696 | return (!context() ? true : false); |
697 | } |
698 | |
699 | bool KeyStoreEntry::isAvailable() const |
700 | { |
701 | return static_cast<const KeyStoreEntryContext *>(context())->isAvailable(); |
702 | } |
703 | |
704 | bool KeyStoreEntry::isAccessible() const |
705 | { |
706 | return d->accessible; |
707 | } |
708 | |
709 | KeyStoreEntry::Type KeyStoreEntry::type() const |
710 | { |
711 | return static_cast<const KeyStoreEntryContext *>(context())->type(); |
712 | } |
713 | |
714 | QString KeyStoreEntry::name() const |
715 | { |
716 | return static_cast<const KeyStoreEntryContext *>(context())->name(); |
717 | } |
718 | |
719 | QString KeyStoreEntry::id() const |
720 | { |
721 | return static_cast<const KeyStoreEntryContext *>(context())->id(); |
722 | } |
723 | |
724 | QString KeyStoreEntry::storeName() const |
725 | { |
726 | return static_cast<const KeyStoreEntryContext *>(context())->storeName(); |
727 | } |
728 | |
729 | QString KeyStoreEntry::storeId() const |
730 | { |
731 | return static_cast<const KeyStoreEntryContext *>(context())->storeId(); |
732 | } |
733 | |
734 | QString KeyStoreEntry::toString() const |
735 | { |
736 | return static_cast<const KeyStoreEntryContext *>(context())->serialize(); |
737 | } |
738 | |
739 | KeyStoreEntry 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 | |
748 | KeyBundle KeyStoreEntry::keyBundle() const |
749 | { |
750 | return static_cast<const KeyStoreEntryContext *>(context())->keyBundle(); |
751 | } |
752 | |
753 | Certificate KeyStoreEntry::certificate() const |
754 | { |
755 | return static_cast<const KeyStoreEntryContext *>(context())->certificate(); |
756 | } |
757 | |
758 | CRL KeyStoreEntry::crl() const |
759 | { |
760 | return static_cast<const KeyStoreEntryContext *>(context())->crl(); |
761 | } |
762 | |
763 | PGPKey KeyStoreEntry::pgpSecretKey() const |
764 | { |
765 | return static_cast<const KeyStoreEntryContext *>(context())->pgpSecretKey(); |
766 | } |
767 | |
768 | PGPKey KeyStoreEntry::pgpPublicKey() const |
769 | { |
770 | return static_cast<const KeyStoreEntryContext *>(context())->pgpPublicKey(); |
771 | } |
772 | |
773 | bool 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 | |
784 | bool 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 | //---------------------------------------------------------------------------- |
798 | class KeyStoreEntryWatcher::Private : public QObject |
799 | { |
800 | Q_OBJECT |
801 | public: |
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 | |
831 | private 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 | |
876 | KeyStoreEntryWatcher::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 | |
888 | KeyStoreEntryWatcher::~KeyStoreEntryWatcher() |
889 | { |
890 | delete d; |
891 | } |
892 | |
893 | KeyStoreEntry KeyStoreEntryWatcher::entry() const |
894 | { |
895 | return d->entry; |
896 | } |
897 | |
898 | //---------------------------------------------------------------------------- |
899 | // KeyStore |
900 | //---------------------------------------------------------------------------- |
901 | // union thingy |
902 | class KeyStoreWriteEntry |
903 | { |
904 | public: |
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 | |
948 | class KeyStoreOperation : public QThread |
949 | { |
950 | Q_OBJECT |
951 | public: |
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 | |
977 | protected: |
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 | |
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(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 | |
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(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 | |
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(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 | |
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(trackerId: 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(method: "entryList" , args: 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(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 | |
1214 | bool 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 | |
1225 | bool 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 | |
1236 | QString 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 | |
1247 | QString 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 | |
1258 | QString 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 | |
1269 | QString 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 | |
1280 | bool 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 | //---------------------------------------------------------------------------- |
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(obj: KeyStoreTracker::instance(), member: "start" , c: Qt::QueuedConnection); |
1305 | trackercall(method: "spinEventLoop" ); |
1306 | } |
1307 | |
1308 | void 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 | |
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(method: "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(obj: KeyStoreTracker::instance(), member: "scan" , c: 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(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 | |
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(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 |
1546 | void 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 |
1553 | void KeyStorePrivate::reg() |
1554 | { |
1555 | ksm->d->reg(ks: q, trackerId); |
1556 | } |
1557 | |
1558 | void KeyStorePrivate::unreg() |
1559 | { |
1560 | ksm->d->unreg(ks: 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(ksm: d); |
1579 | sync(); |
1580 | } |
1581 | |
1582 | KeyStoreManager::~KeyStoreManager() |
1583 | { |
1584 | Q_ASSERT(KeyStoreTracker::instance()); |
1585 | KeyStoreTracker::instance()->removeTarget(ksm: 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(lockedMutex: &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" |
1680 | |