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"