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
860#if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
874#define DO(op, meth, meth2) \
875 if (op key.meth()) { \
877 qDebug("rejecting for encrypting: %s: %s", #meth2, key.primaryFingerprint()); \
880struct ready_for_encryption {
881 bool operator()(
const Key &key)
const
884#if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
895 return key.hasEncrypt() && !key.isRevoked() && !key.isExpired() && !key.isDisabled() && !key.isInvalid();
904std::vector<Key> KeyCache::Private::find_mailbox(
const QString &email,
bool sign)
const
906 if (email.isEmpty()) {
907 return std::vector<Key>();
910 const auto pair = find_email(email.toUtf8().constData());
911 std::vector<Key> result;
912 result.reserve(std::distance(pair.first, pair.second));
914 kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_signing());
916 kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_encryption());
922std::vector<Key> KeyCache::findSubjects(
const GpgME::Key &key, Options options)
const
928 return findSubjects(std::vector<Key>(1, key), options);
931std::vector<Key> KeyCache::findSubjects(
const std::vector<Key> &keys, Options options)
const
933 std::vector<Key> result;
940 for (
const auto &key : keys) {
941 const auto firstAndLastSubject = d->find_subjects(key.primaryFingerprint());
942 result.insert(result.end(), firstAndLastSubject.first, firstAndLastSubject.second);
945 _detail::sort_by_fpr(result);
946 _detail::remove_duplicates_by_fpr(result);
948 if (options & RecursiveSearch) {
949 for (std::vector<Key> furtherSubjects = findSubjects(result, NoOption);
950 !furtherSubjects.empty();
951 furtherSubjects = findSubjects(furtherSubjects, NoOption)) {
952 std::vector<Key> combined;
953 combined.reserve(result.size() + furtherSubjects.size());
954 std::merge(result.begin(),
956 furtherSubjects.begin(),
957 furtherSubjects.end(),
958 std::back_inserter(combined),
959 _detail::ByFingerprint<std::less>());
960 _detail::remove_duplicates_by_fpr(combined);
961 if (result.size() == combined.size()) {
965 result.swap(combined);
972std::vector<Key> KeyCache::findIssuers(
const Key &key, Options options)
const
974 std::vector<Key> result;
980 if (options & IncludeSubject) {
981 result.push_back(key);
988 Key issuer = findByFingerprint(key.chainID());
990 if (issuer.isNull()) {
994 result.push_back(issuer);
996 if (!(options & RecursiveSearch)) {
1000 while (!issuer.isRoot()) {
1001 issuer = findByFingerprint(result.back().chainID());
1002 if (issuer.isNull()) {
1005 const bool chainAlreadyContainsIssuer = Kleo::contains_if(result, [issuer](
const auto &key) {
1006 return _detail::ByFingerprint<std::equal_to>()(issuer, key);
1010 result.push_back(issuer);
1011 if (chainAlreadyContainsIssuer) {
1020static std::string email(
const UserID &uid)
1023 const std::string addr = uid.addrSpec();
1024 if (!addr.empty()) {
1027 const std::string email = uid.email();
1028 if (email.empty()) {
1029 return DN(uid.id())[QStringLiteral(
"EMAIL")].trimmed().toUtf8().constData();
1031 if (email[0] ==
'<' && email[email.size() - 1] ==
'>') {
1032 return email.substr(1, email.size() - 2);
1038static std::vector<std::string> emails(
const Key &key)
1040 std::vector<std::string> emails;
1041 const auto userIDs = key.userIDs();
1042 for (
const UserID &uid : userIDs) {
1043 const std::string e = email(uid);
1045 emails.push_back(e);
1048 std::sort(emails.begin(), emails.end(), ByEMail<std::less>());
1049 emails.erase(std::unique(emails.begin(), emails.end(), ByEMail<std::equal_to>()), emails.end());
1053void KeyCache::remove(
const Key &key)
1059 const char *fpr = key.primaryFingerprint();
1065 const auto range = std::equal_range(d->by.fpr.begin(), d->by.fpr.end(), fpr, _detail::ByFingerprint<std::less>());
1066 d->by.fpr.erase(range.first, range.second);
1069 if (
const char *keyid = key.keyID()) {
1070 const auto range = std::equal_range(d->by.keyid.begin(), d->by.keyid.end(), keyid, _detail::ByKeyID<std::less>());
1071 const auto it = std::remove_if(range.first, range.second, [fpr](
const GpgME::Key &key) {
1072 return _detail::ByFingerprint<std::equal_to>()(fpr, key);
1074 d->by.keyid.erase(it, range.second);
1077 if (
const char *chainid = key.chainID()) {
1078 const auto range = std::equal_range(d->by.chainid.begin(), d->by.chainid.end(), chainid, _detail::ByChainID<std::less>());
1079 const auto range2 = std::equal_range(range.first, range.second, fpr, _detail::ByFingerprint<std::less>());
1080 d->by.chainid.erase(range2.first, range2.second);
1083 const auto emailsKey{emails(key)};
1084 for (
const std::string &email : emailsKey) {
1085 const auto range = std::equal_range(d->by.email.begin(), d->by.email.end(), email, ByEMail<std::less>());
1086 const auto it = std::remove_if(range.first, range.second, [fpr](
const std::pair<std::string, Key> &pair) {
1087 return qstricmp(fpr, pair.second.primaryFingerprint()) == 0;
1089 d->by.email.erase(it, range.second);
1092 const auto keySubKeys{key.subkeys()};
1093 for (
const Subkey &subkey : keySubKeys) {
1094 if (
const char *subkeyfpr = subkey.fingerprint()) {
1095 const auto range = std::equal_range(d->by.subkeyfpr.begin(), d->by.subkeyfpr.end(), subkeyfpr, _detail::BySubkeyFingerprint<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.subkeyfpr.erase(it, range.second);
1101 if (
const char *keyid = subkey.keyID()) {
1102 const auto range = std::equal_range(d->by.subkeyid.begin(), d->by.subkeyid.end(), keyid, _detail::ByKeyID<std::less>());
1103 const auto it = std::remove_if(range.first, range.second, [fpr](
const Subkey &subkey) {
1104 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1106 d->by.subkeyid.erase(it, range.second);
1108 if (
const char *keygrip = subkey.keyGrip()) {
1109 const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), keygrip, _detail::ByKeyGrip<std::less>());
1110 const auto it = std::remove_if(range.first, range.second, [fpr](
const Subkey &subkey) {
1111 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1113 d->by.keygrip.erase(it, range.second);
1118void KeyCache::remove(
const std::vector<Key> &keys)
1120 for (
const Key &key : keys) {
1125const std::vector<GpgME::Key> &KeyCache::keys()
const
1127 d->ensureCachePopulated();
1131std::vector<Key> KeyCache::secretKeys()
const
1133 std::vector<Key> keys = this->keys();
1134 keys.erase(std::remove_if(keys.begin(),
1136 [](
const Key &key) {
1137 return !key.hasSecret();
1143KeyGroup KeyCache::group(
const QString &
id)
const
1146 const auto it = std::find_if(std::cbegin(d->m_groups), std::cend(d->m_groups), [
id](
const auto &g) {
1147 return g.id() == id;
1149 if (it != std::cend(d->m_groups)) {
1155std::vector<KeyGroup> KeyCache::groups()
const
1157 d->ensureCachePopulated();
1161std::vector<KeyGroup> KeyCache::configurableGroups()
const
1163 std::vector<KeyGroup> groups;
1164 groups.reserve(d->m_groups.size());
1165 std::copy_if(d->m_groups.cbegin(), d->m_groups.cend(), std::back_inserter(groups), [](
const KeyGroup &group) {
1166 return group.source() == KeyGroup::ApplicationConfig;
1173bool compareById(
const KeyGroup &lhs,
const KeyGroup &rhs)
1175 return lhs.id() < rhs.id();
1178std::vector<KeyGroup> sortedById(std::vector<KeyGroup> groups)
1180 std::sort(groups.begin(), groups.end(), &compareById);
1185void KeyCache::saveConfigurableGroups(
const std::vector<KeyGroup> &groups)
1187 const std::vector<KeyGroup> oldGroups = sortedById(configurableGroups());
1188 const std::vector<KeyGroup> newGroups = sortedById(groups);
1191 std::vector<KeyGroup> removedGroups;
1192 std::set_difference(oldGroups.begin(), oldGroups.end(), newGroups.begin(), newGroups.end(), std::back_inserter(removedGroups), &compareById);
1193 for (
const auto &group : std::as_const(removedGroups)) {
1194 qCDebug(LIBKLEO_LOG) <<
"Removing group" << group;
1199 std::vector<KeyGroup> updatedGroups;
1200 std::set_intersection(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(updatedGroups), &compareById);
1201 for (
const auto &group : std::as_const(updatedGroups)) {
1202 qCDebug(LIBKLEO_LOG) <<
"Updating group" << group;
1207 std::vector<KeyGroup> addedGroups;
1208 std::set_difference(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(addedGroups), &compareById);
1209 for (
const auto &group : std::as_const(addedGroups)) {
1210 qCDebug(LIBKLEO_LOG) <<
"Adding group" << group;
1215 Q_EMIT keysMayHaveChanged();
1218bool KeyCache::insert(
const KeyGroup &group)
1220 if (!d->insert(group)) {
1224 Q_EMIT keysMayHaveChanged();
1229bool KeyCache::update(
const KeyGroup &group)
1231 if (!d->update(group)) {
1235 Q_EMIT keysMayHaveChanged();
1240bool KeyCache::remove(
const KeyGroup &group)
1242 if (!d->remove(group)) {
1246 Q_EMIT keysMayHaveChanged();
1251void KeyCache::refresh(
const std::vector<Key> &keys)
1258void KeyCache::insert(
const Key &key)
1260 insert(std::vector<Key>(1, key));
1266template<
template<
template<
typename T>
class Op>
class T1, template<template<typename T> class Op> class T2>
1267struct lexicographically {
1268 using result_type = bool;
1270 template<
typename U,
typename V>
1271 bool operator()(
const U &lhs,
const V &rhs)
const
1273 return T1<std::less>()(lhs, rhs)
1274 || (T1<std::equal_to>()(lhs, rhs) && T2<std::less>()(lhs, rhs));
1280void KeyCache::insert(
const std::vector<Key> &keys)
1283 std::vector<Key> sorted;
1284 sorted.reserve(keys.size());
1285 std::copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), [](
const Key &key) {
1286 auto fp = key.primaryFingerprint();
1294 std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
1297 std::vector<Key> by_fpr;
1298 by_fpr.reserve(sorted.size() + d->by.fpr.size());
1299 std::merge(sorted.begin(), sorted.end(), d->by.fpr.begin(), d->by.fpr.end(), std::back_inserter(by_fpr), _detail::ByFingerprint<std::less>());
1302 std::vector<std::pair<std::string, Key>> pairs;
1303 pairs.reserve(sorted.size());
1304 for (
const Key &key : std::as_const(sorted)) {
1305 const std::vector<std::string> emails = ::emails(key);
1306 for (
const std::string &e : emails) {
1307 pairs.push_back(std::make_pair(e, key));
1310 std::sort(pairs.begin(), pairs.end(), ByEMail<std::less>());
1313 std::vector<std::pair<std::string, Key>> by_email;
1314 by_email.reserve(pairs.size() + d->by.email.size());
1315 std::merge(pairs.begin(), pairs.end(), d->by.email.begin(), d->by.email.end(), std::back_inserter(by_email), ByEMail<std::less>());
1318 std::stable_sort(sorted.begin(), sorted.end(), _detail::ByChainID<std::less>());
1321 std::vector<Key> nonroot;
1322 nonroot.reserve(sorted.size());
1323 std::vector<Key> by_chainid;
1324 by_chainid.reserve(sorted.size() + d->by.chainid.size());
1325 std::copy_if(sorted.cbegin(), sorted.cend(), std::back_inserter(nonroot), [](
const Key &key) {
1326 return !key.isRoot();
1328 std::merge(nonroot.cbegin(),
1330 d->by.chainid.cbegin(),
1331 d->by.chainid.cend(),
1332 std::back_inserter(by_chainid),
1333 lexicographically<_detail::ByChainID, _detail::ByFingerprint>());
1336 std::sort(sorted.begin(), sorted.end(), _detail::ByKeyID<std::less>());
1339 std::vector<Key> by_keyid;
1340 by_keyid.reserve(sorted.size() + d->by.keyid.size());
1341 std::merge(sorted.begin(), sorted.end(), d->by.keyid.begin(), d->by.keyid.end(), std::back_inserter(by_keyid), _detail::ByKeyID<std::less>());
1346 std::vector<Subkey> subkeys;
1347 subkeys.reserve(sorted.size());
1348 for (
const Key &key : std::as_const(sorted)) {
1349 const auto keySubkeys{key.subkeys()};
1350 for (
const Subkey &subkey : keySubkeys) {
1351 subkeys.push_back(subkey);
1356 std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyID<std::less>());
1359 std::vector<Subkey> by_subkeyid;
1360 by_subkeyid.reserve(subkeys.size() + d->by.subkeyid.size());
1361 std::merge(subkeys.begin(), subkeys.end(), d->by.subkeyid.begin(), d->by.subkeyid.end(), std::back_inserter(by_subkeyid), _detail::ByKeyID<std::less>());
1364 std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyGrip<std::less>());
1367 std::vector<Subkey> by_keygrip;
1368 by_keygrip.reserve(subkeys.size() + d->by.keygrip.size());
1369 std::merge(subkeys.begin(), subkeys.end(), d->by.keygrip.begin(), d->by.keygrip.end(), std::back_inserter(by_keygrip), _detail::ByKeyGrip<std::less>());
1372 std::sort(subkeys.begin(), subkeys.end(), _detail::BySubkeyFingerprint<std::less>());
1375 std::vector<Subkey> by_subkeyfpr;
1376 by_subkeyfpr.reserve(subkeys.size() + d->by.subkeyfpr.size());
1377 std::merge(subkeys.begin(),
1379 d->by.subkeyfpr.begin(),
1380 d->by.subkeyfpr.end(),
1381 std::back_inserter(by_subkeyfpr),
1382 _detail::BySubkeyFingerprint<std::less>());
1385 by_fpr.swap(d->by.fpr);
1386 by_keyid.swap(d->by.keyid);
1387 by_email.swap(d->by.email);
1388 by_subkeyfpr.swap(d->by.subkeyfpr);
1389 by_subkeyid.swap(d->by.subkeyid);
1390 by_keygrip.swap(d->by.keygrip);
1391 by_chainid.swap(d->by.chainid);
1393 for (
const Key &key : std::as_const(sorted)) {
1394 d->m_pgpOnly &= key.protocol() == GpgME::OpenPGP;
1398 for (
const auto &key : keys) {
1399 for (
const auto &subkey : key.subkeys()) {
1400 if (!subkey.isSecret() || !d->m_cards[
QByteArray(subkey.keyGrip())].empty()) {
1404 for (
const auto &line : data) {
1405 if (line.startsWith(QByteArrayLiteral(
"Token"))) {
1406 const auto split = line.split(
' ');
1407 if (split.size() > 2) {
1422 Q_EMIT keysMayHaveChanged();
1425void KeyCache::clear()
1427 d->by = Private::By();
1436class KeyCache::RefreshKeysJob::Private
1438 RefreshKeysJob *
const q;
1441 Private(KeyCache *cache, RefreshKeysJob *qq);
1443 void startNextJob();
1444 QGpgME::ListAllKeysJob *createKeyListingJob(GpgME::Protocol protocol);
1445 void listAllKeysJobDone(
const KeyListResult &res,
const std::vector<Key> &nextKeys)
1447 if (!nextKeys.empty()) {
1448 std::vector<Key> keys;
1449 keys.reserve(m_keys.size() + nextKeys.size());
1450 if (m_keys.empty()) {
1453 std::merge(m_keys.begin(), m_keys.end(), nextKeys.begin(), nextKeys.end(), std::back_inserter(keys), _detail::ByFingerprint<std::less>());
1459 void emitDone(
const KeyListResult &result);
1460 void updateKeyCache();
1464 std::vector<Key> m_keys;
1465 KeyListResult m_mergedResult;
1469 void jobDone(
const KeyListResult &res);
1472KeyCache::RefreshKeysJob::Private::Private(KeyCache *cache, RefreshKeysJob *qq)
1480void KeyCache::RefreshKeysJob::Private::jobDone(
const KeyListResult &result)
1491 Q_ASSERT(!m_jobsPending.
empty());
1492 m_jobsPending.
removeOne(qobject_cast<QGpgME::ListAllKeysJob *>(sender));
1493 m_mergedResult.mergeWith(result);
1494 if (!m_jobsPending.
empty()) {
1499 emitDone(m_mergedResult);
1502void KeyCache::RefreshKeysJob::Private::emitDone(
const KeyListResult &res)
1505 Q_EMIT q->done(res);
1508KeyCache::RefreshKeysJob::RefreshKeysJob(KeyCache *cache,
QObject *parent)
1510 , d(new Private(cache, this))
1514KeyCache::RefreshKeysJob::~RefreshKeysJob()
1519void KeyCache::RefreshKeysJob::start()
1521 qCDebug(LIBKLEO_LOG) <<
"KeyCache::RefreshKeysJob" << __func__;
1527void KeyCache::RefreshKeysJob::cancel()
1529 d->m_canceled =
true;
1530 if (!d->m_jobsPending.empty()) {
1531 d->m_jobsPending.first()->slotCancel();
1536void KeyCache::RefreshKeysJob::Private::doStart()
1543 Q_ASSERT(m_jobsPending.
empty());
1545 if (
auto job = createKeyListingJob(GpgME::OpenPGP)) {
1548 if (
auto job = createKeyListingJob(GpgME::CMS)) {
1552 if (m_jobsPending.
empty()) {
1553 emitDone(KeyListResult(Error(GPG_ERR_UNSUPPORTED_OPERATION)));
1560void KeyCache::RefreshKeysJob::Private::startNextJob()
1562 Q_ASSERT(!m_jobsPending.empty());
1564 auto job = m_jobsPending.first();
1566 const Error
error = job->start(
true);
1567 if (error ||
error.isCanceled()) {
1571 listAllKeysJobDone(KeyListResult{
error}, {});
1577void KeyCache::RefreshKeysJob::Private::updateKeyCache()
1579 if (!m_cache || m_canceled) {
1584 std::vector<Key> cachedKeys = m_cache->initialized() ? m_cache->keys() : std::vector<Key>();
1585 std::sort(cachedKeys.begin(), cachedKeys.end(), _detail::ByFingerprint<std::less>());
1586 std::vector<Key> keysToRemove;
1587 std::set_difference(cachedKeys.begin(),
1591 std::back_inserter(keysToRemove),
1592 _detail::ByFingerprint<std::less>());
1593 m_cache->remove(keysToRemove);
1594 m_cache->refresh(m_keys);
1597QGpgME::ListAllKeysJob *KeyCache::RefreshKeysJob::Private::createKeyListingJob(GpgME::Protocol proto)
1599 const auto *
const protocol = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
1603 QGpgME::ListAllKeysJob *
const job = protocol->listAllKeysJob(
false,
true);
1607 if (!m_cache->initialized()) {
1609 job->setOptions(QGpgME::ListAllKeysJob::DisableAutomaticTrustDatabaseCheck);
1613 aheinecke: 2017.01.12:
1615 For unknown reasons the
new style
connect fails at runtime
1616 over library borders into QGpgME from the GpgME repo
1617 when cross compiled
for Windows and
default arguments
1618 are used in the Signal.
1620 This was tested with gcc 4.9 (Mingw 3.0.2) and we could not
1621 find an explanation
for this. So until
this is fixed or we understand
1622 the problem we need to use the old style connect
for QGpgME signals.
1624 The
new style connect of the canceled signal right below
1627 connect(job, &QGpgME::ListAllKeysJob::result,
1628 q, [
this](
const GpgME::KeyListResult &res,
const std::vector<GpgME::Key> &keys) {
1629 listAllKeysJobDone(res, keys);
1632 connect(job, SIGNAL(result(GpgME::KeyListResult, std::vector<GpgME::Key>)), q, SLOT(listAllKeysJobDone(GpgME::KeyListResult, std::vector<GpgME::Key>)));
1634 connect(q, &RefreshKeysJob::canceled, job, &QGpgME::Job::slotCancel);
1638 if (proto == GpgME::OpenPGP && m_cache->remarksEnabled() && m_cache->initialized()) {
1639 auto ctx = QGpgME::Job::context(job);
1641 ctx->addKeyListMode(KeyListMode::Signatures | KeyListMode::SignatureNotations);
1648bool KeyCache::initialized()
const
1650 return d->m_initalized;
1653void KeyCache::Private::ensureCachePopulated()
const
1655 if (!m_initalized) {
1656 q->startKeyListing();
1659 qCDebug(LIBKLEO_LOG) <<
"Waiting for keycache.";
1661 qCDebug(LIBKLEO_LOG) <<
"Keycache available.";
1665bool KeyCache::pgpOnly()
const
1667 return d->m_pgpOnly;
1670static bool keyIsOk(
const Key &k)
1672 return !k.isExpired() && !k.isRevoked() && !k.isInvalid() && !k.isDisabled();
1675static bool uidIsOk(
const UserID &uid)
1677 return keyIsOk(uid.parent()) && !uid.isRevoked() && !uid.isInvalid();
1680static bool subkeyIsOk(
const Subkey &s)
1682 return !s.isRevoked() && !s.isInvalid() && !s.isDisabled();
1687time_t creationTimeOfNewestSuitableSubKey(
const Key &key, KeyCache::KeyUsage usage)
1689 time_t creationTime = 0;
1690 for (
const Subkey &s : key.subkeys()) {
1691 if (!subkeyIsOk(s)) {
1694 if (usage == KeyCache::KeyUsage::Sign && !s.canSign()) {
1697 if (usage == KeyCache::KeyUsage::Encrypt && !s.canEncrypt()) {
1700 if (s.creationTime() > creationTime) {
1701 creationTime = s.creationTime();
1704 return creationTime;
1710 time_t creationTime = 0;
1714GpgME::Key KeyCache::findBestByMailBox(
const char *addr, GpgME::Protocol proto, KeyUsage usage)
const
1716 d->ensureCachePopulated();
1723 if (
address.size() > 1 && address[0] ==
'<' && address[
address.size() - 1] ==
'>') {
1729 for (
const Key &k : findByEMailAddress(
address.constData())) {
1730 if (proto != Protocol::UnknownProtocol && k.protocol() != proto) {
1733 if (usage == KeyUsage::Encrypt && !keyHasEncrypt(k)) {
1736 if (usage == KeyUsage::Sign && (!keyHasSign(k) || !k.hasSecret())) {
1739 const time_t creationTime = creationTimeOfNewestSuitableSubKey(k, usage);
1740 if (creationTime == 0) {
1744 for (
const UserID &u : k.userIDs()) {
1749 if (best.uid.isNull()) {
1751 best = {k, u, creationTime};
1752 }
else if (!uidIsOk(best.uid) && uidIsOk(u)) {
1754 best = {k, u, creationTime};
1755 }
else if (!k.isExpired() && best.uid.validity() < u.validity()) {
1757 best = {k, u, creationTime};
1758 }
else if (best.key.isExpired() && !k.isExpired()) {
1760 best = {k, u, creationTime};
1761 }
else if (best.uid.validity() == u.validity() && uidIsOk(u) && best.creationTime < creationTime) {
1763 best = {k, u, creationTime};
1774bool allKeysAllowUsage(
const T &keys, KeyCache::KeyUsage usage)
1777 case KeyCache::KeyUsage::AnyUsage:
1779 case KeyCache::KeyUsage::Sign:
1780 return std::all_of(std::begin(keys),
1782#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1783 std::mem_fn(&Key::hasSign)
1788 case KeyCache::KeyUsage::Encrypt:
1789 return std::all_of(std::begin(keys),
1791#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1792 std::mem_fn(&Key::hasEncrypt)
1797 case KeyCache::KeyUsage::Certify:
1798 return std::all_of(std::begin(keys),
1800#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1801 std::mem_fn(&Key::hasCertify)
1806 case KeyCache::KeyUsage::Authenticate:
1807 return std::all_of(std::begin(keys),
1809#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1810 std::mem_fn(&Key::hasAuthenticate)
1812 Kleo::keyHasAuthenticate
1816 qCDebug(LIBKLEO_LOG) << __func__ <<
"called with invalid usage" << int(usage);
1821KeyGroup KeyCache::findGroup(
const QString &name, Protocol protocol, KeyUsage usage)
const
1823 d->ensureCachePopulated();
1825 Q_ASSERT(usage == KeyUsage::Sign || usage == KeyUsage::Encrypt);
1826 for (
const auto &group : std::as_const(d->m_groups)) {
1827 if (group.name() == name) {
1828 const KeyGroup::Keys &keys = group.keys();
1829 if (allKeysAllowUsage(keys, usage) && (protocol == UnknownProtocol || allKeysHaveProtocol(keys, protocol))) {
1838std::vector<Key> KeyCache::getGroupKeys(
const QString &groupName)
const
1840 std::vector<Key> result;
1841 for (
const KeyGroup &g : std::as_const(d->m_groups)) {
1842 if (g.name() == groupName) {
1843 const KeyGroup::Keys &keys = g.keys();
1844 std::copy(keys.cbegin(), keys.cend(), std::back_inserter(result));
1847 _detail::sort_by_fpr(result);
1848 _detail::remove_duplicates_by_fpr(result);
1852void KeyCache::setKeys(
const std::vector<GpgME::Key> &keys)
1855 setRefreshInterval(0);
1859 d->m_initalized =
true;
1860 Q_EMIT keyListingDone(KeyListResult());
1863void KeyCache::setGroups(
const std::vector<KeyGroup> &groups)
1865 Q_ASSERT(d->m_initalized &&
"Call setKeys() before setting groups");
1866 d->m_groups = groups;
1867 Q_EMIT keysMayHaveChanged();
1870std::vector<CardKeyStorageInfo> KeyCache::cardsForSubkey(
const GpgME::Subkey &subkey)
const
1872 return d->m_cards[
QByteArray(subkey.keyGrip())];
1875#include "moc_keycache.cpp"
1876#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)
void push_back(parameter_type value)
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)