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

KDE's Doxygen guidelines are available online.