Messagelib

keyresolver.cpp
1 /* -*- c++ -*-
2  keyresolver.cpp
3 
4  This file is part of libkleopatra, the KDE keymanagement library
5  SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
6 
7  Based on kpgp.cpp
8  Copyright (C) 2001,2002 the KPGP authors
9  See file libkdenetwork/AUTHORS.kpgp for details
10 
11  SPDX-License-Identifier: GPL-2.0-or-later
12 */
13 
14 #include "composer/keyresolver.h"
15 #include "composer/nearexpirychecker.h"
16 #include "contactpreference/savecontactpreferencejob.h"
17 
18 #include "utils/kleo_util.h"
19 #include <KCursorSaver>
20 
21 #include <KEmailAddress>
22 
23 #include <Libkleo/Algorithm>
24 #include <Libkleo/Compliance>
25 #include <Libkleo/Dn>
26 #include <Libkleo/Formatting>
27 #include <Libkleo/KeySelectionDialog>
28 
29 #include <QGpgME/KeyListJob>
30 #include <QGpgME/Protocol>
31 
32 #include <gpgme++/key.h>
33 #include <gpgme++/keylistresult.h>
34 
35 #include "messagecomposer_debug.h"
36 #include <Akonadi/ContactSearchJob>
37 #include <KLocalizedString>
38 #include <KMessageBox>
39 
40 #include <MessageCore/AutocryptRecipient>
41 #include <MessageCore/AutocryptStorage>
42 
43 #include <QPointer>
44 
45 #include <algorithm>
46 #include <cassert>
47 #include <ctime>
48 #include <functional>
49 #include <iostream>
50 #include <iterator>
51 #include <map>
52 #include <memory>
53 #include <set>
54 
55 //
56 // some predicates to be used in STL algorithms:
57 //
58 
59 static inline bool EmptyKeyList(const Kleo::KeyApprovalDialog::Item &item)
60 {
61  return item.keys.empty();
62 }
63 
64 static inline QString ItemDotAddress(const Kleo::KeyResolver::Item &item)
65 {
66  return item.address;
67 }
68 
69 static bool keyIsCompliant(const GpgME::Key &key)
70 {
71  return (key.keyListMode() & GpgME::Validate) //
72  && Kleo::Formatting::uidsHaveFullValidity(key) //
73  && Kleo::Formatting::isKeyDeVs(key);
74 }
75 
76 static inline bool ApprovalNeeded(const Kleo::KeyResolver::Item &item)
77 {
78  bool approvalNeeded = item.pref == Kleo::NeverEncrypt || item.keys.empty();
79  if (!approvalNeeded && Kleo::DeVSCompliance::isCompliant()) {
80  approvalNeeded = !Kleo::all_of(item.keys, &keyIsCompliant);
81  }
82  return approvalNeeded;
83 }
84 
85 static inline Kleo::KeyResolver::Item CopyKeysAndEncryptionPreferences(const Kleo::KeyResolver::Item &oldItem, const Kleo::KeyApprovalDialog::Item &newItem)
86 {
87  return Kleo::KeyResolver::Item(oldItem.address, newItem.keys, newItem.pref, oldItem.signPref, oldItem.format);
88 }
89 
90 static bool ValidOpenPGPEncryptionKey(const GpgME::Key &key)
91 {
92  if (key.protocol() != GpgME::OpenPGP) {
93  return false;
94  }
95  if (key.isRevoked()) {
96  qCWarning(MESSAGECOMPOSER_LOG) << "is revoked";
97  }
98  if (key.isExpired()) {
99  qCWarning(MESSAGECOMPOSER_LOG) << "is expired";
100  }
101  if (key.isDisabled()) {
102  qCWarning(MESSAGECOMPOSER_LOG) << "is disabled";
103  }
104  if (!key.canEncrypt()) {
105  qCWarning(MESSAGECOMPOSER_LOG) << "can't encrypt";
106  }
107  if (key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt()) {
108  return false;
109  }
110  return true;
111 }
112 
113 static bool ValidTrustedOpenPGPEncryptionKey(const GpgME::Key &key)
114 {
115  if (!ValidOpenPGPEncryptionKey(key)) {
116  return false;
117  }
118  const std::vector<GpgME::UserID> uids = key.userIDs();
119  auto end(uids.end());
120  for (auto it = uids.begin(); it != end; ++it) {
121  if (!it->isRevoked() && it->validity() >= GpgME::UserID::Marginal) {
122  return true;
123  } else if (it->isRevoked()) {
124  qCWarning(MESSAGECOMPOSER_LOG) << "a userid is revoked";
125  } else {
126  qCWarning(MESSAGECOMPOSER_LOG) << "bad validity" << int(it->validity());
127  }
128  }
129  return false;
130 }
131 
132 static bool ValidSMIMEEncryptionKey(const GpgME::Key &key)
133 {
134  if (key.protocol() != GpgME::CMS) {
135  return false;
136  }
137  if (key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt()) {
138  return false;
139  }
140  return true;
141 }
142 
143 static bool ValidTrustedSMIMEEncryptionKey(const GpgME::Key &key)
144 {
145  if (!ValidSMIMEEncryptionKey(key)) {
146  return false;
147  }
148  return true;
149 }
150 
151 static inline bool ValidTrustedEncryptionKey(const GpgME::Key &key)
152 {
153  switch (key.protocol()) {
154  case GpgME::OpenPGP:
155  return ValidTrustedOpenPGPEncryptionKey(key);
156  case GpgME::CMS:
157  return ValidTrustedSMIMEEncryptionKey(key);
158  default:
159  return false;
160  }
161 }
162 
163 static inline bool ValidEncryptionKey(const GpgME::Key &key)
164 {
165  switch (key.protocol()) {
166  case GpgME::OpenPGP:
167  return ValidOpenPGPEncryptionKey(key);
168  case GpgME::CMS:
169  return ValidSMIMEEncryptionKey(key);
170  default:
171  return false;
172  }
173 }
174 
175 static inline bool ValidSigningKey(const GpgME::Key &key)
176 {
177  if (key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canSign()) {
178  return false;
179  }
180  return key.hasSecret();
181 }
182 
183 static inline bool ValidOpenPGPSigningKey(const GpgME::Key &key)
184 {
185  return key.protocol() == GpgME::OpenPGP && ValidSigningKey(key);
186 }
187 
188 static inline bool ValidSMIMESigningKey(const GpgME::Key &key)
189 {
190  return key.protocol() == GpgME::CMS && ValidSigningKey(key);
191 }
192 
193 static inline bool NotValidTrustedOpenPGPEncryptionKey(const GpgME::Key &key)
194 {
195  return !ValidTrustedOpenPGPEncryptionKey(key);
196 }
197 
198 static inline bool NotValidTrustedSMIMEEncryptionKey(const GpgME::Key &key)
199 {
200  return !ValidTrustedSMIMEEncryptionKey(key);
201 }
202 
203 static inline bool NotValidTrustedEncryptionKey(const GpgME::Key &key)
204 {
205  return !ValidTrustedEncryptionKey(key);
206 }
207 
208 static inline bool NotValidEncryptionKey(const GpgME::Key &key)
209 {
210  return !ValidEncryptionKey(key);
211 }
212 
213 static inline bool NotValidOpenPGPSigningKey(const GpgME::Key &key)
214 {
215  return !ValidOpenPGPSigningKey(key);
216 }
217 
218 static inline bool NotValidSMIMESigningKey(const GpgME::Key &key)
219 {
220  return !ValidSMIMESigningKey(key);
221 }
222 
223 namespace
224 {
225 struct ByTrustScore {
226  static int score(const GpgME::UserID &uid)
227  {
228  return uid.isRevoked() || uid.isInvalid() ? -1 : uid.validity();
229  }
230 
231  bool operator()(const GpgME::UserID &lhs, const GpgME::UserID &rhs) const
232  {
233  return score(lhs) < score(rhs);
234  }
235 };
236 }
237 
238 static std::vector<GpgME::UserID> matchingUIDs(const std::vector<GpgME::UserID> &uids, const QString &address)
239 {
240  if (address.isEmpty()) {
241  return {};
242  }
243 
244  std::vector<GpgME::UserID> result;
245  result.reserve(uids.size());
246 
247  for (auto it = uids.begin(), end = uids.end(); it != end; ++it) {
248  // PENDING(marc) check DN for an EMAIL, too, in case of X.509 certs... :/
249  if (const char *email = it->email()) {
250  if (*email && QString::fromUtf8(email).simplified().toLower() == address) {
251  result.push_back(*it);
252  }
253  }
254  }
255  return result;
256 }
257 
258 static GpgME::UserID findBestMatchUID(const GpgME::Key &key, const QString &address)
259 {
260  const std::vector<GpgME::UserID> all = key.userIDs();
261  if (all.empty()) {
262  return {};
263  }
264  const std::vector<GpgME::UserID> matching = matchingUIDs(all, address.toLower());
265  const std::vector<GpgME::UserID> &v = matching.empty() ? all : matching;
266  return *std::max_element(v.begin(), v.end(), ByTrustScore());
267 }
268 
269 static QStringList keysAsStrings(const std::vector<GpgME::Key> &keys)
270 {
271  QStringList strings;
272  strings.reserve(keys.size());
273  for (auto it = keys.begin(); it != keys.end(); ++it) {
274  assert(!(*it).userID(0).isNull());
275  const auto userID = (*it).userID(0);
276  QString keyLabel = QString::fromUtf8(userID.email());
277  if (keyLabel.isEmpty()) {
278  keyLabel = QString::fromUtf8(userID.name());
279  }
280  if (keyLabel.isEmpty()) {
281  keyLabel = QString::fromUtf8(userID.id());
282  }
283  strings.append(keyLabel);
284  }
285  return strings;
286 }
287 
288 static std::vector<GpgME::Key> trustedOrConfirmed(const std::vector<GpgME::Key> &keys, const QString &address, bool &canceled)
289 {
290  // PENDING(marc) work on UserIDs here?
291  std::vector<GpgME::Key> fishies;
292  std::vector<GpgME::Key> ickies;
293  std::vector<GpgME::Key> rewookies;
294  auto it = keys.begin();
295  const auto end = keys.end();
296  for (; it != end; ++it) {
297  const GpgME::Key &key = *it;
298  assert(ValidEncryptionKey(key));
299  const GpgME::UserID uid = findBestMatchUID(key, address);
300  if (uid.isRevoked()) {
301  rewookies.push_back(key);
302  }
303  if (!uid.isRevoked() && uid.validity() == GpgME::UserID::Marginal) {
304  fishies.push_back(key);
305  }
306  if (!uid.isRevoked() && uid.validity() < GpgME::UserID::Never) {
307  ickies.push_back(key);
308  }
309  }
310 
311  if (fishies.empty() && ickies.empty() && rewookies.empty()) {
312  return keys;
313  }
314 
315  // if some keys are not fully trusted, let the user confirm their use
316  QString msg = address.isEmpty() ? i18n(
317  "One or more of your configured OpenPGP encryption "
318  "keys or S/MIME certificates is not fully trusted "
319  "for encryption.")
320  : i18n(
321  "One or more of the OpenPGP encryption keys or S/MIME "
322  "certificates for recipient \"%1\" is not fully trusted "
323  "for encryption.",
324  address);
325 
326  if (!fishies.empty()) {
327  // certificates can't have marginal trust
328  msg += i18n("\nThe following keys are only marginally trusted: \n");
329  msg += keysAsStrings(fishies).join(QLatin1Char(','));
330  }
331  if (!ickies.empty()) {
332  msg += i18n("\nThe following keys or certificates have unknown trust level: \n");
333  msg += keysAsStrings(ickies).join(QLatin1Char(','));
334  }
335  if (!rewookies.empty()) {
336  msg += i18n("\nThe following keys or certificates are <b>revoked</b>: \n");
337  msg += keysAsStrings(rewookies).join(QLatin1Char(','));
338  }
339 
341  msg,
342  i18n("Not Fully Trusted Encryption Keys"),
345  QStringLiteral("not fully trusted encryption key warning"))
346  == KMessageBox::Continue) {
347  return keys;
348  } else {
349  canceled = true;
350  }
351  return {};
352 }
353 
354 namespace
355 {
356 struct IsNotForFormat : public std::unary_function<GpgME::Key, bool> {
357  IsNotForFormat(Kleo::CryptoMessageFormat f)
358  : format(f)
359  {
360  }
361 
362  bool operator()(const GpgME::Key &key) const
363  {
364  return (isOpenPGP(format) && key.protocol() != GpgME::OpenPGP) || (isSMIME(format) && key.protocol() != GpgME::CMS);
365  }
366 
367  const Kleo::CryptoMessageFormat format;
368 };
369 
370 struct IsForFormat : std::unary_function<GpgME::Key, bool> {
371  explicit IsForFormat(Kleo::CryptoMessageFormat f)
372  : protocol(isOpenPGP(f) ? GpgME::OpenPGP
373  : isSMIME(f) ? GpgME::CMS
374  : GpgME::UnknownProtocol)
375  {
376  }
377 
378  bool operator()(const GpgME::Key &key) const
379  {
380  return key.protocol() == protocol;
381  }
382 
383  const GpgME::Protocol protocol;
384 };
385 }
386 
387 class Kleo::KeyResolver::SigningPreferenceCounter : public std::unary_function<Kleo::KeyResolver::Item, void>
388 {
389 public:
390  SigningPreferenceCounter() = default;
391 
392  void operator()(const Kleo::KeyResolver::Item &item);
393 #define make_int_accessor(x) \
394  unsigned int num##x() const \
395  { \
396  return m##x; \
397  }
398  make_int_accessor(UnknownSigningPreference) make_int_accessor(NeverSign) make_int_accessor(AlwaysSign) make_int_accessor(AlwaysSignIfPossible)
399  make_int_accessor(AlwaysAskForSigning) make_int_accessor(AskSigningWheneverPossible) make_int_accessor(Total)
400 #undef make_int_accessor
401  private : unsigned int mTotal = 0;
402  unsigned int mUnknownSigningPreference = 0;
403  unsigned int mNeverSign = 0;
404  unsigned int mAlwaysSign = 0;
405  unsigned int mAlwaysSignIfPossible = 0;
406  unsigned int mAlwaysAskForSigning = 0;
407  unsigned int mAskSigningWheneverPossible = 0;
408 };
409 
410 void Kleo::KeyResolver::SigningPreferenceCounter::operator()(const Kleo::KeyResolver::Item &item)
411 {
412  switch (item.signPref) {
413 #define CASE(x) \
414  case x: \
415  ++m##x; \
416  break
417  CASE(UnknownSigningPreference);
418  CASE(NeverSign);
419  CASE(AlwaysSign);
420  CASE(AlwaysSignIfPossible);
421  CASE(AlwaysAskForSigning);
422  CASE(AskSigningWheneverPossible);
423 #undef CASE
424  }
425  ++mTotal;
426 }
427 
428 class Kleo::KeyResolver::EncryptionPreferenceCounter : public std::unary_function<Item, void>
429 {
430  const Kleo::KeyResolver *_this;
431 
432 public:
433  EncryptionPreferenceCounter(const Kleo::KeyResolver *kr, EncryptionPreference defaultPreference)
434  : _this(kr)
435  , mDefaultPreference(defaultPreference)
436  {
437  }
438 
439  void operator()(Item &item);
440 
441  template<typename Container>
442  void process(Container &c)
443  {
444  *this = std::for_each(c.begin(), c.end(), *this);
445  }
446 
447 #define make_int_accessor(x) \
448  unsigned int num##x() const \
449  { \
450  return m##x; \
451  }
452  make_int_accessor(NoKey) make_int_accessor(NeverEncrypt) make_int_accessor(UnknownPreference) make_int_accessor(AlwaysEncrypt)
453  make_int_accessor(AlwaysEncryptIfPossible) make_int_accessor(AlwaysAskForEncryption) make_int_accessor(AskWheneverPossible) make_int_accessor(Total)
454 #undef make_int_accessor
455  private : EncryptionPreference mDefaultPreference;
456  unsigned int mTotal = 0;
457  unsigned int mNoKey = 0;
458  unsigned int mNeverEncrypt = 0;
459  unsigned int mUnknownPreference = 0;
460  unsigned int mAlwaysEncrypt = 0;
461  unsigned int mAlwaysEncryptIfPossible = 0;
462  unsigned int mAlwaysAskForEncryption = 0;
463  unsigned int mAskWheneverPossible = 0;
464 };
465 
466 void Kleo::KeyResolver::EncryptionPreferenceCounter::operator()(Item &item)
467 {
468  if (_this) {
469  if (item.needKeys) {
470  item.keys = _this->getEncryptionKeys(item.address, true);
471  }
472  if (item.keys.empty()) {
473  ++mNoKey;
474  return;
475  }
476  }
477  switch (!item.pref ? mDefaultPreference : item.pref) {
478 #define CASE(x) \
479  case Kleo::x: \
480  ++m##x; \
481  break
482  CASE(NeverEncrypt);
483  CASE(UnknownPreference);
484  CASE(AlwaysEncrypt);
485  CASE(AlwaysEncryptIfPossible);
486  CASE(AlwaysAskForEncryption);
487  CASE(AskWheneverPossible);
488 #undef CASE
489  }
490  ++mTotal;
491 }
492 
493 namespace
494 {
495 class FormatPreferenceCounterBase : public std::unary_function<Kleo::KeyResolver::Item, void>
496 {
497 public:
498  FormatPreferenceCounterBase() = default;
499 
500 #define make_int_accessor(x) \
501  unsigned int num##x() const \
502  { \
503  return m##x; \
504  }
505  make_int_accessor(Total) make_int_accessor(InlineOpenPGP) make_int_accessor(OpenPGPMIME) make_int_accessor(SMIME) make_int_accessor(SMIMEOpaque)
506 #undef make_int_accessor
507 
508  Q_REQUIRED_RESULT unsigned int numOf(Kleo::CryptoMessageFormat f) const
509  {
510  switch (f) {
511 #define CASE(x) \
512  case Kleo::x##Format: \
513  return m##x
514  CASE(InlineOpenPGP);
515  CASE(OpenPGPMIME);
516  CASE(SMIME);
517  CASE(SMIMEOpaque);
518 #undef CASE
519  default:
520  return 0;
521  }
522  }
523 
524 protected:
525  unsigned int mTotal = 0;
526  unsigned int mInlineOpenPGP = 0;
527  unsigned int mOpenPGPMIME = 0;
528  unsigned int mSMIME = 0;
529  unsigned int mSMIMEOpaque = 0;
530 };
531 
532 class EncryptionFormatPreferenceCounter : public FormatPreferenceCounterBase
533 {
534 public:
535  EncryptionFormatPreferenceCounter()
536  : FormatPreferenceCounterBase()
537  {
538  }
539 
540  void operator()(const Kleo::KeyResolver::Item &item);
541 };
542 
543 class SigningFormatPreferenceCounter : public FormatPreferenceCounterBase
544 {
545 public:
546  SigningFormatPreferenceCounter()
547  : FormatPreferenceCounterBase()
548  {
549  }
550 
551  void operator()(const Kleo::KeyResolver::Item &item);
552 };
553 
554 #define CASE(x) \
555  if (item.format & Kleo::x##Format) { \
556  ++m##x; \
557  }
558 void EncryptionFormatPreferenceCounter::operator()(const Kleo::KeyResolver::Item &item)
559 {
560  if (item.format & (Kleo::InlineOpenPGPFormat | Kleo::OpenPGPMIMEFormat)
561  && std::any_of(item.keys.begin(),
562  item.keys.end(),
563  ValidTrustedOpenPGPEncryptionKey)) { // -= trusted?
564  CASE(OpenPGPMIME);
565  CASE(InlineOpenPGP);
566  }
567  if (item.format & (Kleo::SMIMEFormat | Kleo::SMIMEOpaqueFormat)
568  && std::any_of(item.keys.begin(),
569  item.keys.end(),
570  ValidTrustedSMIMEEncryptionKey)) { // -= trusted?
571  CASE(SMIME);
572  CASE(SMIMEOpaque);
573  }
574  ++mTotal;
575 }
576 
577 void SigningFormatPreferenceCounter::operator()(const Kleo::KeyResolver::Item &item)
578 {
579  CASE(InlineOpenPGP);
580  CASE(OpenPGPMIME);
581  CASE(SMIME);
582  CASE(SMIMEOpaque);
583  ++mTotal;
584 }
585 
586 #undef CASE
587 } // anon namespace
588 
589 static QString canonicalAddress(const QString &_address)
590 {
592  if (!address.contains(QLatin1Char('@'))) {
593  // local address
594  // return address + '@' + KNetwork::KResolver::localHostName();
595  return address + QLatin1String("@localdomain");
596  } else {
597  return address;
598  }
599 }
600 
601 struct FormatInfo {
602  std::vector<Kleo::KeyResolver::SplitInfo> splitInfos;
603  std::vector<GpgME::Key> signKeys;
604 };
605 
606 struct Q_DECL_HIDDEN Kleo::KeyResolver::KeyResolverPrivate {
607  bool mAkonadiLookupEnabled = true;
608  bool mAutocryptEnabled = false;
609  std::set<QByteArray> alreadyWarnedFingerprints;
610 
611  std::vector<GpgME::Key> mOpenPGPSigningKeys; // signing
612  std::vector<GpgME::Key> mSMIMESigningKeys; // signing
613 
614  std::vector<GpgME::Key> mOpenPGPEncryptToSelfKeys; // encryption to self
615  std::vector<GpgME::Key> mSMIMEEncryptToSelfKeys; // encryption to self
616 
617  std::vector<Item> mPrimaryEncryptionKeys; // encryption to To/CC
618  std::vector<Item> mSecondaryEncryptionKeys; // encryption to BCC
619 
620  std::map<CryptoMessageFormat, FormatInfo> mFormatInfoMap;
621 
622  // key=email address, value=crypto preferences for this contact (from kabc)
623  using ContactPreferencesMap = std::map<QString, MessageComposer::ContactPreference>;
624  ContactPreferencesMap mContactPreferencesMap;
625  std::map<QByteArray, QString> mAutocryptMap;
626  MessageComposer::NearExpiryChecker::Ptr nearExpiryChecker;
627 };
628 
629 Kleo::KeyResolver::KeyResolver(bool encToSelf,
630  bool showApproval,
631  bool oppEncryption,
632  unsigned int f,
633  const MessageComposer::NearExpiryCheckerPtr &nearExpiryChecker)
634  : d(new KeyResolverPrivate)
635  , mEncryptToSelf(encToSelf)
636  , mShowApprovalDialog(showApproval)
637  , mOpportunisticEncyption(oppEncryption)
638  , mCryptoMessageFormats(f)
639 {
640  d->nearExpiryChecker = nearExpiryChecker;
641 }
642 
643 Kleo::KeyResolver::~KeyResolver() = default;
644 
645 Kleo::Result Kleo::KeyResolver::setEncryptToSelfKeys(const QStringList &fingerprints)
646 {
647  if (!encryptToSelf()) {
648  return Kleo::Ok;
649  }
650 
651  std::vector<GpgME::Key> keys = lookup(fingerprints);
652  std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mOpenPGPEncryptToSelfKeys),
653  NotValidTrustedOpenPGPEncryptionKey); // -= trusted?
654  std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mSMIMEEncryptToSelfKeys),
655  NotValidTrustedSMIMEEncryptionKey); // -= trusted?
656 
657  if (d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size() < keys.size()) {
658  // too few keys remain...
659  const QString msg = i18n(
660  "One or more of your configured OpenPGP encryption "
661  "keys or S/MIME certificates is not usable for "
662  "encryption. Please reconfigure your encryption keys "
663  "and certificates for this identity in the identity "
664  "configuration dialog.\n"
665  "If you choose to continue, and the keys are needed "
666  "later on, you will be prompted to specify the keys "
667  "to use.");
668  return KMessageBox::warningContinueCancel(nullptr,
669  msg,
670  i18n("Unusable Encryption Keys"),
673  QStringLiteral("unusable own encryption key warning"))
674  == KMessageBox::Continue
675  ? Kleo::Ok
676  : Kleo::Canceled;
677  }
678 
679  // check for near-expiry:
680  std::vector<GpgME::Key>::const_iterator end(d->mOpenPGPEncryptToSelfKeys.end());
681 
682  for (auto it = d->mOpenPGPEncryptToSelfKeys.begin(); it != end; ++it) {
683  d->nearExpiryChecker->checkOwnKey(*it);
684  }
685  std::vector<GpgME::Key>::const_iterator end2(d->mSMIMEEncryptToSelfKeys.end());
686  for (auto it = d->mSMIMEEncryptToSelfKeys.begin(); it != end2; ++it) {
687  d->nearExpiryChecker->checkOwnKey(*it);
688  }
689 
690  return Kleo::Ok;
691 }
692 
693 Kleo::Result Kleo::KeyResolver::setSigningKeys(const QStringList &fingerprints)
694 {
695  std::vector<GpgME::Key> keys = lookup(fingerprints, true); // secret keys
696  std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mOpenPGPSigningKeys), NotValidOpenPGPSigningKey);
697  std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mSMIMESigningKeys), NotValidSMIMESigningKey);
698 
699  if (d->mOpenPGPSigningKeys.size() + d->mSMIMESigningKeys.size() < keys.size()) {
700  // too few keys remain...
701  const QString msg = i18n(
702  "One or more of your configured OpenPGP signing keys "
703  "or S/MIME signing certificates is not usable for "
704  "signing. Please reconfigure your signing keys "
705  "and certificates for this identity in the identity "
706  "configuration dialog.\n"
707  "If you choose to continue, and the keys are needed "
708  "later on, you will be prompted to specify the keys "
709  "to use.");
710  return KMessageBox::warningContinueCancel(nullptr,
711  msg,
712  i18n("Unusable Signing Keys"),
715  QStringLiteral("unusable signing key warning"))
716  == KMessageBox::Continue
717  ? Kleo::Ok
718  : Kleo::Canceled;
719  }
720 
721  // check for near expiry:
722 
723  for (auto it = d->mOpenPGPSigningKeys.begin(), total = d->mOpenPGPSigningKeys.end(); it != total; ++it) {
724  d->nearExpiryChecker->checkOwnSigningKey(*it);
725  }
726 
727  for (auto it = d->mSMIMESigningKeys.begin(), total = d->mSMIMESigningKeys.end(); it != total; ++it) {
728  d->nearExpiryChecker->checkOwnSigningKey(*it);
729  }
730 
731  return Kleo::Ok;
732 }
733 
735 {
736  d->mPrimaryEncryptionKeys = getEncryptionItems(addresses);
737 }
738 
740 {
741  d->mSecondaryEncryptionKeys = getEncryptionItems(addresses);
742 }
743 
744 std::vector<Kleo::KeyResolver::Item> Kleo::KeyResolver::getEncryptionItems(const QStringList &addresses)
745 {
746  std::vector<Item> items;
747  items.reserve(addresses.size());
748  QStringList::const_iterator end(addresses.constEnd());
749  for (QStringList::const_iterator it = addresses.constBegin(); it != end; ++it) {
750  QString addr = canonicalAddress(*it).toLower();
751  const auto pref = lookupContactPreferences(addr);
752 
753  items.emplace_back(*it, /*getEncryptionKeys( *it, true ),*/
754  pref.encryptionPreference,
755  pref.signingPreference,
756  pref.cryptoMessageFormat);
757  }
758  return items;
759 }
760 
761 static Kleo::Action action(bool doit, bool ask, bool donot, bool requested)
762 {
763  if (requested && !donot) {
764  return Kleo::DoIt;
765  }
766  if (doit && !ask && !donot) {
767  return Kleo::DoIt;
768  }
769  if (!doit && ask && !donot) {
770  return Kleo::Ask;
771  }
772  if (!doit && !ask && donot) {
773  return requested ? Kleo::Conflict : Kleo::DontDoIt;
774  }
775  if (!doit && !ask && !donot) {
776  return Kleo::DontDoIt;
777  }
778  return Kleo::Conflict;
779 }
780 
781 Kleo::Action Kleo::KeyResolver::checkSigningPreferences(bool signingRequested) const
782 {
783  if (signingRequested && d->mOpenPGPSigningKeys.empty() && d->mSMIMESigningKeys.empty()) {
784  return Impossible;
785  }
786 
787  SigningPreferenceCounter count;
788  count = std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count);
789  count = std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), count);
790 
791  unsigned int sign = count.numAlwaysSign();
792  unsigned int ask = count.numAlwaysAskForSigning();
793  const unsigned int dontSign = count.numNeverSign();
794  if (signingPossible()) {
795  sign += count.numAlwaysSignIfPossible();
796  ask += count.numAskSigningWheneverPossible();
797  }
798 
799  return action(sign, ask, dontSign, signingRequested);
800 }
801 
802 bool Kleo::KeyResolver::signingPossible() const
803 {
804  return !d->mOpenPGPSigningKeys.empty() || !d->mSMIMESigningKeys.empty();
805 }
806 
807 Kleo::Action Kleo::KeyResolver::checkEncryptionPreferences(bool encryptionRequested) const
808 {
809  if (d->mPrimaryEncryptionKeys.empty() && d->mSecondaryEncryptionKeys.empty()) {
810  return DontDoIt;
811  }
812 
813  if (encryptionRequested && encryptToSelf() && d->mOpenPGPEncryptToSelfKeys.empty() && d->mSMIMEEncryptToSelfKeys.empty()) {
814  return Impossible;
815  }
816 
817  if (!encryptionRequested && !mOpportunisticEncyption) {
818  // try to minimize crypto ops (including key lookups) by only
819  // looking up keys when at least one of the encryption
820  // preferences needs it:
821  EncryptionPreferenceCounter count(nullptr, UnknownPreference);
822  count.process(d->mPrimaryEncryptionKeys);
823  count.process(d->mSecondaryEncryptionKeys);
824  if (!count.numAlwaysEncrypt()
825  && !count.numAlwaysAskForEncryption() // this guy might not need a lookup, when declined, but it's too complex to implement that here
826  && !count.numAlwaysEncryptIfPossible() && !count.numAskWheneverPossible()) {
827  return DontDoIt;
828  }
829  }
830 
831  EncryptionPreferenceCounter count(this, mOpportunisticEncyption ? AskWheneverPossible : UnknownPreference);
832  count = std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count);
833  count = std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), count);
834 
835  unsigned int encrypt = count.numAlwaysEncrypt();
836  unsigned int ask = count.numAlwaysAskForEncryption();
837  const unsigned int dontEncrypt = count.numNeverEncrypt() + count.numNoKey();
838  if (encryptionPossible()) {
839  encrypt += count.numAlwaysEncryptIfPossible();
840  ask += count.numAskWheneverPossible();
841  }
842 
843  const Action act = action(encrypt, ask, dontEncrypt, encryptionRequested);
844  if (act != Ask
845  || std::for_each(
846  d->mPrimaryEncryptionKeys.begin(),
847  d->mPrimaryEncryptionKeys.end(),
848  std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), EncryptionPreferenceCounter(this, UnknownPreference)))
849  .numAlwaysAskForEncryption()) {
850  return act;
851  } else {
852  return AskOpportunistic;
853  }
854 }
855 
856 bool Kleo::KeyResolver::encryptionPossible() const
857 {
858  return std::none_of(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), EmptyKeyList)
859  && std::none_of(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), EmptyKeyList);
860 }
861 
862 Kleo::Result Kleo::KeyResolver::resolveAllKeys(bool &signingRequested, bool &encryptionRequested)
863 {
864  if (!encryptionRequested && !signingRequested) {
865  // make a dummy entry with all recipients, but no signing or
866  // encryption keys to avoid special-casing on the caller side:
867  dump();
868  d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.emplace_back(allRecipients());
869  dump();
870  return Kleo::Ok;
871  }
872  Kleo::Result result = Kleo::Ok;
873  if (encryptionRequested) {
874  bool finalySendUnencrypted = false;
875  result = resolveEncryptionKeys(signingRequested, finalySendUnencrypted);
876  if (finalySendUnencrypted) {
877  encryptionRequested = false;
878  }
879  }
880  if (result != Kleo::Ok) {
881  return result;
882  }
883  if (encryptionRequested) {
884  result = resolveSigningKeysForEncryption();
885  } else {
886  result = resolveSigningKeysForSigningOnly();
887  if (result == Kleo::Failure) {
888  signingRequested = false;
889  return Kleo::Ok;
890  }
891  }
892  return result;
893 }
894 
895 Kleo::Result Kleo::KeyResolver::resolveEncryptionKeys(bool signingRequested, bool &finalySendUnencrypted)
896 {
897  //
898  // 1. Get keys for all recipients:
899  //
900  qCDebug(MESSAGECOMPOSER_LOG) << "resolving enc keys" << d->mPrimaryEncryptionKeys.size();
901  for (auto it = d->mPrimaryEncryptionKeys.begin(); it != d->mPrimaryEncryptionKeys.end(); ++it) {
902  qCDebug(MESSAGECOMPOSER_LOG) << "checking primary:" << it->address;
903  if (!it->needKeys) {
904  continue;
905  }
906  it->keys = getEncryptionKeys(it->address, false);
907  qCDebug(MESSAGECOMPOSER_LOG) << "got # keys:" << it->keys.size();
908  if (it->keys.empty()) {
909  return Kleo::Canceled;
910  }
911  QString addr = canonicalAddress(it->address).toLower();
912  const auto pref = lookupContactPreferences(addr);
913  it->pref = pref.encryptionPreference;
914  it->signPref = pref.signingPreference;
915  it->format = pref.cryptoMessageFormat;
916  qCDebug(MESSAGECOMPOSER_LOG) << "set key data:" << int(it->pref) << int(it->signPref) << int(it->format);
917  }
918 
919  for (auto it = d->mSecondaryEncryptionKeys.begin(), total = d->mSecondaryEncryptionKeys.end(); it != total; ++it) {
920  if (!it->needKeys) {
921  continue;
922  }
923  it->keys = getEncryptionKeys(it->address, false);
924  if (it->keys.empty()) {
925  return Kleo::Canceled;
926  }
927  QString addr = canonicalAddress(it->address).toLower();
928  const auto pref = lookupContactPreferences(addr);
929  it->pref = pref.encryptionPreference;
930  it->signPref = pref.signingPreference;
931  it->format = pref.cryptoMessageFormat;
932  }
933 
934  // 1a: Present them to the user
935 
936  const Kleo::Result res = showKeyApprovalDialog(finalySendUnencrypted);
937  if (res != Kleo::Ok) {
938  return res;
939  }
940 
941  //
942  // 2. Check what the primary recipients need
943  //
944 
945  // 2a. Try to find a common format for all primary recipients,
946  // else use as many formats as needed
947 
948  const EncryptionFormatPreferenceCounter primaryCount =
949  std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), EncryptionFormatPreferenceCounter());
950 
951  CryptoMessageFormat commonFormat = AutoFormat;
952  for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
953  if (!(concreteCryptoMessageFormats[i] & mCryptoMessageFormats)) {
954  continue;
955  }
956  if (signingRequested && signingKeysFor(concreteCryptoMessageFormats[i]).empty()) {
957  continue;
958  }
959  if (encryptToSelf() && encryptToSelfKeysFor(concreteCryptoMessageFormats[i]).empty()) {
960  continue;
961  }
962  if (primaryCount.numOf(concreteCryptoMessageFormats[i]) == primaryCount.numTotal()) {
963  commonFormat = concreteCryptoMessageFormats[i];
964  break;
965  }
966  }
967  qCDebug(MESSAGECOMPOSER_LOG) << "got commonFormat for primary recipients:" << int(commonFormat);
968  if (commonFormat != AutoFormat) {
969  addKeys(d->mPrimaryEncryptionKeys, commonFormat);
970  } else {
971  addKeys(d->mPrimaryEncryptionKeys);
972  }
973 
974  collapseAllSplitInfos(); // these can be encrypted together
975 
976  // 2b. Just try to find _something_ for each secondary recipient,
977  // with a preference to a common format (if that exists)
978 
979  const EncryptionFormatPreferenceCounter secondaryCount =
980  std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), EncryptionFormatPreferenceCounter());
981 
982  if (commonFormat != AutoFormat && secondaryCount.numOf(commonFormat) == secondaryCount.numTotal()) {
983  addKeys(d->mSecondaryEncryptionKeys, commonFormat);
984  } else {
985  addKeys(d->mSecondaryEncryptionKeys);
986  }
987 
988  // 3. Check for expiry:
989 
990  for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
991  const std::vector<SplitInfo> si_list = encryptionItems(concreteCryptoMessageFormats[i]);
992  for (auto sit = si_list.begin(), total = si_list.end(); sit != total; ++sit) {
993  for (auto kit = sit->keys.begin(); kit != sit->keys.end(); ++kit) {
994  d->nearExpiryChecker->checkKey(*kit);
995  }
996  }
997  }
998 
999  // 4. Check that we have the right keys for encryptToSelf()
1000 
1001  if (!encryptToSelf()) {
1002  return Kleo::Ok;
1003  }
1004 
1005  // 4a. Check for OpenPGP keys
1006 
1007  qCDebug(MESSAGECOMPOSER_LOG) << "sizes of encryption items:" << encryptionItems(InlineOpenPGPFormat).size() << encryptionItems(OpenPGPMIMEFormat).size()
1008  << encryptionItems(SMIMEFormat).size() << encryptionItems(SMIMEOpaqueFormat).size();
1009  if (!encryptionItems(InlineOpenPGPFormat).empty() || !encryptionItems(OpenPGPMIMEFormat).empty()) {
1010  // need them
1011  if (d->mOpenPGPEncryptToSelfKeys.empty()) {
1012  const QString msg = i18n(
1013  "Examination of recipient's encryption preferences "
1014  "yielded that the message should be encrypted using "
1015  "OpenPGP, at least for some recipients;\n"
1016  "however, you have not configured valid trusted "
1017  "OpenPGP encryption keys for this identity.\n"
1018  "You may continue without encrypting to yourself, "
1019  "but be aware that you will not be able to read your "
1020  "own messages if you do so.");
1022  msg,
1023  i18n("Unusable Encryption Keys"),
1026  QStringLiteral("encrypt-to-self will fail warning"))
1027  == KMessageBox::Cancel) {
1028  return Kleo::Canceled;
1029  }
1030  // FIXME: Allow selection
1031  }
1032  addToAllSplitInfos(d->mOpenPGPEncryptToSelfKeys, InlineOpenPGPFormat | OpenPGPMIMEFormat);
1033  }
1034 
1035  // 4b. Check for S/MIME certs:
1036 
1037  if (!encryptionItems(SMIMEFormat).empty() || !encryptionItems(SMIMEOpaqueFormat).empty()) {
1038  // need them
1039  if (d->mSMIMEEncryptToSelfKeys.empty()) {
1040  // don't have one
1041  const QString msg = i18n(
1042  "Examination of recipient's encryption preferences "
1043  "yielded that the message should be encrypted using "
1044  "S/MIME, at least for some recipients;\n"
1045  "however, you have not configured valid "
1046  "S/MIME encryption certificates for this identity.\n"
1047  "You may continue without encrypting to yourself, "
1048  "but be aware that you will not be able to read your "
1049  "own messages if you do so.");
1051  msg,
1052  i18n("Unusable Encryption Keys"),
1055  QStringLiteral("encrypt-to-self will fail warning"))
1056  == KMessageBox::Cancel) {
1057  return Kleo::Canceled;
1058  }
1059  // FIXME: Allow selection
1060  }
1061  addToAllSplitInfos(d->mSMIMEEncryptToSelfKeys, SMIMEFormat | SMIMEOpaqueFormat);
1062  }
1063 
1064  // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
1065  // are missing.
1066 
1067  return Kleo::Ok;
1068 }
1069 
1070 Kleo::Result Kleo::KeyResolver::resolveSigningKeysForEncryption()
1071 {
1072  if ((!encryptionItems(InlineOpenPGPFormat).empty() || !encryptionItems(OpenPGPMIMEFormat).empty()) && d->mOpenPGPSigningKeys.empty()) {
1073  const QString msg = i18n(
1074  "Examination of recipient's signing preferences "
1075  "yielded that the message should be signed using "
1076  "OpenPGP, at least for some recipients;\n"
1077  "however, you have not configured valid "
1078  "OpenPGP signing certificates for this identity.");
1080  msg,
1081  i18n("Unusable Signing Keys"),
1082  KGuiItem(i18n("Do Not OpenPGP-Sign")),
1084  QStringLiteral("signing will fail warning"))
1085  == KMessageBox::Cancel) {
1086  return Kleo::Canceled;
1087  }
1088  // FIXME: Allow selection
1089  }
1090  if ((!encryptionItems(SMIMEFormat).empty() || !encryptionItems(SMIMEOpaqueFormat).empty()) && d->mSMIMESigningKeys.empty()) {
1091  const QString msg = i18n(
1092  "Examination of recipient's signing preferences "
1093  "yielded that the message should be signed using "
1094  "S/MIME, at least for some recipients;\n"
1095  "however, you have not configured valid "
1096  "S/MIME signing certificates for this identity.");
1098  msg,
1099  i18n("Unusable Signing Keys"),
1100  KGuiItem(i18n("Do Not S/MIME-Sign")),
1102  QStringLiteral("signing will fail warning"))
1103  == KMessageBox::Cancel) {
1104  return Kleo::Canceled;
1105  }
1106  // FIXME: Allow selection
1107  }
1108 
1109  // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
1110  // are missing.
1111 
1112  for (auto it = d->mFormatInfoMap.begin(); it != d->mFormatInfoMap.end(); ++it) {
1113  if (!it->second.splitInfos.empty()) {
1114  dump();
1115  it->second.signKeys = signingKeysFor(it->first);
1116  dump();
1117  }
1118  }
1119 
1120  return Kleo::Ok;
1121 }
1122 
1123 Kleo::Result Kleo::KeyResolver::resolveSigningKeysForSigningOnly()
1124 {
1125  //
1126  // we don't need to distinguish between primary and secondary
1127  // recipients here:
1128  //
1129  SigningFormatPreferenceCounter count;
1130  count = std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count);
1131  count = std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), count);
1132 
1133  // try to find a common format that works for all (and that we have signing keys for):
1134 
1135  CryptoMessageFormat commonFormat = AutoFormat;
1136 
1137  for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1138  const auto res = concreteCryptoMessageFormats[i];
1139  if (!(mCryptoMessageFormats & res)) {
1140  continue; // skip
1141  }
1142  if (signingKeysFor(res).empty()) {
1143  continue; // skip
1144  }
1145  if (count.numOf(res) == count.numTotal()) {
1146  commonFormat = res;
1147  break;
1148  }
1149  }
1150 
1151  if (commonFormat != AutoFormat) { // found
1152  dump();
1153  FormatInfo &fi = d->mFormatInfoMap[commonFormat];
1154  fi.signKeys = signingKeysFor(commonFormat);
1155  fi.splitInfos.resize(1);
1156  fi.splitInfos.front() = SplitInfo(allRecipients());
1157  dump();
1158  return Kleo::Ok;
1159  }
1160 
1161  const QString msg = i18n(
1162  "Examination of recipient's signing preferences "
1163  "showed no common type of signature matching your "
1164  "available signing keys.\n"
1165  "Send message without signing?");
1166  if (KMessageBox::warningContinueCancel(nullptr, msg, i18n("No signing possible"), KStandardGuiItem::cont()) == KMessageBox::Continue) {
1167  d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.emplace_back(allRecipients());
1168  return Kleo::Failure; // means "Ok, but without signing"
1169  }
1170  return Kleo::Canceled;
1171 }
1172 
1173 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeysFor(CryptoMessageFormat f) const
1174 {
1175  if (isOpenPGP(f)) {
1176  return d->mOpenPGPSigningKeys;
1177  }
1178  if (isSMIME(f)) {
1179  return d->mSMIMESigningKeys;
1180  }
1181  return {};
1182 }
1183 
1184 std::vector<GpgME::Key> Kleo::KeyResolver::encryptToSelfKeysFor(CryptoMessageFormat f) const
1185 {
1186  if (isOpenPGP(f)) {
1187  return d->mOpenPGPEncryptToSelfKeys;
1188  }
1189  if (isSMIME(f)) {
1190  return d->mSMIMEEncryptToSelfKeys;
1191  }
1192  return {};
1193 }
1194 
1195 QStringList Kleo::KeyResolver::allRecipients() const
1196 {
1197  QStringList result;
1198  std::transform(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), std::back_inserter(result), ItemDotAddress);
1199  std::transform(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), std::back_inserter(result), ItemDotAddress);
1200  return result;
1201 }
1202 
1203 void Kleo::KeyResolver::collapseAllSplitInfos()
1204 {
1205  dump();
1206  for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1207  auto pos = d->mFormatInfoMap.find(concreteCryptoMessageFormats[i]);
1208  if (pos == d->mFormatInfoMap.end()) {
1209  continue;
1210  }
1211  std::vector<SplitInfo> &v = pos->second.splitInfos;
1212  if (v.size() < 2) {
1213  continue;
1214  }
1215  SplitInfo &si = v.front();
1216  for (auto it = v.begin() + 1; it != v.end(); ++it) {
1217  si.keys.insert(si.keys.end(), it->keys.begin(), it->keys.end());
1218  std::copy(it->recipients.begin(), it->recipients.end(), std::back_inserter(si.recipients));
1219  }
1220  v.resize(1);
1221  }
1222  dump();
1223 }
1224 
1225 void Kleo::KeyResolver::addToAllSplitInfos(const std::vector<GpgME::Key> &keys, unsigned int f)
1226 {
1227  dump();
1228  if (!f || keys.empty()) {
1229  return;
1230  }
1231  for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1232  if (!(f & concreteCryptoMessageFormats[i])) {
1233  continue;
1234  }
1235  auto pos = d->mFormatInfoMap.find(concreteCryptoMessageFormats[i]);
1236  if (pos == d->mFormatInfoMap.end()) {
1237  continue;
1238  }
1239  std::vector<SplitInfo> &v = pos->second.splitInfos;
1240  for (auto it = v.begin(); it != v.end(); ++it) {
1241  it->keys.insert(it->keys.end(), keys.begin(), keys.end());
1242  }
1243  }
1244  dump();
1245 }
1246 
1247 void Kleo::KeyResolver::dump() const
1248 {
1249 #ifndef NDEBUG
1250  if (d->mFormatInfoMap.empty()) {
1251  qCDebug(MESSAGECOMPOSER_LOG) << "Keyresolver: Format info empty";
1252  }
1253  for (auto it = d->mFormatInfoMap.begin(); it != d->mFormatInfoMap.end(); ++it) {
1254  qCDebug(MESSAGECOMPOSER_LOG) << "Format info for " << Kleo::cryptoMessageFormatToString(it->first) << ": Signing keys: ";
1255  for (auto sit = it->second.signKeys.begin(); sit != it->second.signKeys.end(); ++sit) {
1256  qCDebug(MESSAGECOMPOSER_LOG) << " " << sit->shortKeyID() << " ";
1257  }
1258  unsigned int i = 0;
1259  for (auto sit = it->second.splitInfos.begin(), sitEnd = it->second.splitInfos.end(); sit != sitEnd; ++sit, ++i) {
1260  qCDebug(MESSAGECOMPOSER_LOG) << " SplitInfo #" << i << " encryption keys: ";
1261  for (auto kit = sit->keys.begin(), sitEnd = sit->keys.end(); kit != sitEnd; ++kit) {
1262  qCDebug(MESSAGECOMPOSER_LOG) << " " << kit->shortKeyID();
1263  }
1264  qCDebug(MESSAGECOMPOSER_LOG) << " SplitInfo #" << i << " recipients: " << qPrintable(sit->recipients.join(QLatin1String(", ")));
1265  }
1266  }
1267 #endif
1268 }
1269 
1270 Kleo::Result Kleo::KeyResolver::showKeyApprovalDialog(bool &finalySendUnencrypted)
1271 {
1272  const bool showKeysForApproval = showApprovalDialog() || std::any_of(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), ApprovalNeeded)
1273  || std::any_of(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), ApprovalNeeded);
1274 
1275  if (!showKeysForApproval) {
1276  return Kleo::Ok;
1277  }
1278 
1279  std::vector<Kleo::KeyApprovalDialog::Item> items;
1280  items.reserve(d->mPrimaryEncryptionKeys.size() + d->mSecondaryEncryptionKeys.size());
1281  std::copy(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), std::back_inserter(items));
1282  std::copy(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), std::back_inserter(items));
1283 
1284  std::vector<GpgME::Key> senderKeys;
1285  senderKeys.reserve(d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size());
1286  std::copy(d->mOpenPGPEncryptToSelfKeys.begin(), d->mOpenPGPEncryptToSelfKeys.end(), std::back_inserter(senderKeys));
1287  std::copy(d->mSMIMEEncryptToSelfKeys.begin(), d->mSMIMEEncryptToSelfKeys.end(), std::back_inserter(senderKeys));
1288 
1290 
1291  QPointer<Kleo::KeyApprovalDialog> dlg = new Kleo::KeyApprovalDialog(items, senderKeys);
1292 
1293  if (dlg->exec() == QDialog::Rejected) {
1294  delete dlg;
1295  return Kleo::Canceled;
1296  }
1297 
1298  items = dlg->items();
1299  senderKeys = dlg->senderKeys();
1300  const bool prefsChanged = dlg->preferencesChanged();
1301  delete dlg;
1302 
1303  if (prefsChanged) {
1304  for (uint i = 0, total = items.size(); i < total; ++i) {
1305  auto pref = lookupContactPreferences(items[i].address);
1306  pref.encryptionPreference = items[i].pref;
1307  pref.pgpKeyFingerprints.clear();
1308  pref.smimeCertFingerprints.clear();
1309  const std::vector<GpgME::Key> &keys = items[i].keys;
1310  for (auto it = keys.begin(), end = keys.end(); it != end; ++it) {
1311  if (it->protocol() == GpgME::OpenPGP) {
1312  if (const char *fpr = it->primaryFingerprint()) {
1313  pref.pgpKeyFingerprints.push_back(QLatin1String(fpr));
1314  }
1315  } else if (it->protocol() == GpgME::CMS) {
1316  if (const char *fpr = it->primaryFingerprint()) {
1317  pref.smimeCertFingerprints.push_back(QLatin1String(fpr));
1318  }
1319  }
1320  }
1321  saveContactPreference(items[i].address, pref);
1322  }
1323  }
1324 
1325  // show a warning if the user didn't select an encryption key for
1326  // herself:
1327  if (encryptToSelf() && senderKeys.empty()) {
1328  const QString msg = i18n(
1329  "You did not select an encryption key for yourself "
1330  "(encrypt to self). You will not be able to decrypt "
1331  "your own message if you encrypt it.");
1332  if (KMessageBox::warningContinueCancel(nullptr, msg, i18n("Missing Key Warning"), KGuiItem(i18n("&Encrypt"))) == KMessageBox::Cancel) {
1333  return Kleo::Canceled;
1334  } else {
1335  mEncryptToSelf = false;
1336  }
1337  }
1338 
1339  // count empty key ID lists
1340  const unsigned int emptyListCount = std::count_if(items.begin(), items.end(), EmptyKeyList);
1341 
1342  // show a warning if the user didn't select an encryption key for
1343  // some of the recipients
1344  if (items.size() == emptyListCount) {
1345  const QString msg = (d->mPrimaryEncryptionKeys.size() + d->mSecondaryEncryptionKeys.size() == 1)
1346  ? i18n(
1347  "You did not select an encryption key for the "
1348  "recipient of this message; therefore, the message "
1349  "will not be encrypted.")
1350  : i18n(
1351  "You did not select an encryption key for any of the "
1352  "recipients of this message; therefore, the message "
1353  "will not be encrypted.");
1354  if (KMessageBox::warningContinueCancel(nullptr, msg, i18n("Missing Key Warning"), KGuiItem(i18n("Send &Unencrypted"))) == KMessageBox::Cancel) {
1355  return Kleo::Canceled;
1356  }
1357  finalySendUnencrypted = true;
1358  } else if (emptyListCount > 0) {
1359  const QString msg = (emptyListCount == 1) ? i18n(
1360  "You did not select an encryption key for one of "
1361  "the recipients: this person will not be able to "
1362  "decrypt the message if you encrypt it.")
1363  : i18n(
1364  "You did not select encryption keys for some of "
1365  "the recipients: these persons will not be able to "
1366  "decrypt the message if you encrypt it.");
1368  if (KMessageBox::warningContinueCancel(nullptr, msg, i18n("Missing Key Warning"), KGuiItem(i18n("&Encrypt"))) == KMessageBox::Cancel) {
1369  return Kleo::Canceled;
1370  }
1371  }
1372 
1373  std::transform(d->mPrimaryEncryptionKeys.begin(),
1374  d->mPrimaryEncryptionKeys.end(),
1375  items.begin(),
1376  d->mPrimaryEncryptionKeys.begin(),
1377  CopyKeysAndEncryptionPreferences);
1378  std::transform(d->mSecondaryEncryptionKeys.begin(),
1379  d->mSecondaryEncryptionKeys.end(),
1380  items.begin() + d->mPrimaryEncryptionKeys.size(),
1381  d->mSecondaryEncryptionKeys.begin(),
1382  CopyKeysAndEncryptionPreferences);
1383 
1384  d->mOpenPGPEncryptToSelfKeys.clear();
1385  d->mSMIMEEncryptToSelfKeys.clear();
1386 
1387  std::remove_copy_if(senderKeys.begin(),
1388  senderKeys.end(),
1389  std::back_inserter(d->mOpenPGPEncryptToSelfKeys),
1390  NotValidTrustedOpenPGPEncryptionKey); // -= trusted (see above, too)?
1391  std::remove_copy_if(senderKeys.begin(),
1392  senderKeys.end(),
1393  std::back_inserter(d->mSMIMEEncryptToSelfKeys),
1394  NotValidTrustedSMIMEEncryptionKey); // -= trusted (see above, too)?
1395 
1396  return Kleo::Ok;
1397 }
1398 
1399 std::vector<Kleo::KeyResolver::SplitInfo> Kleo::KeyResolver::encryptionItems(Kleo::CryptoMessageFormat f) const
1400 {
1401  dump();
1402  auto it = d->mFormatInfoMap.find(f);
1403  return it != d->mFormatInfoMap.end() ? it->second.splitInfos : std::vector<SplitInfo>();
1404 }
1405 
1406 void Kleo::KeyResolver::setAutocryptEnabled(bool autocryptEnabled)
1407 {
1408  d->mAutocryptEnabled = autocryptEnabled;
1409 }
1410 
1411 std::map<QByteArray, QString> Kleo::KeyResolver::useAutocrypt() const
1412 {
1413  return d->mAutocryptMap;
1414 }
1415 
1416 void Kleo::KeyResolver::setAkonadiLookupEnabled(bool akonadiLoopkupEnabled)
1417 {
1418  d->mAkonadiLookupEnabled = akonadiLoopkupEnabled;
1419 }
1420 
1421 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeys(CryptoMessageFormat f) const
1422 {
1423  dump();
1424  auto it = d->mFormatInfoMap.find(f);
1425  return it != d->mFormatInfoMap.end() ? it->second.signKeys : std::vector<GpgME::Key>();
1426 }
1427 
1428 //
1429 //
1430 // KeyResolverPrivate helper methods below:
1431 //
1432 //
1433 
1434 std::vector<GpgME::Key> Kleo::KeyResolver::selectKeys(const QString &person, const QString &msg, const std::vector<GpgME::Key> &selectedKeys) const
1435 {
1436  const bool opgp = containsOpenPGP(mCryptoMessageFormats);
1437  const bool x509 = containsSMIME(mCryptoMessageFormats);
1438 
1439  QPointer<Kleo::KeySelectionDialog> dlg = new Kleo::KeySelectionDialog(
1440  i18n("Encryption Key Selection"),
1441  msg,
1443  selectedKeys,
1444  Kleo::KeySelectionDialog::ValidEncryptionKeys & ~(opgp ? 0 : Kleo::KeySelectionDialog::OpenPGPKeys) & ~(x509 ? 0 : Kleo::KeySelectionDialog::SMIMEKeys),
1445  true,
1446  true); // multi-selection and "remember choice" box
1447 
1448  if (dlg->exec() != QDialog::Accepted) {
1449  delete dlg;
1450  return {};
1451  }
1452 
1453  std::vector<GpgME::Key> keys = dlg->selectedKeys();
1454  keys.erase(std::remove_if(keys.begin(), keys.end(), NotValidEncryptionKey), keys.end());
1455  if (!keys.empty() && dlg->rememberSelection()) {
1456  setKeysForAddress(person, dlg->pgpKeyFingerprints(), dlg->smimeFingerprints());
1457  }
1458 
1459  delete dlg;
1460  return keys;
1461 }
1462 
1463 std::vector<GpgME::Key> Kleo::KeyResolver::getEncryptionKeys(const QString &person, bool quiet) const
1464 {
1465  const QString address = canonicalAddress(person).toLower();
1466 
1467  // First look for this person's address in the address->key dictionary
1468  const QStringList fingerprints = keysForAddress(address);
1469 
1470  if (!fingerprints.empty()) {
1471  qCDebug(MESSAGECOMPOSER_LOG) << "Using encryption keys 0x" << fingerprints.join(QLatin1String(", 0x")) << "for" << person;
1472  std::vector<GpgME::Key> keys = lookup(fingerprints);
1473  if (!keys.empty()) {
1474  // Check if all of the keys are trusted and valid encryption keys
1475  if (std::any_of(keys.begin(), keys.end(),
1476  NotValidTrustedEncryptionKey)) { // -= trusted?
1477  // not ok, let the user select: this is not conditional on !quiet,
1478  // since it's a bug in the configuration and the user should be
1479  // notified about it as early as possible:
1480  keys = selectKeys(person,
1481  i18nc("if in your language something like "
1482  "'certificate(s)' is not possible please "
1483  "use the plural in the translation",
1484  "There is a problem with the "
1485  "encryption certificate(s) for \"%1\".\n\n"
1486  "Please re-select the certificate(s) which should "
1487  "be used for this recipient.",
1488  person),
1489  keys);
1490  }
1491  bool canceled = false;
1492  keys = trustedOrConfirmed(keys, address, canceled);
1493  if (canceled) {
1494  return {};
1495  }
1496 
1497  if (!keys.empty()) {
1498  return keys;
1499  }
1500  // keys.empty() is considered cancel by callers, so go on
1501  }
1502  }
1503 
1504  // Now search all public keys for matching keys
1505  std::vector<GpgME::Key> matchingKeys = lookup(QStringList(address));
1506  matchingKeys.erase(std::remove_if(matchingKeys.begin(), matchingKeys.end(), NotValidEncryptionKey), matchingKeys.end());
1507 
1508  if (matchingKeys.empty() && d->mAutocryptEnabled) {
1509  qCDebug(MESSAGECOMPOSER_LOG) << "Search in Autocrypt storage a key for " << address;
1510  const auto storage = MessageCore::AutocryptStorage::self();
1511  const auto recipient = storage->getRecipient(address.toUtf8());
1512  if (recipient) {
1513  const auto key = recipient->gpgKey();
1514  if (!key.isNull() && ValidEncryptionKey(key)) {
1515  qCDebug(MESSAGECOMPOSER_LOG) << "Found an valid autocrypt key.";
1516  matchingKeys.push_back(key);
1517  } else {
1518  const auto gossipKey = recipient->gossipKey();
1519  if (!gossipKey.isNull() && ValidEncryptionKey(gossipKey)) {
1520  qCDebug(MESSAGECOMPOSER_LOG) << "Found an valid autocrypt gossip key.";
1521  matchingKeys.push_back(gossipKey);
1522  }
1523  }
1524  }
1525  // Accept any autocrypt key, as the validility is not used in Autocrypt.
1526  if (matchingKeys.size() == 1) {
1527  if (recipient->prefer_encrypt()) {
1528  d->mContactPreferencesMap[address].encryptionPreference = AlwaysEncryptIfPossible;
1529  }
1530  d->mAutocryptMap[matchingKeys[0].primaryFingerprint()] = address;
1531  return matchingKeys;
1532  }
1533  }
1534 
1535  // if called with quite == true (from EncryptionPreferenceCounter), we only want to
1536  // check if there are keys for this recipients, not (yet) their validity, so
1537  // don't show the untrusted encryption key warning in that case
1538  bool canceled = false;
1539  if (!quiet) {
1540  matchingKeys = trustedOrConfirmed(matchingKeys, address, canceled);
1541  }
1542  if (canceled) {
1543  return {};
1544  }
1545  if (quiet || matchingKeys.size() == 1) {
1546  return matchingKeys;
1547  }
1548 
1549  // no match until now, or more than one key matches; let the user
1550  // choose the key(s)
1551  // FIXME: let user get the key from keyserver
1552  return trustedOrConfirmed(selectKeys(person,
1553  matchingKeys.empty() ? i18nc("if in your language something like "
1554  "'certificate(s)' is not possible please "
1555  "use the plural in the translation",
1556  "<qt>No valid and trusted encryption certificate was "
1557  "found for \"%1\".<br/><br/>"
1558  "Select the certificate(s) which should "
1559  "be used for this recipient. If there is no suitable certificate in the list "
1560  "you can also search for external certificates by clicking the button: "
1561  "search for external certificates.</qt>",
1562  person.toHtmlEscaped())
1563  : i18nc("if in your language something like "
1564  "'certificate(s)' is not possible please "
1565  "use the plural in the translation",
1566  "More than one certificate matches \"%1\".\n\n"
1567  "Select the certificate(s) which should "
1568  "be used for this recipient.",
1569  person.toHtmlEscaped()),
1570  matchingKeys),
1571  address,
1572  canceled);
1573  // we can ignore 'canceled' here, since trustedOrConfirmed() returns
1574  // an empty vector when canceled == true, and we'd just do the same
1575 }
1576 
1577 std::vector<GpgME::Key> Kleo::KeyResolver::lookup(const QStringList &patterns, bool secret) const
1578 {
1579  if (patterns.empty()) {
1580  return {};
1581  }
1582  qCDebug(MESSAGECOMPOSER_LOG) << "( \"" << patterns.join(QLatin1String("\", \"")) << "\"," << secret << ")";
1583  std::vector<GpgME::Key> result;
1584  if (mCryptoMessageFormats & (InlineOpenPGPFormat | OpenPGPMIMEFormat)) {
1585  if (const QGpgME::Protocol *p = QGpgME::openpgp()) {
1586  std::unique_ptr<QGpgME::KeyListJob> job(p->keyListJob(false, false, true)); // use validating keylisting
1587  if (job.get()) {
1588  std::vector<GpgME::Key> keys;
1589  job->exec(patterns, secret, keys);
1590  result.insert(result.end(), keys.begin(), keys.end());
1591  }
1592  }
1593  }
1594  if (mCryptoMessageFormats & (SMIMEFormat | SMIMEOpaqueFormat)) {
1595  if (const QGpgME::Protocol *p = QGpgME::smime()) {
1596  std::unique_ptr<QGpgME::KeyListJob> job(p->keyListJob(false, false, true)); // use validating keylisting
1597  if (job.get()) {
1598  std::vector<GpgME::Key> keys;
1599  job->exec(patterns, secret, keys);
1600  result.insert(result.end(), keys.begin(), keys.end());
1601  }
1602  }
1603  }
1604  qCDebug(MESSAGECOMPOSER_LOG) << " returned" << result.size() << "keys";
1605  return result;
1606 }
1607 
1608 void Kleo::KeyResolver::addKeys(const std::vector<Item> &items, CryptoMessageFormat f)
1609 {
1610  dump();
1611  for (auto it = items.begin(); it != items.end(); ++it) {
1612  SplitInfo si(QStringList(it->address));
1613  std::remove_copy_if(it->keys.begin(), it->keys.end(), std::back_inserter(si.keys), IsNotForFormat(f));
1614  dump();
1615  if (si.keys.empty()) {
1616  qCWarning(MESSAGECOMPOSER_LOG) << "Kleo::KeyResolver::addKeys(): Fix EncryptionFormatPreferenceCounter."
1617  << "It detected a common format, but the list of such keys for recipient \"" << it->address << "\" is empty!";
1618  }
1619  d->mFormatInfoMap[f].splitInfos.push_back(si);
1620  }
1621  dump();
1622 }
1623 
1624 void Kleo::KeyResolver::addKeys(const std::vector<Item> &items)
1625 {
1626  dump();
1627  for (auto it = items.begin(); it != items.end(); ++it) {
1628  SplitInfo si(QStringList(it->address));
1629  CryptoMessageFormat f = AutoFormat;
1630  for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1631  const CryptoMessageFormat fmt = concreteCryptoMessageFormats[i];
1632  if ((fmt & it->format) && std::any_of(it->keys.begin(), it->keys.end(), IsForFormat(fmt))) {
1633  f = fmt;
1634  break;
1635  }
1636  }
1637  if (f == AutoFormat) {
1638  qCWarning(MESSAGECOMPOSER_LOG) << "Something went wrong. Didn't find a format for \"" << it->address << "\"";
1639  } else {
1640  std::remove_copy_if(it->keys.begin(), it->keys.end(), std::back_inserter(si.keys), IsNotForFormat(f));
1641  }
1642  d->mFormatInfoMap[f].splitInfos.push_back(si);
1643  }
1644  dump();
1645 }
1646 
1647 MessageComposer::ContactPreference Kleo::KeyResolver::lookupContactPreferences(const QString &address) const
1648 {
1649  const auto it = d->mContactPreferencesMap.find(address);
1650  if (it != d->mContactPreferencesMap.end()) {
1651  return it->second;
1652  }
1653 
1654  MessageComposer::ContactPreference pref;
1655 
1656  if (!d->mAkonadiLookupEnabled) {
1657  return pref;
1658  }
1659 
1660  auto job = new Akonadi::ContactSearchJob();
1661  job->setLimit(1);
1662  job->setQuery(Akonadi::ContactSearchJob::Email, address);
1663  job->exec();
1664 
1665  const KContacts::Addressee::List res = job->contacts();
1666  if (!res.isEmpty()) {
1667  KContacts::Addressee addr = res.at(0);
1668  pref.fillFromAddressee(addr);
1669  }
1670  // insert into map and grab resulting iterator
1671  d->mContactPreferencesMap.insert(std::make_pair(address, pref));
1672 
1673  return pref;
1674 }
1675 
1676 void Kleo::KeyResolver::saveContactPreference(const QString &email, const MessageComposer::ContactPreference &pref) const
1677 {
1678  d->mContactPreferencesMap.insert(std::make_pair(email, pref));
1679  auto saveContactPreferencesJob = new MessageComposer::SaveContactPreferenceJob(email, pref);
1680  saveContactPreferencesJob->start();
1681 }
1682 
1683 QStringList Kleo::KeyResolver::keysForAddress(const QString &address) const
1684 {
1685  if (address.isEmpty()) {
1686  return {};
1687  }
1688  const QString addr = canonicalAddress(address).toLower();
1689  const auto pref = lookupContactPreferences(addr);
1690  return pref.pgpKeyFingerprints + pref.smimeCertFingerprints;
1691 }
1692 
1693 void Kleo::KeyResolver::setKeysForAddress(const QString &address, const QStringList &pgpKeyFingerprints, const QStringList &smimeCertFingerprints) const
1694 {
1695  if (address.isEmpty()) {
1696  return;
1697  }
1698  const QString addr = canonicalAddress(address).toLower();
1699  auto pref = lookupContactPreferences(addr);
1700  pref.pgpKeyFingerprints = pgpKeyFingerprints;
1701  pref.smimeCertFingerprints = smimeCertFingerprints;
1702  saveContactPreference(addr, pref);
1703 }
1704 
1705 bool Kleo::KeyResolver::encryptToSelf() const
1706 {
1707  return mEncryptToSelf;
1708 }
1709 
1710 bool Kleo::KeyResolver::showApprovalDialog() const
1711 {
1712  return mShowApprovalDialog;
1713 }
KCODECS_EXPORT QByteArray extractEmailAddress(const QByteArray &address)
void append(const T &value)
QString fromUtf8(const char *str, int size)
QString toHtmlEscaped() const const
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
void setSecondaryRecipients(const QStringList &addresses)
Set the list of secondary (BCC) recipient addresses.
Kleo::Result setEncryptToSelfKeys(const QStringList &fingerprints)
Set the fingerprints of keys to be used for encrypting to self.
QList::const_iterator constBegin() const const
void reserve(int alloc)
KGuiItem cancel()
bool empty() const const
int size() const const
UnknownProtocol
QString i18n(const char *text, const TYPE &arg...)
std::vector< SplitInfo > encryptionItems(CryptoMessageFormat f) const
bool isEmpty() const const
std::vector< GpgME::Key > signingKeys(CryptoMessageFormat f) const
WaitCursor
void setAutocryptEnabled(bool autocryptEnabled)
If Autocrypt keys are used to find valid PGP Keys.
Kleo::Result resolveAllKeys(bool &signingRequested, bool &encryptionRequested)
Queries the user for missing keys and displays a key approval dialog if needed.
PostalAddress address(const QVariant &location)
QString join(const QString &separator) const const
void setAkonadiLookupEnabled(bool akonadiLookupEnabled)
Disable ContactSearchJob in KeyResolver.
QString toLower() const const
Kleo::Result setSigningKeys(const QStringList &fingerprints)
Set the fingerprints of keys to be used for signing.
void setPrimaryRecipients(const QStringList &addresses)
Set the list of primary (To/CC) recipient addresses.
QList::const_iterator constEnd() const const
const QChar at(int position) const const
KGuiItem cont()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
AddresseeList List
Action checkSigningPreferences(bool signingRequested) const
Determine whether to sign or not, depending on the per-recipient signing preferences,...
const QList< QKeySequence > & end()
Action checkEncryptionPreferences(bool encryptionRequested) const
Determine whether to encrypt or not, depending on the per-recipient encryption preferences,...
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Mar 24 2023 04:08:31 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.