13#include <config-libkleo.h>
16#include "keycache_p.h"
18#include <libkleo/algorithm.h>
19#include <libkleo/compat.h>
20#include <libkleo/debug.h>
21#include <libkleo/dn.h>
22#include <libkleo/enum.h>
23#include <libkleo/filesystemwatcher.h>
24#include <libkleo/keygroup.h>
25#include <libkleo/keygroupconfig.h>
26#include <libkleo/keyhelpers.h>
27#include <libkleo/predicates.h>
28#include <libkleo/qtstlhelpers.h>
29#include <libkleo/stl_util.h>
31#include <libkleo_debug.h>
33#include <KSharedConfig>
35#include <QGpgME/CryptoConfig>
36#include <QGpgME/ListAllKeysJob>
37#include <QGpgME/Protocol>
43#include <gpgme++/context.h>
44#include <gpgme++/decryptionresult.h>
45#include <gpgme++/error.h>
46#include <gpgme++/key.h>
47#include <gpgme++/keylistresult.h>
48#include <gpgme++/verificationresult.h>
58using namespace std::chrono_literals;
61using namespace KMime::Types;
63static const unsigned int hours2ms = 1000 * 60 * 60;
74make_comparator_str(ByEMail, .first.c_str());
78class Kleo::KeyCacheAutoRefreshSuspension
80 KeyCacheAutoRefreshSuspension()
82 qCDebug(LIBKLEO_LOG) << __func__;
83 auto cache = KeyCache::mutableInstance();
84 cache->enableFileSystemWatcher(
false);
85 m_refreshInterval = cache->refreshInterval();
86 cache->setRefreshInterval(0);
87 cache->cancelKeyListing();
92 ~KeyCacheAutoRefreshSuspension()
94 qCDebug(LIBKLEO_LOG) << __func__;
95 if (
auto cache = m_cache.lock()) {
96 cache->enableFileSystemWatcher(
true);
97 cache->setRefreshInterval(m_refreshInterval);
101 static std::shared_ptr<KeyCacheAutoRefreshSuspension> instance()
103 static std::weak_ptr<KeyCacheAutoRefreshSuspension> self;
104 if (
auto s = self.lock()) {
107 s = std::shared_ptr<KeyCacheAutoRefreshSuspension>{
new KeyCacheAutoRefreshSuspension{}};
114 std::weak_ptr<KeyCache> m_cache;
115 int m_refreshInterval = 0;
118class KeyCache::Private
120 friend class ::Kleo::KeyCache;
124 explicit Private(KeyCache *qq)
126 , m_refreshInterval(1)
127 , m_initalized(false)
129 , m_remarks_enabled(false)
132 q->startKeyListing();
134 updateAutoKeyListingTimer();
140 m_refreshJob->cancel();
144 template<
template<
template<
typename U>
class Op>
class Comp>
145 std::vector<Key>::const_iterator find(const std::vector<Key> &keys, const char *key) const
147 ensureCachePopulated();
148 const auto it = std::lower_bound(keys.begin(), keys.end(), key, Comp<std::less>());
149 if (it == keys.end() || Comp<std::equal_to>()(*it, key)) {
156 template<
template<
template<
typename U>
class Op>
class Comp>
157 std::vector<Subkey>::const_iterator find(const std::vector<Subkey> &keys, const char *key) const
159 ensureCachePopulated();
160 const auto it = std::lower_bound(keys.begin(), keys.end(), key, Comp<std::less>());
161 if (it == keys.end() || Comp<std::equal_to>()(*it, key)) {
168 std::vector<Key>::const_iterator find_fpr(
const char *fpr)
const
170 return find<_detail::ByFingerprint>(by.fpr, fpr);
173 std::pair<std::vector<std::pair<std::string, Key>>::const_iterator, std::vector<std::pair<std::string, Key>>::const_iterator>
174 find_email(
const char *email)
const
176 ensureCachePopulated();
177 return std::equal_range(by.email.begin(), by.email.end(), email, ByEMail<std::less>());
180 std::vector<Key> find_mailbox(
const QString &email,
bool sign)
const;
182 std::vector<Subkey>::const_iterator find_keygrip(
const char *keygrip)
const
184 return find<_detail::ByKeyGrip>(by.keygrip, keygrip);
187 std::vector<Subkey>::const_iterator find_subkeyid(
const char *subkeyid)
const
189 return find<_detail::ByKeyID>(by.subkeyid, subkeyid);
192 std::vector<Key>::const_iterator find_keyid(
const char *keyid)
const
194 return find<_detail::ByKeyID>(by.keyid, keyid);
197 std::vector<Key>::const_iterator find_shortkeyid(
const char *shortkeyid)
const
199 return find<_detail::ByShortKeyID>(by.shortkeyid, shortkeyid);
202 std::pair<std::vector<Key>::const_iterator, std::vector<Key>::const_iterator> find_subjects(
const char *chain_id)
const
204 ensureCachePopulated();
205 return std::equal_range(by.chainid.begin(), by.chainid.end(), chain_id, _detail::ByChainID<std::less>());
208 void refreshJobDone(
const KeyListResult &result);
210 void setRefreshInterval(
int interval)
212 m_refreshInterval = interval;
213 updateAutoKeyListingTimer();
216 int refreshInterval()
const
218 return m_refreshInterval;
221 void updateAutoKeyListingTimer()
223 setAutoKeyListingInterval(hours2ms * m_refreshInterval);
225 void setAutoKeyListingInterval(
int ms)
227 m_autoKeyListingTimer.
stop();
230 m_autoKeyListingTimer.
start();
234 void ensureCachePopulated()
const;
236 void readGroupsFromGpgConf()
242 auto conf = QGpgME::cryptoConfig();
247 auto entry = getCryptoConfigEntry(conf,
"gpg",
"group");
254 const auto stringValueList = entry->stringValueList();
255 for (
const QString &value : stringValueList) {
257 if (split.size() != 2) {
258 qCDebug(LIBKLEO_LOG) <<
"Ignoring invalid group config:" << value;
261 const QString groupName = split[0];
262 const QString fingerprint = split[1];
263 fingerprints[groupName].push_back(fingerprint);
267 for (
auto it = fingerprints.
cbegin(); it != fingerprints.
cend(); ++it) {
268 const QString groupName = it.key();
269 const std::vector<Key> groupKeys = q->findByFingerprint(toStdStrings(it.value()));
270 KeyGroup g(groupName, groupName, groupKeys, KeyGroup::GnuPGConfig);
271 m_groups.push_back(g);
275 void readGroupsFromGroupsConfig()
277 Q_ASSERT(m_groupConfig);
278 if (!m_groupConfig) {
279 qCWarning(LIBKLEO_LOG) << __func__ <<
"group config not set";
283 m_groups = m_groupConfig->readGroups();
286 KeyGroup writeGroupToGroupsConfig(
const KeyGroup &group)
288 Q_ASSERT(m_groupConfig);
289 if (!m_groupConfig) {
290 qCWarning(LIBKLEO_LOG) << __func__ <<
"group config not set";
294 Q_ASSERT(!group.isNull());
295 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
296 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
297 qCDebug(LIBKLEO_LOG) << __func__ <<
"group cannot be written to application configuration:" << group;
301 return m_groupConfig->writeGroup(group);
304 bool removeGroupFromGroupsConfig(
const KeyGroup &group)
306 Q_ASSERT(m_groupConfig);
307 if (!m_groupConfig) {
308 qCWarning(LIBKLEO_LOG) << __func__ <<
"group config not set";
312 Q_ASSERT(!group.isNull());
313 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
314 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
315 qCDebug(LIBKLEO_LOG) << __func__ <<
"group cannot be removed from application configuration:" << group;
319 return m_groupConfig->removeGroup(group);
322 void updateGroupCache()
329 if (m_groupsEnabled) {
330 readGroupsFromGpgConf();
331 readGroupsFromGroupsConfig();
335 bool insert(
const KeyGroup &group)
337 Q_ASSERT(!group.isNull());
338 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
339 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
340 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::insert - Invalid group:" << group;
343 const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](
const auto &g) {
344 return g.source() == group.source() && g.id() == group.id();
346 if (it != m_groups.cend()) {
347 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::insert - Group already present in list of groups:" << group;
351 const KeyGroup savedGroup = writeGroupToGroupsConfig(group);
352 if (savedGroup.isNull()) {
353 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::insert - Writing group" << group.id() <<
"to config file failed";
357 m_groups.push_back(savedGroup);
359 Q_EMIT q->groupAdded(savedGroup);
364 bool update(
const KeyGroup &group)
366 Q_ASSERT(!group.isNull());
367 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
368 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
369 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::update - Invalid group:" << group;
372 const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](
const auto &g) {
373 return g.source() == group.source() && g.id() == group.id();
375 if (it == m_groups.cend()) {
376 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::update - Group not found in list of groups:" << group;
379 const auto groupIndex = std::distance(m_groups.cbegin(), it);
381 const KeyGroup savedGroup = writeGroupToGroupsConfig(group);
382 if (savedGroup.isNull()) {
383 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::update - Writing group" << group.id() <<
"to config file failed";
387 m_groups[groupIndex] = savedGroup;
389 Q_EMIT q->groupUpdated(savedGroup);
394 bool remove(
const KeyGroup &group)
396 Q_ASSERT(!group.isNull());
397 Q_ASSERT(group.source() == KeyGroup::ApplicationConfig);
398 if (group.isNull() || group.source() != KeyGroup::ApplicationConfig) {
399 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::remove - Invalid group:" << group;
402 const auto it = std::find_if(m_groups.cbegin(), m_groups.cend(), [group](
const auto &g) {
403 return g.source() == group.source() && g.id() == group.id();
405 if (it == m_groups.cend()) {
406 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::remove - Group not found in list of groups:" << group;
410 const bool success = removeGroupFromGroupsConfig(group);
412 qCDebug(LIBKLEO_LOG) <<
"KeyCache::Private::remove - Removing group" << group.id() <<
"from config file failed";
418 Q_EMIT q->groupRemoved(group);
425 std::vector<std::shared_ptr<FileSystemWatcher>> m_fsWatchers;
426 QTimer m_autoKeyListingTimer;
427 int m_refreshInterval;
430 std::vector<Key> fpr, keyid, shortkeyid, chainid;
431 std::vector<std::pair<std::string, Key>> email;
432 std::vector<Subkey> subkeyid, keygrip;
436 bool m_remarks_enabled;
437 bool m_groupsEnabled =
false;
438 std::shared_ptr<KeyGroupConfig> m_groupConfig;
439 std::vector<KeyGroup> m_groups;
442std::shared_ptr<const KeyCache> KeyCache::instance()
444 return mutableInstance();
447std::shared_ptr<KeyCache> KeyCache::mutableInstance()
449 static std::weak_ptr<KeyCache> self;
451 return std::shared_ptr<KeyCache>(self);
452 }
catch (
const std::bad_weak_ptr &) {
453 const std::shared_ptr<KeyCache> s(
new KeyCache);
461 , d(new Private(this))
469void KeyCache::setGroupsEnabled(
bool enabled)
471 d->m_groupsEnabled = enabled;
472 if (d->m_initalized) {
473 d->updateGroupCache();
477void KeyCache::setGroupConfig(
const std::shared_ptr<KeyGroupConfig> &groupConfig)
479 d->m_groupConfig = groupConfig;
482void KeyCache::enableFileSystemWatcher(
bool enable)
484 for (
const auto &i :
std::as_const(d->m_fsWatchers)) {
485 i->setEnabled(enable);
489void KeyCache::setRefreshInterval(
int hours)
491 d->setRefreshInterval(hours);
494int KeyCache::refreshInterval()
const
496 return d->refreshInterval();
499std::shared_ptr<KeyCacheAutoRefreshSuspension> KeyCache::suspendAutoRefresh()
501 return KeyCacheAutoRefreshSuspension::instance();
504void KeyCache::reload(GpgME::Protocol , ReloadOption option)
506 qCDebug(LIBKLEO_LOG) <<
this << __func__ <<
"option:" << option;
507 const bool forceReload = option & ForceReload;
508 if (d->m_refreshJob && !forceReload) {
509 qCDebug(LIBKLEO_LOG) <<
this << __func__ <<
"- refresh already running";
512 if (d->m_refreshJob) {
513 disconnect(d->m_refreshJob.data(),
nullptr,
this,
nullptr);
514 d->m_refreshJob->cancel();
515 d->m_refreshJob.clear();
518 d->updateAutoKeyListingTimer();
520 enableFileSystemWatcher(
false);
521 d->m_refreshJob =
new RefreshKeysJob(
this);
522 connect(d->m_refreshJob.data(), &RefreshKeysJob::done,
this, [
this](
const GpgME::KeyListResult &r) {
523 qCDebug(LIBKLEO_LOG) << d->m_refreshJob.data() <<
"RefreshKeysJob::done";
524 d->refreshJobDone(r);
526 connect(d->m_refreshJob.data(), &RefreshKeysJob::canceled,
this, [
this]() {
527 qCDebug(LIBKLEO_LOG) << d->m_refreshJob.data() <<
"RefreshKeysJob::canceled";
528 d->m_refreshJob.clear();
530 d->m_refreshJob->start();
533void KeyCache::cancelKeyListing()
535 if (!d->m_refreshJob) {
538 d->m_refreshJob->cancel();
541void KeyCache::addFileSystemWatcher(
const std::shared_ptr<FileSystemWatcher> &watcher)
546 d->m_fsWatchers.push_back(watcher);
547 connect(watcher.get(), &FileSystemWatcher::directoryChanged,
this, [
this]() {
550 connect(watcher.get(), &FileSystemWatcher::fileChanged,
this, [
this]() {
554 watcher->setEnabled(d->m_refreshJob.isNull());
557void KeyCache::enableRemarks(
bool value)
559 if (!d->m_remarks_enabled && value) {
560 d->m_remarks_enabled = value;
561 if (d->m_initalized && !d->m_refreshJob) {
562 qCDebug(LIBKLEO_LOG) <<
"Reloading keycache with remarks enabled";
566 d->m_remarks_enabled = value;
570bool KeyCache::remarksEnabled()
const
572 return d->m_remarks_enabled;
575void KeyCache::Private::refreshJobDone(
const KeyListResult &result)
577 m_refreshJob.
clear();
578 q->enableFileSystemWatcher(
true);
579 if (!m_initalized && q->remarksEnabled()) {
584 qCDebug(LIBKLEO_LOG) <<
"Reloading keycache with remarks enabled";
591 Q_EMIT q->keyListingDone(result);
594const Key &KeyCache::findByFingerprint(
const char *fpr)
const
596 const std::vector<Key>::const_iterator it = d->find_fpr(fpr);
597 if (it == d->by.fpr.end()) {
598 static const Key null;
605const Key &KeyCache::findByFingerprint(
const std::string &fpr)
const
607 return findByFingerprint(fpr.c_str());
610std::vector<GpgME::Key> KeyCache::findByFingerprint(
const std::vector<std::string> &fprs)
const
612 std::vector<Key> keys;
613 keys.reserve(fprs.size());
614 for (
const auto &fpr : fprs) {
615 const Key key = findByFingerprint(fpr.c_str());
617 qCDebug(LIBKLEO_LOG) << __func__ <<
"Ignoring unknown key with fingerprint:" << fpr.c_str();
625std::vector<Key> KeyCache::findByEMailAddress(
const char *email)
const
627 const auto pair = d->find_email(email);
628 std::vector<Key> result;
629 result.reserve(std::distance(pair.first, pair.second));
630 std::transform(pair.first, pair.second, std::back_inserter(result), [](
const std::pair<std::string, Key> &pair) {
636std::vector<Key> KeyCache::findByEMailAddress(
const std::string &email)
const
638 return findByEMailAddress(email.c_str());
641const Key &KeyCache::findByShortKeyID(
const char *
id)
const
643 const std::vector<Key>::const_iterator it = d->find_shortkeyid(
id);
644 if (it != d->by.shortkeyid.end()) {
647 static const Key null;
651const Key &KeyCache::findByShortKeyID(
const std::string &
id)
const
653 return findByShortKeyID(
id.c_str());
656const Key &KeyCache::findByKeyIDOrFingerprint(
const char *
id)
const
660 const std::vector<Key>::const_iterator it = d->find_fpr(
id);
661 if (it != d->by.fpr.end()) {
667 const std::vector<Key>::const_iterator it = d->find_keyid(
id);
668 if (it != d->by.keyid.end()) {
672 static const Key null;
676const Key &KeyCache::findByKeyIDOrFingerprint(
const std::string &
id)
const
678 return findByKeyIDOrFingerprint(
id.c_str());
681std::vector<Key> KeyCache::findByKeyIDOrFingerprint(
const std::vector<std::string> &ids)
const
683 std::vector<std::string> keyids;
684 std::remove_copy_if(ids.begin(), ids.end(), std::back_inserter(keyids), [](
const std::string &str) {
685 return !str.c_str() || !*str.c_str();
689 std::sort(keyids.begin(), keyids.end(), _detail::ByFingerprint<std::less>());
691 std::vector<Key> result;
692 result.reserve(keyids.size());
693 d->ensureCachePopulated();
695 kdtools::set_intersection(d->by.fpr.begin(),
699 std::back_inserter(result),
700 _detail::ByFingerprint<std::less>());
701 if (result.size() < keyids.size()) {
704 kdtools::set_intersection(d->by.keyid.begin(),
708 std::back_inserter(result),
709 _detail::ByKeyID<std::less>());
712 std::sort(result.begin(), result.end(), _detail::ByFingerprint<std::less>());
713 result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
721const Subkey &KeyCache::findSubkeyByKeyGrip(
const char *grip, Protocol protocol)
const
723 static const Subkey null;
724 d->ensureCachePopulated();
725 const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), grip, _detail::ByKeyGrip<std::less>());
726 if (range.first == range.second) {
728 }
else if (protocol == UnknownProtocol) {
731 for (
auto it = range.first; it != range.second; ++it) {
732 if (it->parent().protocol() == protocol) {
740const Subkey &KeyCache::findSubkeyByKeyGrip(
const std::string &grip, Protocol protocol)
const
742 return findSubkeyByKeyGrip(grip.c_str(), protocol);
745std::vector<Subkey> KeyCache::findSubkeysByKeyID(
const std::vector<std::string> &ids)
const
747 std::vector<std::string> sorted;
748 sorted.reserve(ids.size());
749 std::remove_copy_if(ids.begin(), ids.end(), std::back_inserter(sorted), [](
const std::string &str) {
750 return !str.c_str() || !*str.c_str();
753 std::sort(sorted.begin(), sorted.end(), _detail::ByKeyID<std::less>());
755 std::vector<Subkey> result;
756 d->ensureCachePopulated();
757 kdtools::set_intersection(d->by.subkeyid.begin(),
758 d->by.subkeyid.end(),
761 std::back_inserter(result),
762 _detail::ByKeyID<std::less>());
766std::vector<Key> KeyCache::findRecipients(
const DecryptionResult &res)
const
768 std::vector<std::string> keyids;
769 const auto recipients = res.recipients();
770 for (
const DecryptionResult::Recipient &r : recipients) {
771 if (
const char *kid = r.keyID()) {
772 keyids.push_back(kid);
775 const std::vector<Subkey> subkeys = findSubkeysByKeyID(keyids);
776 std::vector<Key> result;
777 result.reserve(subkeys.size());
778 std::transform(subkeys.begin(), subkeys.end(), std::back_inserter(result), std::mem_fn(&Subkey::parent));
780 std::sort(result.begin(), result.end(), _detail::ByFingerprint<std::less>());
781 result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
785std::vector<Key> KeyCache::findSigners(
const VerificationResult &res)
const
787 std::vector<std::string> fprs;
788 const auto signatures = res.signatures();
789 for (
const Signature &s : signatures) {
790 if (
const char *fpr = s.fingerprint()) {
794 return findByKeyIDOrFingerprint(fprs);
797std::vector<Key> KeyCache::findSigningKeysByMailbox(
const QString &mb)
const
799 return d->find_mailbox(mb,
true);
802std::vector<Key> KeyCache::findEncryptionKeysByMailbox(
const QString &mb)
const
804 return d->find_mailbox(mb,
false);
809#define DO(op, meth, meth2) \
810 if (op key.meth()) { \
812 qDebug("rejecting for signing: %s: %s", #meth2, key.primaryFingerprint()); \
815#define ACCEPT(meth) DO(!!, meth, !meth)
816#define REJECT(meth) DO(!, meth, meth)
817struct ready_for_signing {
818 bool operator()(
const Key &key)
const
821#if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
835#define DO(op, meth, meth2) \
836 if (op key.meth()) { \
838 qDebug("rejecting for encrypting: %s: %s", #meth2, key.primaryFingerprint()); \
841struct ready_for_encryption {
842 bool operator()(
const Key &key)
const
845#if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
856 return key.hasEncrypt() && !key.isRevoked() && !key.isExpired() && !key.isDisabled() && !key.isInvalid();
865std::vector<Key> KeyCache::Private::find_mailbox(
const QString &email,
bool sign)
const
867 if (email.isEmpty()) {
868 return std::vector<Key>();
871 const auto pair = find_email(email.toUtf8().constData());
872 std::vector<Key> result;
873 result.reserve(std::distance(pair.first, pair.second));
875 kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_signing());
877 kdtools::copy_2nd_if(pair.first, pair.second, std::back_inserter(result), ready_for_encryption());
883std::vector<Key> KeyCache::findSubjects(
const GpgME::Key &key, Options options)
const
889 return findSubjects(std::vector<Key>(1, key), options);
892std::vector<Key> KeyCache::findSubjects(
const std::vector<Key> &keys, Options options)
const
894 std::vector<Key> result;
901 for (
const auto &key : keys) {
902 const auto firstAndLastSubject = d->find_subjects(key.primaryFingerprint());
903 result.insert(result.end(), firstAndLastSubject.first, firstAndLastSubject.second);
906 _detail::sort_by_fpr(result);
907 _detail::remove_duplicates_by_fpr(result);
909 if (options & RecursiveSearch) {
910 for (std::vector<Key> furtherSubjects = findSubjects(result, NoOption);
911 !furtherSubjects.empty();
912 furtherSubjects = findSubjects(furtherSubjects, NoOption)) {
913 std::vector<Key> combined;
914 combined.reserve(result.size() + furtherSubjects.size());
915 std::merge(result.begin(),
917 furtherSubjects.begin(),
918 furtherSubjects.end(),
919 std::back_inserter(combined),
920 _detail::ByFingerprint<std::less>());
921 _detail::remove_duplicates_by_fpr(combined);
922 if (result.size() == combined.size()) {
926 result.swap(combined);
933std::vector<Key> KeyCache::findIssuers(
const Key &key, Options options)
const
935 std::vector<Key> result;
941 if (options & IncludeSubject) {
942 result.push_back(key);
949 Key issuer = findByFingerprint(key.chainID());
951 if (issuer.isNull()) {
955 result.push_back(issuer);
957 if (!(options & RecursiveSearch)) {
961 while (!issuer.isRoot()) {
962 issuer = findByFingerprint(result.back().chainID());
963 if (issuer.isNull()) {
966 const bool chainAlreadyContainsIssuer = Kleo::contains_if(result, [issuer](
const auto &key) {
967 return _detail::ByFingerprint<std::equal_to>()(issuer, key);
971 result.push_back(issuer);
972 if (chainAlreadyContainsIssuer) {
981static std::string email(
const UserID &uid)
984 const std::string addr = uid.addrSpec();
988 const std::string email = uid.email();
990 return DN(uid.id())[QStringLiteral(
"EMAIL")].trimmed().toUtf8().constData();
992 if (email[0] ==
'<' && email[email.size() - 1] ==
'>') {
993 return email.substr(1, email.size() - 2);
999static std::vector<std::string> emails(
const Key &key)
1001 std::vector<std::string> emails;
1002 const auto userIDs = key.userIDs();
1003 for (
const UserID &uid : userIDs) {
1004 const std::string e = email(uid);
1006 emails.push_back(e);
1009 std::sort(emails.begin(), emails.end(), ByEMail<std::less>());
1010 emails.erase(std::unique(emails.begin(), emails.end(), ByEMail<std::equal_to>()), emails.end());
1014void KeyCache::remove(
const Key &key)
1020 const char *fpr = key.primaryFingerprint();
1026 const auto range = std::equal_range(d->by.fpr.begin(), d->by.fpr.end(), fpr, _detail::ByFingerprint<std::less>());
1027 d->by.fpr.erase(range.first, range.second);
1030 if (
const char *keyid = key.keyID()) {
1031 const auto range = std::equal_range(d->by.keyid.begin(), d->by.keyid.end(), keyid, _detail::ByKeyID<std::less>());
1032 const auto it = std::remove_if(range.first, range.second, [fpr](
const GpgME::Key &key) {
1033 return _detail::ByFingerprint<std::equal_to>()(fpr, key);
1035 d->by.keyid.erase(it, range.second);
1038 if (
const char *shortkeyid = key.shortKeyID()) {
1039 const auto range = std::equal_range(d->by.shortkeyid.begin(), d->by.shortkeyid.end(), shortkeyid, _detail::ByShortKeyID<std::less>());
1040 const auto it = std::remove_if(range.first, range.second, [fpr](
const GpgME::Key &key) {
1041 return _detail::ByFingerprint<std::equal_to>()(fpr, key);
1043 d->by.shortkeyid.erase(it, range.second);
1046 if (
const char *chainid = key.chainID()) {
1047 const auto range = std::equal_range(d->by.chainid.begin(), d->by.chainid.end(), chainid, _detail::ByChainID<std::less>());
1048 const auto range2 = std::equal_range(range.first, range.second, fpr, _detail::ByFingerprint<std::less>());
1049 d->by.chainid.erase(range2.first, range2.second);
1052 const auto emailsKey{emails(key)};
1053 for (
const std::string &email : emailsKey) {
1054 const auto range = std::equal_range(d->by.email.begin(), d->by.email.end(), email, ByEMail<std::less>());
1055 const auto it = std::remove_if(range.first, range.second, [fpr](
const std::pair<std::string, Key> &pair) {
1056 return qstricmp(fpr, pair.second.primaryFingerprint()) == 0;
1058 d->by.email.erase(it, range.second);
1061 const auto keySubKeys{key.subkeys()};
1062 for (
const Subkey &subkey : keySubKeys) {
1063 if (
const char *keyid = subkey.keyID()) {
1064 const auto range = std::equal_range(d->by.subkeyid.begin(), d->by.subkeyid.end(), keyid, _detail::ByKeyID<std::less>());
1065 const auto it = std::remove_if(range.first, range.second, [fpr](
const Subkey &subkey) {
1066 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1068 d->by.subkeyid.erase(it, range.second);
1070 if (
const char *keygrip = subkey.keyGrip()) {
1071 const auto range = std::equal_range(d->by.keygrip.begin(), d->by.keygrip.end(), keygrip, _detail::ByKeyGrip<std::less>());
1072 const auto it = std::remove_if(range.first, range.second, [fpr](
const Subkey &subkey) {
1073 return !qstricmp(fpr, subkey.parent().primaryFingerprint());
1075 d->by.keygrip.erase(it, range.second);
1080void KeyCache::remove(
const std::vector<Key> &keys)
1082 for (
const Key &key : keys) {
1087const std::vector<GpgME::Key> &KeyCache::keys()
const
1089 d->ensureCachePopulated();
1093std::vector<Key> KeyCache::secretKeys()
const
1095 std::vector<Key> keys = this->keys();
1096 keys.erase(std::remove_if(keys.begin(),
1098 [](
const Key &key) {
1099 return !key.hasSecret();
1105KeyGroup KeyCache::group(
const QString &
id)
const
1108 const auto it = std::find_if(std::cbegin(d->m_groups), std::cend(d->m_groups), [
id](
const auto &g) {
1109 return g.id() == id;
1111 if (it != std::cend(d->m_groups)) {
1117std::vector<KeyGroup> KeyCache::groups()
const
1119 d->ensureCachePopulated();
1123std::vector<KeyGroup> KeyCache::configurableGroups()
const
1125 std::vector<KeyGroup> groups;
1126 groups.reserve(d->m_groups.size());
1127 std::copy_if(d->m_groups.cbegin(), d->m_groups.cend(), std::back_inserter(groups), [](
const KeyGroup &group) {
1128 return group.source() == KeyGroup::ApplicationConfig;
1135bool compareById(
const KeyGroup &lhs,
const KeyGroup &rhs)
1137 return lhs.id() < rhs.id();
1140std::vector<KeyGroup> sortedById(std::vector<KeyGroup> groups)
1142 std::sort(groups.begin(), groups.end(), &compareById);
1147void KeyCache::saveConfigurableGroups(
const std::vector<KeyGroup> &groups)
1149 const std::vector<KeyGroup> oldGroups = sortedById(configurableGroups());
1150 const std::vector<KeyGroup> newGroups = sortedById(groups);
1153 std::vector<KeyGroup> removedGroups;
1154 std::set_difference(oldGroups.begin(), oldGroups.end(), newGroups.begin(), newGroups.end(), std::back_inserter(removedGroups), &compareById);
1155 for (
const auto &group :
std::as_const(removedGroups)) {
1156 qCDebug(LIBKLEO_LOG) <<
"Removing group" << group;
1161 std::vector<KeyGroup> updatedGroups;
1162 std::set_intersection(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(updatedGroups), &compareById);
1163 for (
const auto &group :
std::as_const(updatedGroups)) {
1164 qCDebug(LIBKLEO_LOG) <<
"Updating group" << group;
1169 std::vector<KeyGroup> addedGroups;
1170 std::set_difference(newGroups.begin(), newGroups.end(), oldGroups.begin(), oldGroups.end(), std::back_inserter(addedGroups), &compareById);
1171 for (
const auto &group :
std::as_const(addedGroups)) {
1172 qCDebug(LIBKLEO_LOG) <<
"Adding group" << group;
1177 Q_EMIT keysMayHaveChanged();
1180bool KeyCache::insert(
const KeyGroup &group)
1182 if (!d->insert(group)) {
1186 Q_EMIT keysMayHaveChanged();
1191bool KeyCache::update(
const KeyGroup &group)
1193 if (!d->update(group)) {
1197 Q_EMIT keysMayHaveChanged();
1202bool KeyCache::remove(
const KeyGroup &group)
1204 if (!d->remove(group)) {
1208 Q_EMIT keysMayHaveChanged();
1213void KeyCache::refresh(
const std::vector<Key> &keys)
1220void KeyCache::insert(
const Key &key)
1222 insert(std::vector<Key>(1, key));
1228template<
template<
template<
typename T>
class Op>
class T1, template<template<typename T> class Op> class T2>
1229struct lexicographically {
1230 using result_type = bool;
1232 template<
typename U,
typename V>
1233 bool operator()(
const U &lhs,
const V &rhs)
const
1235 return T1<std::less>()(lhs, rhs)
1236 || (T1<std::equal_to>()(lhs, rhs) && T2<std::less>()(lhs, rhs));
1242void KeyCache::insert(
const std::vector<Key> &keys)
1245 std::vector<Key> sorted;
1246 sorted.reserve(keys.size());
1247 std::copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), [](
const Key &key) {
1248 auto fp = key.primaryFingerprint();
1256 std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
1259 std::vector<Key> by_fpr;
1260 by_fpr.reserve(sorted.size() + d->by.fpr.size());
1261 std::merge(sorted.begin(), sorted.end(), d->by.fpr.begin(), d->by.fpr.end(), std::back_inserter(by_fpr), _detail::ByFingerprint<std::less>());
1264 std::vector<std::pair<std::string, Key>> pairs;
1265 pairs.reserve(sorted.size());
1266 for (
const Key &key :
std::as_const(sorted)) {
1267 const std::vector<std::string> emails = ::emails(key);
1268 for (
const std::string &e : emails) {
1269 pairs.push_back(std::make_pair(e, key));
1272 std::sort(pairs.begin(), pairs.end(), ByEMail<std::less>());
1275 std::vector<std::pair<std::string, Key>> by_email;
1276 by_email.reserve(pairs.size() + d->by.email.size());
1277 std::merge(pairs.begin(), pairs.end(), d->by.email.begin(), d->by.email.end(), std::back_inserter(by_email), ByEMail<std::less>());
1280 std::stable_sort(sorted.begin(), sorted.end(), _detail::ByChainID<std::less>());
1283 std::vector<Key> nonroot;
1284 nonroot.reserve(sorted.size());
1285 std::vector<Key> by_chainid;
1286 by_chainid.reserve(sorted.size() + d->by.chainid.size());
1287 std::copy_if(sorted.cbegin(), sorted.cend(), std::back_inserter(nonroot), [](
const Key &key) {
1288 return !key.isRoot();
1290 std::merge(nonroot.cbegin(),
1292 d->by.chainid.cbegin(),
1293 d->by.chainid.cend(),
1294 std::back_inserter(by_chainid),
1295 lexicographically<_detail::ByChainID, _detail::ByFingerprint>());
1298 std::sort(sorted.begin(), sorted.end(), _detail::ByKeyID<std::less>());
1301 std::vector<Key> by_keyid;
1302 by_keyid.reserve(sorted.size() + d->by.keyid.size());
1303 std::merge(sorted.begin(), sorted.end(), d->by.keyid.begin(), d->by.keyid.end(), std::back_inserter(by_keyid), _detail::ByKeyID<std::less>());
1306 std::sort(sorted.begin(), sorted.end(), _detail::ByShortKeyID<std::less>());
1309 std::vector<Key> by_shortkeyid;
1310 by_shortkeyid.reserve(sorted.size() + d->by.shortkeyid.size());
1311 std::merge(sorted.begin(),
1313 d->by.shortkeyid.begin(),
1314 d->by.shortkeyid.end(),
1315 std::back_inserter(by_shortkeyid),
1316 _detail::ByShortKeyID<std::less>());
1319 std::vector<Subkey> subkeys;
1320 subkeys.reserve(sorted.size());
1321 for (
const Key &key :
std::as_const(sorted)) {
1322 const auto keySubkeys{key.subkeys()};
1323 for (
const Subkey &subkey : keySubkeys) {
1324 subkeys.push_back(subkey);
1329 std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyID<std::less>());
1332 std::vector<Subkey> by_subkeyid;
1333 by_subkeyid.reserve(subkeys.size() + d->by.subkeyid.size());
1334 std::merge(subkeys.begin(), subkeys.end(), d->by.subkeyid.begin(), d->by.subkeyid.end(), std::back_inserter(by_subkeyid), _detail::ByKeyID<std::less>());
1337 std::sort(subkeys.begin(), subkeys.end(), _detail::ByKeyGrip<std::less>());
1340 std::vector<Subkey> by_keygrip;
1341 by_keygrip.reserve(subkeys.size() + d->by.keygrip.size());
1342 std::merge(subkeys.begin(), subkeys.end(), d->by.keygrip.begin(), d->by.keygrip.end(), std::back_inserter(by_keygrip), _detail::ByKeyGrip<std::less>());
1345 by_fpr.swap(d->by.fpr);
1346 by_keyid.swap(d->by.keyid);
1347 by_shortkeyid.swap(d->by.shortkeyid);
1348 by_email.swap(d->by.email);
1349 by_subkeyid.swap(d->by.subkeyid);
1350 by_keygrip.swap(d->by.keygrip);
1351 by_chainid.swap(d->by.chainid);
1353 for (
const Key &key :
std::as_const(sorted)) {
1354 d->m_pgpOnly &= key.protocol() == GpgME::OpenPGP;
1357 Q_EMIT keysMayHaveChanged();
1360void KeyCache::clear()
1362 d->by = Private::By();
1371class KeyCache::RefreshKeysJob::Private
1373 RefreshKeysJob *
const q;
1376 Private(KeyCache *cache, RefreshKeysJob *qq);
1378 Error startKeyListing(GpgME::Protocol protocol);
1379 void listAllKeysJobDone(
const KeyListResult &res,
const std::vector<Key> &nextKeys)
1381 std::vector<Key> keys;
1382 keys.reserve(m_keys.size() + nextKeys.size());
1383 if (m_keys.empty()) {
1386 std::merge(m_keys.begin(), m_keys.end(), nextKeys.begin(), nextKeys.end(), std::back_inserter(keys), _detail::ByFingerprint<std::less>());
1391 void emitDone(
const KeyListResult &result);
1392 void updateKeyCache();
1396 std::vector<Key> m_keys;
1397 KeyListResult m_mergedResult;
1401 void jobDone(
const KeyListResult &res);
1404KeyCache::RefreshKeysJob::Private::Private(KeyCache *cache, RefreshKeysJob *qq)
1412void KeyCache::RefreshKeysJob::Private::jobDone(
const KeyListResult &result)
1423 Q_ASSERT(m_jobsPending.
size() > 0);
1424 m_jobsPending.
removeOne(qobject_cast<QGpgME::ListAllKeysJob *>(sender));
1425 m_mergedResult.mergeWith(result);
1426 if (m_jobsPending.
size() > 0) {
1430 emitDone(m_mergedResult);
1433void KeyCache::RefreshKeysJob::Private::emitDone(
const KeyListResult &res)
1436 Q_EMIT q->done(res);
1439KeyCache::RefreshKeysJob::RefreshKeysJob(KeyCache *cache,
QObject *parent)
1441 , d(new Private(cache, this))
1445KeyCache::RefreshKeysJob::~RefreshKeysJob()
1450void KeyCache::RefreshKeysJob::start()
1452 qCDebug(LIBKLEO_LOG) <<
"KeyCache::RefreshKeysJob" << __func__;
1458void KeyCache::RefreshKeysJob::cancel()
1460 d->m_canceled =
true;
1461 std::for_each(d->m_jobsPending.begin(), d->m_jobsPending.end(), std::mem_fn(&QGpgME::ListAllKeysJob::slotCancel));
1465void KeyCache::RefreshKeysJob::Private::doStart()
1472 Q_ASSERT(m_jobsPending.
size() == 0);
1473 m_mergedResult.mergeWith(KeyListResult(startKeyListing(GpgME::OpenPGP)));
1474 m_mergedResult.mergeWith(KeyListResult(startKeyListing(GpgME::CMS)));
1476 if (m_jobsPending.
size() != 0) {
1480 const bool hasError = m_mergedResult.error() || m_mergedResult.error().isCanceled();
1481 emitDone(hasError ? m_mergedResult : KeyListResult(Error(GPG_ERR_UNSUPPORTED_OPERATION)));
1484void KeyCache::RefreshKeysJob::Private::updateKeyCache()
1486 if (!m_cache || m_canceled) {
1491 std::vector<Key> cachedKeys = m_cache->initialized() ? m_cache->keys() : std::vector<Key>();
1492 std::sort(cachedKeys.begin(), cachedKeys.end(), _detail::ByFingerprint<std::less>());
1493 std::vector<Key> keysToRemove;
1494 std::set_difference(cachedKeys.begin(),
1498 std::back_inserter(keysToRemove),
1499 _detail::ByFingerprint<std::less>());
1500 m_cache->remove(keysToRemove);
1501 m_cache->refresh(m_keys);
1504Error KeyCache::RefreshKeysJob::Private::startKeyListing(GpgME::Protocol proto)
1506 const auto *
const protocol = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
1510 QGpgME::ListAllKeysJob *
const job = protocol->listAllKeysJob(
false,
true);
1514 if (!m_cache->initialized()) {
1516 job->setOptions(QGpgME::ListAllKeysJob::DisableAutomaticTrustDatabaseCheck);
1520 aheinecke: 2017.01.12:
1522 For unknown reasons the
new style
connect fails at runtime
1523 over library borders into QGpgME from the GpgME repo
1524 when cross compiled
for Windows and
default arguments
1525 are used in the Signal.
1527 This was tested with gcc 4.9 (Mingw 3.0.2) and we could not
1528 find an explanation
for this. So until
this is fixed or we understand
1529 the problem we need to use the old style connect
for QGpgME signals.
1531 The
new style connect of the canceled signal right below
1534 connect(job, &QGpgME::ListAllKeysJob::result,
1535 q, [
this](
const GpgME::KeyListResult &res,
const std::vector<GpgME::Key> &keys) {
1536 listAllKeysJobDone(res, keys);
1539 connect(job, SIGNAL(result(GpgME::KeyListResult, std::vector<GpgME::Key>)), q, SLOT(listAllKeysJobDone(GpgME::KeyListResult, std::vector<GpgME::Key>)));
1541 connect(q, &RefreshKeysJob::canceled, job, &QGpgME::Job::slotCancel);
1545 if (proto == GpgME::OpenPGP && m_cache->remarksEnabled() && m_cache->initialized()) {
1546 auto ctx = QGpgME::Job::context(job);
1548 ctx->addKeyListMode(KeyListMode::Signatures | KeyListMode::SignatureNotations);
1552 const Error
error = job->start(
true);
1554 if (!error && !
error.isCanceled()) {
1555 m_jobsPending.push_back(job);
1560bool KeyCache::initialized()
const
1562 return d->m_initalized;
1565void KeyCache::Private::ensureCachePopulated()
const
1567 if (!m_initalized) {
1568 q->startKeyListing();
1571 qCDebug(LIBKLEO_LOG) <<
"Waiting for keycache.";
1573 qCDebug(LIBKLEO_LOG) <<
"Keycache available.";
1577bool KeyCache::pgpOnly()
const
1579 return d->m_pgpOnly;
1582static bool keyIsOk(
const Key &k)
1584 return !k.isExpired() && !k.isRevoked() && !k.isInvalid() && !k.isDisabled();
1587static bool uidIsOk(
const UserID &uid)
1589 return keyIsOk(uid.parent()) && !uid.isRevoked() && !uid.isInvalid();
1592static bool subkeyIsOk(
const Subkey &s)
1594 return !s.isRevoked() && !s.isInvalid() && !s.isDisabled();
1599time_t creationTimeOfNewestSuitableSubKey(
const Key &key, KeyCache::KeyUsage usage)
1601 time_t creationTime = 0;
1602 for (
const Subkey &s : key.subkeys()) {
1603 if (!subkeyIsOk(s)) {
1606 if (usage == KeyCache::KeyUsage::Sign && !s.canSign()) {
1609 if (usage == KeyCache::KeyUsage::Encrypt && !s.canEncrypt()) {
1612 if (s.creationTime() > creationTime) {
1613 creationTime = s.creationTime();
1616 return creationTime;
1622 time_t creationTime = 0;
1626GpgME::Key KeyCache::findBestByMailBox(
const char *addr, GpgME::Protocol proto, KeyUsage usage)
const
1628 d->ensureCachePopulated();
1635 if (
address.size() > 1 && address[0] ==
'<' && address[
address.size() - 1] ==
'>') {
1641 for (
const Key &k : findByEMailAddress(
address.constData())) {
1642 if (proto != Protocol::UnknownProtocol && k.protocol() != proto) {
1645 if (usage == KeyUsage::Encrypt && !keyHasEncrypt(k)) {
1648 if (usage == KeyUsage::Sign && (!keyHasSign(k) || !k.hasSecret())) {
1651 const time_t creationTime = creationTimeOfNewestSuitableSubKey(k, usage);
1652 if (creationTime == 0) {
1656 for (
const UserID &u : k.userIDs()) {
1661 if (best.uid.isNull()) {
1663 best = {k, u, creationTime};
1664 }
else if (!uidIsOk(best.uid) && uidIsOk(u)) {
1666 best = {k, u, creationTime};
1667 }
else if (!k.isExpired() && best.uid.validity() < u.validity()) {
1669 best = {k, u, creationTime};
1670 }
else if (best.key.isExpired() && !k.isExpired()) {
1672 best = {k, u, creationTime};
1673 }
else if (best.uid.validity() == u.validity() && uidIsOk(u) && best.creationTime < creationTime) {
1675 best = {k, u, creationTime};
1686bool allKeysAllowUsage(
const T &keys, KeyCache::KeyUsage usage)
1689 case KeyCache::KeyUsage::AnyUsage:
1691 case KeyCache::KeyUsage::Sign:
1692 return std::all_of(std::begin(keys),
1694#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1695 std::mem_fn(&Key::hasSign)
1700 case KeyCache::KeyUsage::Encrypt:
1701 return std::all_of(std::begin(keys),
1703#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1704 std::mem_fn(&Key::hasEncrypt)
1709 case KeyCache::KeyUsage::Certify:
1710 return std::all_of(std::begin(keys),
1712#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1713 std::mem_fn(&Key::hasCertify)
1718 case KeyCache::KeyUsage::Authenticate:
1719 return std::all_of(std::begin(keys),
1721#
if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
1722 std::mem_fn(&Key::hasAuthenticate)
1724 Kleo::keyHasAuthenticate
1728 qCDebug(LIBKLEO_LOG) << __func__ <<
"called with invalid usage" << int(usage);
1733KeyGroup KeyCache::findGroup(
const QString &name, Protocol protocol, KeyUsage usage)
const
1735 d->ensureCachePopulated();
1737 Q_ASSERT(usage == KeyUsage::Sign || usage == KeyUsage::Encrypt);
1738 for (
const auto &group :
std::as_const(d->m_groups)) {
1739 if (group.name() == name) {
1740 const KeyGroup::Keys &keys = group.keys();
1741 if (allKeysAllowUsage(keys, usage) && (protocol == UnknownProtocol || allKeysHaveProtocol(keys, protocol))) {
1750std::vector<Key> KeyCache::getGroupKeys(
const QString &groupName)
const
1752 std::vector<Key> result;
1753 for (
const KeyGroup &g :
std::as_const(d->m_groups)) {
1754 if (g.name() == groupName) {
1755 const KeyGroup::Keys &keys = g.keys();
1756 std::copy(keys.cbegin(), keys.cend(), std::back_inserter(result));
1759 _detail::sort_by_fpr(result);
1760 _detail::remove_duplicates_by_fpr(result);
1764void KeyCache::setKeys(
const std::vector<GpgME::Key> &keys)
1767 setRefreshInterval(0);
1771 d->m_initalized =
true;
1772 Q_EMIT keyListingDone(KeyListResult());
1775void KeyCache::setGroups(
const std::vector<KeyGroup> &groups)
1777 Q_ASSERT(d->m_initalized &&
"Call setKeys() before setting groups");
1778 d->m_groups = groups;
1779 Q_EMIT keysMayHaveChanged();
1782#include "moc_keycache.cpp"
1783#include "moc_keycache_p.cpp"
PostalAddress address(const QVariant &location)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QByteArray fromStdString(const std::string &str)
QByteArray toLower() const const
int exec(ProcessEventsFlags flags)
bool removeOne(const AT &t)
qsizetype size() const const
const_iterator cbegin() const const
const_iterator cend() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * sender() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setInterval(int msec)