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;
62using namespace KMime::Types;
64static const unsigned int hours2ms = 1000 * 60 * 60;
75make_comparator_str(ByEMail, .first.c_str());
79class Kleo::KeyCacheAutoRefreshSuspension
81 KeyCacheAutoRefreshSuspension()
83 qCDebug(LIBKLEO_LOG) << __func__;
84 auto cache = KeyCache::mutableInstance();
85 cache->enableFileSystemWatcher(
false);
86 m_refreshInterval = cache->refreshInterval();
87 cache->setRefreshInterval(0);
88 cache->cancelKeyListing();
93 ~KeyCacheAutoRefreshSuspension()
95 qCDebug(LIBKLEO_LOG) << __func__;
96 if (
auto cache = m_cache.lock()) {
97 cache->enableFileSystemWatcher(
true);
98 cache->setRefreshInterval(m_refreshInterval);
102 static std::shared_ptr<KeyCacheAutoRefreshSuspension> instance()
104 static std::weak_ptr<KeyCacheAutoRefreshSuspension> self;
105 if (
auto s = self.lock()) {
108 s = std::shared_ptr<KeyCacheAutoRefreshSuspension>{
new KeyCacheAutoRefreshSuspension{}};
115 std::weak_ptr<KeyCache> m_cache;
116 int m_refreshInterval = 0;
119class KeyCache::Private
121 friend class ::Kleo::KeyCache;
125 explicit Private(KeyCache *qq)
127 , m_refreshInterval(1)
128 , m_initalized(false)
130 , m_remarks_enabled(false)
133 q->startKeyListing();
135 updateAutoKeyListingTimer();
141 m_refreshJob->cancel();
145 template<
template<
template<
typename U>
class Op>
class Comp>
146 std::vector<Key>::const_iterator find(const std::vector<Key> &keys, const char *key) const
148 ensureCachePopulated();
149 const auto it = std::lower_bound(keys.begin(), keys.end(), key, Comp<std::less>());
150 if (it == keys.end() || Comp<std::equal_to>()(*it, key)) {
157 template<
template<
template<
typename U>
class Op>
class Comp>
158 std::vector<Subkey>::const_iterator find(const std::vector<Subkey> &keys, const char *key) const
160 ensureCachePopulated();
161 const auto it = std::lower_bound(keys.begin(), keys.end(), key, Comp<std::less>());
162 if (it == keys.end() || Comp<std::equal_to>()(*it, key)) {
169 std::vector<Key>::const_iterator find_fpr(
const char *fpr)
const
171 return find<_detail::ByFingerprint>(by.fpr, fpr);
174 std::pair<std::vector<std::pair<std::string, Key>>::const_iterator, std::vector<std::pair<std::string, Key>>::const_iterator>
175 find_email(
const char *email)
const
177 ensureCachePopulated();
178 return std::equal_range(by.email.begin(), by.email.end(), email, ByEMail<std::less>());
181 std::vector<Key> find_mailbox(
const QString &email,
bool sign)
const;
183 std::vector<Subkey>::const_iterator find_keygrip(
const char *keygrip)
const
185 return find<_detail::ByKeyGrip>(by.keygrip, keygrip);
188 std::vector<Subkey>::const_iterator find_subkeyid(
const char *subkeyid)
const
190 return find<_detail::ByKeyID>(by.subkeyid, subkeyid);
193 std::vector<Key>::const_iterator find_keyid(
const char *keyid)
const
195 return find<_detail::ByKeyID>(by.keyid, keyid);
198 std::vector<Key>::const_iterator find_shortkeyid(
const char *shortkeyid)
const
200 return find<_detail::ByShortKeyID>(by.shortkeyid, shortkeyid);
203 std::pair<std::vector<Key>::const_iterator, std::vector<Key>::const_iterator> find_subjects(
const char *chain_id)
const
205 ensureCachePopulated();
206 return std::equal_range(by.chainid.begin(), by.chainid.end(), chain_id, _detail::ByChainID<std::less>());
209 void refreshJobDone(
const KeyListResult &result);
211 void setRefreshInterval(
int interval)
213 m_refreshInterval = interval;
214 updateAutoKeyListingTimer();
217 int refreshInterval()
const
219 return m_refreshInterval;
222 void updateAutoKeyListingTimer()
224 setAutoKeyListingInterval(hours2ms * m_refreshInterval);
226 void setAutoKeyListingInterval(
int ms)
228 m_autoKeyListingTimer.
stop();
231 m_autoKeyListingTimer.
start();
235 void ensureCachePopulated()
const;
237 void readGroupsFromGpgConf()
243 auto conf = QGpgME::cryptoConfig();
248 auto entry = getCryptoConfigEntry(conf,
"gpg",
"group");
255 const auto stringValueList = entry->stringValueList();
256 for (
const QString &value : stringValueList) {
258 if (split.size() != 2) {
259 qCDebug(LIBKLEO_LOG) <<
"Ignoring invalid group config:" << value;
262 const QString groupName = split[0];
263 const QString fingerprint = split[1];
264 fingerprints[groupName].push_back(fingerprint);
268 for (
auto it = fingerprints.
cbegin(); it != fingerprints.
cend(); ++it) {
269 const QString groupName = it.key();
270 const std::vector<Key> groupKeys = q->findByFingerprint(toStdStrings(it.value()));
271 KeyGroup g(groupName, groupName, groupKeys, KeyGroup::GnuPGConfig);
272 m_groups.push_back(g);
276 void readGroupsFromGroupsConfig()
278 Q_ASSERT(m_groupConfig);
279 if (!m_groupConfig) {
280 qCWarning(LIBKLEO_LOG) << __func__ <<
"group config not set";
284 m_groups = m_groupConfig->readGroups();
287 KeyGroup writeGroupToGroupsConfig(
const KeyGroup &group)
289 Q_ASSERT(m_groupConfig);
290 if (!m_groupConfig) {
291 qCWarning(LIBKLEO_LOG) << __func__ <<
"group config not set";
295 Q_ASSERT(!group.isNull());
296 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
297 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
298 qCDebug(LIBKLEO_LOG) << __func__ <<
"group cannot be written to application configuration:" << group;
302 return m_groupConfig->writeGroup(group);
305 bool removeGroupFromGroupsConfig(
const KeyGroup &group)
307 Q_ASSERT(m_groupConfig);
308 if (!m_groupConfig) {
309 qCWarning(LIBKLEO_LOG) << __func__ <<
"group config not set";
313 Q_ASSERT(!group.isNull());
314 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
315 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
316 qCDebug(LIBKLEO_LOG) << __func__ <<
"group cannot be removed from application configuration:" << group;
320 return m_groupConfig->removeGroup(group);
323 void updateGroupCache()
330 if (m_groupsEnabled) {
331 readGroupsFromGpgConf();
332 readGroupsFromGroupsConfig();
336 bool insert(
const KeyGroup &group)
338 Q_ASSERT(!group.isNull());
339 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
340 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
341 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::insert - Invalid group:" << group;
344 const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](
const auto &g) {
345 return g.source() == group.source() && g.id() == group.id();
347 if (it != m_groups.cend()) {
348 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::insert - Group already present in list of groups:" << group;
352 const KeyGroup savedGroup = writeGroupToGroupsConfig(group);
353 if (savedGroup.isNull()) {
354 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::insert - Writing group" << group.id() <<
"to config file failed";
358 m_groups.push_back(savedGroup);
360 Q_EMIT q->groupAdded(savedGroup);
365 bool update(
const KeyGroup &group)
367 Q_ASSERT(!group.isNull());
368 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
369 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
370 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::update - Invalid group:" << group;
373 const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](
const auto &g) {
374 return g.source() == group.source() && g.id() == group.id();
376 if (it == m_groups.cend()) {
377 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::update - Group not found in list of groups:" << group;
380 const auto groupIndex = std::distance(m_groups.cbegin(), it);
382 const KeyGroup savedGroup = writeGroupToGroupsConfig(group);
383 if (savedGroup.isNull()) {
384 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::update - Writing group" << group.id() <<
"to config file failed";
388 m_groups[groupIndex] = savedGroup;
390 Q_EMIT q->groupUpdated(savedGroup);
395 bool remove(
const KeyGroup &group)
397 Q_ASSERT(!group.isNull());
398 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
399 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
400 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::remove - Invalid group:" << group;
403 const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](
const auto &g) {
404 return g.source() == group.source() && g.id() == group.id();
406 if (it == m_groups.cend()) {
407 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::remove - Group not found in list of groups:" << group;
411 const bool success = removeGroupFromGroupsConfig(group);
413 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::remove - Removing group" << group.id() <<
"from config file failed";
419 Q_EMIT q->groupRemoved(group);
426 std::vector<std::shared_ptr<FileSystemWatcher>> m_fsWatchers;
427 QTimer m_autoKeyListingTimer;
428 int m_refreshInterval;
431 std::vector<Key> fpr, keyid, shortkeyid, chainid;
432 std::vector<std::pair<std::string, Key>> email;
433 std::vector<Subkey> subkeyid, keygrip;
437 bool m_remarks_enabled;
438 bool m_groupsEnabled =
false;
439 std::shared_ptr<KeyGroupConfig> m_groupConfig;
440 std::vector<KeyGroup> m_groups;
441 std::unordered_map<QByteArray, std::vector<CardKeyStorageInfo>> m_cards;
444std::shared_ptr<const KeyCache> KeyCache::instance()
446 return mutableInstance();
449std::shared_ptr<KeyCache> KeyCache::mutableInstance()
451 static std::weak_ptr<KeyCache> self;
453 return std::shared_ptr<KeyCache>(self);
454 }
catch (
const std::bad_weak_ptr &) {
455 const std::shared_ptr<KeyCache> s(
new KeyCache);
463 , d(new Private(this))
471void KeyCache::setGroupsEnabled(
bool enabled)
473 d->m_groupsEnabled = enabled;
474 if (d->m_initalized) {
475 d->updateGroupCache();
479void KeyCache::setGroupConfig(
const std::shared_ptr<KeyGroupConfig> &groupConfig)
481 d->m_groupConfig = groupConfig;
484void KeyCache::enableFileSystemWatcher(
bool enable)
486 for (
const auto &i : std::as_const(d->m_fsWatchers)) {
487 i->setEnabled(enable);
491void KeyCache::setRefreshInterval(
int hours)
493 d->setRefreshInterval(hours);
496int KeyCache::refreshInterval()
const
498 return d->refreshInterval();
501std::shared_ptr<KeyCacheAutoRefreshSuspension> KeyCache::suspendAutoRefresh()
503 return KeyCacheAutoRefreshSuspension::instance();
506void KeyCache::reload(GpgME::Protocol , ReloadOption option)
508 qCDebug(LIBKLEO_LOG) <<
this << __func__ <<
"option:" << option;
509 const bool forceReload = option & ForceReload;
510 if (d->m_refreshJob && !forceReload) {
511 qCDebug(LIBKLEO_LOG) <<
this << __func__ <<
"- refresh already running";
514 if (d->m_refreshJob) {
515 disconnect(d->m_refreshJob.data(),
nullptr,
this,
nullptr);
516 d->m_refreshJob->cancel();
517 d->m_refreshJob.clear();
520 d->updateAutoKeyListingTimer();
522 enableFileSystemWatcher(
false);
523 d->m_refreshJob =
new RefreshKeysJob(
this);
524 connect(d->m_refreshJob.data(), &RefreshKeysJob::done,
this, [
this](
const GpgME::KeyListResult &r) {
525 qCDebug(LIBKLEO_LOG) << d->m_refreshJob.data() <<
"RefreshKeysJob::done";
526 d->refreshJobDone(r);
528 connect(d->m_refreshJob.data(), &RefreshKeysJob::canceled,
this, [
this]() {
529 qCDebug(LIBKLEO_LOG) << d->m_refreshJob.data() <<
"RefreshKeysJob::canceled";
530 d->m_refreshJob.clear();
532 d->m_refreshJob->start();
535void KeyCache::cancelKeyListing()
537 if (!d->m_refreshJob) {
540 d->m_refreshJob->cancel();
543void KeyCache::addFileSystemWatcher(
const std::shared_ptr<FileSystemWatcher> &watcher)
548 d->m_fsWatchers.push_back(watcher);
549 connect(watcher.get(), &FileSystemWatcher::directoryChanged,
this, [
this]() {
552 connect(watcher.get(), &FileSystemWatcher::fileChanged,
this, [
this]() {
556 watcher->setEnabled(d->m_refreshJob.isNull());
559void KeyCache::enableRemarks(
bool value)
561 if (!d->m_remarks_enabled && value) {
562 d->m_remarks_enabled = value;
563 if (d->m_initalized && !d->m_refreshJob) {
564 qCDebug(LIBKLEO_LOG) <<
"Reloading keycache with remarks enabled";
568 d->m_remarks_enabled = value;
572bool KeyCache::remarksEnabled()
const
574 return d->m_remarks_enabled;
577void KeyCache::Private::refreshJobDone(
const KeyListResult &result)
579 m_refreshJob.
clear();
580 q->enableFileSystemWatcher(
true);
581 if (!m_initalized && q->remarksEnabled()) {
586 qCDebug(LIBKLEO_LOG) <<
"Reloading keycache with remarks enabled";
593 Q_EMIT q->keyListingDone(result);
596const Key &KeyCache::findByFingerprint(
const char *fpr)
const
598 const std::vector<Key>::const_iterator it = d->find_fpr(fpr);
599 if (it == d->by.fpr.end()) {
600 static const Key null;
607const Key &KeyCache::findByFingerprint(
const std::string &fpr)
const
609 return findByFingerprint(fpr.c_str());
612std::vector<GpgME::Key> KeyCache::findByFingerprint(
const std::vector<std::string> &fprs)
const
614 std::vector<Key> keys;
615 keys.reserve(fprs.size());
616 for (
const auto &fpr : fprs) {
617 const Key key = findByFingerprint(fpr.c_str());
619 qCDebug(LIBKLEO_LOG) << __func__ <<
"Ignoring unknown key with fingerprint:" << fpr.c_str();
627std::vector<Key> KeyCache::findByEMailAddress(
const char *email)
const
629 const auto pair = d->find_email(email);
630 std::vector<Key> result;
631 result.reserve(std::distance(pair.first, pair.second));
632 std::transform(pair.first, pair.second, std::back_inserter(result), [](
const std::pair<std::string, Key> &pair) {
638std::vector<Key> KeyCache::findByEMailAddress(
const std::string &email)
const
640 return findByEMailAddress(email.c_str());
643const Key &KeyCache::findByShortKeyID(
const char *
id)
const
645 const std::vector<Key>::const_iterator it = d->find_shortkeyid(
id);
646 if (it != d->by.shortkeyid.end()) {
649 static const Key null;
653const Key &KeyCache::findByShortKeyID(
const std::string &
id)
const
655 return findByShortKeyID(
id.c_str());
658const Key &KeyCache::findByKeyIDOrFingerprint(
const char *
id)
const
662 const std::vector<Key>::const_iterator it = d->find_fpr(
id);
663 if (it != d->by.fpr.end()) {
669 const std::vector<Key>::const_iterator it = d->find_keyid(
id);
670 if (it != d->by.keyid.end()) {
674 static const Key null;
678const Key &KeyCache::findByKeyIDOrFingerprint(
const std::string &
id)
const
680 return findByKeyIDOrFingerprint(
id.c_str());
683std::vector<Key> KeyCache::findByKeyIDOrFingerprint(
const std::vector<std::string> &ids)
const
685 std::vector<std::string> keyids;
686 std::remove_copy_if(ids.begin(), ids.end(), std::back_inserter(keyids), [](
const std::string &str) {
687 return !str.c_str() || !*str.c_str();
691 std::sort(keyids.begin(), keyids.end(), _detail::ByFingerprint<std::less>());
693 std::vector<Key> result;
694 result.reserve(keyids.size());
695 d->ensureCachePopulated();
697 kdtools::set_intersection(d->by.fpr.begin(),
701 std::back_inserter(result),
702 _detail::ByFingerprint<std::less>());
703 if (result.size() < keyids.size()) {
706 kdtools::set_intersection(d->by.keyid.begin(),
710 std::back_inserter(result),
711 _detail::ByKeyID<std::less>());
714 std::sort(result.begin(), result.end(), _detail::ByFingerprint<std::less>());
715 result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
723const Subkey &KeyCache::findSubkeyByKeyGrip(
const char *grip, Protocol protocol)
const
725 static const Subkey null;
726 d->ensureCachePopulated();
727 const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), grip, _detail::ByKeyGrip<std::less>());
728 if (range.first == range.second) {
730 }
else if (protocol == UnknownProtocol) {
733 for (
auto it = range.first; it != range.second; ++it) {
734 if (it->parent().protocol() == protocol) {
742const Subkey &KeyCache::findSubkeyByKeyGrip(
const std::string &grip, Protocol protocol)
const
744 return findSubkeyByKeyGrip(grip.c_str(), protocol);
747std::vector<GpgME::Subkey> Kleo::KeyCache::findSubkeysByKeyGrip(
const char *grip, GpgME::Protocol protocol)
const
749 d->ensureCachePopulated();
751 std::vector<GpgME::Subkey> subkeys;
752 const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), grip, _detail::ByKeyGrip<std::less>());
753 subkeys.reserve(std::distance(range.first, range.second));
754 if (protocol == UnknownProtocol) {
755 std::copy(range.first, range.second, std::back_inserter(subkeys));
757 std::copy_if(range.first, range.second, std::back_inserter(subkeys), [protocol](
const auto &subkey) {
758 return subkey.parent().protocol() == protocol;
764std::vector<GpgME::Subkey> Kleo::KeyCache::findSubkeysByKeyGrip(
const std::string &grip, GpgME::Protocol protocol)
const
766 return findSubkeysByKeyGrip(grip.c_str(), protocol);
769std::vector<Subkey> KeyCache::findSubkeysByKeyID(
const std::vector<std::string> &ids)
const
771 std::vector<std::string> sorted;
772 sorted.reserve(ids.size());
773 std::remove_copy_if(ids.begin(), ids.end(), std::back_inserter(sorted), [](
const std::string &str) {
774 return !str.c_str() || !*str.c_str();
777 std::sort(sorted.begin(), sorted.end(), _detail::ByKeyID<std::less>());
779 std::vector<Subkey> result;
780 d->ensureCachePopulated();
781 kdtools::set_intersection(d->by.subkeyid.begin(),
782 d->by.subkeyid.end(),
785 std::back_inserter(result),
786 _detail::ByKeyID<std::less>());
790std::vector<Key> KeyCache::findRecipients(
const DecryptionResult &res)
const
792 std::vector<std::string> keyids;
793 const auto recipients = res.recipients();
794 for (
const DecryptionResult::Recipient &r : recipients) {
795 if (
const char *kid = r.keyID()) {
796 keyids.push_back(kid);
799 const std::vector<Subkey> subkeys = findSubkeysByKeyID(keyids);
800 std::vector<Key> result;
801 result.reserve(subkeys.size());
802 std::transform(subkeys.begin(), subkeys.end(), std::back_inserter(result), std::mem_fn(&Subkey::parent));
804 std::sort(result.begin(), result.end(), _detail::ByFingerprint<std::less>());
805 result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
809std::vector<Key> KeyCache::findSigners(
const VerificationResult &res)
const
811 std::vector<std::string> fprs;
812 const auto signatures = res.signatures();
813 for (
const Signature &s : signatures) {
814 if (
const char *fpr = s.fingerprint()) {
818 return findByKeyIDOrFingerprint(fprs);
821std::vector<Key> KeyCache::findSigningKeysByMailbox(
const QString &mb)
const
823 return d->find_mailbox(mb,
true);
826std::vector<Key> KeyCache::findEncryptionKeysByMailbox(
const QString &mb)
const
828 return d->find_mailbox(mb,
false);
833#define DO(op, meth, meth2) \
834 if (op key.meth()) { \
836 qDebug("rejecting for signing: %s: %s", #meth2, key.primaryFingerprint()); \
839#define ACCEPT(meth) DO(!!, meth, !meth)
840#define REJECT(meth) DO(!, meth, meth)
841struct ready_for_signing {
842 bool operator()(
const Key &key)
const
845#if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
859#define DO(op, meth, meth2) \
860 if (op key.meth()) { \
862 qDebug("rejecting for encrypting: %s: %s", #meth2, key.primaryFingerprint()); \
865struct ready_for_encryption {
866 bool operator()(
const Key &key)
const
869#if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
880 return key.hasEncrypt() && !key.isRevoked() && !key.isExpired() && !key.isDisabled() && !key.isInvalid();
889std::vector<Key> KeyCache::Private::find_mailbox(
const QString &email,
bool sign)
const
891 if (email.isEmpty()) {
892 return std::vector<Key>();
895 const auto pair = find_email(email.toUtf8().constData());
896 std::vector<Key> result;
897 result.reserve(std::distance(pair.first, pair.second));
899 kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_signing());
901 kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_encryption());
907std::vector<Key> KeyCache::findSubjects(
const GpgME::Key &key, Options options)
const
913 return findSubjects(std::vector<Key>(1, key), options);
916std::vector<Key> KeyCache::findSubjects(
const std::vector<Key> &keys, Options options)
const
918 std::vector<Key> result;
925 for (
const auto &key : keys) {
926 const auto firstAndLastSubject = d->find_subjects(key.primaryFingerprint());
927 result.insert(result.end(), firstAndLastSubject.first, firstAndLastSubject.second);
930 _detail::sort_by_fpr(result);
931 _detail::remove_duplicates_by_fpr(result);
933 if (options & RecursiveSearch) {
934 for (std::vector<Key> furtherSubjects = findSubjects(result, NoOption);
935 !furtherSubjects.empty();
936 furtherSubjects = findSubjects(furtherSubjects, NoOption)) {
937 std::vector<Key> combined;
938 combined.reserve(result.size() + furtherSubjects.size());
939 std::merge(result.begin(),
941 furtherSubjects.begin(),
942 furtherSubjects.end(),
943 std::back_inserter(combined),
944 _detail::ByFingerprint<std::less>());
945 _detail::remove_duplicates_by_fpr(combined);
946 if (result.size() == combined.size()) {
950 result.swap(combined);
957std::vector<Key> KeyCache::findIssuers(
const Key &key, Options options)
const
959 std::vector<Key> result;
965 if (options & IncludeSubject) {
966 result.push_back(key);
973 Key issuer = findByFingerprint(key.chainID());
975 if (issuer.isNull()) {
979 result.push_back(issuer);
981 if (!(options & RecursiveSearch)) {
985 while (!issuer.isRoot()) {
986 issuer = findByFingerprint(result.back().chainID());
987 if (issuer.isNull()) {
990 const bool chainAlreadyContainsIssuer = Kleo::contains_if(result, [issuer](
const auto &key) {
991 return _detail::ByFingerprint<std::equal_to>()(issuer, key);
995 result.push_back(issuer);
996 if (chainAlreadyContainsIssuer) {
1005static std::string email(
const UserID &uid)
1008 const std::string addr = uid.addrSpec();
1009 if (!addr.empty()) {
1012 const std::string email = uid.email();
1013 if (email.empty()) {
1014 return DN(uid.id())[QStringLiteral(
"EMAIL")].trimmed().toUtf8().constData();
1016 if (email[0] ==
'<' && email[email.size() - 1] ==
'>') {
1017 return email.substr(1, email.size() - 2);
1023static std::vector<std::string> emails(
const Key &key)
1025 std::vector<std::string> emails;
1026 const auto userIDs = key.userIDs();
1027 for (
const UserID &uid : userIDs) {
1028 const std::string e = email(uid);
1030 emails.push_back(e);
1033 std::sort(emails.begin(), emails.end(), ByEMail<std::less>());
1034 emails.erase(std::unique(emails.begin(), emails.end(), ByEMail<std::equal_to>()), emails.end());
1038void KeyCache::remove(
const Key &key)
1044 const char *fpr = key.primaryFingerprint();
1050 const auto range = std::equal_range(d->by.fpr.begin(), d->by.fpr.end(), fpr, _detail::ByFingerprint<std::less>());
1051 d->by.fpr.erase(range.first, range.second);
1054 if (
const char *keyid = key.keyID()) {
1055 const auto range = std::equal_range(d->by.keyid.begin(), d->by.keyid.end(), keyid, _detail::ByKeyID<std::less>());
1056 const auto it = std::remove_if(range.first, range.second, [fpr](
const GpgME::Key &key) {
1057 return _detail::ByFingerprint<std::equal_to>()(fpr, key);
1059 d->by.keyid.erase(it, range.second);
1062 if (
const char *shortkeyid = key.shortKeyID()) {
1063 const auto range = std::equal_range(d->by.shortkeyid.begin(), d->by.shortkeyid.end(), shortkeyid, _detail::ByShortKeyID<std::less>());
1064 const auto it = std::remove_if(range.first, range.second, [fpr](
const GpgME::Key &key) {
1065 return _detail::ByFingerprint<std::equal_to>()(fpr, key);
1067 d->by.shortkeyid.erase(it, range.second);
1070 if (
const char *chainid = key.chainID()) {
1071 const auto range = std::equal_range(d->by.chainid.begin(), d->by.chainid.end(), chainid, _detail::ByChainID<std::less>());
1072 const auto range2 = std::equal_range(range.first, range.second, fpr, _detail::ByFingerprint<std::less>());
1073 d->by.chainid.erase(range2.first, range2.second);
1076 const auto emailsKey{emails(key)};
1077 for (
const std::string &email : emailsKey) {
1078 const auto range = std::equal_range(d->by.email.begin(), d->by.email.end(), email, ByEMail<std::less>());
1079 const auto it = std::remove_if(range.first, range.second, [fpr](
const std::pair<std::string, Key> &pair) {
1080 return qstricmp(fpr, pair.second.primaryFingerprint()) == 0;
1082 d->by.email.erase(it, range.second);
1085 const auto keySubKeys{key.subkeys()};
1086 for (
const Subkey &subkey : keySubKeys) {
1087 if (
const char *keyid = subkey.keyID()) {
1088 const auto range = std::equal_range(d->by.subkeyid.begin(), d->by.subkeyid.end(), keyid, _detail::ByKeyID<std::less>());
1089 const auto it = std::remove_if(range.first, range.second, [fpr](
const Subkey &subkey) {
1090 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1092 d->by.subkeyid.erase(it, range.second);
1094 if (
const char *keygrip = subkey.keyGrip()) {
1095 const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), keygrip, _detail::ByKeyGrip<std::less>());
1096 const auto it = std::remove_if(range.first, range.second, [fpr](
const Subkey &subkey) {
1097 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1099 d->by.keygrip.erase(it, range.second);
1104void KeyCache::remove(
const std::vector<Key> &keys)
1106 for (
const Key &key : keys) {
1111const std::vector<GpgME::Key> &KeyCache::keys()
const
1113 d->ensureCachePopulated();
1117std::vector<Key> KeyCache::secretKeys()
const
1119 std::vector<Key> keys = this->keys();
1120 keys.erase(std::remove_if(keys.begin(),
1122 [](
const Key &key) {
1123 return !key.hasSecret();
1129KeyGroup KeyCache::group(
const QString &
id)
const
1132 const auto it = std::find_if(std::cbegin(d->m_groups), std::cend(d->m_groups), [
id](
const auto &g) {
1133 return g.id() == id;
1135 if (it != std::cend(d->m_groups)) {
1141std::vector<KeyGroup> KeyCache::groups()
const
1143 d->ensureCachePopulated();
1147std::vector<KeyGroup> KeyCache::configurableGroups()
const
1149 std::vector<KeyGroup> groups;
1150 groups.reserve(d->m_groups.size());
1151 std::copy_if(d->m_groups.cbegin(), d->m_groups.cend(), std::back_inserter(groups), [](
const KeyGroup &group) {
1152 return group.source() == KeyGroup::ApplicationConfig;
1159bool compareById(
const KeyGroup &lhs,
const KeyGroup &rhs)
1161 return lhs.id() < rhs.id();
1164std::vector<KeyGroup> sortedById(std::vector<KeyGroup> groups)
1166 std::sort(groups.begin(), groups.end(), &compareById);
1171void KeyCache::saveConfigurableGroups(
const std::vector<KeyGroup> &groups)
1173 const std::vector<KeyGroup> oldGroups = sortedById(configurableGroups());
1174 const std::vector<KeyGroup> newGroups = sortedById(groups);
1177 std::vector<KeyGroup> removedGroups;
1178 std::set_difference(oldGroups.begin(), oldGroups.end(), newGroups.begin(), newGroups.end(), std::back_inserter(removedGroups), &compareById);
1179 for (
const auto &group : std::as_const(removedGroups)) {
1180 qCDebug(LIBKLEO_LOG) <<
"Removing group" << group;
1185 std::vector<KeyGroup> updatedGroups;
1186 std::set_intersection(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(updatedGroups), &compareById);
1187 for (
const auto &group : std::as_const(updatedGroups)) {
1188 qCDebug(LIBKLEO_LOG) <<
"Updating group" << group;
1193 std::vector<KeyGroup> addedGroups;
1194 std::set_difference(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(addedGroups), &compareById);
1195 for (
const auto &group : std::as_const(addedGroups)) {
1196 qCDebug(LIBKLEO_LOG) <<
"Adding group" << group;
1201 Q_EMIT keysMayHaveChanged();
1204bool KeyCache::insert(
const KeyGroup &group)
1206 if (!d->insert(group)) {
1210 Q_EMIT keysMayHaveChanged();
1215bool KeyCache::update(
const KeyGroup &group)
1217 if (!d->update(group)) {
1221 Q_EMIT keysMayHaveChanged();
1226bool KeyCache::remove(
const KeyGroup &group)
1228 if (!d->remove(group)) {
1232 Q_EMIT keysMayHaveChanged();
1237void KeyCache::refresh(
const std::vector<Key> &keys)
1244void KeyCache::insert(
const Key &key)
1246 insert(std::vector<Key>(1, key));
1252template<
template<
template<
typename T>
class Op>
class T1, template<template<typename T> class Op> class T2>
1253struct lexicographically {
1254 using result_type = bool;
1256 template<
typename U,
typename V>
1257 bool operator()(
const U &lhs,
const V &rhs)
const
1259 return T1<std::less>()(lhs, rhs)
1260 || (T1<std::equal_to>()(lhs, rhs) && T2<std::less>()(lhs, rhs));
1266void KeyCache::insert(
const std::vector<Key> &keys)
1269 std::vector<Key> sorted;
1270 sorted.reserve(keys.size());
1271 std::copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), [](
const Key &key) {
1272 auto fp = key.primaryFingerprint();
1280 std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
1283 std::vector<Key> by_fpr;
1284 by_fpr.reserve(sorted.size() + d->by.fpr.size());
1285 std::merge(sorted.begin(), sorted.end(), d->by.fpr.begin(), d->by.fpr.end(), std::back_inserter(by_fpr), _detail::ByFingerprint<std::less>());
1288 std::vector<std::pair<std::string, Key>> pairs;
1289 pairs.reserve(sorted.size());
1290 for (
const Key &key : std::as_const(sorted)) {
1291 const std::vector<std::string> emails = ::emails(key);
1292 for (
const std::string &e : emails) {
1293 pairs.push_back(std::make_pair(e, key));
1296 std::sort(pairs.begin(), pairs.end(), ByEMail<std::less>());
1299 std::vector<std::pair<std::string, Key>> by_email;
1300 by_email.reserve(pairs.size() + d->by.email.size());
1301 std::merge(pairs.begin(), pairs.end(), d->by.email.begin(), d->by.email.end(), std::back_inserter(by_email), ByEMail<std::less>());
1304 std::stable_sort(sorted.begin(), sorted.end(), _detail::ByChainID<std::less>());
1307 std::vector<Key> nonroot;
1308 nonroot.reserve(sorted.size());
1309 std::vector<Key> by_chainid;
1310 by_chainid.reserve(sorted.size() + d->by.chainid.size());
1311 std::copy_if(sorted.cbegin(), sorted.cend(), std::back_inserter(nonroot), [](
const Key &key) {
1312 return !key.isRoot();
1314 std::merge(nonroot.cbegin(),
1316 d->by.chainid.cbegin(),
1317 d->by.chainid.cend(),
1318 std::back_inserter(by_chainid),
1319 lexicographically<_detail::ByChainID, _detail::ByFingerprint>());
1322 std::sort(sorted.begin(), sorted.end(), _detail::ByKeyID<std::less>());
1325 std::vector<Key> by_keyid;
1326 by_keyid.reserve(sorted.size() + d->by.keyid.size());
1327 std::merge(sorted.begin(), sorted.end(), d->by.keyid.begin(), d->by.keyid.end(), std::back_inserter(by_keyid), _detail::ByKeyID<std::less>());
1330 std::sort(sorted.begin(), sorted.end(), _detail::ByShortKeyID<std::less>());
1333 std::vector<Key> by_shortkeyid;
1334 by_shortkeyid.reserve(sorted.size() + d->by.shortkeyid.size());
1335 std::merge(sorted.begin(),
1337 d->by.shortkeyid.begin(),
1338 d->by.shortkeyid.end(),
1339 std::back_inserter(by_shortkeyid),
1340 _detail::ByShortKeyID<std::less>());
1343 std::vector<Subkey> subkeys;
1344 subkeys.reserve(sorted.size());
1345 for (
const Key &key : std::as_const(sorted)) {
1346 const auto keySubkeys{key.subkeys()};
1347 for (
const Subkey &subkey : keySubkeys) {
1348 subkeys.push_back(subkey);
1353 std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyID<std::less>());
1356 std::vector<Subkey> by_subkeyid;
1357 by_subkeyid.reserve(subkeys.size() + d->by.subkeyid.size());
1358 std::merge(subkeys.begin(), subkeys.end(), d->by.subkeyid.begin(), d->by.subkeyid.end(), std::back_inserter(by_subkeyid), _detail::ByKeyID<std::less>());
1361 std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyGrip<std::less>());
1364 std::vector<Subkey> by_keygrip;
1365 by_keygrip.reserve(subkeys.size() + d->by.keygrip.size());
1366 std::merge(subkeys.begin(), subkeys.end(), d->by.keygrip.begin(), d->by.keygrip.end(), std::back_inserter(by_keygrip), _detail::ByKeyGrip<std::less>());
1369 by_fpr.swap(d->by.fpr);
1370 by_keyid.swap(d->by.keyid);
1371 by_shortkeyid.swap(d->by.shortkeyid);
1372 by_email.swap(d->by.email);
1373 by_subkeyid.swap(d->by.subkeyid);
1374 by_keygrip.swap(d->by.keygrip);
1375 by_chainid.swap(d->by.chainid);
1377 for (
const Key &key : std::as_const(sorted)) {
1378 d->m_pgpOnly &= key.protocol() == GpgME::OpenPGP;
1382 for (
const auto &key : keys) {
1383 for (
const auto &subkey : key.subkeys()) {
1384 if (!subkey.isSecret() || d->m_cards[
QByteArray(subkey.keyGrip())].size() > 0) {
1388 for (
const auto &line : data) {
1389 if (line.startsWith(QByteArrayLiteral(
"Token"))) {
1390 const auto split = line.split(
' ');
1391 if (split.size() > 2) {
1406 Q_EMIT keysMayHaveChanged();
1409void KeyCache::clear()
1411 d->by = Private::By();
1420class KeyCache::RefreshKeysJob::Private
1422 RefreshKeysJob *
const q;
1425 Private(KeyCache *cache, RefreshKeysJob *qq);
1427 Error startKeyListing(GpgME::Protocol protocol);
1428 void listAllKeysJobDone(
const KeyListResult &res,
const std::vector<Key> &nextKeys)
1430 std::vector<Key> keys;
1431 keys.reserve(m_keys.size() + nextKeys.size());
1432 if (m_keys.empty()) {
1435 std::merge(m_keys.begin(), m_keys.end(), nextKeys.begin(), nextKeys.end(), std::back_inserter(keys), _detail::ByFingerprint<std::less>());
1440 void emitDone(
const KeyListResult &result);
1441 void updateKeyCache();
1445 std::vector<Key> m_keys;
1446 KeyListResult m_mergedResult;
1450 void jobDone(
const KeyListResult &res);
1453KeyCache::RefreshKeysJob::Private::Private(KeyCache *cache, RefreshKeysJob *qq)
1461void KeyCache::RefreshKeysJob::Private::jobDone(
const KeyListResult &result)
1472 Q_ASSERT(m_jobsPending.
size() > 0);
1473 m_jobsPending.
removeOne(qobject_cast<QGpgME::ListAllKeysJob *>(sender));
1474 m_mergedResult.mergeWith(result);
1475 if (m_jobsPending.
size() > 0) {
1479 emitDone(m_mergedResult);
1482void KeyCache::RefreshKeysJob::Private::emitDone(
const KeyListResult &res)
1485 Q_EMIT q->done(res);
1488KeyCache::RefreshKeysJob::RefreshKeysJob(KeyCache *cache,
QObject *parent)
1490 , d(new Private(cache, this))
1494KeyCache::RefreshKeysJob::~RefreshKeysJob()
1499void KeyCache::RefreshKeysJob::start()
1501 qCDebug(LIBKLEO_LOG) <<
"KeyCache::RefreshKeysJob" << __func__;
1507void KeyCache::RefreshKeysJob::cancel()
1509 d->m_canceled =
true;
1510 std::for_each(d->m_jobsPending.begin(), d->m_jobsPending.end(), std::mem_fn(&QGpgME::ListAllKeysJob::slotCancel));
1514void KeyCache::RefreshKeysJob::Private::doStart()
1521 Q_ASSERT(m_jobsPending.
size() == 0);
1522 m_mergedResult.mergeWith(KeyListResult(startKeyListing(GpgME::OpenPGP)));
1523 m_mergedResult.mergeWith(KeyListResult(startKeyListing(GpgME::CMS)));
1525 if (m_jobsPending.
size() != 0) {
1529 const bool hasError = m_mergedResult.error() || m_mergedResult.error().isCanceled();
1530 emitDone(hasError ? m_mergedResult : KeyListResult(Error(GPG_ERR_UNSUPPORTED_OPERATION)));
1533void KeyCache::RefreshKeysJob::Private::updateKeyCache()
1535 if (!m_cache || m_canceled) {
1540 std::vector<Key> cachedKeys = m_cache->initialized() ? m_cache->keys() : std::vector<Key>();
1541 std::sort(cachedKeys.begin(), cachedKeys.end(), _detail::ByFingerprint<std::less>());
1542 std::vector<Key> keysToRemove;
1543 std::set_difference(cachedKeys.begin(),
1547 std::back_inserter(keysToRemove),
1548 _detail::ByFingerprint<std::less>());
1549 m_cache->remove(keysToRemove);
1550 m_cache->refresh(m_keys);
1553Error KeyCache::RefreshKeysJob::Private::startKeyListing(GpgME::Protocol proto)
1555 const auto *
const protocol = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
1559 QGpgME::ListAllKeysJob *
const job = protocol->listAllKeysJob(
false,
true);
1563 if (!m_cache->initialized()) {
1565 job->setOptions(QGpgME::ListAllKeysJob::DisableAutomaticTrustDatabaseCheck);
1569 aheinecke: 2017.01.12:
1571 For unknown reasons the
new style
connect fails at runtime
1572 over library borders into QGpgME from the GpgME repo
1573 when cross compiled
for Windows and
default arguments
1574 are used in the Signal.
1576 This was tested with gcc 4.9 (Mingw 3.0.2) and we could not
1577 find an explanation
for this. So until
this is fixed or we understand
1578 the problem we need to use the old style connect
for QGpgME signals.
1580 The
new style connect of the canceled signal right below
1583 connect(job, &QGpgME::ListAllKeysJob::result,
1584 q, [
this](
const GpgME::KeyListResult &res,
const std::vector<GpgME::Key> &keys) {
1585 listAllKeysJobDone(res, keys);
1588 connect(job, SIGNAL(result(GpgME::KeyListResult, std::vector<GpgME::Key>)), q, SLOT(listAllKeysJobDone(GpgME::KeyListResult, std::vector<GpgME::Key>)));
1590 connect(q, &RefreshKeysJob::canceled, job, &QGpgME::Job::slotCancel);
1594 if (proto == GpgME::OpenPGP && m_cache->remarksEnabled() && m_cache->initialized()) {
1595 auto ctx = QGpgME::Job::context(job);
1597 ctx->addKeyListMode(KeyListMode::Signatures | KeyListMode::SignatureNotations);
1601 const Error
error = job->start(
true);
1603 if (!error && !
error.isCanceled()) {
1604 m_jobsPending.push_back(job);
1609bool KeyCache::initialized()
const
1611 return d->m_initalized;
1614void KeyCache::Private::ensureCachePopulated()
const
1616 if (!m_initalized) {
1617 q->startKeyListing();
1620 qCDebug(LIBKLEO_LOG) <<
"Waiting for keycache.";
1622 qCDebug(LIBKLEO_LOG) <<
"Keycache available.";
1626bool KeyCache::pgpOnly()
const
1628 return d->m_pgpOnly;
1631static bool keyIsOk(
const Key &k)
1633 return !k.isExpired() && !k.isRevoked() && !k.isInvalid() && !k.isDisabled();
1636static bool uidIsOk(
const UserID &uid)
1638 return keyIsOk(uid.parent()) && !uid.isRevoked() && !uid.isInvalid();
1641static bool subkeyIsOk(
const Subkey &s)
1643 return !s.isRevoked() && !s.isInvalid() && !s.isDisabled();
1648time_t creationTimeOfNewestSuitableSubKey(
const Key &key, KeyCache::KeyUsage usage)
1650 time_t creationTime = 0;
1651 for (
const Subkey &s : key.subkeys()) {
1652 if (!subkeyIsOk(s)) {
1655 if (usage == KeyCache::KeyUsage::Sign && !s.canSign()) {
1658 if (usage == KeyCache::KeyUsage::Encrypt && !s.canEncrypt()) {
1661 if (s.creationTime() > creationTime) {
1662 creationTime = s.creationTime();
1665 return creationTime;
1671 time_t creationTime = 0;
1675GpgME::Key KeyCache::findBestByMailBox(
const char *addr, GpgME::Protocol proto, KeyUsage usage)
const
1677 d->ensureCachePopulated();
1684 if (
address.size() > 1 && address[0] ==
'<' && address[
address.size() - 1] ==
'>') {
1690 for (
const Key &k : findByEMailAddress(
address.constData())) {
1691 if (proto != Protocol::UnknownProtocol && k.protocol() != proto) {
1694 if (usage == KeyUsage::Encrypt && !keyHasEncrypt(k)) {
1697 if (usage == KeyUsage::Sign && (!keyHasSign(k) || !k.hasSecret())) {
1700 const time_t creationTime = creationTimeOfNewestSuitableSubKey(k, usage);
1701 if (creationTime == 0) {
1705 for (
const UserID &u : k.userIDs()) {
1710 if (best.uid.isNull()) {
1712 best = {k, u, creationTime};
1713 }
else if (!uidIsOk(best.uid) && uidIsOk(u)) {
1715 best = {k, u, creationTime};
1716 }
else if (!k.isExpired() && best.uid.validity() < u.validity()) {
1718 best = {k, u, creationTime};
1719 }
else if (best.key.isExpired() && !k.isExpired()) {
1721 best = {k, u, creationTime};
1722 }
else if (best.uid.validity() == u.validity() && uidIsOk(u) && best.creationTime < creationTime) {
1724 best = {k, u, creationTime};
1735bool allKeysAllowUsage(
const T &keys, KeyCache::KeyUsage usage)
1738 case KeyCache::KeyUsage::AnyUsage:
1740 case KeyCache::KeyUsage::Sign:
1741 return std::all_of(std::begin(keys),
1743#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1744 std::mem_fn(&Key::hasSign)
1749 case KeyCache::KeyUsage::Encrypt:
1750 return std::all_of(std::begin(keys),
1752#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1753 std::mem_fn(&Key::hasEncrypt)
1758 case KeyCache::KeyUsage::Certify:
1759 return std::all_of(std::begin(keys),
1761#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1762 std::mem_fn(&Key::hasCertify)
1767 case KeyCache::KeyUsage::Authenticate:
1768 return std::all_of(std::begin(keys),
1770#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1771 std::mem_fn(&Key::hasAuthenticate)
1773 Kleo::keyHasAuthenticate
1777 qCDebug(LIBKLEO_LOG) << __func__ <<
"called with invalid usage" << int(usage);
1782KeyGroup KeyCache::findGroup(
const QString &name, Protocol protocol, KeyUsage usage)
const
1784 d->ensureCachePopulated();
1786 Q_ASSERT(usage == KeyUsage::Sign || usage == KeyUsage::Encrypt);
1787 for (
const auto &group : std::as_const(d->m_groups)) {
1788 if (group.name() == name) {
1789 const KeyGroup::Keys &keys = group.keys();
1790 if (allKeysAllowUsage(keys, usage) && (protocol == UnknownProtocol || allKeysHaveProtocol(keys, protocol))) {
1799std::vector<Key> KeyCache::getGroupKeys(
const QString &groupName)
const
1801 std::vector<Key> result;
1802 for (
const KeyGroup &g : std::as_const(d->m_groups)) {
1803 if (g.name() == groupName) {
1804 const KeyGroup::Keys &keys = g.keys();
1805 std::copy(keys.cbegin(), keys.cend(), std::back_inserter(result));
1808 _detail::sort_by_fpr(result);
1809 _detail::remove_duplicates_by_fpr(result);
1813void KeyCache::setKeys(
const std::vector<GpgME::Key> &keys)
1816 setRefreshInterval(0);
1820 d->m_initalized =
true;
1821 Q_EMIT keyListingDone(KeyListResult());
1824void KeyCache::setGroups(
const std::vector<KeyGroup> &groups)
1826 Q_ASSERT(d->m_initalized &&
"Call setKeys() before setting groups");
1827 d->m_groups = groups;
1828 Q_EMIT keysMayHaveChanged();
1831std::vector<CardKeyStorageInfo> KeyCache::cardsForSubkey(
const GpgME::Subkey &subkey)
const
1833 return d->m_cards[
QByteArray(subkey.keyGrip())];
1836#include "moc_keycache.cpp"
1837#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)
qsizetype size() const const
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)