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"