File indexing completed on 2024-06-23 05:18:28

0001 /*  -*- c++ -*-
0002     keyresolver.cpp
0003 
0004     This file is part of libkleopatra, the KDE keymanagement library
0005     SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
0006 
0007     Based on kpgp.cpp
0008     Copyright (C) 2001,2002 the KPGP authors
0009     See file libkdenetwork/AUTHORS.kpgp for details
0010 
0011     SPDX-License-Identifier: GPL-2.0-or-later
0012 */
0013 
0014 #include "composer/keyresolver.h"
0015 
0016 #include "contactpreference/savecontactpreferencejob.h"
0017 
0018 #include "utils/kleo_util.h"
0019 #include <KCursorSaver>
0020 
0021 #include <KEmailAddress>
0022 
0023 #include <Libkleo/Algorithm>
0024 #include <Libkleo/Compliance>
0025 #include <Libkleo/ExpiryChecker>
0026 #include <Libkleo/KeySelectionDialog>
0027 
0028 #include <QGpgME/KeyListJob>
0029 #include <QGpgME/Protocol>
0030 
0031 #include <gpgme++/key.h>
0032 #include <gpgme++/keylistresult.h>
0033 
0034 #include "messagecomposer_debug.h"
0035 #include <Akonadi/ContactSearchJob>
0036 #include <KLocalizedString>
0037 #include <KMessageBox>
0038 
0039 #include <MessageCore/AutocryptRecipient>
0040 #include <MessageCore/AutocryptStorage>
0041 
0042 #include <QPointer>
0043 
0044 #include <algorithm>
0045 #include <cassert>
0046 #include <ctime>
0047 #include <functional>
0048 #include <iostream>
0049 #include <iterator>
0050 #include <map>
0051 #include <memory>
0052 #include <set>
0053 
0054 //
0055 // some predicates to be used in STL algorithms:
0056 //
0057 
0058 static inline bool EmptyKeyList(const Kleo::KeyApprovalDialog::Item &item)
0059 {
0060     return item.keys.empty();
0061 }
0062 
0063 static inline QString ItemDotAddress(const Kleo::KeyResolver::Item &item)
0064 {
0065     return item.address;
0066 }
0067 
0068 static inline bool ApprovalNeeded(const Kleo::KeyResolver::Item &item)
0069 {
0070     bool approvalNeeded = item.pref == Kleo::NeverEncrypt || item.keys.empty();
0071     if (!approvalNeeded && Kleo::DeVSCompliance::isCompliant()) {
0072         approvalNeeded = !Kleo::all_of(item.keys, &Kleo::DeVSCompliance::keyIsCompliant);
0073     }
0074     return approvalNeeded;
0075 }
0076 
0077 static inline Kleo::KeyResolver::Item CopyKeysAndEncryptionPreferences(const Kleo::KeyResolver::Item &oldItem, const Kleo::KeyApprovalDialog::Item &newItem)
0078 {
0079     return Kleo::KeyResolver::Item(oldItem.address, newItem.keys, newItem.pref, oldItem.signPref, oldItem.format);
0080 }
0081 
0082 static bool ValidOpenPGPEncryptionKey(const GpgME::Key &key)
0083 {
0084     if (key.protocol() != GpgME::OpenPGP) {
0085         return false;
0086     }
0087     if (key.isRevoked()) {
0088         qCWarning(MESSAGECOMPOSER_LOG) << "is revoked";
0089     }
0090     if (key.isExpired()) {
0091         qCWarning(MESSAGECOMPOSER_LOG) << "is expired";
0092     }
0093     if (key.isDisabled()) {
0094         qCWarning(MESSAGECOMPOSER_LOG) << "is disabled";
0095     }
0096     if (!key.canEncrypt()) {
0097         qCWarning(MESSAGECOMPOSER_LOG) << "can't encrypt";
0098     }
0099     if (key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt()) {
0100         return false;
0101     }
0102     return true;
0103 }
0104 
0105 static bool ValidTrustedOpenPGPEncryptionKey(const GpgME::Key &key)
0106 {
0107     if (!ValidOpenPGPEncryptionKey(key)) {
0108         return false;
0109     }
0110     const std::vector<GpgME::UserID> uids = key.userIDs();
0111     auto end(uids.end());
0112     for (auto it = uids.begin(); it != end; ++it) {
0113         if (!it->isRevoked() && it->validity() >= GpgME::UserID::Marginal) {
0114             return true;
0115         } else if (it->isRevoked()) {
0116             qCWarning(MESSAGECOMPOSER_LOG) << "a userid is revoked";
0117         } else {
0118             qCWarning(MESSAGECOMPOSER_LOG) << "bad validity" << int(it->validity());
0119         }
0120     }
0121     return false;
0122 }
0123 
0124 static bool ValidSMIMEEncryptionKey(const GpgME::Key &key)
0125 {
0126     if (key.protocol() != GpgME::CMS) {
0127         return false;
0128     }
0129     if (key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt()) {
0130         return false;
0131     }
0132     return true;
0133 }
0134 
0135 static bool ValidTrustedSMIMEEncryptionKey(const GpgME::Key &key)
0136 {
0137     if (!ValidSMIMEEncryptionKey(key)) {
0138         return false;
0139     }
0140     return true;
0141 }
0142 
0143 static inline bool ValidTrustedEncryptionKey(const GpgME::Key &key)
0144 {
0145     switch (key.protocol()) {
0146     case GpgME::OpenPGP:
0147         return ValidTrustedOpenPGPEncryptionKey(key);
0148     case GpgME::CMS:
0149         return ValidTrustedSMIMEEncryptionKey(key);
0150     default:
0151         return false;
0152     }
0153 }
0154 
0155 static inline bool ValidEncryptionKey(const GpgME::Key &key)
0156 {
0157     switch (key.protocol()) {
0158     case GpgME::OpenPGP:
0159         return ValidOpenPGPEncryptionKey(key);
0160     case GpgME::CMS:
0161         return ValidSMIMEEncryptionKey(key);
0162     default:
0163         return false;
0164     }
0165 }
0166 
0167 static inline bool ValidSigningKey(const GpgME::Key &key)
0168 {
0169     if (key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canSign()) {
0170         return false;
0171     }
0172     return key.hasSecret();
0173 }
0174 
0175 static inline bool ValidOpenPGPSigningKey(const GpgME::Key &key)
0176 {
0177     return key.protocol() == GpgME::OpenPGP && ValidSigningKey(key);
0178 }
0179 
0180 static inline bool ValidSMIMESigningKey(const GpgME::Key &key)
0181 {
0182     return key.protocol() == GpgME::CMS && ValidSigningKey(key);
0183 }
0184 
0185 static inline bool NotValidTrustedOpenPGPEncryptionKey(const GpgME::Key &key)
0186 {
0187     return !ValidTrustedOpenPGPEncryptionKey(key);
0188 }
0189 
0190 static inline bool NotValidTrustedSMIMEEncryptionKey(const GpgME::Key &key)
0191 {
0192     return !ValidTrustedSMIMEEncryptionKey(key);
0193 }
0194 
0195 static inline bool NotValidTrustedEncryptionKey(const GpgME::Key &key)
0196 {
0197     return !ValidTrustedEncryptionKey(key);
0198 }
0199 
0200 static inline bool NotValidEncryptionKey(const GpgME::Key &key)
0201 {
0202     return !ValidEncryptionKey(key);
0203 }
0204 
0205 static inline bool NotValidOpenPGPSigningKey(const GpgME::Key &key)
0206 {
0207     return !ValidOpenPGPSigningKey(key);
0208 }
0209 
0210 static inline bool NotValidSMIMESigningKey(const GpgME::Key &key)
0211 {
0212     return !ValidSMIMESigningKey(key);
0213 }
0214 
0215 namespace
0216 {
0217 struct ByTrustScore {
0218     static int score(const GpgME::UserID &uid)
0219     {
0220         return uid.isRevoked() || uid.isInvalid() ? -1 : uid.validity();
0221     }
0222 
0223     bool operator()(const GpgME::UserID &lhs, const GpgME::UserID &rhs) const
0224     {
0225         return score(lhs) < score(rhs);
0226     }
0227 };
0228 }
0229 
0230 static std::vector<GpgME::UserID> matchingUIDs(const std::vector<GpgME::UserID> &uids, const QString &address)
0231 {
0232     if (address.isEmpty()) {
0233         return {};
0234     }
0235 
0236     std::vector<GpgME::UserID> result;
0237     result.reserve(uids.size());
0238 
0239     for (auto it = uids.begin(), end = uids.end(); it != end; ++it) {
0240         // PENDING(marc) check DN for an EMAIL, too, in case of X.509 certs... :/
0241         if (const char *email = it->email()) {
0242             if (*email && QString::fromUtf8(email).simplified().toLower() == address) {
0243                 result.push_back(*it);
0244             }
0245         }
0246     }
0247     return result;
0248 }
0249 
0250 static GpgME::UserID findBestMatchUID(const GpgME::Key &key, const QString &address)
0251 {
0252     const std::vector<GpgME::UserID> all = key.userIDs();
0253     if (all.empty()) {
0254         return {};
0255     }
0256     const std::vector<GpgME::UserID> matching = matchingUIDs(all, address.toLower());
0257     const std::vector<GpgME::UserID> &v = matching.empty() ? all : matching;
0258     return *std::max_element(v.begin(), v.end(), ByTrustScore());
0259 }
0260 
0261 static QStringList keysAsStrings(const std::vector<GpgME::Key> &keys)
0262 {
0263     QStringList strings;
0264     strings.reserve(keys.size());
0265     for (auto it = keys.begin(); it != keys.end(); ++it) {
0266         assert(!(*it).userID(0).isNull());
0267         const auto userID = (*it).userID(0);
0268         QString keyLabel = QString::fromUtf8(userID.email());
0269         if (keyLabel.isEmpty()) {
0270             keyLabel = QString::fromUtf8(userID.name());
0271         }
0272         if (keyLabel.isEmpty()) {
0273             keyLabel = QString::fromUtf8(userID.id());
0274         }
0275         strings.append(keyLabel);
0276     }
0277     return strings;
0278 }
0279 
0280 static std::vector<GpgME::Key> trustedOrConfirmed(const std::vector<GpgME::Key> &keys, const QString &address, bool &canceled)
0281 {
0282     // PENDING(marc) work on UserIDs here?
0283     std::vector<GpgME::Key> fishies;
0284     std::vector<GpgME::Key> ickies;
0285     std::vector<GpgME::Key> rewookies;
0286     auto it = keys.begin();
0287     const auto end = keys.end();
0288     for (; it != end; ++it) {
0289         const GpgME::Key &key = *it;
0290         assert(ValidEncryptionKey(key));
0291         const GpgME::UserID uid = findBestMatchUID(key, address);
0292         if (uid.isRevoked()) {
0293             rewookies.push_back(key);
0294         }
0295         if (!uid.isRevoked() && uid.validity() == GpgME::UserID::Marginal) {
0296             fishies.push_back(key);
0297         }
0298         if (!uid.isRevoked() && uid.validity() < GpgME::UserID::Never) {
0299             ickies.push_back(key);
0300         }
0301     }
0302 
0303     if (fishies.empty() && ickies.empty() && rewookies.empty()) {
0304         return keys;
0305     }
0306 
0307     // if  some keys are not fully trusted, let the user confirm their use
0308     QString msg = address.isEmpty() ? i18n(
0309                       "One or more of your configured OpenPGP encryption "
0310                       "keys or S/MIME certificates is not fully trusted "
0311                       "for encryption.")
0312                                     : i18n(
0313                                         "One or more of the OpenPGP encryption keys or S/MIME "
0314                                         "certificates for recipient \"%1\" is not fully trusted "
0315                                         "for encryption.",
0316                                         address);
0317 
0318     if (!fishies.empty()) {
0319         // certificates can't have marginal trust
0320         msg += i18n("\nThe following keys are only marginally trusted: \n");
0321         msg += keysAsStrings(fishies).join(QLatin1Char(','));
0322     }
0323     if (!ickies.empty()) {
0324         msg += i18n("\nThe following keys or certificates have unknown trust level: \n");
0325         msg += keysAsStrings(ickies).join(QLatin1Char(','));
0326     }
0327     if (!rewookies.empty()) {
0328         msg += i18n("\nThe following keys or certificates are <b>revoked</b>: \n");
0329         msg += keysAsStrings(rewookies).join(QLatin1Char(','));
0330     }
0331 
0332     if (KMessageBox::warningContinueCancel(nullptr,
0333                                            msg,
0334                                            i18nc("@title:window", "Not Fully Trusted Encryption Keys"),
0335                                            KStandardGuiItem::cont(),
0336                                            KStandardGuiItem::cancel(),
0337                                            QStringLiteral("not fully trusted encryption key warning"))
0338         == KMessageBox::Continue) {
0339         return keys;
0340     } else {
0341         canceled = true;
0342     }
0343     return {};
0344 }
0345 
0346 namespace
0347 {
0348 struct IsNotForFormat : public std::function<bool(GpgME::Key)> {
0349     IsNotForFormat(Kleo::CryptoMessageFormat f)
0350         : format(f)
0351     {
0352     }
0353 
0354     bool operator()(const GpgME::Key &key) const
0355     {
0356         return (isOpenPGP(format) && key.protocol() != GpgME::OpenPGP) || (isSMIME(format) && key.protocol() != GpgME::CMS);
0357     }
0358 
0359     const Kleo::CryptoMessageFormat format;
0360 };
0361 
0362 struct IsForFormat : std::function<bool(GpgME::Key)> {
0363     explicit IsForFormat(Kleo::CryptoMessageFormat f)
0364         : protocol(isOpenPGP(f)     ? GpgME::OpenPGP
0365                        : isSMIME(f) ? GpgME::CMS
0366                                     : GpgME::UnknownProtocol)
0367     {
0368     }
0369 
0370     bool operator()(const GpgME::Key &key) const
0371     {
0372         return key.protocol() == protocol;
0373     }
0374 
0375     const GpgME::Protocol protocol;
0376 };
0377 }
0378 
0379 class Kleo::KeyResolver::SigningPreferenceCounter : public std::function<void(Kleo::KeyResolver::Item)>
0380 {
0381 public:
0382     SigningPreferenceCounter() = default;
0383 
0384     void operator()(const Kleo::KeyResolver::Item &item);
0385 #define make_int_accessor(x)                                                                                                                                   \
0386     unsigned int num##x() const                                                                                                                                \
0387     {                                                                                                                                                          \
0388         return m##x;                                                                                                                                           \
0389     }
0390     make_int_accessor(UnknownSigningPreference) make_int_accessor(NeverSign) make_int_accessor(AlwaysSign) make_int_accessor(AlwaysSignIfPossible)
0391         make_int_accessor(AlwaysAskForSigning) make_int_accessor(AskSigningWheneverPossible) make_int_accessor(Total)
0392 #undef make_int_accessor
0393             private : unsigned int mTotal = 0;
0394     unsigned int mUnknownSigningPreference = 0;
0395     unsigned int mNeverSign = 0;
0396     unsigned int mAlwaysSign = 0;
0397     unsigned int mAlwaysSignIfPossible = 0;
0398     unsigned int mAlwaysAskForSigning = 0;
0399     unsigned int mAskSigningWheneverPossible = 0;
0400 };
0401 
0402 void Kleo::KeyResolver::SigningPreferenceCounter::operator()(const Kleo::KeyResolver::Item &item)
0403 {
0404     switch (item.signPref) {
0405 #define CASE(x)                                                                                                                                                \
0406     case x:                                                                                                                                                    \
0407         ++m##x;                                                                                                                                                \
0408         break
0409         CASE(UnknownSigningPreference);
0410         CASE(NeverSign);
0411         CASE(AlwaysSign);
0412         CASE(AlwaysSignIfPossible);
0413         CASE(AlwaysAskForSigning);
0414         CASE(AskSigningWheneverPossible);
0415 #undef CASE
0416     }
0417     ++mTotal;
0418 }
0419 
0420 class Kleo::KeyResolver::EncryptionPreferenceCounter : public std::function<void(Item)>
0421 {
0422     const Kleo::KeyResolver *_this;
0423 
0424 public:
0425     EncryptionPreferenceCounter(const Kleo::KeyResolver *kr, EncryptionPreference defaultPreference)
0426         : _this(kr)
0427         , mDefaultPreference(defaultPreference)
0428     {
0429     }
0430 
0431     void operator()(Item &item);
0432 
0433     template<typename Container>
0434     void process(Container &c)
0435     {
0436         *this = std::for_each(c.begin(), c.end(), *this);
0437     }
0438 
0439 #define make_int_accessor(x)                                                                                                                                   \
0440     unsigned int num##x() const                                                                                                                                \
0441     {                                                                                                                                                          \
0442         return m##x;                                                                                                                                           \
0443     }
0444     make_int_accessor(NoKey) make_int_accessor(NeverEncrypt) make_int_accessor(UnknownPreference) make_int_accessor(AlwaysEncrypt)
0445         make_int_accessor(AlwaysEncryptIfPossible) make_int_accessor(AlwaysAskForEncryption) make_int_accessor(AskWheneverPossible) make_int_accessor(Total)
0446 #undef make_int_accessor
0447             private : EncryptionPreference mDefaultPreference;
0448     unsigned int mTotal = 0;
0449     unsigned int mNoKey = 0;
0450     unsigned int mNeverEncrypt = 0;
0451     unsigned int mUnknownPreference = 0;
0452     unsigned int mAlwaysEncrypt = 0;
0453     unsigned int mAlwaysEncryptIfPossible = 0;
0454     unsigned int mAlwaysAskForEncryption = 0;
0455     unsigned int mAskWheneverPossible = 0;
0456 };
0457 
0458 void Kleo::KeyResolver::EncryptionPreferenceCounter::operator()(Item &item)
0459 {
0460     if (_this) {
0461         if (item.needKeys) {
0462             item.keys = _this->getEncryptionKeys(item.address, true);
0463         }
0464         if (item.keys.empty()) {
0465             ++mNoKey;
0466             return;
0467         }
0468     }
0469     switch (!item.pref ? mDefaultPreference : item.pref) {
0470 #define CASE(x)                                                                                                                                                \
0471     case Kleo::x:                                                                                                                                              \
0472         ++m##x;                                                                                                                                                \
0473         break
0474         CASE(NeverEncrypt);
0475         CASE(UnknownPreference);
0476         CASE(AlwaysEncrypt);
0477         CASE(AlwaysEncryptIfPossible);
0478         CASE(AlwaysAskForEncryption);
0479         CASE(AskWheneverPossible);
0480 #undef CASE
0481     }
0482     ++mTotal;
0483 }
0484 
0485 namespace
0486 {
0487 class FormatPreferenceCounterBase : public std::function<void(Kleo::KeyResolver::Item)>
0488 {
0489 public:
0490     FormatPreferenceCounterBase() = default;
0491 
0492 #define make_int_accessor(x)                                                                                                                                   \
0493     unsigned int num##x() const                                                                                                                                \
0494     {                                                                                                                                                          \
0495         return m##x;                                                                                                                                           \
0496     }
0497     make_int_accessor(Total) make_int_accessor(InlineOpenPGP) make_int_accessor(OpenPGPMIME) make_int_accessor(SMIME) make_int_accessor(SMIMEOpaque)
0498 #undef make_int_accessor
0499 
0500         [[nodiscard]] unsigned int numOf(Kleo::CryptoMessageFormat f) const
0501     {
0502         switch (f) {
0503 #define CASE(x)                                                                                                                                                \
0504     case Kleo::x##Format:                                                                                                                                      \
0505         return m##x
0506             CASE(InlineOpenPGP);
0507             CASE(OpenPGPMIME);
0508             CASE(SMIME);
0509             CASE(SMIMEOpaque);
0510 #undef CASE
0511         default:
0512             return 0;
0513         }
0514     }
0515 
0516 protected:
0517     unsigned int mTotal = 0;
0518     unsigned int mInlineOpenPGP = 0;
0519     unsigned int mOpenPGPMIME = 0;
0520     unsigned int mSMIME = 0;
0521     unsigned int mSMIMEOpaque = 0;
0522 };
0523 
0524 class EncryptionFormatPreferenceCounter : public FormatPreferenceCounterBase
0525 {
0526 public:
0527     EncryptionFormatPreferenceCounter()
0528         : FormatPreferenceCounterBase()
0529     {
0530     }
0531 
0532     void operator()(const Kleo::KeyResolver::Item &item);
0533 };
0534 
0535 class SigningFormatPreferenceCounter : public FormatPreferenceCounterBase
0536 {
0537 public:
0538     SigningFormatPreferenceCounter()
0539         : FormatPreferenceCounterBase()
0540     {
0541     }
0542 
0543     void operator()(const Kleo::KeyResolver::Item &item);
0544 };
0545 
0546 #define CASE(x)                                                                                                                                                \
0547     if (item.format & Kleo::x##Format) {                                                                                                                       \
0548         ++m##x;                                                                                                                                                \
0549     }
0550 void EncryptionFormatPreferenceCounter::operator()(const Kleo::KeyResolver::Item &item)
0551 {
0552     if (item.format & (Kleo::InlineOpenPGPFormat | Kleo::OpenPGPMIMEFormat)
0553         && std::any_of(item.keys.begin(),
0554                        item.keys.end(),
0555                        ValidTrustedOpenPGPEncryptionKey)) { // -= trusted?
0556         CASE(OpenPGPMIME);
0557         CASE(InlineOpenPGP);
0558     }
0559     if (item.format & (Kleo::SMIMEFormat | Kleo::SMIMEOpaqueFormat)
0560         && std::any_of(item.keys.begin(),
0561                        item.keys.end(),
0562                        ValidTrustedSMIMEEncryptionKey)) { // -= trusted?
0563         CASE(SMIME);
0564         CASE(SMIMEOpaque);
0565     }
0566     ++mTotal;
0567 }
0568 
0569 void SigningFormatPreferenceCounter::operator()(const Kleo::KeyResolver::Item &item)
0570 {
0571     CASE(InlineOpenPGP);
0572     CASE(OpenPGPMIME);
0573     CASE(SMIME);
0574     CASE(SMIMEOpaque);
0575     ++mTotal;
0576 }
0577 
0578 #undef CASE
0579 } // anon namespace
0580 
0581 static QString canonicalAddress(const QString &_address)
0582 {
0583     const QString address = KEmailAddress::extractEmailAddress(_address);
0584     if (!address.contains(QLatin1Char('@'))) {
0585         // local address
0586         // return address + '@' + KNetwork::KResolver::localHostName();
0587         return address + QLatin1StringView("@localdomain");
0588     } else {
0589         return address;
0590     }
0591 }
0592 
0593 struct FormatInfo {
0594     std::vector<Kleo::KeyResolver::SplitInfo> splitInfos;
0595     std::vector<GpgME::Key> signKeys;
0596 };
0597 
0598 struct Q_DECL_HIDDEN Kleo::KeyResolver::KeyResolverPrivate {
0599     bool mAkonadiLookupEnabled = true;
0600     bool mAutocryptEnabled = false;
0601     std::set<QByteArray> alreadyWarnedFingerprints;
0602 
0603     std::vector<GpgME::Key> mOpenPGPSigningKeys; // signing
0604     std::vector<GpgME::Key> mSMIMESigningKeys; // signing
0605 
0606     std::vector<GpgME::Key> mOpenPGPEncryptToSelfKeys; // encryption to self
0607     std::vector<GpgME::Key> mSMIMEEncryptToSelfKeys; // encryption to self
0608 
0609     std::vector<Item> mPrimaryEncryptionKeys; // encryption to To/CC
0610     std::vector<Item> mSecondaryEncryptionKeys; // encryption to BCC
0611 
0612     std::map<CryptoMessageFormat, FormatInfo> mFormatInfoMap;
0613 
0614     // key=email address, value=crypto preferences for this contact (from kabc)
0615     using ContactPreferencesMap = std::map<QString, MessageComposer::ContactPreference>;
0616     ContactPreferencesMap mContactPreferencesMap;
0617     std::map<QByteArray, QString> mAutocryptMap;
0618     std::shared_ptr<Kleo::ExpiryChecker> expiryChecker;
0619 };
0620 
0621 Kleo::KeyResolver::KeyResolver(bool encToSelf, bool showApproval, bool oppEncryption, unsigned int f, const std::shared_ptr<Kleo::ExpiryChecker> &expiryChecker)
0622     : d(new KeyResolverPrivate)
0623     , mEncryptToSelf(encToSelf)
0624     , mShowApprovalDialog(showApproval)
0625     , mOpportunisticEncyption(oppEncryption)
0626     , mCryptoMessageFormats(f)
0627 {
0628     d->expiryChecker = expiryChecker;
0629 }
0630 
0631 Kleo::KeyResolver::~KeyResolver() = default;
0632 
0633 Kleo::Result Kleo::KeyResolver::setEncryptToSelfKeys(const QStringList &fingerprints)
0634 {
0635     if (!encryptToSelf()) {
0636         return Kleo::Ok;
0637     }
0638 
0639     std::vector<GpgME::Key> keys = lookup(fingerprints);
0640     std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mOpenPGPEncryptToSelfKeys),
0641                         NotValidTrustedOpenPGPEncryptionKey); // -= trusted?
0642     std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mSMIMEEncryptToSelfKeys),
0643                         NotValidTrustedSMIMEEncryptionKey); // -= trusted?
0644 
0645     if (d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size() < keys.size()) {
0646         // too few keys remain...
0647         const QString msg = i18n(
0648             "One or more of your configured OpenPGP encryption "
0649             "keys or S/MIME certificates is not usable for "
0650             "encryption. Please reconfigure your encryption keys "
0651             "and certificates for this identity in the identity "
0652             "configuration dialog.\n"
0653             "If you choose to continue, and the keys are needed "
0654             "later on, you will be prompted to specify the keys "
0655             "to use.");
0656         return KMessageBox::warningContinueCancel(nullptr,
0657                                                   msg,
0658                                                   i18nc("@title:window", "Unusable Encryption Keys"),
0659                                                   KStandardGuiItem::cont(),
0660                                                   KStandardGuiItem::cancel(),
0661                                                   QStringLiteral("unusable own encryption key warning"))
0662                 == KMessageBox::Continue
0663             ? Kleo::Ok
0664             : Kleo::Canceled;
0665     }
0666 
0667     // check for near-expiry:
0668     std::vector<GpgME::Key>::const_iterator end(d->mOpenPGPEncryptToSelfKeys.end());
0669 
0670     for (auto it = d->mOpenPGPEncryptToSelfKeys.begin(); it != end; ++it) {
0671         d->expiryChecker->checkKey(*it, Kleo::ExpiryChecker::OwnEncryptionKey);
0672     }
0673     std::vector<GpgME::Key>::const_iterator end2(d->mSMIMEEncryptToSelfKeys.end());
0674     for (auto it = d->mSMIMEEncryptToSelfKeys.begin(); it != end2; ++it) {
0675         d->expiryChecker->checkKey(*it, Kleo::ExpiryChecker::OwnEncryptionKey);
0676     }
0677 
0678     return Kleo::Ok;
0679 }
0680 
0681 Kleo::Result Kleo::KeyResolver::setSigningKeys(const QStringList &fingerprints)
0682 {
0683     std::vector<GpgME::Key> keys = lookup(fingerprints, true); // secret keys
0684     std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mOpenPGPSigningKeys), NotValidOpenPGPSigningKey);
0685     std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mSMIMESigningKeys), NotValidSMIMESigningKey);
0686 
0687     if (d->mOpenPGPSigningKeys.size() + d->mSMIMESigningKeys.size() < keys.size()) {
0688         // too few keys remain...
0689         const QString msg = i18n(
0690             "One or more of your configured OpenPGP signing keys "
0691             "or S/MIME signing certificates is not usable for "
0692             "signing. Please reconfigure your signing keys "
0693             "and certificates for this identity in the identity "
0694             "configuration dialog.\n"
0695             "If you choose to continue, and the keys are needed "
0696             "later on, you will be prompted to specify the keys "
0697             "to use.");
0698         return KMessageBox::warningContinueCancel(nullptr,
0699                                                   msg,
0700                                                   i18nc("@title:window", "Unusable Signing Keys"),
0701                                                   KStandardGuiItem::cont(),
0702                                                   KStandardGuiItem::cancel(),
0703                                                   QStringLiteral("unusable signing key warning"))
0704                 == KMessageBox::Continue
0705             ? Kleo::Ok
0706             : Kleo::Canceled;
0707     }
0708 
0709     // check for near expiry:
0710 
0711     for (auto it = d->mOpenPGPSigningKeys.begin(), total = d->mOpenPGPSigningKeys.end(); it != total; ++it) {
0712         d->expiryChecker->checkKey(*it, Kleo::ExpiryChecker::OwnSigningKey);
0713     }
0714 
0715     for (auto it = d->mSMIMESigningKeys.begin(), total = d->mSMIMESigningKeys.end(); it != total; ++it) {
0716         d->expiryChecker->checkKey(*it, Kleo::ExpiryChecker::OwnSigningKey);
0717     }
0718 
0719     return Kleo::Ok;
0720 }
0721 
0722 void Kleo::KeyResolver::setPrimaryRecipients(const QStringList &addresses)
0723 {
0724     d->mPrimaryEncryptionKeys = getEncryptionItems(addresses);
0725 }
0726 
0727 void Kleo::KeyResolver::setSecondaryRecipients(const QStringList &addresses)
0728 {
0729     d->mSecondaryEncryptionKeys = getEncryptionItems(addresses);
0730 }
0731 
0732 std::vector<Kleo::KeyResolver::Item> Kleo::KeyResolver::getEncryptionItems(const QStringList &addresses)
0733 {
0734     std::vector<Item> items;
0735     items.reserve(addresses.size());
0736     QStringList::const_iterator end(addresses.constEnd());
0737     for (QStringList::const_iterator it = addresses.constBegin(); it != end; ++it) {
0738         QString addr = canonicalAddress(*it).toLower();
0739         const auto pref = lookupContactPreferences(addr);
0740 
0741         items.emplace_back(*it, /*getEncryptionKeys( *it, true ),*/
0742                            pref.encryptionPreference,
0743                            pref.signingPreference,
0744                            pref.cryptoMessageFormat);
0745     }
0746     return items;
0747 }
0748 
0749 static Kleo::Action action(bool doit, bool ask, bool donot, bool requested)
0750 {
0751     if (requested && !donot) {
0752         return Kleo::DoIt;
0753     }
0754     if (doit && !ask && !donot) {
0755         return Kleo::DoIt;
0756     }
0757     if (!doit && ask && !donot) {
0758         return Kleo::Ask;
0759     }
0760     if (!doit && !ask && donot) {
0761         return requested ? Kleo::Conflict : Kleo::DontDoIt;
0762     }
0763     if (!doit && !ask && !donot) {
0764         return Kleo::DontDoIt;
0765     }
0766     return Kleo::Conflict;
0767 }
0768 
0769 Kleo::Action Kleo::KeyResolver::checkSigningPreferences(bool signingRequested) const
0770 {
0771     if (signingRequested && d->mOpenPGPSigningKeys.empty() && d->mSMIMESigningKeys.empty()) {
0772         return Impossible;
0773     }
0774 
0775     SigningPreferenceCounter count;
0776     count = std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count);
0777     count = std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), count);
0778 
0779     unsigned int sign = count.numAlwaysSign();
0780     unsigned int ask = count.numAlwaysAskForSigning();
0781     const unsigned int dontSign = count.numNeverSign();
0782     if (signingPossible()) {
0783         sign += count.numAlwaysSignIfPossible();
0784         ask += count.numAskSigningWheneverPossible();
0785     }
0786 
0787     return action(sign, ask, dontSign, signingRequested);
0788 }
0789 
0790 bool Kleo::KeyResolver::signingPossible() const
0791 {
0792     return !d->mOpenPGPSigningKeys.empty() || !d->mSMIMESigningKeys.empty();
0793 }
0794 
0795 Kleo::Action Kleo::KeyResolver::checkEncryptionPreferences(bool encryptionRequested) const
0796 {
0797     if (d->mPrimaryEncryptionKeys.empty() && d->mSecondaryEncryptionKeys.empty()) {
0798         return DontDoIt;
0799     }
0800 
0801     if (encryptionRequested && encryptToSelf() && d->mOpenPGPEncryptToSelfKeys.empty() && d->mSMIMEEncryptToSelfKeys.empty()) {
0802         return Impossible;
0803     }
0804 
0805     if (!encryptionRequested && !mOpportunisticEncyption) {
0806         // try to minimize crypto ops (including key lookups) by only
0807         // looking up keys when at least one of the encryption
0808         // preferences needs it:
0809         EncryptionPreferenceCounter count(nullptr, UnknownPreference);
0810         count.process(d->mPrimaryEncryptionKeys);
0811         count.process(d->mSecondaryEncryptionKeys);
0812         if (!count.numAlwaysEncrypt()
0813             && !count.numAlwaysAskForEncryption() // this guy might not need a lookup, when declined, but it's too complex to implement that here
0814             && !count.numAlwaysEncryptIfPossible() && !count.numAskWheneverPossible()) {
0815             return DontDoIt;
0816         }
0817     }
0818 
0819     EncryptionPreferenceCounter count(this, mOpportunisticEncyption ? AskWheneverPossible : UnknownPreference);
0820     count = std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count);
0821     count = std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), count);
0822 
0823     unsigned int encrypt = count.numAlwaysEncrypt();
0824     unsigned int ask = count.numAlwaysAskForEncryption();
0825     const unsigned int dontEncrypt = count.numNeverEncrypt() + count.numNoKey();
0826     if (encryptionPossible()) {
0827         encrypt += count.numAlwaysEncryptIfPossible();
0828         ask += count.numAskWheneverPossible();
0829     }
0830 
0831     const Action act = action(encrypt, ask, dontEncrypt, encryptionRequested);
0832     if (act != Ask
0833         || std::for_each(
0834                d->mPrimaryEncryptionKeys.begin(),
0835                d->mPrimaryEncryptionKeys.end(),
0836                std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), EncryptionPreferenceCounter(this, UnknownPreference)))
0837                .numAlwaysAskForEncryption()) {
0838         return act;
0839     } else {
0840         return AskOpportunistic;
0841     }
0842 }
0843 
0844 bool Kleo::KeyResolver::encryptionPossible() const
0845 {
0846     return std::none_of(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), EmptyKeyList)
0847         && std::none_of(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), EmptyKeyList);
0848 }
0849 
0850 Kleo::Result Kleo::KeyResolver::resolveAllKeys(bool &signingRequested, bool &encryptionRequested)
0851 {
0852     if (!encryptionRequested && !signingRequested) {
0853         // make a dummy entry with all recipients, but no signing or
0854         // encryption keys to avoid special-casing on the caller side:
0855         dump();
0856         d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.emplace_back(allRecipients());
0857         dump();
0858         return Kleo::Ok;
0859     }
0860     Kleo::Result result = Kleo::Ok;
0861     if (encryptionRequested) {
0862         bool finalySendUnencrypted = false;
0863         result = resolveEncryptionKeys(signingRequested, finalySendUnencrypted);
0864         if (finalySendUnencrypted) {
0865             encryptionRequested = false;
0866         }
0867     }
0868     if (result != Kleo::Ok) {
0869         return result;
0870     }
0871     if (encryptionRequested) {
0872         result = resolveSigningKeysForEncryption();
0873     } else {
0874         result = resolveSigningKeysForSigningOnly();
0875         if (result == Kleo::Failure) {
0876             signingRequested = false;
0877             return Kleo::Ok;
0878         }
0879     }
0880     return result;
0881 }
0882 
0883 Kleo::Result Kleo::KeyResolver::resolveEncryptionKeys(bool signingRequested, bool &finalySendUnencrypted)
0884 {
0885     //
0886     // 1. Get keys for all recipients:
0887     //
0888     qCDebug(MESSAGECOMPOSER_LOG) << "resolving enc keys" << d->mPrimaryEncryptionKeys.size();
0889     for (auto it = d->mPrimaryEncryptionKeys.begin(); it != d->mPrimaryEncryptionKeys.end(); ++it) {
0890         qCDebug(MESSAGECOMPOSER_LOG) << "checking primary:" << it->address;
0891         if (!it->needKeys) {
0892             continue;
0893         }
0894         it->keys = getEncryptionKeys(it->address, false);
0895         qCDebug(MESSAGECOMPOSER_LOG) << "got # keys:" << it->keys.size();
0896         if (it->keys.empty()) {
0897             return Kleo::Canceled;
0898         }
0899         QString addr = canonicalAddress(it->address).toLower();
0900         const auto pref = lookupContactPreferences(addr);
0901         it->pref = pref.encryptionPreference;
0902         it->signPref = pref.signingPreference;
0903         it->format = pref.cryptoMessageFormat;
0904         qCDebug(MESSAGECOMPOSER_LOG) << "set key data:" << int(it->pref) << int(it->signPref) << int(it->format);
0905     }
0906 
0907     for (auto it = d->mSecondaryEncryptionKeys.begin(), total = d->mSecondaryEncryptionKeys.end(); it != total; ++it) {
0908         if (!it->needKeys) {
0909             continue;
0910         }
0911         it->keys = getEncryptionKeys(it->address, false);
0912         if (it->keys.empty()) {
0913             return Kleo::Canceled;
0914         }
0915         QString addr = canonicalAddress(it->address).toLower();
0916         const auto pref = lookupContactPreferences(addr);
0917         it->pref = pref.encryptionPreference;
0918         it->signPref = pref.signingPreference;
0919         it->format = pref.cryptoMessageFormat;
0920     }
0921 
0922     // 1a: Present them to the user
0923 
0924     const Kleo::Result res = showKeyApprovalDialog(finalySendUnencrypted);
0925     if (res != Kleo::Ok) {
0926         return res;
0927     }
0928 
0929     //
0930     // 2. Check what the primary recipients need
0931     //
0932 
0933     // 2a. Try to find a common format for all primary recipients,
0934     //     else use as many formats as needed
0935 
0936     const EncryptionFormatPreferenceCounter primaryCount =
0937         std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), EncryptionFormatPreferenceCounter());
0938 
0939     CryptoMessageFormat commonFormat = AutoFormat;
0940     for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
0941         if (!(concreteCryptoMessageFormats[i] & mCryptoMessageFormats)) {
0942             continue;
0943         }
0944         if (signingRequested && signingKeysFor(concreteCryptoMessageFormats[i]).empty()) {
0945             continue;
0946         }
0947         if (encryptToSelf() && encryptToSelfKeysFor(concreteCryptoMessageFormats[i]).empty()) {
0948             continue;
0949         }
0950         if (primaryCount.numOf(concreteCryptoMessageFormats[i]) == primaryCount.numTotal()) {
0951             commonFormat = concreteCryptoMessageFormats[i];
0952             break;
0953         }
0954     }
0955     qCDebug(MESSAGECOMPOSER_LOG) << "got commonFormat for primary recipients:" << int(commonFormat);
0956     if (commonFormat != AutoFormat) {
0957         addKeys(d->mPrimaryEncryptionKeys, commonFormat);
0958     } else {
0959         addKeys(d->mPrimaryEncryptionKeys);
0960     }
0961 
0962     collapseAllSplitInfos(); // these can be encrypted together
0963 
0964     // 2b. Just try to find _something_ for each secondary recipient,
0965     //     with a preference to a common format (if that exists)
0966 
0967     const EncryptionFormatPreferenceCounter secondaryCount =
0968         std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), EncryptionFormatPreferenceCounter());
0969 
0970     if (commonFormat != AutoFormat && secondaryCount.numOf(commonFormat) == secondaryCount.numTotal()) {
0971         addKeys(d->mSecondaryEncryptionKeys, commonFormat);
0972     } else {
0973         addKeys(d->mSecondaryEncryptionKeys);
0974     }
0975 
0976     // 3. Check for expiry:
0977 
0978     for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
0979         const std::vector<SplitInfo> si_list = encryptionItems(concreteCryptoMessageFormats[i]);
0980         for (auto sit = si_list.begin(), total = si_list.end(); sit != total; ++sit) {
0981             for (auto kit = sit->keys.begin(); kit != sit->keys.end(); ++kit) {
0982                 d->expiryChecker->checkKey(*kit, Kleo::ExpiryChecker::EncryptionKey);
0983             }
0984         }
0985     }
0986 
0987     // 4. Check that we have the right keys for encryptToSelf()
0988 
0989     if (!encryptToSelf()) {
0990         return Kleo::Ok;
0991     }
0992 
0993     // 4a. Check for OpenPGP keys
0994 
0995     qCDebug(MESSAGECOMPOSER_LOG) << "sizes of encryption items:" << encryptionItems(InlineOpenPGPFormat).size() << encryptionItems(OpenPGPMIMEFormat).size()
0996                                  << encryptionItems(SMIMEFormat).size() << encryptionItems(SMIMEOpaqueFormat).size();
0997     if (!encryptionItems(InlineOpenPGPFormat).empty() || !encryptionItems(OpenPGPMIMEFormat).empty()) {
0998         // need them
0999         if (d->mOpenPGPEncryptToSelfKeys.empty()) {
1000             const QString msg = i18n(
1001                 "Examination of recipient's encryption preferences "
1002                 "yielded that the message should be encrypted using "
1003                 "OpenPGP, at least for some recipients;\n"
1004                 "however, you have not configured valid trusted "
1005                 "OpenPGP encryption keys for this identity.\n"
1006                 "You may continue without encrypting to yourself, "
1007                 "but be aware that you will not be able to read your "
1008                 "own messages if you do so.");
1009             if (KMessageBox::warningContinueCancel(nullptr,
1010                                                    msg,
1011                                                    i18nc("@title:window", "Unusable Encryption Keys"),
1012                                                    KStandardGuiItem::cont(),
1013                                                    KStandardGuiItem::cancel(),
1014                                                    QStringLiteral("encrypt-to-self will fail warning"))
1015                 == KMessageBox::Cancel) {
1016                 return Kleo::Canceled;
1017             }
1018             // FIXME: Allow selection
1019         }
1020         addToAllSplitInfos(d->mOpenPGPEncryptToSelfKeys, InlineOpenPGPFormat | OpenPGPMIMEFormat);
1021     }
1022 
1023     // 4b. Check for S/MIME certs:
1024 
1025     if (!encryptionItems(SMIMEFormat).empty() || !encryptionItems(SMIMEOpaqueFormat).empty()) {
1026         // need them
1027         if (d->mSMIMEEncryptToSelfKeys.empty()) {
1028             // don't have one
1029             const QString msg = i18n(
1030                 "Examination of recipient's encryption preferences "
1031                 "yielded that the message should be encrypted using "
1032                 "S/MIME, at least for some recipients;\n"
1033                 "however, you have not configured valid "
1034                 "S/MIME encryption certificates for this identity.\n"
1035                 "You may continue without encrypting to yourself, "
1036                 "but be aware that you will not be able to read your "
1037                 "own messages if you do so.");
1038             if (KMessageBox::warningContinueCancel(nullptr,
1039                                                    msg,
1040                                                    i18nc("@title:window", "Unusable Encryption Keys"),
1041                                                    KStandardGuiItem::cont(),
1042                                                    KStandardGuiItem::cancel(),
1043                                                    QStringLiteral("encrypt-to-self will fail warning"))
1044                 == KMessageBox::Cancel) {
1045                 return Kleo::Canceled;
1046             }
1047             // FIXME: Allow selection
1048         }
1049         addToAllSplitInfos(d->mSMIMEEncryptToSelfKeys, SMIMEFormat | SMIMEOpaqueFormat);
1050     }
1051 
1052     // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
1053     // are missing.
1054 
1055     return Kleo::Ok;
1056 }
1057 
1058 Kleo::Result Kleo::KeyResolver::resolveSigningKeysForEncryption()
1059 {
1060     if ((!encryptionItems(InlineOpenPGPFormat).empty() || !encryptionItems(OpenPGPMIMEFormat).empty()) && d->mOpenPGPSigningKeys.empty()) {
1061         const QString msg = i18n(
1062             "Examination of recipient's signing preferences "
1063             "yielded that the message should be signed using "
1064             "OpenPGP, at least for some recipients;\n"
1065             "however, you have not configured valid "
1066             "OpenPGP signing certificates for this identity.");
1067         if (KMessageBox::warningContinueCancel(nullptr,
1068                                                msg,
1069                                                i18nc("@title:window", "Unusable Signing Keys"),
1070                                                KGuiItem(i18n("Do Not OpenPGP-Sign")),
1071                                                KStandardGuiItem::cancel(),
1072                                                QStringLiteral("signing will fail warning"))
1073             == KMessageBox::Cancel) {
1074             return Kleo::Canceled;
1075         }
1076         // FIXME: Allow selection
1077     }
1078     if ((!encryptionItems(SMIMEFormat).empty() || !encryptionItems(SMIMEOpaqueFormat).empty()) && d->mSMIMESigningKeys.empty()) {
1079         const QString msg = i18n(
1080             "Examination of recipient's signing preferences "
1081             "yielded that the message should be signed using "
1082             "S/MIME, at least for some recipients;\n"
1083             "however, you have not configured valid "
1084             "S/MIME signing certificates for this identity.");
1085         if (KMessageBox::warningContinueCancel(nullptr,
1086                                                msg,
1087                                                i18nc("@title:window", "Unusable Signing Keys"),
1088                                                KGuiItem(i18n("Do Not S/MIME-Sign")),
1089                                                KStandardGuiItem::cancel(),
1090                                                QStringLiteral("signing will fail warning"))
1091             == KMessageBox::Cancel) {
1092             return Kleo::Canceled;
1093         }
1094         // FIXME: Allow selection
1095     }
1096 
1097     // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
1098     // are missing.
1099 
1100     for (auto it = d->mFormatInfoMap.begin(); it != d->mFormatInfoMap.end(); ++it) {
1101         if (!it->second.splitInfos.empty()) {
1102             dump();
1103             it->second.signKeys = signingKeysFor(it->first);
1104             dump();
1105         }
1106     }
1107 
1108     return Kleo::Ok;
1109 }
1110 
1111 Kleo::Result Kleo::KeyResolver::resolveSigningKeysForSigningOnly()
1112 {
1113     //
1114     // we don't need to distinguish between primary and secondary
1115     // recipients here:
1116     //
1117     SigningFormatPreferenceCounter count;
1118     count = std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count);
1119     count = std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), count);
1120 
1121     // try to find a common format that works for all (and that we have signing keys for):
1122 
1123     CryptoMessageFormat commonFormat = AutoFormat;
1124 
1125     for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1126         const auto res = concreteCryptoMessageFormats[i];
1127         if (!(mCryptoMessageFormats & res)) {
1128             continue; // skip
1129         }
1130         if (signingKeysFor(res).empty()) {
1131             continue; // skip
1132         }
1133         if (count.numOf(res) == count.numTotal()) {
1134             commonFormat = res;
1135             break;
1136         }
1137     }
1138 
1139     if (commonFormat != AutoFormat) { // found
1140         dump();
1141         FormatInfo &fi = d->mFormatInfoMap[commonFormat];
1142         fi.signKeys = signingKeysFor(commonFormat);
1143         fi.splitInfos.resize(1);
1144         fi.splitInfos.front() = SplitInfo(allRecipients());
1145         dump();
1146         return Kleo::Ok;
1147     }
1148 
1149     const QString msg = i18n(
1150         "Examination of recipient's signing preferences "
1151         "showed no common type of signature matching your "
1152         "available signing keys.\n"
1153         "Send message without signing?");
1154     if (KMessageBox::warningContinueCancel(nullptr, msg, i18nc("@title:window", "No signing possible"), KStandardGuiItem::cont()) == KMessageBox::Continue) {
1155         d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.emplace_back(allRecipients());
1156         return Kleo::Failure; // means "Ok, but without signing"
1157     }
1158     return Kleo::Canceled;
1159 }
1160 
1161 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeysFor(CryptoMessageFormat f) const
1162 {
1163     if (isOpenPGP(f)) {
1164         return d->mOpenPGPSigningKeys;
1165     }
1166     if (isSMIME(f)) {
1167         return d->mSMIMESigningKeys;
1168     }
1169     return {};
1170 }
1171 
1172 std::vector<GpgME::Key> Kleo::KeyResolver::encryptToSelfKeysFor(CryptoMessageFormat f) const
1173 {
1174     if (isOpenPGP(f)) {
1175         return d->mOpenPGPEncryptToSelfKeys;
1176     }
1177     if (isSMIME(f)) {
1178         return d->mSMIMEEncryptToSelfKeys;
1179     }
1180     return {};
1181 }
1182 
1183 QStringList Kleo::KeyResolver::allRecipients() const
1184 {
1185     QStringList result;
1186     std::transform(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), std::back_inserter(result), ItemDotAddress);
1187     std::transform(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), std::back_inserter(result), ItemDotAddress);
1188     return result;
1189 }
1190 
1191 void Kleo::KeyResolver::collapseAllSplitInfos()
1192 {
1193     dump();
1194     for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1195         auto pos = d->mFormatInfoMap.find(concreteCryptoMessageFormats[i]);
1196         if (pos == d->mFormatInfoMap.end()) {
1197             continue;
1198         }
1199         std::vector<SplitInfo> &v = pos->second.splitInfos;
1200         if (v.size() < 2) {
1201             continue;
1202         }
1203         SplitInfo &si = v.front();
1204         for (auto it = v.begin() + 1; it != v.end(); ++it) {
1205             si.keys.insert(si.keys.end(), it->keys.begin(), it->keys.end());
1206             std::copy(it->recipients.begin(), it->recipients.end(), std::back_inserter(si.recipients));
1207         }
1208         v.resize(1);
1209     }
1210     dump();
1211 }
1212 
1213 void Kleo::KeyResolver::addToAllSplitInfos(const std::vector<GpgME::Key> &keys, unsigned int f)
1214 {
1215     dump();
1216     if (!f || keys.empty()) {
1217         return;
1218     }
1219     for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1220         if (!(f & concreteCryptoMessageFormats[i])) {
1221             continue;
1222         }
1223         auto pos = d->mFormatInfoMap.find(concreteCryptoMessageFormats[i]);
1224         if (pos == d->mFormatInfoMap.end()) {
1225             continue;
1226         }
1227         std::vector<SplitInfo> &v = pos->second.splitInfos;
1228         for (auto it = v.begin(); it != v.end(); ++it) {
1229             it->keys.insert(it->keys.end(), keys.begin(), keys.end());
1230         }
1231     }
1232     dump();
1233 }
1234 
1235 void Kleo::KeyResolver::dump() const
1236 {
1237 #ifndef NDEBUG
1238     if (d->mFormatInfoMap.empty()) {
1239         qCDebug(MESSAGECOMPOSER_LOG) << "Keyresolver: Format info empty";
1240     }
1241     for (auto it = d->mFormatInfoMap.begin(); it != d->mFormatInfoMap.end(); ++it) {
1242         qCDebug(MESSAGECOMPOSER_LOG) << "Format info for " << Kleo::cryptoMessageFormatToString(it->first) << ":  Signing keys: ";
1243         for (auto sit = it->second.signKeys.begin(); sit != it->second.signKeys.end(); ++sit) {
1244             qCDebug(MESSAGECOMPOSER_LOG) << "  " << sit->shortKeyID() << " ";
1245         }
1246         unsigned int i = 0;
1247         for (auto sit = it->second.splitInfos.begin(), sitEnd = it->second.splitInfos.end(); sit != sitEnd; ++sit, ++i) {
1248             qCDebug(MESSAGECOMPOSER_LOG) << "  SplitInfo #" << i << " encryption keys: ";
1249             for (auto kit = sit->keys.begin(), sitEnd = sit->keys.end(); kit != sitEnd; ++kit) {
1250                 qCDebug(MESSAGECOMPOSER_LOG) << "  " << kit->shortKeyID();
1251             }
1252             qCDebug(MESSAGECOMPOSER_LOG) << "  SplitInfo #" << i << " recipients: " << qPrintable(sit->recipients.join(QLatin1StringView(", ")));
1253         }
1254     }
1255 #endif
1256 }
1257 
1258 Kleo::Result Kleo::KeyResolver::showKeyApprovalDialog(bool &finalySendUnencrypted)
1259 {
1260     const bool showKeysForApproval = showApprovalDialog() || std::any_of(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), ApprovalNeeded)
1261         || std::any_of(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), ApprovalNeeded);
1262 
1263     if (!showKeysForApproval) {
1264         return Kleo::Ok;
1265     }
1266 
1267     std::vector<Kleo::KeyApprovalDialog::Item> items;
1268     items.reserve(d->mPrimaryEncryptionKeys.size() + d->mSecondaryEncryptionKeys.size());
1269     std::copy(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), std::back_inserter(items));
1270     std::copy(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), std::back_inserter(items));
1271 
1272     std::vector<GpgME::Key> senderKeys;
1273     senderKeys.reserve(d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size());
1274     std::copy(d->mOpenPGPEncryptToSelfKeys.begin(), d->mOpenPGPEncryptToSelfKeys.end(), std::back_inserter(senderKeys));
1275     std::copy(d->mSMIMEEncryptToSelfKeys.begin(), d->mSMIMEEncryptToSelfKeys.end(), std::back_inserter(senderKeys));
1276 
1277     KCursorSaver saver(Qt::WaitCursor);
1278 
1279     QPointer<Kleo::KeyApprovalDialog> dlg = new Kleo::KeyApprovalDialog(items, senderKeys);
1280 
1281     if (dlg->exec() == QDialog::Rejected) {
1282         delete dlg;
1283         return Kleo::Canceled;
1284     }
1285 
1286     items = dlg->items();
1287     senderKeys = dlg->senderKeys();
1288     const bool prefsChanged = dlg->preferencesChanged();
1289     delete dlg;
1290 
1291     if (prefsChanged) {
1292         for (uint i = 0, total = items.size(); i < total; ++i) {
1293             auto pref = lookupContactPreferences(items[i].address);
1294             pref.encryptionPreference = items[i].pref;
1295             pref.pgpKeyFingerprints.clear();
1296             pref.smimeCertFingerprints.clear();
1297             const std::vector<GpgME::Key> &keys = items[i].keys;
1298             for (auto it = keys.begin(), end = keys.end(); it != end; ++it) {
1299                 if (it->protocol() == GpgME::OpenPGP) {
1300                     if (const char *fpr = it->primaryFingerprint()) {
1301                         pref.pgpKeyFingerprints.push_back(QLatin1StringView(fpr));
1302                     }
1303                 } else if (it->protocol() == GpgME::CMS) {
1304                     if (const char *fpr = it->primaryFingerprint()) {
1305                         pref.smimeCertFingerprints.push_back(QLatin1StringView(fpr));
1306                     }
1307                 }
1308             }
1309             saveContactPreference(items[i].address, pref);
1310         }
1311     }
1312 
1313     // show a warning if the user didn't select an encryption key for
1314     // herself:
1315     if (encryptToSelf() && senderKeys.empty()) {
1316         const QString msg = i18n(
1317             "You did not select an encryption key for yourself "
1318             "(encrypt to self). You will not be able to decrypt "
1319             "your own message if you encrypt it.");
1320         if (KMessageBox::warningContinueCancel(nullptr, msg, i18nc("@title:window", "Missing Key Warning"), KGuiItem(i18n("&Encrypt")))
1321             == KMessageBox::Cancel) {
1322             return Kleo::Canceled;
1323         } else {
1324             mEncryptToSelf = false;
1325         }
1326     }
1327 
1328     // count empty key ID lists
1329     const unsigned int emptyListCount = std::count_if(items.begin(), items.end(), EmptyKeyList);
1330 
1331     // show a warning if the user didn't select an encryption key for
1332     // some of the recipients
1333     if (items.size() == emptyListCount) {
1334         const QString msg = (d->mPrimaryEncryptionKeys.size() + d->mSecondaryEncryptionKeys.size() == 1)
1335             ? i18n(
1336                 "You did not select an encryption key for the "
1337                 "recipient of this message; therefore, the message "
1338                 "will not be encrypted.")
1339             : i18n(
1340                 "You did not select an encryption key for any of the "
1341                 "recipients of this message; therefore, the message "
1342                 "will not be encrypted.");
1343         if (KMessageBox::warningContinueCancel(nullptr, msg, i18nc("@title:window", "Missing Key Warning"), KGuiItem(i18n("Send &Unencrypted")))
1344             == KMessageBox::Cancel) {
1345             return Kleo::Canceled;
1346         }
1347         finalySendUnencrypted = true;
1348     } else if (emptyListCount > 0) {
1349         const QString msg = (emptyListCount == 1) ? i18n(
1350                                 "You did not select an encryption key for one of "
1351                                 "the recipients: this person will not be able to "
1352                                 "decrypt the message if you encrypt it.")
1353                                                   : i18n(
1354                                                       "You did not select encryption keys for some of "
1355                                                       "the recipients: these persons will not be able to "
1356                                                       "decrypt the message if you encrypt it.");
1357         KCursorSaver saver(Qt::WaitCursor);
1358         if (KMessageBox::warningContinueCancel(nullptr, msg, i18nc("@title:window", "Missing Key Warning"), KGuiItem(i18n("&Encrypt")))
1359             == KMessageBox::Cancel) {
1360             return Kleo::Canceled;
1361         }
1362     }
1363 
1364     std::transform(d->mPrimaryEncryptionKeys.begin(),
1365                    d->mPrimaryEncryptionKeys.end(),
1366                    items.begin(),
1367                    d->mPrimaryEncryptionKeys.begin(),
1368                    CopyKeysAndEncryptionPreferences);
1369     std::transform(d->mSecondaryEncryptionKeys.begin(),
1370                    d->mSecondaryEncryptionKeys.end(),
1371                    items.begin() + d->mPrimaryEncryptionKeys.size(),
1372                    d->mSecondaryEncryptionKeys.begin(),
1373                    CopyKeysAndEncryptionPreferences);
1374 
1375     d->mOpenPGPEncryptToSelfKeys.clear();
1376     d->mSMIMEEncryptToSelfKeys.clear();
1377 
1378     std::remove_copy_if(senderKeys.begin(),
1379                         senderKeys.end(),
1380                         std::back_inserter(d->mOpenPGPEncryptToSelfKeys),
1381                         NotValidTrustedOpenPGPEncryptionKey); // -= trusted (see above, too)?
1382     std::remove_copy_if(senderKeys.begin(),
1383                         senderKeys.end(),
1384                         std::back_inserter(d->mSMIMEEncryptToSelfKeys),
1385                         NotValidTrustedSMIMEEncryptionKey); // -= trusted (see above, too)?
1386 
1387     return Kleo::Ok;
1388 }
1389 
1390 std::vector<Kleo::KeyResolver::SplitInfo> Kleo::KeyResolver::encryptionItems(Kleo::CryptoMessageFormat f) const
1391 {
1392     dump();
1393     auto it = d->mFormatInfoMap.find(f);
1394     return it != d->mFormatInfoMap.end() ? it->second.splitInfos : std::vector<SplitInfo>();
1395 }
1396 
1397 void Kleo::KeyResolver::setAutocryptEnabled(bool autocryptEnabled)
1398 {
1399     d->mAutocryptEnabled = autocryptEnabled;
1400 }
1401 
1402 std::map<QByteArray, QString> Kleo::KeyResolver::useAutocrypt() const
1403 {
1404     return d->mAutocryptMap;
1405 }
1406 
1407 void Kleo::KeyResolver::setAkonadiLookupEnabled(bool akonadiLoopkupEnabled)
1408 {
1409     d->mAkonadiLookupEnabled = akonadiLoopkupEnabled;
1410 }
1411 
1412 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeys(CryptoMessageFormat f) const
1413 {
1414     dump();
1415     auto it = d->mFormatInfoMap.find(f);
1416     return it != d->mFormatInfoMap.end() ? it->second.signKeys : std::vector<GpgME::Key>();
1417 }
1418 
1419 //
1420 //
1421 // KeyResolverPrivate helper methods below:
1422 //
1423 //
1424 
1425 std::vector<GpgME::Key> Kleo::KeyResolver::selectKeys(const QString &person, const QString &msg, const std::vector<GpgME::Key> &selectedKeys) const
1426 {
1427     const bool opgp = containsOpenPGP(mCryptoMessageFormats);
1428     const bool x509 = containsSMIME(mCryptoMessageFormats);
1429 
1430     QPointer<Kleo::KeySelectionDialog> dlg = new Kleo::KeySelectionDialog(
1431         i18n("Encryption Key Selection"),
1432         msg,
1433         KEmailAddress::extractEmailAddress(person),
1434         selectedKeys,
1435         Kleo::KeySelectionDialog::ValidEncryptionKeys & ~(opgp ? 0 : Kleo::KeySelectionDialog::OpenPGPKeys) & ~(x509 ? 0 : Kleo::KeySelectionDialog::SMIMEKeys),
1436         true,
1437         true); // multi-selection and "remember choice" box
1438 
1439     if (dlg->exec() != QDialog::Accepted) {
1440         delete dlg;
1441         return {};
1442     }
1443 
1444     std::vector<GpgME::Key> keys = dlg->selectedKeys();
1445     keys.erase(std::remove_if(keys.begin(), keys.end(), NotValidEncryptionKey), keys.end());
1446     if (!keys.empty() && dlg->rememberSelection()) {
1447         setKeysForAddress(person, dlg->pgpKeyFingerprints(), dlg->smimeFingerprints());
1448     }
1449 
1450     delete dlg;
1451     return keys;
1452 }
1453 
1454 std::vector<GpgME::Key> Kleo::KeyResolver::getEncryptionKeys(const QString &person, bool quiet) const
1455 {
1456     const QString address = canonicalAddress(person).toLower();
1457 
1458     // First look for this person's address in the address->key dictionary
1459     const QStringList fingerprints = keysForAddress(address);
1460 
1461     if (!fingerprints.empty()) {
1462         qCDebug(MESSAGECOMPOSER_LOG) << "Using encryption keys 0x" << fingerprints.join(QLatin1StringView(", 0x")) << "for" << person;
1463         std::vector<GpgME::Key> keys = lookup(fingerprints);
1464         if (!keys.empty()) {
1465             // Check if all of the keys are trusted and valid encryption keys
1466             if (std::any_of(keys.begin(), keys.end(),
1467                             NotValidTrustedEncryptionKey)) { // -= trusted?
1468                 // not ok, let the user select: this is not conditional on !quiet,
1469                 // since it's a bug in the configuration and the user should be
1470                 // notified about it as early as possible:
1471                 keys = selectKeys(person,
1472                                   i18nc("if in your language something like "
1473                                         "'certificate(s)' is not possible please "
1474                                         "use the plural in the translation",
1475                                         "There is a problem with the "
1476                                         "encryption certificate(s) for \"%1\".\n\n"
1477                                         "Please re-select the certificate(s) which should "
1478                                         "be used for this recipient.",
1479                                         person),
1480                                   keys);
1481             }
1482             bool canceled = false;
1483             keys = trustedOrConfirmed(keys, address, canceled);
1484             if (canceled) {
1485                 return {};
1486             }
1487 
1488             if (!keys.empty()) {
1489                 return keys;
1490             }
1491             // keys.empty() is considered cancel by callers, so go on
1492         }
1493     }
1494 
1495     // Now search all public keys for matching keys
1496     std::vector<GpgME::Key> matchingKeys = lookup(QStringList(address));
1497     matchingKeys.erase(std::remove_if(matchingKeys.begin(), matchingKeys.end(), NotValidEncryptionKey), matchingKeys.end());
1498 
1499     if (matchingKeys.empty() && d->mAutocryptEnabled) {
1500         qCDebug(MESSAGECOMPOSER_LOG) << "Search in Autocrypt storage a key for " << address;
1501         const auto storage = MessageCore::AutocryptStorage::self();
1502         const auto recipient = storage->getRecipient(address.toUtf8());
1503         if (recipient) {
1504             const auto key = recipient->gpgKey();
1505             if (!key.isNull() && ValidEncryptionKey(key)) {
1506                 qCDebug(MESSAGECOMPOSER_LOG) << "Found an valid autocrypt key.";
1507                 matchingKeys.push_back(key);
1508             } else {
1509                 const auto gossipKey = recipient->gossipKey();
1510                 if (!gossipKey.isNull() && ValidEncryptionKey(gossipKey)) {
1511                     qCDebug(MESSAGECOMPOSER_LOG) << "Found an valid autocrypt gossip key.";
1512                     matchingKeys.push_back(gossipKey);
1513                 }
1514             }
1515         }
1516         // Accept any autocrypt key, as the validility is not used in Autocrypt.
1517         if (matchingKeys.size() == 1) {
1518             if (recipient->prefer_encrypt()) {
1519                 d->mContactPreferencesMap[address].encryptionPreference = AlwaysEncryptIfPossible;
1520             }
1521             d->mAutocryptMap[matchingKeys[0].primaryFingerprint()] = address;
1522             return matchingKeys;
1523         }
1524     }
1525 
1526     // if called with quite == true (from EncryptionPreferenceCounter), we only want to
1527     // check if there are keys for this recipients, not (yet) their validity, so
1528     // don't show the untrusted encryption key warning in that case
1529     bool canceled = false;
1530     if (!quiet) {
1531         matchingKeys = trustedOrConfirmed(matchingKeys, address, canceled);
1532     }
1533     if (canceled) {
1534         return {};
1535     }
1536     if (quiet || matchingKeys.size() == 1) {
1537         return matchingKeys;
1538     }
1539 
1540     // no match until now, or more than one key matches; let the user
1541     // choose the key(s)
1542     // FIXME: let user get the key from keyserver
1543     return trustedOrConfirmed(selectKeys(person,
1544                                          matchingKeys.empty() ? i18nc("if in your language something like "
1545                                                                       "'certificate(s)' is not possible please "
1546                                                                       "use the plural in the translation",
1547                                                                       "<qt>No valid and trusted encryption certificate was "
1548                                                                       "found for \"%1\".<br/><br/>"
1549                                                                       "Select the certificate(s) which should "
1550                                                                       "be used for this recipient. If there is no suitable certificate in the list "
1551                                                                       "you can also search for external certificates by clicking the button: "
1552                                                                       "search for external certificates.</qt>",
1553                                                                       person.toHtmlEscaped())
1554                                                               : i18nc("if in your language something like "
1555                                                                       "'certificate(s)' is not possible please "
1556                                                                       "use the plural in the translation",
1557                                                                       "More than one certificate matches \"%1\".\n\n"
1558                                                                       "Select the certificate(s) which should "
1559                                                                       "be used for this recipient.",
1560                                                                       person.toHtmlEscaped()),
1561                                          matchingKeys),
1562                               address,
1563                               canceled);
1564     // we can ignore 'canceled' here, since trustedOrConfirmed() returns
1565     // an empty vector when canceled == true, and we'd just do the same
1566 }
1567 
1568 std::vector<GpgME::Key> Kleo::KeyResolver::lookup(const QStringList &patterns, bool secret) const
1569 {
1570     if (patterns.empty()) {
1571         return {};
1572     }
1573     qCDebug(MESSAGECOMPOSER_LOG) << "( \"" << patterns.join(QLatin1StringView("\", \"")) << "\"," << secret << ")";
1574     std::vector<GpgME::Key> result;
1575     if (mCryptoMessageFormats & (InlineOpenPGPFormat | OpenPGPMIMEFormat)) {
1576         if (const QGpgME::Protocol *p = QGpgME::openpgp()) {
1577             std::unique_ptr<QGpgME::KeyListJob> job(p->keyListJob(false, false, true)); // use validating keylisting
1578             if (job.get()) {
1579                 std::vector<GpgME::Key> keys;
1580                 job->exec(patterns, secret, keys);
1581                 result.insert(result.end(), keys.begin(), keys.end());
1582             }
1583         }
1584     }
1585     if (mCryptoMessageFormats & (SMIMEFormat | SMIMEOpaqueFormat)) {
1586         if (const QGpgME::Protocol *p = QGpgME::smime()) {
1587             std::unique_ptr<QGpgME::KeyListJob> job(p->keyListJob(false, false, true)); // use validating keylisting
1588             if (job.get()) {
1589                 std::vector<GpgME::Key> keys;
1590                 job->exec(patterns, secret, keys);
1591                 result.insert(result.end(), keys.begin(), keys.end());
1592             }
1593         }
1594     }
1595     qCDebug(MESSAGECOMPOSER_LOG) << " returned" << result.size() << "keys";
1596     return result;
1597 }
1598 
1599 void Kleo::KeyResolver::addKeys(const std::vector<Item> &items, CryptoMessageFormat f)
1600 {
1601     dump();
1602     for (auto it = items.begin(); it != items.end(); ++it) {
1603         SplitInfo si(QStringList(it->address));
1604         std::remove_copy_if(it->keys.begin(), it->keys.end(), std::back_inserter(si.keys), IsNotForFormat(f));
1605         dump();
1606         if (si.keys.empty()) {
1607             qCWarning(MESSAGECOMPOSER_LOG) << "Kleo::KeyResolver::addKeys(): Fix EncryptionFormatPreferenceCounter."
1608                                            << "It detected a common format, but the list of such keys for recipient \"" << it->address << "\" is empty!";
1609         }
1610         d->mFormatInfoMap[f].splitInfos.push_back(si);
1611     }
1612     dump();
1613 }
1614 
1615 void Kleo::KeyResolver::addKeys(const std::vector<Item> &items)
1616 {
1617     dump();
1618     for (auto it = items.begin(); it != items.end(); ++it) {
1619         SplitInfo si(QStringList(it->address));
1620         CryptoMessageFormat f = AutoFormat;
1621         for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1622             const CryptoMessageFormat fmt = concreteCryptoMessageFormats[i];
1623             if ((fmt & it->format) && std::any_of(it->keys.begin(), it->keys.end(), IsForFormat(fmt))) {
1624                 f = fmt;
1625                 break;
1626             }
1627         }
1628         if (f == AutoFormat) {
1629             qCWarning(MESSAGECOMPOSER_LOG) << "Something went wrong. Didn't find a format for \"" << it->address << "\"";
1630         } else {
1631             std::remove_copy_if(it->keys.begin(), it->keys.end(), std::back_inserter(si.keys), IsNotForFormat(f));
1632         }
1633         d->mFormatInfoMap[f].splitInfos.push_back(si);
1634     }
1635     dump();
1636 }
1637 
1638 MessageComposer::ContactPreference Kleo::KeyResolver::lookupContactPreferences(const QString &address) const
1639 {
1640     const auto it = d->mContactPreferencesMap.find(address);
1641     if (it != d->mContactPreferencesMap.end()) {
1642         return it->second;
1643     }
1644 
1645     MessageComposer::ContactPreference pref;
1646 
1647     if (!d->mAkonadiLookupEnabled) {
1648         return pref;
1649     }
1650 
1651     auto job = new Akonadi::ContactSearchJob();
1652     job->setLimit(1);
1653     job->setQuery(Akonadi::ContactSearchJob::Email, address);
1654     job->exec();
1655 
1656     const KContacts::Addressee::List res = job->contacts();
1657     if (!res.isEmpty()) {
1658         KContacts::Addressee addr = res.at(0);
1659         pref.fillFromAddressee(addr);
1660     }
1661 
1662     const_cast<KeyResolver *>(this)->setContactPreferences(address, pref);
1663 
1664     return pref;
1665 }
1666 
1667 void Kleo::KeyResolver::setContactPreferences(const QString &address, const MessageComposer::ContactPreference &pref)
1668 {
1669     d->mContactPreferencesMap.insert(std::make_pair(address, pref));
1670 }
1671 
1672 void Kleo::KeyResolver::saveContactPreference(const QString &email, const MessageComposer::ContactPreference &pref) const
1673 {
1674     d->mContactPreferencesMap.insert(std::make_pair(email, pref));
1675     auto saveContactPreferencesJob = new MessageComposer::SaveContactPreferenceJob(email, pref);
1676     saveContactPreferencesJob->start();
1677 }
1678 
1679 QStringList Kleo::KeyResolver::keysForAddress(const QString &address) const
1680 {
1681     if (address.isEmpty()) {
1682         return {};
1683     }
1684     const QString addr = canonicalAddress(address).toLower();
1685     const auto pref = lookupContactPreferences(addr);
1686     return pref.pgpKeyFingerprints + pref.smimeCertFingerprints;
1687 }
1688 
1689 void Kleo::KeyResolver::setKeysForAddress(const QString &address, const QStringList &pgpKeyFingerprints, const QStringList &smimeCertFingerprints) const
1690 {
1691     if (address.isEmpty()) {
1692         return;
1693     }
1694     const QString addr = canonicalAddress(address).toLower();
1695     auto pref = lookupContactPreferences(addr);
1696     pref.pgpKeyFingerprints = pgpKeyFingerprints;
1697     pref.smimeCertFingerprints = smimeCertFingerprints;
1698     saveContactPreference(addr, pref);
1699 }
1700 
1701 bool Kleo::KeyResolver::encryptToSelf() const
1702 {
1703     return mEncryptToSelf;
1704 }
1705 
1706 bool Kleo::KeyResolver::showApprovalDialog() const
1707 {
1708     return mShowApprovalDialog;
1709 }