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 }