13#include <config-libkleo.h>
16#include "keycache_p.h"
18#include <libkleo/algorithm.h>
19#include <libkleo/compat.h>
20#include <libkleo/debug.h>
21#include <libkleo/dn.h>
22#include <libkleo/enum.h>
23#include <libkleo/filesystemwatcher.h>
24#include <libkleo/gnupg.h>
25#include <libkleo/keygroup.h>
26#include <libkleo/keygroupconfig.h>
27#include <libkleo/keyhelpers.h>
28#include <libkleo/predicates.h>
29#include <libkleo/qtstlhelpers.h>
30#include <libkleo/stl_util.h>
32#include <libkleo_debug.h>
34#include <KSharedConfig>
36#include <QGpgME/CryptoConfig>
37#include <QGpgME/ListAllKeysJob>
38#include <QGpgME/Protocol>
44#include <gpgme++/context.h>
45#include <gpgme++/decryptionresult.h>
46#include <gpgme++/error.h>
47#include <gpgme++/key.h>
48#include <gpgme++/keylistresult.h>
49#include <gpgme++/verificationresult.h>
59using namespace std::chrono_literals;
63static const unsigned int hours2ms = 1000 * 60 * 60;
74make_comparator_str(ByEMail, .first.c_str());
78class Kleo::KeyCacheAutoRefreshSuspension
80 KeyCacheAutoRefreshSuspension()
82 qCDebug(LIBKLEO_LOG) << __func__;
83 auto cache = KeyCache::mutableInstance();
84 cache->enableFileSystemWatcher(
false);
85 m_refreshInterval = cache->refreshInterval();
86 cache->setRefreshInterval(0);
87 cache->cancelKeyListing();
92 ~KeyCacheAutoRefreshSuspension()
94 qCDebug(LIBKLEO_LOG) << __func__;
95 if (
auto cache = m_cache.lock()) {
96 cache->enableFileSystemWatcher(
true);
97 cache->setRefreshInterval(m_refreshInterval);
101 static std::shared_ptr<KeyCacheAutoRefreshSuspension> instance()
103 static std::weak_ptr<KeyCacheAutoRefreshSuspension> self;
104 if (
auto s = self.lock()) {
107 s = std::shared_ptr<KeyCacheAutoRefreshSuspension>{
new KeyCacheAutoRefreshSuspension{}};
114 std::weak_ptr<KeyCache> m_cache;
115 int m_refreshInterval = 0;
118class KeyCache::Private
120 friend class ::Kleo::KeyCache;
124 explicit Private(KeyCache *qq)
126 , m_refreshInterval(1)
127 , m_initalized(false)
129 , m_remarks_enabled(false)
132 q->startKeyListing();
134 updateAutoKeyListingTimer();
140 m_refreshJob->cancel();
144 template<
template<
template<
typename U>
class Op>
class Comp>
145 std::vector<Key>::const_iterator find(const std::vector<Key> &keys, const char *key) const
147 ensureCachePopulated();
148 const auto it = std::lower_bound(keys.begin(), keys.end(), key, Comp<std::less>());
149 if (it == keys.end() || Comp<std::equal_to>()(*it, key)) {
156 template<
template<
template<
typename U>
class Op>
class Comp>
157 std::vector<Subkey>::const_iterator find(const std::vector<Subkey> &keys, const char *key) const
159 ensureCachePopulated();
160 const auto it = std::lower_bound(keys.begin(), keys.end(), key, Comp<std::less>());
161 if (it == keys.end() || Comp<std::equal_to>()(*it, key)) {
168 std::vector<Key>::const_iterator find_fpr(
const char *fpr)
const
170 return find<_detail::ByFingerprint>(by.fpr, fpr);
173 std::pair<std::vector<std::pair<std::string, Key>>::const_iterator, std::vector<std::pair<std::string, Key>>::const_iterator>
174 find_email(
const char *email)
const
176 ensureCachePopulated();
177 return std::equal_range(by.email.begin(), by.email.end(), email, ByEMail<std::less>());
180 std::vector<Key> find_mailbox(
const QString &email,
bool sign)
const;
182 std::vector<Subkey>::const_iterator find_subkeyfpr(
const char *subkeyfpr)
const
184 return find<_detail::BySubkeyFingerprint>(by.subkeyfpr, subkeyfpr);
187 std::vector<Subkey>::const_iterator find_keygrip(
const char *keygrip)
const
189 return find<_detail::ByKeyGrip>(by.keygrip, keygrip);
192 std::vector<Subkey>::const_iterator find_subkeyid(
const char *subkeyid)
const
194 return find<_detail::ByKeyID>(by.subkeyid, subkeyid);
197 std::vector<Key>::const_iterator find_keyid(
const char *keyid)
const
199 return find<_detail::ByKeyID>(by.keyid, keyid);
202 std::pair<std::vector<Key>::const_iterator, std::vector<Key>::const_iterator> find_subjects(
const char *chain_id)
const
204 ensureCachePopulated();
205 return std::equal_range(by.chainid.begin(), by.chainid.end(), chain_id, _detail::ByChainID<std::less>());
208 void refreshJobDone(
const KeyListResult &result);
210 void setRefreshInterval(
int interval)
212 m_refreshInterval = interval;
213 updateAutoKeyListingTimer();
216 int refreshInterval()
const
218 return m_refreshInterval;
221 void updateAutoKeyListingTimer()
223 setAutoKeyListingInterval(hours2ms * m_refreshInterval);
225 void setAutoKeyListingInterval(
int ms)
227 m_autoKeyListingTimer.
stop();
230 m_autoKeyListingTimer.
start();
234 void ensureCachePopulated()
const;
236 void readGroupsFromGpgConf()
242 auto conf = QGpgME::cryptoConfig();
247 auto entry = getCryptoConfigEntry(conf,
"gpg",
"group");
254 const auto stringValueList = entry->stringValueList();
255 for (
const QString &value : stringValueList) {
257 if (split.size() != 2) {
258 qCDebug(LIBKLEO_LOG) <<
"Ignoring invalid group config:" << value;
261 const QString groupName = split[0];
262 const QString fingerprint = split[1];
263 fingerprints[groupName].push_back(fingerprint);
267 for (
auto it = fingerprints.
cbegin(); it != fingerprints.
cend(); ++it) {
268 const QString groupName = it.key();
269 const std::vector<Key> groupKeys = q->findByFingerprint(toStdStrings(it.value()));
270 KeyGroup g(groupName, groupName, groupKeys, KeyGroup::GnuPGConfig);
271 m_groups.push_back(g);
275 void readGroupsFromGroupsConfig()
277 Q_ASSERT(m_groupConfig);
278 if (!m_groupConfig) {
279 qCWarning(LIBKLEO_LOG) << __func__ <<
"group config not set";
283 m_groups = m_groupConfig->readGroups();
286 KeyGroup writeGroupToGroupsConfig(
const KeyGroup &group)
288 Q_ASSERT(m_groupConfig);
289 if (!m_groupConfig) {
290 qCWarning(LIBKLEO_LOG) << __func__ <<
"group config not set";
294 Q_ASSERT(!group.isNull());
295 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
296 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
297 qCDebug(LIBKLEO_LOG) << __func__ <<
"group cannot be written to application configuration:" << group;
301 return m_groupConfig->writeGroup(group);
304 bool removeGroupFromGroupsConfig(
const KeyGroup &group)
306 Q_ASSERT(m_groupConfig);
307 if (!m_groupConfig) {
308 qCWarning(LIBKLEO_LOG) << __func__ <<
"group config not set";
312 Q_ASSERT(!group.isNull());
313 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
314 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
315 qCDebug(LIBKLEO_LOG) << __func__ <<
"group cannot be removed from application configuration:" << group;
319 return m_groupConfig->removeGroup(group);
322 void updateGroupCache()
329 if (m_groupsEnabled) {
330 readGroupsFromGpgConf();
331 readGroupsFromGroupsConfig();
335 bool insert(
const KeyGroup &group)
337 Q_ASSERT(!group.isNull());
338 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
339 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
340 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::insert - Invalid group:" << group;
343 const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](
const auto &g) {
344 return g.source() == group.source() && g.id() == group.id();
346 if (it != m_groups.cend()) {
347 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::insert - Group already present in list of groups:" << group;
351 const KeyGroup savedGroup = writeGroupToGroupsConfig(group);
352 if (savedGroup.isNull()) {
353 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::insert - Writing group" << group.id() <<
"to config file failed";
357 m_groups.push_back(savedGroup);
359 Q_EMIT q->groupAdded(savedGroup);
364 bool update(
const KeyGroup &group)
366 Q_ASSERT(!group.isNull());
367 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
368 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
369 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::update - Invalid group:" << group;
372 const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](
const auto &g) {
373 return g.source() == group.source() && g.id() == group.id();
375 if (it == m_groups.cend()) {
376 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::update - Group not found in list of groups:" << group;
379 const auto groupIndex = std::distance(m_groups.cbegin(), it);
381 const KeyGroup savedGroup = writeGroupToGroupsConfig(group);
382 if (savedGroup.isNull()) {
383 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::update - Writing group" << group.id() <<
"to config file failed";
387 m_groups[groupIndex] = savedGroup;
389 Q_EMIT q->groupUpdated(savedGroup);
394 bool remove(
const KeyGroup &group)
396 Q_ASSERT(!group.isNull());
397 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
398 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
399 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::remove - Invalid group:" << group;
402 const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](
const auto &g) {
403 return g.source() == group.source() && g.id() == group.id();
405 if (it == m_groups.cend()) {
406 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::remove - Group not found in list of groups:" << group;
410 const bool success = removeGroupFromGroupsConfig(group);
412 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::remove - Removing group" << group.id() <<
"from config file failed";
418 Q_EMIT q->groupRemoved(group);
425 std::vector<std::shared_ptr<FileSystemWatcher>> m_fsWatchers;
426 QTimer m_autoKeyListingTimer;
427 int m_refreshInterval;
430 std::vector<Key> fpr, keyid, chainid;
431 std::vector<std::pair<std::string, Key>> email;
432 std::vector<Subkey> subkeyfpr, subkeyid, keygrip;
436 bool m_remarks_enabled;
437 bool m_groupsEnabled =
false;
438 std::shared_ptr<KeyGroupConfig> m_groupConfig;
439 std::vector<KeyGroup> m_groups;
440 std::unordered_map<QByteArray, std::vector<CardKeyStorageInfo>> m_cards;
443std::shared_ptr<const KeyCache> KeyCache::instance()
445 return mutableInstance();
448std::shared_ptr<KeyCache> KeyCache::mutableInstance()
450 static std::weak_ptr<KeyCache> self;
452 return std::shared_ptr<KeyCache>(self);
453 }
catch (
const std::bad_weak_ptr &) {
454 const std::shared_ptr<KeyCache> s(
new KeyCache);
462 , d(new Private(this))
470void KeyCache::setGroupsEnabled(
bool enabled)
472 d->m_groupsEnabled = enabled;
473 if (d->m_initalized) {
474 d->updateGroupCache();
478void KeyCache::setGroupConfig(
const std::shared_ptr<KeyGroupConfig> &groupConfig)
480 d->m_groupConfig = groupConfig;
483void KeyCache::enableFileSystemWatcher(
bool enable)
485 for (
const auto &i : std::as_const(d->m_fsWatchers)) {
486 i->setEnabled(enable);
490void KeyCache::setRefreshInterval(
int hours)
492 d->setRefreshInterval(hours);
495int KeyCache::refreshInterval()
const
497 return d->refreshInterval();
500std::shared_ptr<KeyCacheAutoRefreshSuspension> KeyCache::suspendAutoRefresh()
502 return KeyCacheAutoRefreshSuspension::instance();
505void KeyCache::reload(GpgME::Protocol , ReloadOption option)
507 qCDebug(LIBKLEO_LOG) <<
this << __func__ <<
"option:" << option;
508 const bool forceReload = option & ForceReload;
509 if (d->m_refreshJob && !forceReload) {
510 qCDebug(LIBKLEO_LOG) <<
this << __func__ <<
"- refresh already running";
513 if (d->m_refreshJob) {
514 disconnect(d->m_refreshJob.data(),
nullptr,
this,
nullptr);
515 d->m_refreshJob->cancel();
516 d->m_refreshJob.clear();
519 d->updateAutoKeyListingTimer();
521 enableFileSystemWatcher(
false);
522 d->m_refreshJob =
new RefreshKeysJob(
this);
523 connect(d->m_refreshJob.data(), &RefreshKeysJob::done,
this, [
this](
const GpgME::KeyListResult &r) {
524 qCDebug(LIBKLEO_LOG) << d->m_refreshJob.data() <<
"RefreshKeysJob::done";
525 d->refreshJobDone(r);
527 connect(d->m_refreshJob.data(), &RefreshKeysJob::canceled,
this, [
this]() {
528 qCDebug(LIBKLEO_LOG) << d->m_refreshJob.data() <<
"RefreshKeysJob::canceled";
529 d->m_refreshJob.clear();
531 d->m_refreshJob->start();
534void KeyCache::cancelKeyListing()
536 if (!d->m_refreshJob) {
539 d->m_refreshJob->cancel();
542void KeyCache::addFileSystemWatcher(
const std::shared_ptr<FileSystemWatcher> &watcher)
547 d->m_fsWatchers.push_back(watcher);
548 connect(watcher.get(), &FileSystemWatcher::directoryChanged,
this, [
this]() {
551 connect(watcher.get(), &FileSystemWatcher::fileChanged,
this, [
this]() {
555 watcher->setEnabled(d->m_refreshJob.isNull());
558void KeyCache::enableRemarks(
bool value)
560 if (!d->m_remarks_enabled && value) {
561 d->m_remarks_enabled = value;
562 if (d->m_initalized && !d->m_refreshJob) {
563 qCDebug(LIBKLEO_LOG) <<
"Reloading keycache with remarks enabled";
567 d->m_remarks_enabled = value;
571bool KeyCache::remarksEnabled()
const
573 return d->m_remarks_enabled;
576void KeyCache::Private::refreshJobDone(
const KeyListResult &result)
578 m_refreshJob.
clear();
579 q->enableFileSystemWatcher(
true);
580 if (!m_initalized && q->remarksEnabled()) {
585 qCDebug(LIBKLEO_LOG) <<
"Reloading keycache with remarks enabled";
592 Q_EMIT q->keyListingDone(result);
595const Key &KeyCache::findByFingerprint(
const char *fpr)
const
597 const std::vector<Key>::const_iterator it = d->find_fpr(fpr);
598 if (it == d->by.fpr.end()) {
599 static const Key null;
606const Key &KeyCache::findByFingerprint(
const std::string &fpr)
const
608 return findByFingerprint(fpr.c_str());
611std::vector<GpgME::Key> KeyCache::findByFingerprint(
const std::vector<std::string> &fprs)
const
613 std::vector<Key> keys;
614 keys.reserve(fprs.size());
615 for (
const auto &fpr : fprs) {
616 const Key key = findByFingerprint(fpr.c_str());
618 qCDebug(LIBKLEO_LOG) << __func__ <<
"Ignoring unknown key with fingerprint:" << fpr.c_str();
626std::vector<Key> KeyCache::findByEMailAddress(
const char *email)
const
628 const auto pair = d->find_email(email);
629 std::vector<Key> result;
630 result.reserve(std::distance(pair.first, pair.second));
631 std::transform(pair.first, pair.second, std::back_inserter(result), [](
const std::pair<std::string, Key> &pair) {
637std::vector<Key> KeyCache::findByEMailAddress(
const std::string &email)
const
639 return findByEMailAddress(email.c_str());
642const Key &KeyCache::findByKeyIDOrFingerprint(
const char *
id)
const
646 const std::vector<Key>::const_iterator it = d->find_fpr(
id);
647 if (it != d->by.fpr.end()) {
653 const std::vector<Key>::const_iterator it = d->find_keyid(
id);
654 if (it != d->by.keyid.end()) {
658 static const Key null;
662const Key &KeyCache::findByKeyIDOrFingerprint(
const std::string &
id)
const
664 return findByKeyIDOrFingerprint(
id.c_str());
667std::vector<Key> KeyCache::findByKeyIDOrFingerprint(
const std::vector<std::string> &ids)
const
669 std::vector<std::string> keyids;
670 std::remove_copy_if(ids.begin(), ids.end(), std::back_inserter(keyids), [](
const std::string &str) {
671 return !str.c_str() || !*str.c_str();
675 std::sort(keyids.begin(), keyids.end(), _detail::ByFingerprint<std::less>());
677 std::vector<Key> result;
678 result.reserve(keyids.size());
679 d->ensureCachePopulated();
681 kdtools::set_intersection(d->by.fpr.begin(),
685 std::back_inserter(result),
686 _detail::ByFingerprint<std::less>());
687 if (result.size() < keyids.size()) {
690 kdtools::set_intersection(d->by.keyid.begin(),
694 std::back_inserter(result),
695 _detail::ByKeyID<std::less>());
698 std::sort(result.begin(), result.end(), _detail::ByFingerprint<std::less>());
699 result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
707const Subkey &KeyCache::findSubkeyByKeyGrip(
const char *grip, Protocol protocol)
const
709 static const Subkey null;
710 d->ensureCachePopulated();
711 const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), grip, _detail::ByKeyGrip<std::less>());
712 if (range.first == range.second) {
714 }
else if (protocol == UnknownProtocol) {
717 for (
auto it = range.first; it != range.second; ++it) {
718 if (it->parent().protocol() == protocol) {
726const Subkey &KeyCache::findSubkeyByKeyGrip(
const std::string &grip, Protocol protocol)
const
728 return findSubkeyByKeyGrip(grip.c_str(), protocol);
731std::vector<GpgME::Subkey> Kleo::KeyCache::findSubkeysByKeyGrip(
const char *grip, GpgME::Protocol protocol)
const
733 d->ensureCachePopulated();
735 std::vector<GpgME::Subkey> subkeys;
736 const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), grip, _detail::ByKeyGrip<std::less>());
737 subkeys.reserve(std::distance(range.first, range.second));
738 if (protocol == UnknownProtocol) {
739 std::copy(range.first, range.second, std::back_inserter(subkeys));
741 std::copy_if(range.first, range.second, std::back_inserter(subkeys), [protocol](
const auto &subkey) {
742 return subkey.parent().protocol() == protocol;
748std::vector<GpgME::Subkey> Kleo::KeyCache::findSubkeysByKeyGrip(
const std::string &grip, GpgME::Protocol protocol)
const
750 return findSubkeysByKeyGrip(grip.c_str(), protocol);
753std::vector<Subkey> KeyCache::findSubkeysByKeyID(
const std::vector<std::string> &ids)
const
755 std::vector<std::string> sorted;
756 sorted.reserve(ids.size());
757 std::remove_copy_if(ids.begin(), ids.end(), std::back_inserter(sorted), [](
const std::string &str) {
758 return !str.c_str() || !*str.c_str();
761 std::sort(sorted.begin(), sorted.end(), _detail::ByKeyID<std::less>());
763 std::vector<Subkey> result;
764 d->ensureCachePopulated();
765 kdtools::set_intersection(d->by.subkeyid.begin(),
766 d->by.subkeyid.end(),
769 std::back_inserter(result),
770 _detail::ByKeyID<std::less>());
774const GpgME::Subkey &KeyCache::findSubkeyByFingerprint(
const std::string &fpr)
const
776 static const Subkey null;
778 const auto it = d->find_subkeyfpr(fpr.c_str());
779 if (it != d->by.subkeyfpr.end()) {
785std::vector<Key> KeyCache::findRecipients(
const DecryptionResult &res)
const
787 std::vector<std::string> keyids;
788 const auto recipients = res.recipients();
789 for (
const DecryptionResult::Recipient &r : recipients) {
790 if (
const char *kid = r.keyID()) {
791 keyids.push_back(kid);
794 const std::vector<Subkey> subkeys = findSubkeysByKeyID(keyids);
795 std::vector<Key> result;
796 result.reserve(subkeys.size());
797 std::transform(subkeys.begin(), subkeys.end(), std::back_inserter(result), std::mem_fn(&Subkey::parent));
799 std::sort(result.begin(), result.end(), _detail::ByFingerprint<std::less>());
800 result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
804GpgME::Key KeyCache::findSigner(
const GpgME::Signature &signature)
const
806 if (signature.isNull()) {
810 GpgME::Key key = signature.key();
811 if (key.isNull() && signature.fingerprint()) {
812 key = findByFingerprint(signature.fingerprint());
814 if (key.isNull() && signature.fingerprint()) {
816 const auto subkey = findSubkeyByFingerprint(signature.fingerprint());
817 if (!subkey.isNull()) {
818 key = subkey.parent();
824std::vector<Key> KeyCache::findSigners(
const VerificationResult &res)
const
826 std::vector<Key> signers;
827 if (res.numSignatures() > 0) {
828 signers.reserve(res.numSignatures());
829 Kleo::transform(res.signatures(), std::back_inserter(signers), [
this](
const auto &sig) {
830 return findSigner(sig);
836std::vector<Key> KeyCache::findSigningKeysByMailbox(
const QString &mb)
const
838 return d->find_mailbox(mb,
true);
841std::vector<Key> KeyCache::findEncryptionKeysByMailbox(
const QString &mb)
const
843 return d->find_mailbox(mb,
false);
848#define DO(op, meth, meth2) \
849 if (op key.meth()) { \
851 qDebug("rejecting for signing: %s: %s", #meth2, key.primaryFingerprint()); \
854#define ACCEPT(meth) DO(!!, meth, !meth)
855#define REJECT(meth) DO(!, meth, meth)
856struct ready_for_signing {
857 bool operator()(
const Key &key)
const
870#define DO(op, meth, meth2) \
871 if (op key.meth()) { \
873 qDebug("rejecting for encrypting: %s: %s", #meth2, key.primaryFingerprint()); \
876struct ready_for_encryption {
877 bool operator()(
const Key &key)
const
892std::vector<Key> KeyCache::Private::find_mailbox(
const QString &email,
bool sign)
const
894 if (email.isEmpty()) {
895 return std::vector<Key>();
898 const auto pair = find_email(email.toUtf8().constData());
899 std::vector<Key> result;
900 result.reserve(std::distance(pair.first, pair.second));
902 kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_signing());
904 kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_encryption());
910std::vector<Key> KeyCache::findSubjects(
const GpgME::Key &key, Options options)
const
916 return findSubjects(std::vector<Key>(1, key), options);
919std::vector<Key> KeyCache::findSubjects(
const std::vector<Key> &keys, Options options)
const
921 std::vector<Key> result;
928 for (
const auto &key : keys) {
929 const auto firstAndLastSubject = d->find_subjects(key.primaryFingerprint());
930 result.insert(result.end(), firstAndLastSubject.first, firstAndLastSubject.second);
933 _detail::sort_by_fpr(result);
934 _detail::remove_duplicates_by_fpr(result);
936 if (options & RecursiveSearch) {
937 for (std::vector<Key> furtherSubjects = findSubjects(result, NoOption);
938 !furtherSubjects.empty();
939 furtherSubjects = findSubjects(furtherSubjects, NoOption)) {
940 std::vector<Key> combined;
941 combined.reserve(result.size() + furtherSubjects.size());
942 std::merge(result.begin(),
944 furtherSubjects.begin(),
945 furtherSubjects.end(),
946 std::back_inserter(combined),
947 _detail::ByFingerprint<std::less>());
948 _detail::remove_duplicates_by_fpr(combined);
949 if (result.size() == combined.size()) {
953 result.swap(combined);
960std::vector<Key> KeyCache::findIssuers(
const Key &key, Options options)
const
962 std::vector<Key> result;
968 if (options & IncludeSubject) {
969 result.push_back(key);
976 Key issuer = findByFingerprint(key.chainID());
978 if (issuer.isNull()) {
982 result.push_back(issuer);
984 if (!(options & RecursiveSearch)) {
988 while (!issuer.isRoot()) {
989 issuer = findByFingerprint(result.back().chainID());
990 if (issuer.isNull()) {
993 const bool chainAlreadyContainsIssuer = Kleo::contains_if(result, [issuer](
const auto &key) {
994 return _detail::ByFingerprint<std::equal_to>()(issuer, key);
998 result.push_back(issuer);
999 if (chainAlreadyContainsIssuer) {
1008static std::string email(
const UserID &uid)
1011 const std::string addr = uid.addrSpec();
1012 if (!addr.empty()) {
1015 const std::string email = uid.email();
1016 if (email.empty()) {
1017 return DN(uid.id())[QStringLiteral(
"EMAIL")].trimmed().toUtf8().constData();
1019 if (email[0] ==
'<' && email[email.size() - 1] ==
'>') {
1020 return email.substr(1, email.size() - 2);
1026static std::vector<std::string> emails(
const Key &key)
1028 std::vector<std::string> emails;
1029 const auto userIDs = key.userIDs();
1030 for (
const UserID &uid : userIDs) {
1031 const std::string e = email(uid);
1033 emails.push_back(e);
1036 std::sort(emails.begin(), emails.end(), ByEMail<std::less>());
1037 emails.erase(std::unique(emails.begin(), emails.end(), ByEMail<std::equal_to>()), emails.end());
1041void KeyCache::remove(
const Key &key)
1047 const char *fpr = key.primaryFingerprint();
1053 const auto range = std::equal_range(d->by.fpr.begin(), d->by.fpr.end(), fpr, _detail::ByFingerprint<std::less>());
1054 d->by.fpr.erase(range.first, range.second);
1057 if (
const char *keyid = key.keyID()) {
1058 const auto range = std::equal_range(d->by.keyid.begin(), d->by.keyid.end(), keyid, _detail::ByKeyID<std::less>());
1059 const auto it = std::remove_if(range.first, range.second, [fpr](
const GpgME::Key &key) {
1060 return _detail::ByFingerprint<std::equal_to>()(fpr, key);
1062 d->by.keyid.erase(it, range.second);
1065 if (
const char *chainid = key.chainID()) {
1066 const auto range = std::equal_range(d->by.chainid.begin(), d->by.chainid.end(), chainid, _detail::ByChainID<std::less>());
1067 const auto range2 = std::equal_range(range.first, range.second, fpr, _detail::ByFingerprint<std::less>());
1068 d->by.chainid.erase(range2.first, range2.second);
1071 const auto emailsKey{emails(key)};
1072 for (
const std::string &email : emailsKey) {
1073 const auto range = std::equal_range(d->by.email.begin(), d->by.email.end(), email, ByEMail<std::less>());
1074 const auto it = std::remove_if(range.first, range.second, [fpr](
const std::pair<std::string, Key> &pair) {
1075 return qstricmp(fpr, pair.second.primaryFingerprint()) == 0;
1077 d->by.email.erase(it, range.second);
1080 const auto keySubKeys{key.subkeys()};
1081 for (
const Subkey &subkey : keySubKeys) {
1082 if (
const char *subkeyfpr = subkey.fingerprint()) {
1083 const auto range = std::equal_range(d->by.subkeyfpr.begin(), d->by.subkeyfpr.end(), subkeyfpr, _detail::BySubkeyFingerprint<std::less>());
1084 const auto it = std::remove_if(range.first, range.second, [fpr](
const Subkey &subkey) {
1085 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1087 d->by.subkeyfpr.erase(it, range.second);
1089 if (
const char *keyid = subkey.keyID()) {
1090 const auto range = std::equal_range(d->by.subkeyid.begin(), d->by.subkeyid.end(), keyid, _detail::ByKeyID<std::less>());
1091 const auto it = std::remove_if(range.first, range.second, [fpr](
const Subkey &subkey) {
1092 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1094 d->by.subkeyid.erase(it, range.second);
1096 if (
const char *keygrip = subkey.keyGrip()) {
1097 const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), keygrip, _detail::ByKeyGrip<std::less>());
1098 const auto it = std::remove_if(range.first, range.second, [fpr](
const Subkey &subkey) {
1099 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1101 d->by.keygrip.erase(it, range.second);
1106void KeyCache::remove(
const std::vector<Key> &keys)
1108 for (
const Key &key : keys) {
1113const std::vector<GpgME::Key> &KeyCache::keys()
const
1115 d->ensureCachePopulated();
1119std::vector<Key> KeyCache::secretKeys()
const
1121 std::vector<Key> keys = this->keys();
1122 keys.erase(std::remove_if(keys.begin(),
1124 [](
const Key &key) {
1125 return !key.hasSecret();
1131KeyGroup KeyCache::group(
const QString &
id)
const
1134 const auto it = std::find_if(std::cbegin(d->m_groups), std::cend(d->m_groups), [
id](
const auto &g) {
1135 return g.id() == id;
1137 if (it != std::cend(d->m_groups)) {
1143std::vector<KeyGroup> KeyCache::groups()
const
1145 d->ensureCachePopulated();
1149std::vector<KeyGroup> KeyCache::configurableGroups()
const
1151 std::vector<KeyGroup> groups;
1152 groups.reserve(d->m_groups.size());
1153 std::copy_if(d->m_groups.cbegin(), d->m_groups.cend(), std::back_inserter(groups), [](
const KeyGroup &group) {
1154 return group.source() == KeyGroup::ApplicationConfig;
1161bool compareById(
const KeyGroup &lhs,
const KeyGroup &rhs)
1163 return lhs.id() < rhs.id();
1166std::vector<KeyGroup> sortedById(std::vector<KeyGroup> groups)
1168 std::sort(groups.begin(), groups.end(), &compareById);
1173void KeyCache::saveConfigurableGroups(
const std::vector<KeyGroup> &groups)
1175 const std::vector<KeyGroup> oldGroups = sortedById(configurableGroups());
1176 const std::vector<KeyGroup> newGroups = sortedById(groups);
1179 std::vector<KeyGroup> removedGroups;
1180 std::set_difference(oldGroups.begin(), oldGroups.end(), newGroups.begin(), newGroups.end(), std::back_inserter(removedGroups), &compareById);
1181 for (
const auto &group : std::as_const(removedGroups)) {
1182 qCDebug(LIBKLEO_LOG) <<
"Removing group" << group;
1187 std::vector<KeyGroup> updatedGroups;
1188 std::set_intersection(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(updatedGroups), &compareById);
1189 for (
const auto &group : std::as_const(updatedGroups)) {
1190 qCDebug(LIBKLEO_LOG) <<
"Updating group" << group;
1195 std::vector<KeyGroup> addedGroups;
1196 std::set_difference(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(addedGroups), &compareById);
1197 for (
const auto &group : std::as_const(addedGroups)) {
1198 qCDebug(LIBKLEO_LOG) <<
"Adding group" << group;
1203 Q_EMIT keysMayHaveChanged();
1206bool KeyCache::insert(
const KeyGroup &group)
1208 if (!d->insert(group)) {
1212 Q_EMIT keysMayHaveChanged();
1217bool KeyCache::update(
const KeyGroup &group)
1219 if (!d->update(group)) {
1223 Q_EMIT keysMayHaveChanged();
1228bool KeyCache::remove(
const KeyGroup &group)
1230 if (!d->remove(group)) {
1234 Q_EMIT keysMayHaveChanged();
1239void KeyCache::refresh(
const std::vector<Key> &keys)
1246void KeyCache::insert(
const Key &key)
1248 insert(std::vector<Key>(1, key));
1254template<
template<
template<
typename T>
class Op>
class T1, template<template<typename T> class Op> class T2>
1255struct lexicographically {
1256 using result_type = bool;
1258 template<
typename U,
typename V>
1259 bool operator()(
const U &lhs,
const V &rhs)
const
1261 return T1<std::less>()(lhs, rhs)
1262 || (T1<std::equal_to>()(lhs, rhs) && T2<std::less>()(lhs, rhs));
1268void KeyCache::insert(
const std::vector<Key> &keys)
1271 std::vector<Key> sorted;
1272 sorted.reserve(keys.size());
1273 std::copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), [](
const Key &key) {
1274 auto fp = key.primaryFingerprint();
1282 std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
1285 std::vector<Key> by_fpr;
1286 by_fpr.reserve(sorted.size() + d->by.fpr.size());
1287 std::merge(sorted.begin(), sorted.end(), d->by.fpr.begin(), d->by.fpr.end(), std::back_inserter(by_fpr), _detail::ByFingerprint<std::less>());
1290 std::vector<std::pair<std::string, Key>> pairs;
1291 pairs.reserve(sorted.size());
1292 for (
const Key &key : std::as_const(sorted)) {
1293 const std::vector<std::string> emails = ::emails(key);
1294 for (
const std::string &e : emails) {
1295 pairs.push_back(std::make_pair(e, key));
1298 std::sort(pairs.begin(), pairs.end(), ByEMail<std::less>());
1301 std::vector<std::pair<std::string, Key>> by_email;
1302 by_email.reserve(pairs.size() + d->by.email.size());
1303 std::merge(pairs.begin(), pairs.end(), d->by.email.begin(), d->by.email.end(), std::back_inserter(by_email), ByEMail<std::less>());
1306 std::stable_sort(sorted.begin(), sorted.end(), _detail::ByChainID<std::less>());
1309 std::vector<Key> nonroot;
1310 nonroot.reserve(sorted.size());
1311 std::vector<Key> by_chainid;
1312 by_chainid.reserve(sorted.size() + d->by.chainid.size());
1313 std::copy_if(sorted.cbegin(), sorted.cend(), std::back_inserter(nonroot), [](
const Key &key) {
1314 return !key.isRoot();
1316 std::merge(nonroot.cbegin(),
1318 d->by.chainid.cbegin(),
1319 d->by.chainid.cend(),
1320 std::back_inserter(by_chainid),
1321 lexicographically<_detail::ByChainID, _detail::ByFingerprint>());
1324 std::sort(sorted.begin(), sorted.end(), _detail::ByKeyID<std::less>());
1327 std::vector<Key> by_keyid;
1328 by_keyid.reserve(sorted.size() + d->by.keyid.size());
1329 std::merge(sorted.begin(), sorted.end(), d->by.keyid.begin(), d->by.keyid.end(), std::back_inserter(by_keyid), _detail::ByKeyID<std::less>());
1334 std::vector<Subkey> subkeys;
1335 subkeys.reserve(sorted.size());
1336 for (
const Key &key : std::as_const(sorted)) {
1337 const auto keySubkeys{key.subkeys()};
1338 for (
const Subkey &subkey : keySubkeys) {
1339 if (subkey.canRenc()) {
1342 subkeys.push_back(subkey);
1347 std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyID<std::less>());
1350 std::vector<Subkey> by_subkeyid;
1351 by_subkeyid.reserve(subkeys.size() + d->by.subkeyid.size());
1352 std::merge(subkeys.begin(), subkeys.end(), d->by.subkeyid.begin(), d->by.subkeyid.end(), std::back_inserter(by_subkeyid), _detail::ByKeyID<std::less>());
1355 std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyGrip<std::less>());
1358 std::vector<Subkey> by_keygrip;
1359 by_keygrip.reserve(subkeys.size() + d->by.keygrip.size());
1360 std::merge(subkeys.begin(), subkeys.end(), d->by.keygrip.begin(), d->by.keygrip.end(), std::back_inserter(by_keygrip), _detail::ByKeyGrip<std::less>());
1363 std::sort(subkeys.begin(), subkeys.end(), _detail::BySubkeyFingerprint<std::less>());
1366 std::vector<Subkey> by_subkeyfpr;
1367 by_subkeyfpr.reserve(subkeys.size() + d->by.subkeyfpr.size());
1368 std::merge(subkeys.begin(),
1370 d->by.subkeyfpr.begin(),
1371 d->by.subkeyfpr.end(),
1372 std::back_inserter(by_subkeyfpr),
1373 _detail::BySubkeyFingerprint<std::less>());
1376 by_fpr.swap(d->by.fpr);
1377 by_keyid.swap(d->by.keyid);
1378 by_email.swap(d->by.email);
1379 by_subkeyfpr.swap(d->by.subkeyfpr);
1380 by_subkeyid.swap(d->by.subkeyid);
1381 by_keygrip.swap(d->by.keygrip);
1382 by_chainid.swap(d->by.chainid);
1384 for (
const Key &key : std::as_const(sorted)) {
1385 d->m_pgpOnly &= key.protocol() == GpgME::OpenPGP;
1389 for (
const auto &key : keys) {
1390 for (
const auto &subkey : key.subkeys()) {
1391 if (!subkey.isSecret() || !d->m_cards[
QByteArray(subkey.keyGrip())].empty()) {
1395 for (
const auto &line : data) {
1396 if (line.startsWith(QByteArrayLiteral(
"Token"))) {
1397 const auto split = line.split(
' ');
1398 if (split.size() > 2) {
1413 Q_EMIT keysMayHaveChanged();
1416void KeyCache::clear()
1418 d->by = Private::By();
1427class KeyCache::RefreshKeysJob::Private
1429 RefreshKeysJob *
const q;
1432 Private(KeyCache *cache, RefreshKeysJob *qq);
1434 Error startKeyListing(GpgME::Protocol protocol);
1435 void listAllKeysJobDone(
const KeyListResult &res,
const std::vector<Key> &nextKeys)
1437 if (!nextKeys.empty()) {
1438 std::vector<Key> keys;
1439 keys.reserve(m_keys.size() + nextKeys.size());
1440 if (m_keys.empty()) {
1443 std::merge(m_keys.begin(), m_keys.end(), nextKeys.begin(), nextKeys.end(), std::back_inserter(keys), _detail::ByFingerprint<std::less>());
1449 void emitDone(
const KeyListResult &result);
1450 void updateKeyCache();
1454 std::vector<Key> m_keys;
1455 KeyListResult m_mergedResult;
1459 void jobDone(
const KeyListResult &res);
1462KeyCache::RefreshKeysJob::Private::Private(KeyCache *cache, RefreshKeysJob *qq)
1470void KeyCache::RefreshKeysJob::Private::jobDone(
const KeyListResult &result)
1481 Q_ASSERT(!m_jobsPending.
empty());
1482 m_jobsPending.
removeOne(qobject_cast<QGpgME::ListAllKeysJob *>(sender));
1483 m_mergedResult.mergeWith(result);
1484 if (!m_jobsPending.
empty()) {
1488 emitDone(m_mergedResult);
1491void KeyCache::RefreshKeysJob::Private::emitDone(
const KeyListResult &res)
1494 Q_EMIT q->done(res);
1497KeyCache::RefreshKeysJob::RefreshKeysJob(KeyCache *cache,
QObject *parent)
1499 , d(new Private(cache, this))
1503KeyCache::RefreshKeysJob::~RefreshKeysJob()
1508void KeyCache::RefreshKeysJob::start()
1510 qCDebug(LIBKLEO_LOG) <<
"KeyCache::RefreshKeysJob" << __func__;
1516void KeyCache::RefreshKeysJob::cancel()
1518 d->m_canceled =
true;
1519 std::for_each(d->m_jobsPending.begin(), d->m_jobsPending.end(), std::mem_fn(&QGpgME::ListAllKeysJob::slotCancel));
1523void KeyCache::RefreshKeysJob::Private::doStart()
1530 Q_ASSERT(m_jobsPending.
empty());
1531 m_mergedResult.mergeWith(KeyListResult(startKeyListing(GpgME::OpenPGP)));
1532 m_mergedResult.mergeWith(KeyListResult(startKeyListing(GpgME::CMS)));
1534 if (!m_jobsPending.
empty()) {
1538 const bool hasError = m_mergedResult.error() || m_mergedResult.error().isCanceled();
1539 emitDone(hasError ? m_mergedResult : KeyListResult(Error(GPG_ERR_UNSUPPORTED_OPERATION)));
1542void KeyCache::RefreshKeysJob::Private::updateKeyCache()
1544 if (!m_cache || m_canceled) {
1549 std::vector<Key> cachedKeys = m_cache->initialized() ? m_cache->keys() : std::vector<Key>();
1550 std::sort(cachedKeys.begin(), cachedKeys.end(), _detail::ByFingerprint<std::less>());
1551 std::vector<Key> keysToRemove;
1552 std::set_difference(cachedKeys.begin(),
1556 std::back_inserter(keysToRemove),
1557 _detail::ByFingerprint<std::less>());
1558 m_cache->remove(keysToRemove);
1559 m_cache->refresh(m_keys);
1562Error KeyCache::RefreshKeysJob::Private::startKeyListing(GpgME::Protocol proto)
1564 const auto *
const protocol = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
1568 QGpgME::ListAllKeysJob *
const job = protocol->listAllKeysJob(
false,
true);
1572 if (!m_cache->initialized()) {
1574 job->setOptions(QGpgME::ListAllKeysJob::DisableAutomaticTrustDatabaseCheck);
1578 aheinecke: 2017.01.12:
1580 For unknown reasons the
new style
connect fails at runtime
1581 over library borders into QGpgME from the GpgME repo
1582 when cross compiled
for Windows and
default arguments
1583 are used in the Signal.
1585 This was tested with gcc 4.9 (Mingw 3.0.2) and we could not
1586 find an explanation
for this. So until
this is fixed or we understand
1587 the problem we need to use the old style connect
for QGpgME signals.
1589 The
new style connect of the canceled signal right below
1592 connect(job, &QGpgME::ListAllKeysJob::result,
1593 q, [
this](
const GpgME::KeyListResult &res,
const std::vector<GpgME::Key> &keys) {
1594 listAllKeysJobDone(res, keys);
1597 connect(job, SIGNAL(result(GpgME::KeyListResult, std::vector<GpgME::Key>)), q, SLOT(listAllKeysJobDone(GpgME::KeyListResult, std::vector<GpgME::Key>)));
1599 connect(q, &RefreshKeysJob::canceled, job, &QGpgME::Job::slotCancel);
1603 if (proto == GpgME::OpenPGP && m_cache->remarksEnabled() && m_cache->initialized()) {
1604 auto ctx = QGpgME::Job::context(job);
1606 ctx->addKeyListMode(KeyListMode::Signatures | KeyListMode::SignatureNotations);
1610 const Error
error = job->start(
true);
1612 if (!error && !
error.isCanceled()) {
1613 m_jobsPending.push_back(job);
1618bool KeyCache::initialized()
const
1620 return d->m_initalized;
1623void KeyCache::Private::ensureCachePopulated()
const
1625 if (!m_initalized) {
1626 q->startKeyListing();
1629 qCDebug(LIBKLEO_LOG) <<
"Waiting for keycache.";
1631 qCDebug(LIBKLEO_LOG) <<
"Keycache available.";
1635bool KeyCache::pgpOnly()
const
1637 return d->m_pgpOnly;
1640static bool keyIsOk(
const Key &k)
1642 return !k.isExpired() && !k.isRevoked() && !k.isInvalid() && !k.isDisabled();
1645static bool uidIsOk(
const UserID &uid)
1647 return keyIsOk(uid.parent()) && !uid.isRevoked() && !uid.isInvalid();
1650static bool subkeyIsOk(
const Subkey &s)
1652 return !s.isRevoked() && !s.isInvalid() && !s.isDisabled();
1657time_t creationTimeOfNewestSuitableSubKey(
const Key &key, KeyCache::KeyUsage usage)
1659 time_t creationTime = 0;
1660 for (
const Subkey &s : key.subkeys()) {
1661 if (!subkeyIsOk(s)) {
1664 if (usage == KeyCache::KeyUsage::Sign && !s.canSign()) {
1667 if (usage == KeyCache::KeyUsage::Encrypt && !s.canEncrypt()) {
1670 if (s.creationTime() > creationTime) {
1671 creationTime = s.creationTime();
1674 return creationTime;
1680 time_t creationTime = 0;
1684GpgME::Key KeyCache::findBestByMailBox(
const char *addr, GpgME::Protocol proto, KeyUsage usage)
const
1686 d->ensureCachePopulated();
1693 if (
address.size() > 1 && address[0] ==
'<' && address[
address.size() - 1] ==
'>') {
1699 for (
const Key &k : findByEMailAddress(
address.constData())) {
1700 if (proto != Protocol::UnknownProtocol && k.protocol() != proto) {
1703 if (usage == KeyUsage::Encrypt && !keyHasEncrypt(k)) {
1706 if (usage == KeyUsage::Sign && (!keyHasSign(k) || !k.hasSecret())) {
1709 const time_t creationTime = creationTimeOfNewestSuitableSubKey(k, usage);
1710 if (creationTime == 0) {
1714 for (
const UserID &u : k.userIDs()) {
1719 if (best.uid.isNull()) {
1721 best = {k, u, creationTime};
1722 }
else if (!uidIsOk(best.uid) && uidIsOk(u)) {
1724 best = {k, u, creationTime};
1725 }
else if (!k.isExpired() && best.uid.validity() < u.validity()) {
1727 best = {k, u, creationTime};
1728 }
else if (best.key.isExpired() && !k.isExpired()) {
1730 best = {k, u, creationTime};
1731 }
else if (best.uid.validity() == u.validity() && uidIsOk(u) && best.creationTime < creationTime) {
1733 best = {k, u, creationTime};
1744bool allKeysAllowUsage(
const T &keys, KeyCache::KeyUsage usage)
1747 case KeyCache::KeyUsage::AnyUsage:
1749 case KeyCache::KeyUsage::Sign:
1750 return std::all_of(std::begin(keys), std::end(keys), std::mem_fn(&Key::hasSign));
1751 case KeyCache::KeyUsage::Encrypt:
1752 return std::all_of(std::begin(keys), std::end(keys), std::mem_fn(&Key::hasEncrypt));
1753 case KeyCache::KeyUsage::Certify:
1754 return std::all_of(std::begin(keys), std::end(keys), std::mem_fn(&Key::hasCertify));
1755 case KeyCache::KeyUsage::Authenticate:
1756 return std::all_of(std::begin(keys), std::end(keys), std::mem_fn(&Key::hasAuthenticate));
1758 qCDebug(LIBKLEO_LOG) << __func__ <<
"called with invalid usage" << int(usage);
1763KeyGroup KeyCache::findGroup(
const QString &name, Protocol protocol, KeyUsage usage)
const
1765 d->ensureCachePopulated();
1767 Q_ASSERT(usage == KeyUsage::Sign || usage == KeyUsage::Encrypt);
1768 for (
const auto &group : std::as_const(d->m_groups)) {
1769 if (group.name() == name) {
1770 const KeyGroup::Keys &keys = group.keys();
1771 if (allKeysAllowUsage(keys, usage) && (protocol == UnknownProtocol || allKeysHaveProtocol(keys, protocol))) {
1780std::vector<Key> KeyCache::getGroupKeys(
const QString &groupName)
const
1782 std::vector<Key> result;
1783 for (
const KeyGroup &g : std::as_const(d->m_groups)) {
1784 if (g.name() == groupName) {
1785 const KeyGroup::Keys &keys = g.keys();
1786 std::copy(keys.cbegin(), keys.cend(), std::back_inserter(result));
1789 _detail::sort_by_fpr(result);
1790 _detail::remove_duplicates_by_fpr(result);
1794void KeyCache::setKeys(
const std::vector<GpgME::Key> &keys)
1797 setRefreshInterval(0);
1801 d->m_initalized =
true;
1802 Q_EMIT keyListingDone(KeyListResult());
1805void KeyCache::setGroups(
const std::vector<KeyGroup> &groups)
1807 Q_ASSERT(d->m_initalized &&
"Call setKeys() before setting groups");
1808 d->m_groups = groups;
1809 Q_EMIT keysMayHaveChanged();
1812std::vector<CardKeyStorageInfo> KeyCache::cardsForSubkey(
const GpgME::Subkey &subkey)
const
1814 return d->m_cards[
QByteArray(subkey.keyGrip())];
1817#include "moc_keycache.cpp"
1818#include "moc_keycache_p.cpp"
PostalAddress address(const QVariant &location)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QAction * replace(const QObject *recvr, const char *slot, QObject *parent)
QByteArray fromStdString(const std::string &str)
QByteArray toLower() const const
int exec(ProcessEventsFlags flags)
bool removeOne(const AT &t)
const_iterator cbegin() const const
const_iterator cend() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * sender() const const
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
QString trimmed() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setInterval(int msec)