File indexing completed on 2024-06-16 04:55:58

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     crypto/recipient.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "recipient.h"
0013 
0014 #include <Libkleo/KeyCache>
0015 #include <Libkleo/Predicates>
0016 #include <Libkleo/Stl_Util>
0017 
0018 #include <utils/cached.h>
0019 #include <utils/kleo_assert.h>
0020 
0021 #include <KMime/Types>
0022 
0023 #include <gpgme++/key.h>
0024 
0025 using namespace Kleo;
0026 using namespace Kleo::Crypto;
0027 using namespace KMime::Types;
0028 using namespace GpgME;
0029 
0030 namespace KMime
0031 {
0032 namespace Types
0033 {
0034 static bool operator==(const AddrSpec &lhs, const AddrSpec &rhs)
0035 {
0036     return lhs.localPart == rhs.localPart && lhs.domain == rhs.domain;
0037 }
0038 
0039 static bool operator==(const Mailbox &lhs, const Mailbox &rhs)
0040 {
0041     return lhs.name() == rhs.name() && lhs.addrSpec() == rhs.addrSpec();
0042 }
0043 
0044 static bool determine_ambiguous(const Mailbox &mb, const std::vector<Key> &keys)
0045 {
0046     Q_UNUSED(mb)
0047     // ### really do check when we don't only show matching keys
0048     return keys.size() != 1;
0049 }
0050 
0051 } // namespace Types
0052 } // namespace KMime
0053 
0054 class Recipient::Private
0055 {
0056     friend class ::Kleo::Crypto::Recipient;
0057 
0058 public:
0059     explicit Private(const Mailbox &mb)
0060         : mailbox(mb)
0061     {
0062         // ### also fill up to a certain number of keys with those
0063         // ### that don't match, for the case where there's a low
0064         // ### total number of keys
0065         const std::vector<Key> encrypt = KeyCache::instance()->findEncryptionKeysByMailbox(mb.addrSpec().asString());
0066         kdtools::separate_if(encrypt.cbegin(),
0067                              encrypt.cend(),
0068                              std::back_inserter(pgpEncryptionKeys),
0069                              std::back_inserter(cmsEncryptionKeys),
0070                              [](const Key &key) {
0071                                  return key.protocol() == OpenPGP;
0072                              });
0073     }
0074 
0075 private:
0076     const Mailbox mailbox;
0077     std::vector<Key> pgpEncryptionKeys, cmsEncryptionKeys;
0078     Key cmsEncryptionKey;
0079     UserID pgpEncryptionUid;
0080     cached<bool> encryptionAmbiguous[2];
0081 };
0082 
0083 Recipient::Recipient(const Mailbox &mb)
0084     : d(new Private(mb))
0085 {
0086 }
0087 
0088 void Recipient::detach()
0089 {
0090     if (d && !d.unique()) {
0091         d.reset(new Private(*d));
0092     }
0093 }
0094 
0095 bool Recipient::deepEquals(const Recipient &other) const
0096 {
0097     static const _detail::ByFingerprint<std::equal_to> compare = {};
0098     return mailbox() == other.mailbox() //
0099         && compare(d->cmsEncryptionKey, other.d->cmsEncryptionKey) //
0100         && compare(d->pgpEncryptionUid.parent(), other.d->pgpEncryptionUid.parent()) //
0101         && strcmp(d->pgpEncryptionUid.id(), other.d->pgpEncryptionUid.id()) //
0102         && std::equal(d->pgpEncryptionKeys.cbegin(), d->pgpEncryptionKeys.cend(), other.d->pgpEncryptionKeys.cbegin(), compare)
0103         && std::equal(d->cmsEncryptionKeys.cbegin(), d->pgpEncryptionKeys.cend(), other.d->cmsEncryptionKeys.cbegin(), compare);
0104 }
0105 
0106 bool Recipient::isEncryptionAmbiguous(GpgME::Protocol proto) const
0107 {
0108     if (d->encryptionAmbiguous[proto].dirty()) {
0109         d->encryptionAmbiguous[proto] = determine_ambiguous(d->mailbox, encryptionCertificateCandidates(proto));
0110     }
0111     return d->encryptionAmbiguous[proto];
0112 }
0113 
0114 const Mailbox &Recipient::mailbox() const
0115 {
0116     return d->mailbox;
0117 }
0118 
0119 const std::vector<Key> &Recipient::encryptionCertificateCandidates(GpgME::Protocol proto) const
0120 {
0121     if (proto == OpenPGP) {
0122         return d->pgpEncryptionKeys;
0123     }
0124     if (proto == CMS) {
0125         return d->cmsEncryptionKeys;
0126     }
0127     kleo_assert_fail(proto == OpenPGP || proto == CMS);
0128 #if 0
0129     return
0130         proto == OpenPGP ? d->pgpEncryptionKeys :
0131         proto == CMS     ? d->cmsEncryptionKeys :
0132         // even though gcc warns about this line, it's completely ok, promise:
0133         kleo_assert_fail(proto == OpenPGP || proto == CMS);
0134 #endif
0135 }
0136 
0137 void Recipient::setResolvedEncryptionKey(const Key &key)
0138 {
0139     if (key.isNull()) {
0140         return;
0141     }
0142     const Protocol proto = key.protocol();
0143     kleo_assert(proto == OpenPGP || proto == CMS);
0144     detach();
0145     if (proto == OpenPGP) {
0146         d->pgpEncryptionUid = key.userID(0);
0147     } else {
0148         d->cmsEncryptionKey = key;
0149     }
0150     d->encryptionAmbiguous[proto] = false;
0151 }
0152 
0153 Key Recipient::resolvedEncryptionKey(GpgME::Protocol proto) const
0154 {
0155     kleo_assert(proto == OpenPGP || proto == CMS);
0156     if (proto == OpenPGP) {
0157         return d->pgpEncryptionUid.parent();
0158     } else {
0159         return d->cmsEncryptionKey;
0160     }
0161 }
0162 
0163 void Recipient::setResolvedOpenPGPEncryptionUserID(const UserID &uid)
0164 {
0165     if (uid.isNull()) {
0166         return;
0167     }
0168     detach();
0169     d->pgpEncryptionUid = uid;
0170 }
0171 
0172 UserID Recipient::resolvedOpenPGPEncryptionUserID() const
0173 {
0174     return d->pgpEncryptionUid;
0175 }