File indexing completed on 2025-01-05 04:55:44

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     models/keycache.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2007, 2008 Klarälvdalens Datakonsult AB
0006     SPDX-FileCopyrightText: 2018 Intevation GmbH
0007     SPDX-FileCopyrightText: 2020, 2021 g10 Code GmbH
0008     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0009 
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 
0013 #include <config-libkleo.h>
0014 
0015 #include "keycache.h"
0016 #include "keycache_p.h"
0017 
0018 #include <libkleo/algorithm.h>
0019 #include <libkleo/compat.h>
0020 #include <libkleo/debug.h>
0021 #include <libkleo/dn.h>
0022 #include <libkleo/enum.h>
0023 #include <libkleo/filesystemwatcher.h>
0024 #include <libkleo/keygroup.h>
0025 #include <libkleo/keygroupconfig.h>
0026 #include <libkleo/keyhelpers.h>
0027 #include <libkleo/predicates.h>
0028 #include <libkleo/qtstlhelpers.h>
0029 #include <libkleo/stl_util.h>
0030 
0031 #include <libkleo_debug.h>
0032 
0033 #include <KSharedConfig>
0034 
0035 #include <QGpgME/CryptoConfig>
0036 #include <QGpgME/ListAllKeysJob>
0037 #include <QGpgME/Protocol>
0038 
0039 #include <QEventLoop>
0040 #include <QPointer>
0041 #include <QTimer>
0042 
0043 #include <gpgme++/context.h>
0044 #include <gpgme++/decryptionresult.h>
0045 #include <gpgme++/error.h>
0046 #include <gpgme++/key.h>
0047 #include <gpgme++/keylistresult.h>
0048 #include <gpgme++/verificationresult.h>
0049 
0050 #include <gpg-error.h>
0051 
0052 #include <algorithm>
0053 #include <chrono>
0054 #include <functional>
0055 #include <iterator>
0056 #include <utility>
0057 
0058 using namespace std::chrono_literals;
0059 using namespace Kleo;
0060 using namespace GpgME;
0061 using namespace KMime::Types;
0062 
0063 static const unsigned int hours2ms = 1000 * 60 * 60;
0064 
0065 //
0066 //
0067 // KeyCache
0068 //
0069 //
0070 
0071 namespace
0072 {
0073 
0074 make_comparator_str(ByEMail, .first.c_str());
0075 
0076 }
0077 
0078 class Kleo::KeyCacheAutoRefreshSuspension
0079 {
0080     KeyCacheAutoRefreshSuspension()
0081     {
0082         qCDebug(LIBKLEO_LOG) << __func__;
0083         auto cache = KeyCache::mutableInstance();
0084         cache->enableFileSystemWatcher(false);
0085         m_refreshInterval = cache->refreshInterval();
0086         cache->setRefreshInterval(0);
0087         cache->cancelKeyListing();
0088         m_cache = cache;
0089     }
0090 
0091 public:
0092     ~KeyCacheAutoRefreshSuspension()
0093     {
0094         qCDebug(LIBKLEO_LOG) << __func__;
0095         if (auto cache = m_cache.lock()) {
0096             cache->enableFileSystemWatcher(true);
0097             cache->setRefreshInterval(m_refreshInterval);
0098         }
0099     }
0100 
0101     static std::shared_ptr<KeyCacheAutoRefreshSuspension> instance()
0102     {
0103         static std::weak_ptr<KeyCacheAutoRefreshSuspension> self;
0104         if (auto s = self.lock()) {
0105             return s;
0106         } else {
0107             s = std::shared_ptr<KeyCacheAutoRefreshSuspension>{new KeyCacheAutoRefreshSuspension{}};
0108             self = s;
0109             return s;
0110         }
0111     }
0112 
0113 private:
0114     std::weak_ptr<KeyCache> m_cache;
0115     int m_refreshInterval = 0;
0116 };
0117 
0118 class KeyCache::Private
0119 {
0120     friend class ::Kleo::KeyCache;
0121     KeyCache *const q;
0122 
0123 public:
0124     explicit Private(KeyCache *qq)
0125         : q(qq)
0126         , m_refreshInterval(1)
0127         , m_initalized(false)
0128         , m_pgpOnly(true)
0129         , m_remarks_enabled(false)
0130     {
0131         connect(&m_autoKeyListingTimer, &QTimer::timeout, q, [this]() {
0132             q->startKeyListing();
0133         });
0134         updateAutoKeyListingTimer();
0135     }
0136 
0137     ~Private()
0138     {
0139         if (m_refreshJob) {
0140             m_refreshJob->cancel();
0141         }
0142     }
0143 
0144     template<template<template<typename U> class Op> class Comp>
0145     std::vector<Key>::const_iterator find(const std::vector<Key> &keys, const char *key) const
0146     {
0147         ensureCachePopulated();
0148         const auto it = std::lower_bound(keys.begin(), keys.end(), key, Comp<std::less>());
0149         if (it == keys.end() || Comp<std::equal_to>()(*it, key)) {
0150             return it;
0151         } else {
0152             return keys.end();
0153         }
0154     }
0155 
0156     template<template<template<typename U> class Op> class Comp>
0157     std::vector<Subkey>::const_iterator find(const std::vector<Subkey> &keys, const char *key) const
0158     {
0159         ensureCachePopulated();
0160         const auto it = std::lower_bound(keys.begin(), keys.end(), key, Comp<std::less>());
0161         if (it == keys.end() || Comp<std::equal_to>()(*it, key)) {
0162             return it;
0163         } else {
0164             return keys.end();
0165         }
0166     }
0167 
0168     std::vector<Key>::const_iterator find_fpr(const char *fpr) const
0169     {
0170         return find<_detail::ByFingerprint>(by.fpr, fpr);
0171     }
0172 
0173     std::pair<std::vector<std::pair<std::string, Key>>::const_iterator, std::vector<std::pair<std::string, Key>>::const_iterator>
0174     find_email(const char *email) const
0175     {
0176         ensureCachePopulated();
0177         return std::equal_range(by.email.begin(), by.email.end(), email, ByEMail<std::less>());
0178     }
0179 
0180     std::vector<Key> find_mailbox(const QString &email, bool sign) const;
0181 
0182     std::vector<Subkey>::const_iterator find_keygrip(const char *keygrip) const
0183     {
0184         return find<_detail::ByKeyGrip>(by.keygrip, keygrip);
0185     }
0186 
0187     std::vector<Subkey>::const_iterator find_subkeyid(const char *subkeyid) const
0188     {
0189         return find<_detail::ByKeyID>(by.subkeyid, subkeyid);
0190     }
0191 
0192     std::vector<Key>::const_iterator find_keyid(const char *keyid) const
0193     {
0194         return find<_detail::ByKeyID>(by.keyid, keyid);
0195     }
0196 
0197     std::vector<Key>::const_iterator find_shortkeyid(const char *shortkeyid) const
0198     {
0199         return find<_detail::ByShortKeyID>(by.shortkeyid, shortkeyid);
0200     }
0201 
0202     std::pair<std::vector<Key>::const_iterator, std::vector<Key>::const_iterator> find_subjects(const char *chain_id) const
0203     {
0204         ensureCachePopulated();
0205         return std::equal_range(by.chainid.begin(), by.chainid.end(), chain_id, _detail::ByChainID<std::less>());
0206     }
0207 
0208     void refreshJobDone(const KeyListResult &result);
0209 
0210     void setRefreshInterval(int interval)
0211     {
0212         m_refreshInterval = interval;
0213         updateAutoKeyListingTimer();
0214     }
0215 
0216     int refreshInterval() const
0217     {
0218         return m_refreshInterval;
0219     }
0220 
0221     void updateAutoKeyListingTimer()
0222     {
0223         setAutoKeyListingInterval(hours2ms * m_refreshInterval);
0224     }
0225     void setAutoKeyListingInterval(int ms)
0226     {
0227         m_autoKeyListingTimer.stop();
0228         m_autoKeyListingTimer.setInterval(ms);
0229         if (ms != 0) {
0230             m_autoKeyListingTimer.start();
0231         }
0232     }
0233 
0234     void ensureCachePopulated() const;
0235 
0236     void readGroupsFromGpgConf()
0237     {
0238         // According to Werner Koch groups are more of a hack to solve
0239         // a valid usecase (e.g. several keys defined for an internal mailing list)
0240         // that won't make it in the proper keylist interface. And using gpgconf
0241         // was the suggested way to support groups.
0242         auto conf = QGpgME::cryptoConfig();
0243         if (!conf) {
0244             return;
0245         }
0246 
0247         auto entry = getCryptoConfigEntry(conf, "gpg", "group");
0248         if (!entry) {
0249             return;
0250         }
0251 
0252         // collect the key fingerprints for all groups read from the configuration
0253         QMap<QString, QStringList> fingerprints;
0254         const auto stringValueList = entry->stringValueList();
0255         for (const QString &value : stringValueList) {
0256             const QStringList split = value.split(QLatin1Char('='));
0257             if (split.size() != 2) {
0258                 qCDebug(LIBKLEO_LOG) << "Ignoring invalid group config:" << value;
0259                 continue;
0260             }
0261             const QString groupName = split[0];
0262             const QString fingerprint = split[1];
0263             fingerprints[groupName].push_back(fingerprint);
0264         }
0265 
0266         // add all groups read from the configuration to the list of groups
0267         for (auto it = fingerprints.cbegin(); it != fingerprints.cend(); ++it) {
0268             const QString groupName = it.key();
0269             const std::vector<Key> groupKeys = q->findByFingerprint(toStdStrings(it.value()));
0270             KeyGroup g(groupName, groupName, groupKeys, KeyGroup::GnuPGConfig);
0271             m_groups.push_back(g);
0272         }
0273     }
0274 
0275     void readGroupsFromGroupsConfig()
0276     {
0277         Q_ASSERT(m_groupConfig);
0278         if (!m_groupConfig) {
0279             qCWarning(LIBKLEO_LOG) << __func__ << "group config not set";
0280             return;
0281         }
0282 
0283         m_groups = m_groupConfig->readGroups();
0284     }
0285 
0286     KeyGroup writeGroupToGroupsConfig(const KeyGroup &group)
0287     {
0288         Q_ASSERT(m_groupConfig);
0289         if (!m_groupConfig) {
0290             qCWarning(LIBKLEO_LOG) << __func__ << "group config not set";
0291             return {};
0292         }
0293 
0294         Q_ASSERT(!group.isNull());
0295         Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
0296         if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
0297             qCDebug(LIBKLEO_LOG) << __func__ << "group cannot be written to application configuration:" << group;
0298             return group;
0299         }
0300 
0301         return m_groupConfig->writeGroup(group);
0302     }
0303 
0304     bool removeGroupFromGroupsConfig(const KeyGroup &group)
0305     {
0306         Q_ASSERT(m_groupConfig);
0307         if (!m_groupConfig) {
0308             qCWarning(LIBKLEO_LOG) << __func__ << "group config not set";
0309             return false;
0310         }
0311 
0312         Q_ASSERT(!group.isNull());
0313         Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
0314         if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
0315             qCDebug(LIBKLEO_LOG) << __func__ << "group cannot be removed from application configuration:" << group;
0316             return false;
0317         }
0318 
0319         return m_groupConfig->removeGroup(group);
0320     }
0321 
0322     void updateGroupCache()
0323     {
0324         // Update Group Keys
0325         // this is a quick thing as it only involves reading the config
0326         // so no need for a job.
0327 
0328         m_groups.clear();
0329         if (m_groupsEnabled) {
0330             readGroupsFromGpgConf();
0331             readGroupsFromGroupsConfig();
0332         }
0333     }
0334 
0335     bool insert(const KeyGroup &group)
0336     {
0337         Q_ASSERT(!group.isNull());
0338         Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
0339         if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
0340             qCDebug(LIBKLEO_LOG) << "KeyCache::Private::insert - Invalid group:" << group;
0341             return false;
0342         }
0343         const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](const auto &g) {
0344             return g.source() == group.source() && g.id() == group.id();
0345         });
0346         if (it != m_groups.cend()) {
0347             qCDebug(LIBKLEO_LOG) << "KeyCache::Private::insert - Group already present in list of groups:" << group;
0348             return false;
0349         }
0350 
0351         const KeyGroup savedGroup = writeGroupToGroupsConfig(group);
0352         if (savedGroup.isNull()) {
0353             qCDebug(LIBKLEO_LOG) << "KeyCache::Private::insert - Writing group" << group.id() << "to config file failed";
0354             return false;
0355         }
0356 
0357         m_groups.push_back(savedGroup);
0358 
0359         Q_EMIT q->groupAdded(savedGroup);
0360 
0361         return true;
0362     }
0363 
0364     bool update(const KeyGroup &group)
0365     {
0366         Q_ASSERT(!group.isNull());
0367         Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
0368         if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
0369             qCDebug(LIBKLEO_LOG) << "KeyCache::Private::update - Invalid group:" << group;
0370             return false;
0371         }
0372         const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](const auto &g) {
0373             return g.source() == group.source() && g.id() == group.id();
0374         });
0375         if (it == m_groups.cend()) {
0376             qCDebug(LIBKLEO_LOG) << "KeyCache::Private::update - Group not found in list of groups:" << group;
0377             return false;
0378         }
0379         const auto groupIndex = std::distance(m_groups.cbegin(), it);
0380 
0381         const KeyGroup savedGroup = writeGroupToGroupsConfig(group);
0382         if (savedGroup.isNull()) {
0383             qCDebug(LIBKLEO_LOG) << "KeyCache::Private::update - Writing group" << group.id() << "to config file failed";
0384             return false;
0385         }
0386 
0387         m_groups[groupIndex] = savedGroup;
0388 
0389         Q_EMIT q->groupUpdated(savedGroup);
0390 
0391         return true;
0392     }
0393 
0394     bool remove(const KeyGroup &group)
0395     {
0396         Q_ASSERT(!group.isNull());
0397         Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
0398         if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
0399             qCDebug(LIBKLEO_LOG) << "KeyCache::Private::remove - Invalid group:" << group;
0400             return false;
0401         }
0402         const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](const auto &g) {
0403             return g.source() == group.source() && g.id() == group.id();
0404         });
0405         if (it == m_groups.cend()) {
0406             qCDebug(LIBKLEO_LOG) << "KeyCache::Private::remove - Group not found in list of groups:" << group;
0407             return false;
0408         }
0409 
0410         const bool success = removeGroupFromGroupsConfig(group);
0411         if (!success) {
0412             qCDebug(LIBKLEO_LOG) << "KeyCache::Private::remove - Removing group" << group.id() << "from config file failed";
0413             return false;
0414         }
0415 
0416         m_groups.erase(it);
0417 
0418         Q_EMIT q->groupRemoved(group);
0419 
0420         return true;
0421     }
0422 
0423 private:
0424     QPointer<RefreshKeysJob> m_refreshJob;
0425     std::vector<std::shared_ptr<FileSystemWatcher>> m_fsWatchers;
0426     QTimer m_autoKeyListingTimer;
0427     int m_refreshInterval;
0428 
0429     struct By {
0430         std::vector<Key> fpr, keyid, shortkeyid, chainid;
0431         std::vector<std::pair<std::string, Key>> email;
0432         std::vector<Subkey> subkeyid, keygrip;
0433     } by;
0434     bool m_initalized;
0435     bool m_pgpOnly;
0436     bool m_remarks_enabled;
0437     bool m_groupsEnabled = false;
0438     std::shared_ptr<KeyGroupConfig> m_groupConfig;
0439     std::vector<KeyGroup> m_groups;
0440 };
0441 
0442 std::shared_ptr<const KeyCache> KeyCache::instance()
0443 {
0444     return mutableInstance();
0445 }
0446 
0447 std::shared_ptr<KeyCache> KeyCache::mutableInstance()
0448 {
0449     static std::weak_ptr<KeyCache> self;
0450     try {
0451         return std::shared_ptr<KeyCache>(self);
0452     } catch (const std::bad_weak_ptr &) {
0453         const std::shared_ptr<KeyCache> s(new KeyCache);
0454         self = s;
0455         return s;
0456     }
0457 }
0458 
0459 KeyCache::KeyCache()
0460     : QObject()
0461     , d(new Private(this))
0462 {
0463 }
0464 
0465 KeyCache::~KeyCache()
0466 {
0467 }
0468 
0469 void KeyCache::setGroupsEnabled(bool enabled)
0470 {
0471     d->m_groupsEnabled = enabled;
0472     if (d->m_initalized) {
0473         d->updateGroupCache();
0474     }
0475 }
0476 
0477 void KeyCache::setGroupConfig(const std::shared_ptr<KeyGroupConfig> &groupConfig)
0478 {
0479     d->m_groupConfig = groupConfig;
0480 }
0481 
0482 void KeyCache::enableFileSystemWatcher(bool enable)
0483 {
0484     for (const auto &i : std::as_const(d->m_fsWatchers)) {
0485         i->setEnabled(enable);
0486     }
0487 }
0488 
0489 void KeyCache::setRefreshInterval(int hours)
0490 {
0491     d->setRefreshInterval(hours);
0492 }
0493 
0494 int KeyCache::refreshInterval() const
0495 {
0496     return d->refreshInterval();
0497 }
0498 
0499 std::shared_ptr<KeyCacheAutoRefreshSuspension> KeyCache::suspendAutoRefresh()
0500 {
0501     return KeyCacheAutoRefreshSuspension::instance();
0502 }
0503 
0504 void KeyCache::reload(GpgME::Protocol /*proto*/)
0505 {
0506     if (d->m_refreshJob) {
0507         return;
0508     }
0509 
0510     d->updateAutoKeyListingTimer();
0511 
0512     enableFileSystemWatcher(false);
0513     d->m_refreshJob = new RefreshKeysJob(this);
0514     connect(d->m_refreshJob.data(), &RefreshKeysJob::done, this, [this](const GpgME::KeyListResult &r) {
0515         d->refreshJobDone(r);
0516     });
0517     connect(d->m_refreshJob.data(), &RefreshKeysJob::canceled, this, [this]() {
0518         d->m_refreshJob.clear();
0519     });
0520     d->m_refreshJob->start();
0521 }
0522 
0523 void KeyCache::cancelKeyListing()
0524 {
0525     if (!d->m_refreshJob) {
0526         return;
0527     }
0528     d->m_refreshJob->cancel();
0529 }
0530 
0531 void KeyCache::addFileSystemWatcher(const std::shared_ptr<FileSystemWatcher> &watcher)
0532 {
0533     if (!watcher) {
0534         return;
0535     }
0536     d->m_fsWatchers.push_back(watcher);
0537     connect(watcher.get(), &FileSystemWatcher::directoryChanged, this, [this]() {
0538         startKeyListing();
0539     });
0540     connect(watcher.get(), &FileSystemWatcher::fileChanged, this, [this]() {
0541         startKeyListing();
0542     });
0543 
0544     watcher->setEnabled(d->m_refreshJob.isNull());
0545 }
0546 
0547 void KeyCache::enableRemarks(bool value)
0548 {
0549     if (!d->m_remarks_enabled && value) {
0550         d->m_remarks_enabled = value;
0551         if (d->m_initalized && !d->m_refreshJob) {
0552             qCDebug(LIBKLEO_LOG) << "Reloading keycache with remarks enabled";
0553             reload();
0554         }
0555     } else {
0556         d->m_remarks_enabled = value;
0557     }
0558 }
0559 
0560 bool KeyCache::remarksEnabled() const
0561 {
0562     return d->m_remarks_enabled;
0563 }
0564 
0565 void KeyCache::Private::refreshJobDone(const KeyListResult &result)
0566 {
0567     m_refreshJob.clear();
0568     q->enableFileSystemWatcher(true);
0569     if (!m_initalized && q->remarksEnabled()) {
0570         // trigger another key listing to read signatures and signature notations
0571         QMetaObject::invokeMethod(
0572             q,
0573             [this]() {
0574                 qCDebug(LIBKLEO_LOG) << "Reloading keycache with remarks enabled";
0575                 q->reload();
0576             },
0577             Qt::QueuedConnection);
0578     }
0579     m_initalized = true;
0580     updateGroupCache();
0581     Q_EMIT q->keyListingDone(result);
0582 }
0583 
0584 const Key &KeyCache::findByFingerprint(const char *fpr) const
0585 {
0586     const std::vector<Key>::const_iterator it = d->find_fpr(fpr);
0587     if (it == d->by.fpr.end()) {
0588         static const Key null;
0589         return null;
0590     } else {
0591         return *it;
0592     }
0593 }
0594 
0595 const Key &KeyCache::findByFingerprint(const std::string &fpr) const
0596 {
0597     return findByFingerprint(fpr.c_str());
0598 }
0599 
0600 std::vector<GpgME::Key> KeyCache::findByFingerprint(const std::vector<std::string> &fprs) const
0601 {
0602     std::vector<Key> keys;
0603     keys.reserve(fprs.size());
0604     for (const auto &fpr : fprs) {
0605         const Key key = findByFingerprint(fpr.c_str());
0606         if (key.isNull()) {
0607             qCDebug(LIBKLEO_LOG) << __func__ << "Ignoring unknown key with fingerprint:" << fpr.c_str();
0608             continue;
0609         }
0610         keys.push_back(key);
0611     }
0612     return keys;
0613 }
0614 
0615 std::vector<Key> KeyCache::findByEMailAddress(const char *email) const
0616 {
0617     const auto pair = d->find_email(email);
0618     std::vector<Key> result;
0619     result.reserve(std::distance(pair.first, pair.second));
0620     std::transform(pair.first, pair.second, std::back_inserter(result), [](const std::pair<std::string, Key> &pair) {
0621         return pair.second;
0622     });
0623     return result;
0624 }
0625 
0626 std::vector<Key> KeyCache::findByEMailAddress(const std::string &email) const
0627 {
0628     return findByEMailAddress(email.c_str());
0629 }
0630 
0631 const Key &KeyCache::findByShortKeyID(const char *id) const
0632 {
0633     const std::vector<Key>::const_iterator it = d->find_shortkeyid(id);
0634     if (it != d->by.shortkeyid.end()) {
0635         return *it;
0636     }
0637     static const Key null;
0638     return null;
0639 }
0640 
0641 const Key &KeyCache::findByShortKeyID(const std::string &id) const
0642 {
0643     return findByShortKeyID(id.c_str());
0644 }
0645 
0646 const Key &KeyCache::findByKeyIDOrFingerprint(const char *id) const
0647 {
0648     {
0649         // try by.fpr first:
0650         const std::vector<Key>::const_iterator it = d->find_fpr(id);
0651         if (it != d->by.fpr.end()) {
0652             return *it;
0653         }
0654     }
0655     {
0656         // try by.keyid next:
0657         const std::vector<Key>::const_iterator it = d->find_keyid(id);
0658         if (it != d->by.keyid.end()) {
0659             return *it;
0660         }
0661     }
0662     static const Key null;
0663     return null;
0664 }
0665 
0666 const Key &KeyCache::findByKeyIDOrFingerprint(const std::string &id) const
0667 {
0668     return findByKeyIDOrFingerprint(id.c_str());
0669 }
0670 
0671 std::vector<Key> KeyCache::findByKeyIDOrFingerprint(const std::vector<std::string> &ids) const
0672 {
0673     std::vector<std::string> keyids;
0674     std::remove_copy_if(ids.begin(), ids.end(), std::back_inserter(keyids), [](const std::string &str) {
0675         return !str.c_str() || !*str.c_str();
0676     });
0677 
0678     // this is just case-insensitive string search:
0679     std::sort(keyids.begin(), keyids.end(), _detail::ByFingerprint<std::less>());
0680 
0681     std::vector<Key> result;
0682     result.reserve(keyids.size()); // dups shouldn't happen
0683     d->ensureCachePopulated();
0684 
0685     kdtools::set_intersection(d->by.fpr.begin(),
0686                               d->by.fpr.end(),
0687                               keyids.begin(),
0688                               keyids.end(),
0689                               std::back_inserter(result),
0690                               _detail::ByFingerprint<std::less>());
0691     if (result.size() < keyids.size()) {
0692         // note that By{Fingerprint,KeyID,ShortKeyID} define the same
0693         // order for _strings_
0694         kdtools::set_intersection(d->by.keyid.begin(),
0695                                   d->by.keyid.end(),
0696                                   keyids.begin(),
0697                                   keyids.end(),
0698                                   std::back_inserter(result),
0699                                   _detail::ByKeyID<std::less>());
0700     }
0701     // duplicates shouldn't happen, but make sure nonetheless:
0702     std::sort(result.begin(), result.end(), _detail::ByFingerprint<std::less>());
0703     result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
0704 
0705     // we skip looking into short key ids here, as it's highly
0706     // unlikely they're used for this purpose. We might need to revise
0707     // this decision, but only after testing.
0708     return result;
0709 }
0710 
0711 const Subkey &KeyCache::findSubkeyByKeyGrip(const char *grip, Protocol protocol) const
0712 {
0713     static const Subkey null;
0714     d->ensureCachePopulated();
0715     const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), grip, _detail::ByKeyGrip<std::less>());
0716     if (range.first == range.second) {
0717         return null;
0718     } else if (protocol == UnknownProtocol) {
0719         return *range.first;
0720     } else {
0721         for (auto it = range.first; it != range.second; ++it) {
0722             if (it->parent().protocol() == protocol) {
0723                 return *it;
0724             }
0725         }
0726     }
0727     return null;
0728 }
0729 
0730 const Subkey &KeyCache::findSubkeyByKeyGrip(const std::string &grip, Protocol protocol) const
0731 {
0732     return findSubkeyByKeyGrip(grip.c_str(), protocol);
0733 }
0734 
0735 std::vector<Subkey> KeyCache::findSubkeysByKeyID(const std::vector<std::string> &ids) const
0736 {
0737     std::vector<std::string> sorted;
0738     sorted.reserve(ids.size());
0739     std::remove_copy_if(ids.begin(), ids.end(), std::back_inserter(sorted), [](const std::string &str) {
0740         return !str.c_str() || !*str.c_str();
0741     });
0742 
0743     std::sort(sorted.begin(), sorted.end(), _detail::ByKeyID<std::less>());
0744 
0745     std::vector<Subkey> result;
0746     d->ensureCachePopulated();
0747     kdtools::set_intersection(d->by.subkeyid.begin(),
0748                               d->by.subkeyid.end(),
0749                               sorted.begin(),
0750                               sorted.end(),
0751                               std::back_inserter(result),
0752                               _detail::ByKeyID<std::less>());
0753     return result;
0754 }
0755 
0756 std::vector<Key> KeyCache::findRecipients(const DecryptionResult &res) const
0757 {
0758     std::vector<std::string> keyids;
0759     const auto recipients = res.recipients();
0760     for (const DecryptionResult::Recipient &r : recipients) {
0761         if (const char *kid = r.keyID()) {
0762             keyids.push_back(kid);
0763         }
0764     }
0765     const std::vector<Subkey> subkeys = findSubkeysByKeyID(keyids);
0766     std::vector<Key> result;
0767     result.reserve(subkeys.size());
0768     std::transform(subkeys.begin(), subkeys.end(), std::back_inserter(result), std::mem_fn(&Subkey::parent));
0769 
0770     std::sort(result.begin(), result.end(), _detail::ByFingerprint<std::less>());
0771     result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
0772     return result;
0773 }
0774 
0775 std::vector<Key> KeyCache::findSigners(const VerificationResult &res) const
0776 {
0777     std::vector<std::string> fprs;
0778     const auto signatures = res.signatures();
0779     for (const Signature &s : signatures) {
0780         if (const char *fpr = s.fingerprint()) {
0781             fprs.push_back(fpr);
0782         }
0783     }
0784     return findByKeyIDOrFingerprint(fprs);
0785 }
0786 
0787 std::vector<Key> KeyCache::findSigningKeysByMailbox(const QString &mb) const
0788 {
0789     return d->find_mailbox(mb, true);
0790 }
0791 
0792 std::vector<Key> KeyCache::findEncryptionKeysByMailbox(const QString &mb) const
0793 {
0794     return d->find_mailbox(mb, false);
0795 }
0796 
0797 namespace
0798 {
0799 #define DO(op, meth, meth2)                                                                                                                                    \
0800     if (op key.meth()) {                                                                                                                                       \
0801     } else {                                                                                                                                                   \
0802         qDebug("rejecting for signing: %s: %s", #meth2, key.primaryFingerprint());                                                                             \
0803         return false;                                                                                                                                          \
0804     }
0805 #define ACCEPT(meth) DO(!!, meth, !meth)
0806 #define REJECT(meth) DO(!, meth, meth)
0807 struct ready_for_signing {
0808     bool operator()(const Key &key) const
0809     {
0810         ACCEPT(hasSecret);
0811 #if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
0812         ACCEPT(hasSign);
0813 #else
0814         ACCEPT(canSign);
0815 #endif
0816         REJECT(isRevoked);
0817         REJECT(isExpired);
0818         REJECT(isDisabled);
0819         REJECT(isInvalid);
0820         return true;
0821 #undef DO
0822     }
0823 };
0824 
0825 #define DO(op, meth, meth2)                                                                                                                                    \
0826     if (op key.meth()) {                                                                                                                                       \
0827     } else {                                                                                                                                                   \
0828         qDebug("rejecting for encrypting: %s: %s", #meth2, key.primaryFingerprint());                                                                          \
0829         return false;                                                                                                                                          \
0830     }
0831 struct ready_for_encryption {
0832     bool operator()(const Key &key) const
0833     {
0834 #if 1
0835 #if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
0836         ACCEPT(hasEncrypt);
0837 #else
0838         ACCEPT(canEncrypt);
0839 #endif
0840         REJECT(isRevoked);
0841         REJECT(isExpired);
0842         REJECT(isDisabled);
0843         REJECT(isInvalid);
0844         return true;
0845 #else
0846         return key.hasEncrypt() && !key.isRevoked() && !key.isExpired() && !key.isDisabled() && !key.isInvalid();
0847 #endif
0848     }
0849 #undef DO
0850 #undef ACCEPT
0851 #undef REJECT
0852 };
0853 }
0854 
0855 std::vector<Key> KeyCache::Private::find_mailbox(const QString &email, bool sign) const
0856 {
0857     if (email.isEmpty()) {
0858         return std::vector<Key>();
0859     }
0860 
0861     const auto pair = find_email(email.toUtf8().constData());
0862     std::vector<Key> result;
0863     result.reserve(std::distance(pair.first, pair.second));
0864     if (sign) {
0865         kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_signing());
0866     } else {
0867         kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_encryption());
0868     }
0869 
0870     return result;
0871 }
0872 
0873 std::vector<Key> KeyCache::findSubjects(const GpgME::Key &key, Options options) const
0874 {
0875     if (key.isNull()) {
0876         return {};
0877     }
0878 
0879     return findSubjects(std::vector<Key>(1, key), options);
0880 }
0881 
0882 std::vector<Key> KeyCache::findSubjects(const std::vector<Key> &keys, Options options) const
0883 {
0884     std::vector<Key> result;
0885 
0886     if (keys.empty()) {
0887         return result;
0888     }
0889 
0890     // get the immediate subjects
0891     for (const auto &key : keys) {
0892         const auto firstAndLastSubject = d->find_subjects(key.primaryFingerprint());
0893         result.insert(result.end(), firstAndLastSubject.first, firstAndLastSubject.second);
0894     }
0895     // remove duplicates
0896     _detail::sort_by_fpr(result);
0897     _detail::remove_duplicates_by_fpr(result);
0898 
0899     if (options & RecursiveSearch) {
0900         for (std::vector<Key> furtherSubjects = findSubjects(result, NoOption); //
0901              !furtherSubjects.empty();
0902              furtherSubjects = findSubjects(furtherSubjects, NoOption)) {
0903             std::vector<Key> combined;
0904             combined.reserve(result.size() + furtherSubjects.size());
0905             std::merge(result.begin(),
0906                        result.end(),
0907                        furtherSubjects.begin(),
0908                        furtherSubjects.end(),
0909                        std::back_inserter(combined),
0910                        _detail::ByFingerprint<std::less>());
0911             _detail::remove_duplicates_by_fpr(combined);
0912             if (result.size() == combined.size()) {
0913                 // no new subjects were found; this happens if a chain has a cycle
0914                 break;
0915             }
0916             result.swap(combined);
0917         }
0918     }
0919 
0920     return result;
0921 }
0922 
0923 std::vector<Key> KeyCache::findIssuers(const Key &key, Options options) const
0924 {
0925     std::vector<Key> result;
0926 
0927     if (key.isNull()) {
0928         return result;
0929     }
0930 
0931     if (options & IncludeSubject) {
0932         result.push_back(key);
0933     }
0934 
0935     if (key.isRoot()) {
0936         return result;
0937     }
0938 
0939     Key issuer = findByFingerprint(key.chainID());
0940 
0941     if (issuer.isNull()) {
0942         return result;
0943     }
0944 
0945     result.push_back(issuer);
0946 
0947     if (!(options & RecursiveSearch)) {
0948         return result;
0949     }
0950 
0951     while (!issuer.isRoot()) {
0952         issuer = findByFingerprint(result.back().chainID());
0953         if (issuer.isNull()) {
0954             break;
0955         }
0956         const bool chainAlreadyContainsIssuer = Kleo::contains_if(result, [issuer](const auto &key) {
0957             return _detail::ByFingerprint<std::equal_to>()(issuer, key);
0958         });
0959         // we also add the issuer if the chain already contains it, so that
0960         // the user can spot the cycle
0961         result.push_back(issuer);
0962         if (chainAlreadyContainsIssuer) {
0963             // break on cycle in chain
0964             break;
0965         }
0966     }
0967 
0968     return result;
0969 }
0970 
0971 static std::string email(const UserID &uid)
0972 {
0973     // Prefer the gnupg normalized one
0974     const std::string addr = uid.addrSpec();
0975     if (!addr.empty()) {
0976         return addr;
0977     }
0978     const std::string email = uid.email();
0979     if (email.empty()) {
0980         return DN(uid.id())[QStringLiteral("EMAIL")].trimmed().toUtf8().constData();
0981     }
0982     if (email[0] == '<' && email[email.size() - 1] == '>') {
0983         return email.substr(1, email.size() - 2);
0984     } else {
0985         return email;
0986     }
0987 }
0988 
0989 static std::vector<std::string> emails(const Key &key)
0990 {
0991     std::vector<std::string> emails;
0992     const auto userIDs = key.userIDs();
0993     for (const UserID &uid : userIDs) {
0994         const std::string e = email(uid);
0995         if (!e.empty()) {
0996             emails.push_back(e);
0997         }
0998     }
0999     std::sort(emails.begin(), emails.end(), ByEMail<std::less>());
1000     emails.erase(std::unique(emails.begin(), emails.end(), ByEMail<std::equal_to>()), emails.end());
1001     return emails;
1002 }
1003 
1004 void KeyCache::remove(const Key &key)
1005 {
1006     if (key.isNull()) {
1007         return;
1008     }
1009 
1010     const char *fpr = key.primaryFingerprint();
1011     if (!fpr) {
1012         return;
1013     }
1014 
1015     {
1016         const auto range = std::equal_range(d->by.fpr.begin(), d->by.fpr.end(), fpr, _detail::ByFingerprint<std::less>());
1017         d->by.fpr.erase(range.first, range.second);
1018     }
1019 
1020     if (const char *keyid = key.keyID()) {
1021         const auto range = std::equal_range(d->by.keyid.begin(), d->by.keyid.end(), keyid, _detail::ByKeyID<std::less>());
1022         const auto it = std::remove_if(range.first, range.second, [fpr](const GpgME::Key &key) {
1023             return _detail::ByFingerprint<std::equal_to>()(fpr, key);
1024         });
1025         d->by.keyid.erase(it, range.second);
1026     }
1027 
1028     if (const char *shortkeyid = key.shortKeyID()) {
1029         const auto range = std::equal_range(d->by.shortkeyid.begin(), d->by.shortkeyid.end(), shortkeyid, _detail::ByShortKeyID<std::less>());
1030         const auto it = std::remove_if(range.first, range.second, [fpr](const GpgME::Key &key) {
1031             return _detail::ByFingerprint<std::equal_to>()(fpr, key);
1032         });
1033         d->by.shortkeyid.erase(it, range.second);
1034     }
1035 
1036     if (const char *chainid = key.chainID()) {
1037         const auto range = std::equal_range(d->by.chainid.begin(), d->by.chainid.end(), chainid, _detail::ByChainID<std::less>());
1038         const auto range2 = std::equal_range(range.first, range.second, fpr, _detail::ByFingerprint<std::less>());
1039         d->by.chainid.erase(range2.first, range2.second);
1040     }
1041 
1042     const auto emailsKey{emails(key)};
1043     for (const std::string &email : emailsKey) {
1044         const auto range = std::equal_range(d->by.email.begin(), d->by.email.end(), email, ByEMail<std::less>());
1045         const auto it = std::remove_if(range.first, range.second, [fpr](const std::pair<std::string, Key> &pair) {
1046             return qstricmp(fpr, pair.second.primaryFingerprint()) == 0;
1047         });
1048         d->by.email.erase(it, range.second);
1049     }
1050 
1051     const auto keySubKeys{key.subkeys()};
1052     for (const Subkey &subkey : keySubKeys) {
1053         if (const char *keyid = subkey.keyID()) {
1054             const auto range = std::equal_range(d->by.subkeyid.begin(), d->by.subkeyid.end(), keyid, _detail::ByKeyID<std::less>());
1055             const auto it = std::remove_if(range.first, range.second, [fpr](const Subkey &subkey) {
1056                 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1057             });
1058             d->by.subkeyid.erase(it, range.second);
1059         }
1060         if (const char *keygrip = subkey.keyGrip()) {
1061             const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), keygrip, _detail::ByKeyGrip<std::less>());
1062             const auto it = std::remove_if(range.first, range.second, [fpr](const Subkey &subkey) {
1063                 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1064             });
1065             d->by.keygrip.erase(it, range.second);
1066         }
1067     }
1068 }
1069 
1070 void KeyCache::remove(const std::vector<Key> &keys)
1071 {
1072     for (const Key &key : keys) {
1073         remove(key);
1074     }
1075 }
1076 
1077 const std::vector<GpgME::Key> &KeyCache::keys() const
1078 {
1079     d->ensureCachePopulated();
1080     return d->by.fpr;
1081 }
1082 
1083 std::vector<Key> KeyCache::secretKeys() const
1084 {
1085     std::vector<Key> keys = this->keys();
1086     keys.erase(std::remove_if(keys.begin(),
1087                               keys.end(),
1088                               [](const Key &key) {
1089                                   return !key.hasSecret();
1090                               }),
1091                keys.end());
1092     return keys;
1093 }
1094 
1095 KeyGroup KeyCache::group(const QString &id) const
1096 {
1097     KeyGroup result{};
1098     const auto it = std::find_if(std::cbegin(d->m_groups), std::cend(d->m_groups), [id](const auto &g) {
1099         return g.id() == id;
1100     });
1101     if (it != std::cend(d->m_groups)) {
1102         result = *it;
1103     }
1104     return result;
1105 }
1106 
1107 std::vector<KeyGroup> KeyCache::groups() const
1108 {
1109     d->ensureCachePopulated();
1110     return d->m_groups;
1111 }
1112 
1113 std::vector<KeyGroup> KeyCache::configurableGroups() const
1114 {
1115     std::vector<KeyGroup> groups;
1116     groups.reserve(d->m_groups.size());
1117     std::copy_if(d->m_groups.cbegin(), d->m_groups.cend(), std::back_inserter(groups), [](const KeyGroup &group) {
1118         return group.source() == KeyGroup::ApplicationConfig;
1119     });
1120     return groups;
1121 }
1122 
1123 namespace
1124 {
1125 bool compareById(const KeyGroup &lhs, const KeyGroup &rhs)
1126 {
1127     return lhs.id() < rhs.id();
1128 }
1129 
1130 std::vector<KeyGroup> sortedById(std::vector<KeyGroup> groups)
1131 {
1132     std::sort(groups.begin(), groups.end(), &compareById);
1133     return groups;
1134 }
1135 }
1136 
1137 void KeyCache::saveConfigurableGroups(const std::vector<KeyGroup> &groups)
1138 {
1139     const std::vector<KeyGroup> oldGroups = sortedById(configurableGroups());
1140     const std::vector<KeyGroup> newGroups = sortedById(groups);
1141 
1142     {
1143         std::vector<KeyGroup> removedGroups;
1144         std::set_difference(oldGroups.begin(), oldGroups.end(), newGroups.begin(), newGroups.end(), std::back_inserter(removedGroups), &compareById);
1145         for (const auto &group : std::as_const(removedGroups)) {
1146             qCDebug(LIBKLEO_LOG) << "Removing group" << group;
1147             d->remove(group);
1148         }
1149     }
1150     {
1151         std::vector<KeyGroup> updatedGroups;
1152         std::set_intersection(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(updatedGroups), &compareById);
1153         for (const auto &group : std::as_const(updatedGroups)) {
1154             qCDebug(LIBKLEO_LOG) << "Updating group" << group;
1155             d->update(group);
1156         }
1157     }
1158     {
1159         std::vector<KeyGroup> addedGroups;
1160         std::set_difference(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(addedGroups), &compareById);
1161         for (const auto &group : std::as_const(addedGroups)) {
1162             qCDebug(LIBKLEO_LOG) << "Adding group" << group;
1163             d->insert(group);
1164         }
1165     }
1166 
1167     Q_EMIT keysMayHaveChanged();
1168 }
1169 
1170 bool KeyCache::insert(const KeyGroup &group)
1171 {
1172     if (!d->insert(group)) {
1173         return false;
1174     }
1175 
1176     Q_EMIT keysMayHaveChanged();
1177 
1178     return true;
1179 }
1180 
1181 bool KeyCache::update(const KeyGroup &group)
1182 {
1183     if (!d->update(group)) {
1184         return false;
1185     }
1186 
1187     Q_EMIT keysMayHaveChanged();
1188 
1189     return true;
1190 }
1191 
1192 bool KeyCache::remove(const KeyGroup &group)
1193 {
1194     if (!d->remove(group)) {
1195         return false;
1196     }
1197 
1198     Q_EMIT keysMayHaveChanged();
1199 
1200     return true;
1201 }
1202 
1203 void KeyCache::refresh(const std::vector<Key> &keys)
1204 {
1205     // make this better...
1206     clear();
1207     insert(keys);
1208 }
1209 
1210 void KeyCache::insert(const Key &key)
1211 {
1212     insert(std::vector<Key>(1, key));
1213 }
1214 
1215 namespace
1216 {
1217 
1218 template<template<template<typename T> class Op> class T1, template<template<typename T> class Op> class T2>
1219 struct lexicographically {
1220     using result_type = bool;
1221 
1222     template<typename U, typename V>
1223     bool operator()(const U &lhs, const V &rhs) const
1224     {
1225         return T1<std::less>()(lhs, rhs) //
1226             || (T1<std::equal_to>()(lhs, rhs) && T2<std::less>()(lhs, rhs));
1227     }
1228 };
1229 
1230 }
1231 
1232 void KeyCache::insert(const std::vector<Key> &keys)
1233 {
1234     // 1. remove those with empty fingerprints:
1235     std::vector<Key> sorted;
1236     sorted.reserve(keys.size());
1237     std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), [](const Key &key) {
1238         auto fp = key.primaryFingerprint();
1239         return !fp || !*fp;
1240     });
1241 
1242     Q_FOREACH (const Key &key, sorted) {
1243         remove(key); // this is sub-optimal, but makes implementation from here on much easier
1244     }
1245 
1246     // 2. sort by fingerprint:
1247     std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
1248 
1249     // 2a. insert into fpr index:
1250     std::vector<Key> by_fpr;
1251     by_fpr.reserve(sorted.size() + d->by.fpr.size());
1252     std::merge(sorted.begin(), sorted.end(), d->by.fpr.begin(), d->by.fpr.end(), std::back_inserter(by_fpr), _detail::ByFingerprint<std::less>());
1253 
1254     // 3. build email index:
1255     std::vector<std::pair<std::string, Key>> pairs;
1256     pairs.reserve(sorted.size());
1257     for (const Key &key : std::as_const(sorted)) {
1258         const std::vector<std::string> emails = ::emails(key);
1259         for (const std::string &e : emails) {
1260             pairs.push_back(std::make_pair(e, key));
1261         }
1262     }
1263     std::sort(pairs.begin(), pairs.end(), ByEMail<std::less>());
1264 
1265     // 3a. insert into email index:
1266     std::vector<std::pair<std::string, Key>> by_email;
1267     by_email.reserve(pairs.size() + d->by.email.size());
1268     std::merge(pairs.begin(), pairs.end(), d->by.email.begin(), d->by.email.end(), std::back_inserter(by_email), ByEMail<std::less>());
1269 
1270     // 3.5: stable-sort by chain-id (effectively lexicographically<ByChainID,ByFingerprint>)
1271     std::stable_sort(sorted.begin(), sorted.end(), _detail::ByChainID<std::less>());
1272 
1273     // 3.5a: insert into chain-id index:
1274     std::vector<Key> nonroot;
1275     nonroot.reserve(sorted.size());
1276     std::vector<Key> by_chainid;
1277     by_chainid.reserve(sorted.size() + d->by.chainid.size());
1278     std::copy_if(sorted.cbegin(), sorted.cend(), std::back_inserter(nonroot), [](const Key &key) {
1279         return !key.isRoot();
1280     });
1281     std::merge(nonroot.cbegin(),
1282                nonroot.cend(),
1283                d->by.chainid.cbegin(),
1284                d->by.chainid.cend(),
1285                std::back_inserter(by_chainid),
1286                lexicographically<_detail::ByChainID, _detail::ByFingerprint>());
1287 
1288     // 4. sort by key id:
1289     std::sort(sorted.begin(), sorted.end(), _detail::ByKeyID<std::less>());
1290 
1291     // 4a. insert into keyid index:
1292     std::vector<Key> by_keyid;
1293     by_keyid.reserve(sorted.size() + d->by.keyid.size());
1294     std::merge(sorted.begin(), sorted.end(), d->by.keyid.begin(), d->by.keyid.end(), std::back_inserter(by_keyid), _detail::ByKeyID<std::less>());
1295 
1296     // 5. sort by short key id:
1297     std::sort(sorted.begin(), sorted.end(), _detail::ByShortKeyID<std::less>());
1298 
1299     // 5a. insert into short keyid index:
1300     std::vector<Key> by_shortkeyid;
1301     by_shortkeyid.reserve(sorted.size() + d->by.shortkeyid.size());
1302     std::merge(sorted.begin(),
1303                sorted.end(),
1304                d->by.shortkeyid.begin(),
1305                d->by.shortkeyid.end(),
1306                std::back_inserter(by_shortkeyid),
1307                _detail::ByShortKeyID<std::less>());
1308 
1309     // 6. build subkey ID index:
1310     std::vector<Subkey> subkeys;
1311     subkeys.reserve(sorted.size());
1312     for (const Key &key : std::as_const(sorted)) {
1313         const auto keySubkeys{key.subkeys()};
1314         for (const Subkey &subkey : keySubkeys) {
1315             subkeys.push_back(subkey);
1316         }
1317     }
1318 
1319     // 6a sort by key id:
1320     std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyID<std::less>());
1321 
1322     // 6b. insert into subkey ID index:
1323     std::vector<Subkey> by_subkeyid;
1324     by_subkeyid.reserve(subkeys.size() + d->by.subkeyid.size());
1325     std::merge(subkeys.begin(), subkeys.end(), d->by.subkeyid.begin(), d->by.subkeyid.end(), std::back_inserter(by_subkeyid), _detail::ByKeyID<std::less>());
1326 
1327     // 6c. sort by key grip
1328     std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyGrip<std::less>());
1329 
1330     // 6d. insert into subkey keygrip index:
1331     std::vector<Subkey> by_keygrip;
1332     by_keygrip.reserve(subkeys.size() + d->by.keygrip.size());
1333     std::merge(subkeys.begin(), subkeys.end(), d->by.keygrip.begin(), d->by.keygrip.end(), std::back_inserter(by_keygrip), _detail::ByKeyGrip<std::less>());
1334 
1335     // now commit (well, we already removed keys...)
1336     by_fpr.swap(d->by.fpr);
1337     by_keyid.swap(d->by.keyid);
1338     by_shortkeyid.swap(d->by.shortkeyid);
1339     by_email.swap(d->by.email);
1340     by_subkeyid.swap(d->by.subkeyid);
1341     by_keygrip.swap(d->by.keygrip);
1342     by_chainid.swap(d->by.chainid);
1343 
1344     for (const Key &key : std::as_const(sorted)) {
1345         d->m_pgpOnly &= key.protocol() == GpgME::OpenPGP;
1346     }
1347 
1348     Q_EMIT keysMayHaveChanged();
1349 }
1350 
1351 void KeyCache::clear()
1352 {
1353     d->by = Private::By();
1354 }
1355 
1356 //
1357 //
1358 // RefreshKeysJob
1359 //
1360 //
1361 
1362 class KeyCache::RefreshKeysJob::Private
1363 {
1364     RefreshKeysJob *const q;
1365 
1366 public:
1367     Private(KeyCache *cache, RefreshKeysJob *qq);
1368     void doStart();
1369     Error startKeyListing(GpgME::Protocol protocol);
1370     void listAllKeysJobDone(const KeyListResult &res, const std::vector<Key> &nextKeys)
1371     {
1372         std::vector<Key> keys;
1373         keys.reserve(m_keys.size() + nextKeys.size());
1374         if (m_keys.empty()) {
1375             keys = nextKeys;
1376         } else {
1377             std::merge(m_keys.begin(), m_keys.end(), nextKeys.begin(), nextKeys.end(), std::back_inserter(keys), _detail::ByFingerprint<std::less>());
1378         }
1379         m_keys.swap(keys);
1380         jobDone(res);
1381     }
1382     void emitDone(const KeyListResult &result);
1383     void updateKeyCache();
1384 
1385     QPointer<KeyCache> m_cache;
1386     QList<QGpgME::ListAllKeysJob *> m_jobsPending;
1387     std::vector<Key> m_keys;
1388     KeyListResult m_mergedResult;
1389     bool m_canceled;
1390 
1391 private:
1392     void jobDone(const KeyListResult &res);
1393 };
1394 
1395 KeyCache::RefreshKeysJob::Private::Private(KeyCache *cache, RefreshKeysJob *qq)
1396     : q(qq)
1397     , m_cache(cache)
1398     , m_canceled(false)
1399 {
1400     Q_ASSERT(m_cache);
1401 }
1402 
1403 void KeyCache::RefreshKeysJob::Private::jobDone(const KeyListResult &result)
1404 {
1405     if (m_canceled) {
1406         q->deleteLater();
1407         return;
1408     }
1409 
1410     QObject *const sender = q->sender();
1411     if (sender) {
1412         sender->disconnect(q);
1413     }
1414     Q_ASSERT(m_jobsPending.size() > 0);
1415     m_jobsPending.removeOne(qobject_cast<QGpgME::ListAllKeysJob *>(sender));
1416     m_mergedResult.mergeWith(result);
1417     if (m_jobsPending.size() > 0) {
1418         return;
1419     }
1420     updateKeyCache();
1421     emitDone(m_mergedResult);
1422 }
1423 
1424 void KeyCache::RefreshKeysJob::Private::emitDone(const KeyListResult &res)
1425 {
1426     q->deleteLater();
1427     Q_EMIT q->done(res);
1428 }
1429 
1430 KeyCache::RefreshKeysJob::RefreshKeysJob(KeyCache *cache, QObject *parent)
1431     : QObject(parent)
1432     , d(new Private(cache, this))
1433 {
1434 }
1435 
1436 KeyCache::RefreshKeysJob::~RefreshKeysJob()
1437 {
1438     delete d;
1439 }
1440 
1441 void KeyCache::RefreshKeysJob::start()
1442 {
1443     QTimer::singleShot(0, this, [this]() {
1444         d->doStart();
1445     });
1446 }
1447 
1448 void KeyCache::RefreshKeysJob::cancel()
1449 {
1450     d->m_canceled = true;
1451     std::for_each(d->m_jobsPending.begin(), d->m_jobsPending.end(), std::mem_fn(&QGpgME::ListAllKeysJob::slotCancel));
1452     Q_EMIT canceled();
1453 }
1454 
1455 void KeyCache::RefreshKeysJob::Private::doStart()
1456 {
1457     if (m_canceled) {
1458         q->deleteLater();
1459         return;
1460     }
1461 
1462     Q_ASSERT(m_jobsPending.size() == 0);
1463     m_mergedResult.mergeWith(KeyListResult(startKeyListing(GpgME::OpenPGP)));
1464     m_mergedResult.mergeWith(KeyListResult(startKeyListing(GpgME::CMS)));
1465 
1466     if (m_jobsPending.size() != 0) {
1467         return;
1468     }
1469 
1470     const bool hasError = m_mergedResult.error() || m_mergedResult.error().isCanceled();
1471     emitDone(hasError ? m_mergedResult : KeyListResult(Error(GPG_ERR_UNSUPPORTED_OPERATION)));
1472 }
1473 
1474 void KeyCache::RefreshKeysJob::Private::updateKeyCache()
1475 {
1476     if (!m_cache || m_canceled) {
1477         q->deleteLater();
1478         return;
1479     }
1480 
1481     std::vector<Key> cachedKeys = m_cache->initialized() ? m_cache->keys() : std::vector<Key>();
1482     std::sort(cachedKeys.begin(), cachedKeys.end(), _detail::ByFingerprint<std::less>());
1483     std::vector<Key> keysToRemove;
1484     std::set_difference(cachedKeys.begin(),
1485                         cachedKeys.end(),
1486                         m_keys.begin(),
1487                         m_keys.end(),
1488                         std::back_inserter(keysToRemove),
1489                         _detail::ByFingerprint<std::less>());
1490     m_cache->remove(keysToRemove);
1491     m_cache->refresh(m_keys);
1492 }
1493 
1494 Error KeyCache::RefreshKeysJob::Private::startKeyListing(GpgME::Protocol proto)
1495 {
1496     const auto *const protocol = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
1497     if (!protocol) {
1498         return Error();
1499     }
1500     QGpgME::ListAllKeysJob *const job = protocol->listAllKeysJob(/*includeSigs*/ false, /*validate*/ true);
1501     if (!job) {
1502         return Error();
1503     }
1504     if (!m_cache->initialized()) {
1505         // avoid delays during the initial key listing
1506         job->setOptions(QGpgME::ListAllKeysJob::DisableAutomaticTrustDatabaseCheck);
1507     }
1508 
1509 #if 0
1510     aheinecke: 2017.01.12:
1511 
1512     For unknown reasons the new style connect fails at runtime
1513     over library borders into QGpgME from the GpgME repo
1514     when cross compiled for Windows and default arguments
1515     are used in the Signal.
1516 
1517     This was tested with gcc 4.9 (Mingw 3.0.2) and we could not
1518     find an explanation for this. So until this is fixed or we understand
1519     the problem we need to use the old style connect for QGpgME signals.
1520 
1521     The new style connect of the canceled signal right below
1522     works fine.
1523 
1524     connect(job, &QGpgME::ListAllKeysJob::result,
1525             q, [this](const GpgME::KeyListResult &res, const std::vector<GpgME::Key> &keys) {
1526                 listAllKeysJobDone(res, keys);
1527             });
1528 #endif
1529     connect(job, SIGNAL(result(GpgME::KeyListResult, std::vector<GpgME::Key>)), q, SLOT(listAllKeysJobDone(GpgME::KeyListResult, std::vector<GpgME::Key>)));
1530 
1531     connect(q, &RefreshKeysJob::canceled, job, &QGpgME::Job::slotCancel);
1532 
1533     // Only do this for initialized keycaches to avoid huge waits for
1534     // signature notations during initial keylisting.
1535     if (proto == GpgME::OpenPGP && m_cache->remarksEnabled() && m_cache->initialized()) {
1536         auto ctx = QGpgME::Job::context(job);
1537         if (ctx) {
1538             ctx->addKeyListMode(KeyListMode::Signatures | KeyListMode::SignatureNotations);
1539         }
1540     }
1541 
1542     const Error error = job->start(true);
1543 
1544     if (!error && !error.isCanceled()) {
1545         m_jobsPending.push_back(job);
1546     }
1547     return error;
1548 }
1549 
1550 bool KeyCache::initialized() const
1551 {
1552     return d->m_initalized;
1553 }
1554 
1555 void KeyCache::Private::ensureCachePopulated() const
1556 {
1557     if (!m_initalized) {
1558         q->startKeyListing();
1559         QEventLoop loop;
1560         loop.connect(q, &KeyCache::keyListingDone, &loop, &QEventLoop::quit);
1561         qCDebug(LIBKLEO_LOG) << "Waiting for keycache.";
1562         loop.exec();
1563         qCDebug(LIBKLEO_LOG) << "Keycache available.";
1564     }
1565 }
1566 
1567 bool KeyCache::pgpOnly() const
1568 {
1569     return d->m_pgpOnly;
1570 }
1571 
1572 static bool keyIsOk(const Key &k)
1573 {
1574     return !k.isExpired() && !k.isRevoked() && !k.isInvalid() && !k.isDisabled();
1575 }
1576 
1577 static bool uidIsOk(const UserID &uid)
1578 {
1579     return keyIsOk(uid.parent()) && !uid.isRevoked() && !uid.isInvalid();
1580 }
1581 
1582 static bool subkeyIsOk(const Subkey &s)
1583 {
1584     return !s.isRevoked() && !s.isInvalid() && !s.isDisabled();
1585 }
1586 
1587 namespace
1588 {
1589 time_t creationTimeOfNewestSuitableSubKey(const Key &key, KeyCache::KeyUsage usage)
1590 {
1591     time_t creationTime = 0;
1592     for (const Subkey &s : key.subkeys()) {
1593         if (!subkeyIsOk(s)) {
1594             continue;
1595         }
1596         if (usage == KeyCache::KeyUsage::Sign && !s.canSign()) {
1597             continue;
1598         }
1599         if (usage == KeyCache::KeyUsage::Encrypt && !s.canEncrypt()) {
1600             continue;
1601         }
1602         if (s.creationTime() > creationTime) {
1603             creationTime = s.creationTime();
1604         }
1605     }
1606     return creationTime;
1607 }
1608 
1609 struct BestMatch {
1610     Key key;
1611     UserID uid;
1612     time_t creationTime = 0;
1613 };
1614 }
1615 
1616 GpgME::Key KeyCache::findBestByMailBox(const char *addr, GpgME::Protocol proto, KeyUsage usage) const
1617 {
1618     d->ensureCachePopulated();
1619     if (!addr) {
1620         return {};
1621     }
1622 
1623     // support lookup of email addresses enclosed in angle brackets
1624     QByteArray address(addr);
1625     if (address.size() > 1 && address[0] == '<' && address[address.size() - 1] == '>') {
1626         address = address.mid(1, address.size() - 2);
1627     }
1628     address = address.toLower();
1629 
1630     BestMatch best;
1631     for (const Key &k : findByEMailAddress(address.constData())) {
1632         if (proto != Protocol::UnknownProtocol && k.protocol() != proto) {
1633             continue;
1634         }
1635         if (usage == KeyUsage::Encrypt && !keyHasEncrypt(k)) {
1636             continue;
1637         }
1638         if (usage == KeyUsage::Sign && (!keyHasSign(k) || !k.hasSecret())) {
1639             continue;
1640         }
1641         const time_t creationTime = creationTimeOfNewestSuitableSubKey(k, usage);
1642         if (creationTime == 0) {
1643             // key does not have a suitable (and usable) subkey
1644             continue;
1645         }
1646         for (const UserID &u : k.userIDs()) {
1647             if (QByteArray::fromStdString(u.addrSpec()).toLower() != address) {
1648                 // user ID does not match the given email address
1649                 continue;
1650             }
1651             if (best.uid.isNull()) {
1652                 // we have found our first candidate
1653                 best = {k, u, creationTime};
1654             } else if (!uidIsOk(best.uid) && uidIsOk(u)) {
1655                 // validity of the new key is better
1656                 best = {k, u, creationTime};
1657             } else if (!k.isExpired() && best.uid.validity() < u.validity()) {
1658                 // validity of the new key is better
1659                 best = {k, u, creationTime};
1660             } else if (best.key.isExpired() && !k.isExpired()) {
1661                 // validity of the new key is better
1662                 best = {k, u, creationTime};
1663             } else if (best.uid.validity() == u.validity() && uidIsOk(u) && best.creationTime < creationTime) {
1664                 // both keys/user IDs have same validity, but the new key is newer
1665                 best = {k, u, creationTime};
1666             }
1667         }
1668     }
1669 
1670     return best.key;
1671 }
1672 
1673 namespace
1674 {
1675 template<typename T>
1676 bool allKeysAllowUsage(const T &keys, KeyCache::KeyUsage usage)
1677 {
1678     switch (usage) {
1679     case KeyCache::KeyUsage::AnyUsage:
1680         return true;
1681     case KeyCache::KeyUsage::Sign:
1682         return std::all_of(std::begin(keys),
1683                            std::end(keys),
1684 #if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1685                            std::mem_fn(&Key::hasSign)
1686 #else
1687                            Kleo::keyHasSign
1688 #endif
1689         );
1690     case KeyCache::KeyUsage::Encrypt:
1691         return std::all_of(std::begin(keys),
1692                            std::end(keys),
1693 #if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1694                            std::mem_fn(&Key::hasEncrypt)
1695 #else
1696                            Kleo::keyHasEncrypt
1697 #endif
1698         );
1699     case KeyCache::KeyUsage::Certify:
1700         return std::all_of(std::begin(keys),
1701                            std::end(keys),
1702 #if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1703                            std::mem_fn(&Key::hasCertify)
1704 #else
1705                            Kleo::keyHasCertify
1706 #endif
1707         );
1708     case KeyCache::KeyUsage::Authenticate:
1709         return std::all_of(std::begin(keys),
1710                            std::end(keys),
1711 #if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1712                            std::mem_fn(&Key::hasAuthenticate)
1713 #else
1714                            Kleo::keyHasAuthenticate
1715 #endif
1716         );
1717     }
1718     qCDebug(LIBKLEO_LOG) << __func__ << "called with invalid usage" << int(usage);
1719     return false;
1720 }
1721 }
1722 
1723 KeyGroup KeyCache::findGroup(const QString &name, Protocol protocol, KeyUsage usage) const
1724 {
1725     d->ensureCachePopulated();
1726 
1727     Q_ASSERT(usage == KeyUsage::Sign || usage == KeyUsage::Encrypt);
1728     for (const auto &group : std::as_const(d->m_groups)) {
1729         if (group.name() == name) {
1730             const KeyGroup::Keys &keys = group.keys();
1731             if (allKeysAllowUsage(keys, usage) && (protocol == UnknownProtocol || allKeysHaveProtocol(keys, protocol))) {
1732                 return group;
1733             }
1734         }
1735     }
1736 
1737     return {};
1738 }
1739 
1740 std::vector<Key> KeyCache::getGroupKeys(const QString &groupName) const
1741 {
1742     std::vector<Key> result;
1743     for (const KeyGroup &g : std::as_const(d->m_groups)) {
1744         if (g.name() == groupName) {
1745             const KeyGroup::Keys &keys = g.keys();
1746             std::copy(keys.cbegin(), keys.cend(), std::back_inserter(result));
1747         }
1748     }
1749     _detail::sort_by_fpr(result);
1750     _detail::remove_duplicates_by_fpr(result);
1751     return result;
1752 }
1753 
1754 void KeyCache::setKeys(const std::vector<GpgME::Key> &keys)
1755 {
1756     // disable regular key listing and cancel running key listing
1757     setRefreshInterval(0);
1758     cancelKeyListing();
1759     clear();
1760     insert(keys);
1761     d->m_initalized = true;
1762     Q_EMIT keyListingDone(KeyListResult());
1763 }
1764 
1765 void KeyCache::setGroups(const std::vector<KeyGroup> &groups)
1766 {
1767     Q_ASSERT(d->m_initalized && "Call setKeys() before setting groups");
1768     d->m_groups = groups;
1769     Q_EMIT keysMayHaveChanged();
1770 }
1771 
1772 #include "moc_keycache.cpp"
1773 #include "moc_keycache_p.cpp"