File indexing completed on 2024-06-09 05:17:18
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 SPDX-FileCopyrightText: 2018 Intevation GmbH 0007 SPDX-FileCopyrightText: 2021 g10 Code GmbH 0008 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0009 0010 Based on kpgp.cpp 0011 SPDX-FileCopyrightText: 2001, 2002 the KPGP authors 0012 See file libkdenetwork/AUTHORS.kpgp for details 0013 0014 SPDX-License-Identifier: GPL-2.0-or-later 0015 */ 0016 0017 #include <config-libkleo.h> 0018 0019 #include "keyresolver.h" 0020 0021 #include "keyresolvercore.h" 0022 0023 #include <libkleo/formatting.h> 0024 #include <libkleo/keycache.h> 0025 #include <libkleo/keygroup.h> 0026 #include <libkleo/newkeyapprovaldialog.h> 0027 0028 #include <libkleo_debug.h> 0029 0030 #include <gpgme++/key.h> 0031 0032 using namespace Kleo; 0033 using namespace GpgME; 0034 0035 class KeyResolver::Private 0036 { 0037 public: 0038 Private(KeyResolver *qq, bool enc, bool sig, Protocol fmt, bool allowMixed) 0039 : q(qq) 0040 , mCore(enc, sig, fmt) 0041 , mFormat(fmt) 0042 , mEncrypt(enc) 0043 , mSign(sig) 0044 , mAllowMixed(allowMixed) 0045 , mCache(KeyCache::instance()) 0046 , mDialogWindowFlags(Qt::WindowFlags()) 0047 , mPreferredProtocol(UnknownProtocol) 0048 { 0049 mCore.setAllowMixedProtocols(allowMixed); 0050 } 0051 0052 ~Private() = default; 0053 0054 KeyResolver::Solution expandUnresolvedGroups(KeyResolver::Solution solution); 0055 void showApprovalDialog(KeyResolverCore::Result result, QWidget *parent); 0056 void dialogAccepted(); 0057 0058 KeyResolver *const q; 0059 KeyResolverCore mCore; 0060 Solution mResult; 0061 0062 Protocol mFormat; 0063 bool mEncrypt; 0064 bool mSign; 0065 bool mAllowMixed; 0066 // The cache is needed as a member variable to avoid rebuilding 0067 // it between calls if we are the only user. 0068 std::shared_ptr<const KeyCache> mCache; 0069 std::unique_ptr<NewKeyApprovalDialog> mDialog; 0070 Qt::WindowFlags mDialogWindowFlags; 0071 Protocol mPreferredProtocol; 0072 }; 0073 0074 static bool lessThan(const Key &leftKey, const Key &rightKey) 0075 { 0076 // shouldn't happen, but still put null keys at the end 0077 if (leftKey.isNull()) { 0078 return false; 0079 } 0080 if (rightKey.isNull()) { 0081 return true; 0082 } 0083 0084 // first sort by the displayed name and/or email address 0085 const auto leftNameAndOrEmail = Formatting::nameAndEmailForSummaryLine(leftKey); 0086 const auto rightNameAndOrEmail = Formatting::nameAndEmailForSummaryLine(rightKey); 0087 const int cmp = QString::localeAwareCompare(leftNameAndOrEmail, rightNameAndOrEmail); 0088 if (cmp) { 0089 return cmp < 0; 0090 } 0091 0092 // sort certificates with identical name/email address by their fingerprints 0093 return strcmp(leftKey.primaryFingerprint(), rightKey.primaryFingerprint()) < 0; 0094 } 0095 0096 KeyResolver::Solution KeyResolver::Private::expandUnresolvedGroups(KeyResolver::Solution solution) 0097 { 0098 for (auto it = solution.encryptionKeys.begin(); it != solution.encryptionKeys.end(); ++it) { 0099 const auto &address = it.key(); 0100 if (!it.value().empty()) { 0101 continue; 0102 } 0103 const auto keyMatchingAddress = mCache->findBestByMailBox(address.toUtf8().constData(), solution.protocol, KeyCache::KeyUsage::Encrypt); 0104 if (!keyMatchingAddress.isNull()) { 0105 continue; 0106 } 0107 const auto groupMatchingAddress = mCache->findGroup(address, solution.protocol, KeyCache::KeyUsage::Encrypt); 0108 if (!groupMatchingAddress.isNull()) { 0109 qCDebug(LIBKLEO_LOG) << __func__ << "Expanding unresolved" << address << "with matching group"; 0110 const auto &groupKeys = groupMatchingAddress.keys(); 0111 std::vector<Key> keys; 0112 keys.reserve(groupKeys.size()); 0113 std::copy(groupKeys.begin(), groupKeys.end(), std::back_inserter(keys)); 0114 std::sort(keys.begin(), keys.end(), lessThan); 0115 it.value() = keys; 0116 } 0117 } 0118 0119 return solution; 0120 } 0121 0122 void KeyResolver::Private::showApprovalDialog(KeyResolverCore::Result result, QWidget *parent) 0123 { 0124 const auto preferredSolution = expandUnresolvedGroups(std::move(result.solution)); 0125 const auto alternativeSolution = expandUnresolvedGroups(std::move(result.alternative)); 0126 0127 const QString sender = mCore.normalizedSender(); 0128 mDialog = std::make_unique<NewKeyApprovalDialog>(mEncrypt, 0129 mSign, 0130 sender, 0131 std::move(preferredSolution), 0132 std::move(alternativeSolution), 0133 mAllowMixed, 0134 mFormat, 0135 parent, 0136 mDialogWindowFlags); 0137 connect(mDialog.get(), &QDialog::accepted, q, [this]() { 0138 dialogAccepted(); 0139 }); 0140 connect(mDialog.get(), &QDialog::rejected, q, [this]() { 0141 Q_EMIT q->keysResolved(false, false); 0142 }); 0143 mDialog->open(); 0144 } 0145 0146 void KeyResolver::Private::dialogAccepted() 0147 { 0148 mResult = mDialog->result(); 0149 Q_EMIT q->keysResolved(true, false); 0150 } 0151 0152 void KeyResolver::start(bool showApproval, QWidget *parentWidget) 0153 { 0154 qCDebug(LIBKLEO_LOG) << "Starting "; 0155 if (!d->mSign && !d->mEncrypt) { 0156 // nothing to do 0157 return Q_EMIT keysResolved(true, true); 0158 } 0159 const auto result = d->mCore.resolve(); 0160 const bool success = (result.flags & KeyResolverCore::AllResolved); 0161 if (success && !showApproval) { 0162 d->mResult = std::move(result.solution); 0163 Q_EMIT keysResolved(true, false); 0164 return; 0165 } else if (success) { 0166 qCDebug(LIBKLEO_LOG) << "No need for the user showing approval anyway."; 0167 } 0168 0169 d->showApprovalDialog(std::move(result), parentWidget); 0170 } 0171 0172 KeyResolver::KeyResolver(bool encrypt, bool sign, Protocol fmt, bool allowMixed) 0173 : d(new Private(this, encrypt, sign, fmt, allowMixed)) 0174 { 0175 } 0176 0177 Kleo::KeyResolver::~KeyResolver() = default; 0178 0179 void KeyResolver::setRecipients(const QStringList &addresses) 0180 { 0181 d->mCore.setRecipients(addresses); 0182 } 0183 0184 void KeyResolver::setSender(const QString &address) 0185 { 0186 d->mCore.setSender(address); 0187 } 0188 0189 void KeyResolver::setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides) 0190 { 0191 d->mCore.setOverrideKeys(overrides); 0192 } 0193 0194 void KeyResolver::setSigningKeys(const QStringList &fingerprints) 0195 { 0196 d->mCore.setSigningKeys(fingerprints); 0197 } 0198 0199 KeyResolver::Solution KeyResolver::result() const 0200 { 0201 return d->mResult; 0202 } 0203 0204 void KeyResolver::setDialogWindowFlags(Qt::WindowFlags flags) 0205 { 0206 d->mDialogWindowFlags = flags; 0207 } 0208 0209 void KeyResolver::setPreferredProtocol(Protocol proto) 0210 { 0211 d->mCore.setPreferredProtocol(proto); 0212 } 0213 0214 void KeyResolver::setMinimumValidity(int validity) 0215 { 0216 d->mCore.setMinimumValidity(validity); 0217 } 0218 0219 #include "moc_keyresolver.cpp"