Build with non-standard boost locations.
[kdepim.git] / messagecomposer / keyresolver.cpp
blob5e6ee286cc060c34afe5ad50204b2ad33038e014
1 /* -*- c++ -*-
2 keyresolver.cpp
4 This file is part of libkleopatra, the KDE keymanagement library
5 Copyright (c) 2004 Klarälvdalens Datakonsult AB
7 Based on kpgp.cpp
8 Copyright (C) 2001,2002 the KPGP authors
9 See file libkdenetwork/AUTHORS.kpgp for details
11 Libkleopatra is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version.
16 Libkleopatra is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 In addition, as a special exception, the copyright holders give
26 permission to link the code of this program with any edition of
27 the Qt library by Trolltech AS, Norway (or with modified versions
28 of Qt that use the same license as Qt), and distribute linked
29 combinations including the two. You must obey the GNU General
30 Public License in all respects for all of the code used other than
31 Qt. If you modify this file, you may extend this exception to
32 your version of the file, but you are not obligated to do so. If
33 you do not wish to do so, delete this exception statement from
34 your version.
37 #include "keyresolver.h"
39 #include "messageviewer/kcursorsaver.h"
40 #include "kleo_util.h"
42 #include <kpimutils/email.h>
43 #include "libkleo/ui/keyselectiondialog.h"
44 #include "kleo/cryptobackendfactory.h"
45 #include "kleo/keylistjob.h"
46 #include "kleo/dn.h"
48 #include <gpgme++/key.h>
49 #include <gpgme++/keylistresult.h>
51 #include <akonadi/collectiondialog.h>
52 #include <akonadi/contact/contactsearchjob.h>
53 #include <akonadi/itemcreatejob.h>
54 #include <akonadi/itemmodifyjob.h>
55 #include <klocale.h>
56 #include <kdebug.h>
57 #include <kinputdialog.h>
58 #include <kmessagebox.h>
60 #include <QStringList>
61 #include <QTextDocument>
62 #include <time.h>
64 #include <algorithm>
65 #include <memory>
66 #include <iterator>
67 #include <functional>
68 #include <map>
69 #include <set>
70 #include <iostream>
71 #include <cassert>
73 // this should go into stl_util.h, which has since moved into messageviewer.
74 // for lack of a better place put it in here for now.
75 namespace kdtools {
76 template <typename Iterator, typename UnaryPredicate>
77 bool any( Iterator first, Iterator last, UnaryPredicate p )
79 while ( first != last )
80 if ( p( *first ) )
81 return true;
82 else
83 ++first;
84 return false;
86 } // namespace kdtools
89 // some predicates to be used in STL algorithms:
92 static inline bool EmptyKeyList( const Kleo::KeyApprovalDialog::Item & item ) {
93 return item.keys.empty();
96 static inline QString ItemDotAddress( const Kleo::KeyResolver::Item & item ) {
97 return item.address;
100 static inline bool ApprovalNeeded( const Kleo::KeyResolver::Item & item ) {
101 return item.pref == Kleo::UnknownPreference || item.pref == Kleo::NeverEncrypt || item.keys.empty() ;
104 static inline Kleo::KeyResolver::Item
105 CopyKeysAndEncryptionPreferences( const Kleo::KeyResolver::Item & oldItem,
106 const Kleo::KeyApprovalDialog::Item & newItem ) {
107 return Kleo::KeyResolver::Item( oldItem.address, newItem.keys, newItem.pref, oldItem.signPref, oldItem.format );
110 static inline bool ByKeyID( const GpgME::Key & left, const GpgME::Key & right ) {
111 return qstrcmp( left.keyID(), right.keyID() ) < 0 ;
114 static inline bool WithRespectToKeyID( const GpgME::Key & left, const GpgME::Key & right ) {
115 return qstrcmp( left.keyID(), right.keyID() ) == 0 ;
118 static bool ValidOpenPGPEncryptionKey( const GpgME::Key & key ) {
119 if ( key.protocol() != GpgME::OpenPGP ) {
120 return false;
122 #if 1
123 if ( key.isRevoked() )
124 kWarning() << "is revoked";
125 if ( key.isExpired() )
126 kWarning() << "is expired";
127 if ( key.isDisabled() )
128 kWarning() << "is disabled";
129 if ( !key.canEncrypt() )
130 kWarning() << "can't encrypt";
131 #endif
132 if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() )
133 return false;
134 return true;
137 static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
138 if ( !ValidOpenPGPEncryptionKey( key ) )
139 return false;
140 const std::vector<GpgME::UserID> uids = key.userIDs();
141 for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it ) {
142 if ( !it->isRevoked() && it->validity() >= GpgME::UserID::Marginal )
143 return true;
144 #if 1
145 else
146 if ( it->isRevoked() )
147 kWarning() <<"a userid is revoked";
148 else
149 kWarning() <<"bad validity" << it->validity();
150 #endif
152 return false;
155 static bool ValidSMIMEEncryptionKey( const GpgME::Key & key ) {
156 if ( key.protocol() != GpgME::CMS )
157 return false;
158 if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() )
159 return false;
160 return true;
163 static bool ValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
164 if ( !ValidSMIMEEncryptionKey( key ) )
165 return false;
166 return true;
169 static inline bool ValidTrustedEncryptionKey( const GpgME::Key & key ) {
170 switch ( key.protocol() ) {
171 case GpgME::OpenPGP:
172 return ValidTrustedOpenPGPEncryptionKey( key );
173 case GpgME::CMS:
174 return ValidTrustedSMIMEEncryptionKey( key );
175 default:
176 return false;
180 static inline bool ValidEncryptionKey( const GpgME::Key & key ) {
181 switch ( key.protocol() ) {
182 case GpgME::OpenPGP:
183 return ValidOpenPGPEncryptionKey( key );
184 case GpgME::CMS:
185 return ValidSMIMEEncryptionKey( key );
186 default:
187 return false;
191 static inline bool ValidSigningKey( const GpgME::Key & key ) {
192 if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canSign() )
193 return false;
194 return key.hasSecret();
197 static inline bool ValidOpenPGPSigningKey( const GpgME::Key & key ) {
198 return key.protocol() == GpgME::OpenPGP && ValidSigningKey( key );
201 static inline bool ValidSMIMESigningKey( const GpgME::Key & key ) {
202 return key.protocol() == GpgME::CMS && ValidSigningKey( key );
205 static inline bool NotValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
206 return !ValidTrustedOpenPGPEncryptionKey( key );
209 static inline bool NotValidOpenPGPEncryptionKey( const GpgME::Key & key ) {
210 return !ValidOpenPGPEncryptionKey( key );
213 static inline bool NotValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
214 return !ValidTrustedSMIMEEncryptionKey( key );
217 static inline bool NotValidSMIMEEncryptionKey( const GpgME::Key & key ) {
218 return !ValidSMIMEEncryptionKey( key );
221 static inline bool NotValidTrustedEncryptionKey( const GpgME::Key & key ) {
222 return !ValidTrustedEncryptionKey( key );
225 static inline bool NotValidEncryptionKey( const GpgME::Key & key ) {
226 return !ValidEncryptionKey( key );
229 static inline bool NotValidSigningKey( const GpgME::Key & key ) {
230 return !ValidSigningKey( key );
233 static inline bool NotValidOpenPGPSigningKey( const GpgME::Key & key ) {
234 return !ValidOpenPGPSigningKey( key );
237 static inline bool NotValidSMIMESigningKey( const GpgME::Key & key ) {
238 return !ValidSMIMESigningKey( key );
241 namespace {
242 struct ByTrustScore {
243 static int score( const GpgME::UserID & uid )
245 return uid.isRevoked() || uid.isInvalid() ? -1 : uid.validity() ;
247 bool operator()( const GpgME::UserID & lhs, const GpgME::UserID & rhs ) const
249 return score( lhs ) < score( rhs ) ;
254 static std::vector<GpgME::UserID> matchingUIDs( const std::vector<GpgME::UserID> & uids, const QString & address ) {
255 if ( address.isEmpty() )
256 return std::vector<GpgME::UserID>();
258 std::vector<GpgME::UserID> result;
259 result.reserve( uids.size() );
260 for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin(), end = uids.end() ; it != end ; ++it )
261 // PENDING(marc) check DN for an EMAIL, too, in case of X.509 certs... :/
262 if ( const char * email = it->email() )
263 if ( *email && QString::fromUtf8( email ).simplified().toLower() == address )
264 result.push_back( *it );
265 return result;
268 static GpgME::UserID findBestMatchUID( const GpgME::Key & key, const QString & address ) {
269 const std::vector<GpgME::UserID> all = key.userIDs();
270 if ( all.empty() )
271 return GpgME::UserID();
272 const std::vector<GpgME::UserID> matching = matchingUIDs( all, address.toLower() );
273 const std::vector<GpgME::UserID> & v = matching.empty() ? all : matching ;
274 return *std::max_element( v.begin(), v.end(), ByTrustScore() );
277 static QStringList keysAsStrings( const std::vector<GpgME::Key>& keys ) {
278 QStringList strings;
279 for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it ) {
280 assert( !(*it).userID(0).isNull() );
281 QString keyLabel = QString::fromUtf8( (*it).userID(0).email() );
282 if ( keyLabel.isEmpty() )
283 keyLabel = QString::fromUtf8( (*it).userID(0).name() );
284 if ( keyLabel.isEmpty() )
285 keyLabel = QString::fromUtf8( (*it).userID(0).id() );
286 strings.append( keyLabel );
288 return strings;
291 static std::vector<GpgME::Key> trustedOrConfirmed( const std::vector<GpgME::Key> & keys, const QString & address, bool & canceled ) {
292 // PENDING(marc) work on UserIDs here?
293 std::vector<GpgME::Key> fishies;
294 std::vector<GpgME::Key> ickies;
295 std::vector<GpgME::Key> rewookies;
296 std::vector<GpgME::Key>::const_iterator it = keys.begin();
297 const std::vector<GpgME::Key>::const_iterator end = keys.end();
298 for ( ; it != end ; ++it ) {
299 const GpgME::Key & key = *it;
300 assert( ValidEncryptionKey( key ) );
301 const GpgME::UserID uid = findBestMatchUID( key, address );
302 if ( uid.isRevoked() ) {
303 rewookies.push_back( key );
305 if ( !uid.isRevoked() && uid.validity() == GpgME::UserID::Marginal ) {
306 fishies.push_back( key );
308 if ( !uid.isRevoked() && uid.validity() < GpgME::UserID::Never ) {
309 ickies.push_back( key );
313 if ( fishies.empty() && ickies.empty() && rewookies.empty() )
314 return keys;
316 // if some keys are not fully trusted, let the user confirm their use
317 QString msg = address.isEmpty()
318 ? i18n("One or more of your configured OpenPGP encryption "
319 "keys or S/MIME certificates is not fully trusted "
320 "for encryption.")
321 : i18n("One or more of the OpenPGP encryption keys or S/MIME "
322 "certificates for recipient \"%1\" is not fully trusted "
323 "for encryption.", address) ;
325 if ( !fishies.empty() ) {
326 // certificates can't have marginal trust
327 msg += i18n( "\nThe following keys are only marginally trusted: \n");
328 msg += keysAsStrings( fishies ).join( QLatin1String(",") );
330 if ( !ickies.empty() ) {
331 msg += i18n( "\nThe following keys or certificates have unknown trust level: \n");
332 msg += keysAsStrings( ickies ).join( QLatin1String(",") );
334 if ( !rewookies.empty() ) {
335 msg += i18n( "\nThe following keys or certificates are <b>revoked</b>: \n");
336 msg += keysAsStrings( rewookies ).join( QLatin1String(",") );
339 if( KMessageBox::warningContinueCancel( 0, msg, i18n("Not Fully Trusted Encryption Keys"),
340 KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
341 QLatin1String("not fully trusted encryption key warning") )
342 == KMessageBox::Continue )
343 return keys;
344 else
345 canceled = true;
346 return std::vector<GpgME::Key>();
349 namespace {
350 struct IsNotForFormat : public std::unary_function<GpgME::Key,bool> {
351 IsNotForFormat( Kleo::CryptoMessageFormat f ) : format( f ) {}
353 bool operator()( const GpgME::Key & key ) const {
354 return
355 ( isOpenPGP( format ) && key.protocol() != GpgME::OpenPGP ) ||
356 ( isSMIME( format ) && key.protocol() != GpgME::CMS );
359 const Kleo::CryptoMessageFormat format;
362 struct IsForFormat : std::unary_function<GpgME::Key,bool> {
363 explicit IsForFormat( Kleo::CryptoMessageFormat f )
364 : protocol( isOpenPGP( f ) ? GpgME::OpenPGP :
365 isSMIME( f ) ? GpgME::CMS :
366 GpgME::UnknownProtocol ) {}
368 bool operator()( const GpgME::Key & key ) const {
369 return key.protocol() == protocol;
372 const GpgME::Protocol protocol;
377 class Kleo::KeyResolver::SigningPreferenceCounter : public std::unary_function<Kleo::KeyResolver::Item,void> {
378 public:
379 SigningPreferenceCounter()
380 : mTotal( 0 ),
381 mUnknownSigningPreference( 0 ),
382 mNeverSign( 0 ),
383 mAlwaysSign( 0 ),
384 mAlwaysSignIfPossible( 0 ),
385 mAlwaysAskForSigning( 0 ),
386 mAskSigningWheneverPossible( 0 )
390 void operator()( const Kleo::KeyResolver::Item & item );
391 #define make_int_accessor(x) unsigned int num##x() const { return m##x; }
392 make_int_accessor(UnknownSigningPreference)
393 make_int_accessor(NeverSign)
394 make_int_accessor(AlwaysSign)
395 make_int_accessor(AlwaysSignIfPossible)
396 make_int_accessor(AlwaysAskForSigning)
397 make_int_accessor(AskSigningWheneverPossible)
398 make_int_accessor(Total)
399 #undef make_int_accessor
400 private:
401 unsigned int mTotal;
402 unsigned int mUnknownSigningPreference, mNeverSign, mAlwaysSign,
403 mAlwaysSignIfPossible, mAlwaysAskForSigning, mAskSigningWheneverPossible;
406 void Kleo::KeyResolver::SigningPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
407 switch ( item.signPref ) {
408 #define CASE(x) case x: ++m##x; break
409 CASE(UnknownSigningPreference);
410 CASE(NeverSign);
411 CASE(AlwaysSign);
412 CASE(AlwaysSignIfPossible);
413 CASE(AlwaysAskForSigning);
414 CASE(AskSigningWheneverPossible);
415 #undef CASE
417 ++mTotal;
422 class Kleo::KeyResolver::EncryptionPreferenceCounter : public std::unary_function<Item,void> {
423 const Kleo::KeyResolver * _this;
424 public:
425 EncryptionPreferenceCounter( const Kleo::KeyResolver * kr, EncryptionPreference defaultPreference )
426 : _this( kr ),
427 mDefaultPreference( defaultPreference ),
428 mTotal( 0 ),
429 mNoKey( 0 ),
430 mNeverEncrypt( 0 ),
431 mUnknownPreference( 0 ),
432 mAlwaysEncrypt( 0 ),
433 mAlwaysEncryptIfPossible( 0 ),
434 mAlwaysAskForEncryption( 0 ),
435 mAskWheneverPossible( 0 )
439 void operator()( Item & item );
441 template <typename Container>
442 void process( Container & c ) {
443 *this = std::for_each( c.begin(), c.end(), *this );
446 #define make_int_accessor(x) unsigned int num##x() const { return m##x; }
447 make_int_accessor(NoKey)
448 make_int_accessor(NeverEncrypt)
449 make_int_accessor(UnknownPreference)
450 make_int_accessor(AlwaysEncrypt)
451 make_int_accessor(AlwaysEncryptIfPossible)
452 make_int_accessor(AlwaysAskForEncryption)
453 make_int_accessor(AskWheneverPossible)
454 make_int_accessor(Total)
455 #undef make_int_accessor
456 private:
457 EncryptionPreference mDefaultPreference;
458 bool mNoOps;
459 unsigned int mTotal;
460 unsigned int mNoKey;
461 unsigned int mNeverEncrypt, mUnknownPreference, mAlwaysEncrypt,
462 mAlwaysEncryptIfPossible, mAlwaysAskForEncryption, mAskWheneverPossible;
465 void Kleo::KeyResolver::EncryptionPreferenceCounter::operator()( Item & item ) {
466 if ( _this ) {
467 if ( item.needKeys )
468 item.keys = _this->getEncryptionKeys( item.address, true );
469 if ( item.keys.empty() ) {
470 ++mNoKey;
471 return;
474 switch ( !item.pref ? mDefaultPreference : item.pref ) {
475 #define CASE(x) case Kleo::x: ++m##x; break
476 CASE(NeverEncrypt);
477 CASE(UnknownPreference);
478 CASE(AlwaysEncrypt);
479 CASE(AlwaysEncryptIfPossible);
480 CASE(AlwaysAskForEncryption);
481 CASE(AskWheneverPossible);
482 #undef CASE
484 ++mTotal;
487 namespace {
489 class FormatPreferenceCounterBase : public std::unary_function<Kleo::KeyResolver::Item,void> {
490 public:
491 FormatPreferenceCounterBase()
492 : mTotal( 0 ),
493 mInlineOpenPGP( 0 ),
494 mOpenPGPMIME( 0 ),
495 mSMIME( 0 ),
496 mSMIMEOpaque( 0 )
501 #define make_int_accessor(x) unsigned int num##x() const { return m##x; }
502 make_int_accessor(Total)
503 make_int_accessor(InlineOpenPGP)
504 make_int_accessor(OpenPGPMIME)
505 make_int_accessor(SMIME)
506 make_int_accessor(SMIMEOpaque)
507 #undef make_int_accessor
509 unsigned int numOf( Kleo::CryptoMessageFormat f ) const {
510 switch ( f ) {
511 #define CASE(x) case Kleo::x##Format: return m##x
512 CASE(InlineOpenPGP);
513 CASE(OpenPGPMIME);
514 CASE(SMIME);
515 CASE(SMIMEOpaque);
516 #undef CASE
517 default: return 0;
521 protected:
522 unsigned int mTotal;
523 unsigned int mInlineOpenPGP, mOpenPGPMIME, mSMIME, mSMIMEOpaque;
526 class EncryptionFormatPreferenceCounter : public FormatPreferenceCounterBase {
527 public:
528 EncryptionFormatPreferenceCounter() : FormatPreferenceCounterBase() {}
529 void operator()( const Kleo::KeyResolver::Item & item );
532 class SigningFormatPreferenceCounter : public FormatPreferenceCounterBase {
533 public:
534 SigningFormatPreferenceCounter() : FormatPreferenceCounterBase() {}
535 void operator()( const Kleo::KeyResolver::Item & item );
538 #define CASE(x) if ( item.format & Kleo::x##Format ) ++m##x;
539 void EncryptionFormatPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
540 if ( item.format & (Kleo::InlineOpenPGPFormat|Kleo::OpenPGPMIMEFormat) &&
541 std::find_if( item.keys.begin(), item.keys.end(),
542 ValidTrustedOpenPGPEncryptionKey ) != item.keys.end() ) { // -= trusted?
543 CASE(OpenPGPMIME);
544 CASE(InlineOpenPGP);
546 if ( item.format & (Kleo::SMIMEFormat|Kleo::SMIMEOpaqueFormat) &&
547 std::find_if( item.keys.begin(), item.keys.end(),
548 ValidTrustedSMIMEEncryptionKey ) != item.keys.end() ) { // -= trusted?
549 CASE(SMIME);
550 CASE(SMIMEOpaque);
552 ++mTotal;
555 void SigningFormatPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
556 CASE(InlineOpenPGP);
557 CASE(OpenPGPMIME);
558 CASE(SMIME);
559 CASE(SMIMEOpaque);
560 ++mTotal;
562 #undef CASE
564 } // anon namespace
566 static QString canonicalAddress( const QString & _address ) {
567 const QString address = KPIMUtils::extractEmailAddress( _address );
568 if ( !address.contains( QLatin1Char('@') ) ) {
569 // local address
570 //return address + '@' + KNetwork::KResolver::localHostName();
571 return address + QLatin1String("@localdomain");
573 else
574 return address;
578 struct FormatInfo {
579 std::vector<Kleo::KeyResolver::SplitInfo> splitInfos;
580 std::vector<GpgME::Key> signKeys;
583 struct Kleo::KeyResolver::Private {
584 std::set<QByteArray> alreadyWarnedFingerprints;
586 std::vector<GpgME::Key> mOpenPGPSigningKeys; // signing
587 std::vector<GpgME::Key> mSMIMESigningKeys; // signing
589 std::vector<GpgME::Key> mOpenPGPEncryptToSelfKeys; // encryption to self
590 std::vector<GpgME::Key> mSMIMEEncryptToSelfKeys; // encryption to self
592 std::vector<Item> mPrimaryEncryptionKeys; // encryption to To/CC
593 std::vector<Item> mSecondaryEncryptionKeys; // encryption to BCC
595 std::map<CryptoMessageFormat,FormatInfo> mFormatInfoMap;
597 // key=email address, value=crypto preferences for this contact (from kabc)
598 typedef std::map<QString, ContactPreferences> ContactPreferencesMap;
599 ContactPreferencesMap mContactPreferencesMap;
603 Kleo::KeyResolver::KeyResolver( bool encToSelf, bool showApproval, bool oppEncryption,
604 unsigned int f,
605 int encrWarnThresholdKey, int signWarnThresholdKey,
606 int encrWarnThresholdRootCert, int signWarnThresholdRootCert,
607 int encrWarnThresholdChainCert, int signWarnThresholdChainCert )
608 : mEncryptToSelf( encToSelf ),
609 mShowApprovalDialog( showApproval ),
610 mOpportunisticEncyption( oppEncryption ),
611 mCryptoMessageFormats( f ),
612 mEncryptKeyNearExpiryWarningThreshold( encrWarnThresholdKey ),
613 mSigningKeyNearExpiryWarningThreshold( signWarnThresholdKey ),
614 mEncryptRootCertNearExpiryWarningThreshold( encrWarnThresholdRootCert ),
615 mSigningRootCertNearExpiryWarningThreshold( signWarnThresholdRootCert ),
616 mEncryptChainCertNearExpiryWarningThreshold( encrWarnThresholdChainCert ),
617 mSigningChainCertNearExpiryWarningThreshold( signWarnThresholdChainCert )
619 d = new Private();
622 Kleo::KeyResolver::~KeyResolver() {
623 delete d; d = 0;
626 Kpgp::Result Kleo::KeyResolver::checkKeyNearExpiry( const GpgME::Key & key, const char * dontAskAgainName,
627 bool mine, bool sign, bool ca,
628 int recur_limit, const GpgME::Key & orig ) const
630 if ( recur_limit <= 0 ) {
631 kDebug() << "Key chain too long (>100 certs)";
632 return Kpgp::Ok;
634 const GpgME::Subkey subkey = key.subkey(0);
635 if ( d->alreadyWarnedFingerprints.count( subkey.fingerprint() ) )
636 return Kpgp::Ok; // already warned about this one (and so about it's issuers)
638 if ( subkey.neverExpires() )
639 return Kpgp::Ok;
640 static const double secsPerDay = 24 * 60 * 60;
641 const double secsTillExpiry = ::difftime( subkey.expirationTime(), time(0) );
642 if ( secsTillExpiry <= 0 ) {
643 const int daysSinceExpiry = 1 + int( -secsTillExpiry / secsPerDay );
644 kDebug() << "Key 0x" << key.shortKeyID() << " expired less than "
645 << daysSinceExpiry << " days ago";
646 const QString msg =
647 key.protocol() == GpgME::OpenPGP
648 ? ( mine ? sign
649 ? ki18np("<p>Your OpenPGP signing key</p><p align=center><b>%2</b> (KeyID 0x%3)</p>"
650 "<p>expired less than a day ago.</p>",
651 "<p>Your OpenPGP signing key</p><p align=center><b>%2</b> (KeyID 0x%3)</p>"
652 "<p>expired %1 days ago.</p>")
653 : ki18np("<p>Your OpenPGP encryption key</p><p align=center><b>%2</b> (KeyID 0x%3)</p>"
654 "<p>expired less than a day ago.</p>",
655 "<p>Your OpenPGP encryption key</p><p align=center><b>%2</b> (KeyID 0x%3)</p>"
656 "<p>expired %1 days ago.</p>")
657 : ki18np("<p>The OpenPGP key for</p><p align=center><b>%2</b> (KeyID 0x%3)</p>"
658 "<p>expired less than a day ago.</p>",
659 "<p>The OpenPGP key for</p><p align=center><b>%2</b> (KeyID 0x%3)</p>"
660 "<p>expired %1 days ago.</p>") )
661 .subs( daysSinceExpiry )
662 .subs( QString::fromUtf8( key.userID(0).id() ) )
663 .subs( QString::fromLatin1( key.shortKeyID() ) )
664 .toString()
665 : ( ca
666 ? ( key.isRoot()
667 ? ( mine ? sign
668 ? ki18np("<p>The root certificate</p><p align=center><b>%4</b></p>"
669 "<p>for your S/MIME signing certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
670 "<p>expired less than a day ago.</p>",
671 "<p>The root certificate</p><p align=center><b>%4</b></p>"
672 "<p>for your S/MIME signing certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
673 "<p>expired %1 days ago.</p>")
674 : ki18np("<p>The root certificate</p><p align=center><b>%4</b></p>"
675 "<p>for your S/MIME encryption certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
676 "<p>expired less than a day ago.</p>",
677 "<p>The root certificate</p><p align=center><b>%4</b></p>"
678 "<p>for your S/MIME encryption certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
679 "<p>expired %1 days ago.</p>")
680 : ki18np("<p>The root certificate</p><p align=center><b>%4</b></p>"
681 "<p>for S/MIME 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 S/MIME certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
685 "<p>expired %1 days ago.</p>") )
686 : ( mine ? sign
687 ? ki18np("<p>The intermediate CA certificate</p><p align=center><b>%4</b></p>"
688 "<p>for your S/MIME signing certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
689 "<p>expired less than a day ago.</p>",
690 "<p>The intermediate CA certificate</p><p align=center><b>%4</b></p>"
691 "<p>for your S/MIME signing certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
692 "<p>expired %1 days ago.</p>")
693 : ki18np("<p>The intermediate CA certificate</p><p align=center><b>%4</b></p>"
694 "<p>for your S/MIME encryption certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
695 "<p>expired less than a day ago.</p>",
696 "<p>The intermediate CA certificate</p><p align=center><b>%4</b></p>"
697 "<p>for your S/MIME encryption certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
698 "<p>expired %1 days ago.</p>")
699 : ki18np("<p>The intermediate CA certificate</p><p align=center><b>%4</b></p>"
700 "<p>for S/MIME certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
701 "<p>expired less than a day ago.</p>",
702 "<p>The intermediate CA certificate</p><p align=center><b>%4</b></p>"
703 "<p>for S/MIME certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
704 "<p>expired %1 days ago.</p>") ) )
705 .subs( daysSinceExpiry )
706 .subs( Kleo::DN( orig.userID(0).id() ).prettyDN() )
707 .subs( QString::fromLatin1( orig.issuerSerial() ) )
708 .subs( Kleo::DN( key.userID(0).id() ).prettyDN() )
709 .toString()
710 : ( mine ? sign
711 ? ki18np("<p>Your S/MIME signing certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
712 "<p>expired less than a day ago.</p>",
713 "<p>Your S/MIME signing certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
714 "<p>expired %1 days ago.</p>")
715 : ki18np("<p>Your S/MIME encryption certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
716 "<p>expired less than a day ago.</p>",
717 "<p>Your S/MIME encryption certificate</p><p align=center><b>%2</b> (serial number %3)</p>"
718 "<p>expired %1 days ago.</p>")
719 : ki18np("<p>The S/MIME certificate for</p><p align=center><b>%2</b> (serial number %3)</p>"
720 "<p>expired less than a day ago.</p>",
721 "<p>The S/MIME certificate for</p><p align=center><b>%2</b> (serial number %3)</p>"
722 "<p>expired %1 days ago.</p>" ) )
723 .subs( daysSinceExpiry )
724 .subs( Kleo::DN( key.userID(0).id() ).prettyDN() )
725 .subs( QString::fromLatin1( key.issuerSerial() ) )
726 .toString() );
727 d->alreadyWarnedFingerprints.insert( subkey.fingerprint() );
728 if ( KMessageBox::warningContinueCancel( 0, msg,
729 key.protocol() == GpgME::OpenPGP
730 ? i18n("OpenPGP Key Expired" )
731 : i18n("S/MIME Certificate Expired" ),
732 KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QLatin1String(dontAskAgainName) ) == KMessageBox::Cancel )
733 return Kpgp::Canceled;
734 } else {
735 const int daysTillExpiry = 1 + int( secsTillExpiry / secsPerDay );
736 kDebug() << "Key 0x" << key.shortKeyID() <<"expires in less than"
737 << daysTillExpiry << "days";
738 const int threshold =
740 ? ( key.isRoot()
741 ? ( sign
742 ? signingRootCertNearExpiryWarningThresholdInDays()
743 : encryptRootCertNearExpiryWarningThresholdInDays() )
744 : ( sign
745 ? signingChainCertNearExpiryWarningThresholdInDays()
746 : encryptChainCertNearExpiryWarningThresholdInDays() ) )
747 : ( sign
748 ? signingKeyNearExpiryWarningThresholdInDays()
749 : encryptKeyNearExpiryWarningThresholdInDays() );
750 if ( threshold > -1 && daysTillExpiry <= threshold ) {
751 const QString msg =
752 key.protocol() == GpgME::OpenPGP
753 ? ( mine ? sign
754 ? ki18np("<p>Your OpenPGP signing key</p><p align=\"center\"><b>%2</b> (KeyID 0x%3)</p>"
755 "<p>expires in less than a day.</p>",
756 "<p>Your OpenPGP signing key</p><p align=\"center\"><b>%2</b> (KeyID 0x%3)</p>"
757 "<p>expires in less than %1 days.</p>")
758 : ki18np("<p>Your OpenPGP encryption key</p><p align=\"center\"><b>%2</b> (KeyID 0x%3)</p>"
759 "<p>expires in less than a day.</p>",
760 "<p>Your OpenPGP encryption key</p><p align=\"center\"><b>%2</b> (KeyID 0x%3)</p>"
761 "<p>expires in less than %1 days.</p>")
762 : ki18np("<p>The OpenPGP key for</p><p align=\"center\"><b>%2</b> (KeyID 0x%3)</p>"
763 "<p>expires in less than a day.</p>",
764 "<p>The OpenPGP key for</p><p align=\"center\"><b>%2</b> (KeyID 0x%3)</p>"
765 "<p>expires in less than %1 days.</p>") )
766 .subs( daysTillExpiry )
767 .subs( QString::fromUtf8( key.userID(0).id() ) )
768 .subs( QString::fromLatin1( key.shortKeyID() ) )
769 .toString()
770 : ( ca
771 ? ( key.isRoot()
772 ? ( mine ? sign
773 ? 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
792 ? ki18np("<p>The intermediate CA certificate</p><p align=\"center\"><b>%4</b></p>"
793 "<p>for your S/MIME signing certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
794 "<p>expires in less than a day.</p>",
795 "<p>The intermediate CA certificate</p><p align=\"center\"><b>%4</b></p>"
796 "<p>for your S/MIME signing certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
797 "<p>expires in less than %1 days.</p>")
798 : ki18np("<p>The intermediate CA certificate</p><p align=\"center\"><b>%4</b></p>"
799 "<p>for your S/MIME encryption certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
800 "<p>expires in less than a day.</p>",
801 "<p>The intermediate CA certificate</p><p align=\"center\"><b>%4</b></p>"
802 "<p>for your S/MIME encryption certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
803 "<p>expires in less than %1 days.</p>")
804 : ki18np("<p>The intermediate CA certificate</p><p align=\"center\"><b>%4</b></p>"
805 "<p>for S/MIME certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
806 "<p>expires in less than a day.</p>",
807 "<p>The intermediate CA certificate</p><p align=\"center\"><b>%4</b></p>"
808 "<p>for S/MIME certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
809 "<p>expires in less than %1 days.</p>") ) )
810 .subs( daysTillExpiry )
811 .subs( Kleo::DN( orig.userID(0).id() ).prettyDN() )
812 .subs( QString::fromLatin1( orig.issuerSerial() ) )
813 .subs( Kleo::DN( key.userID(0).id() ).prettyDN() )
814 .toString()
815 : ( mine ? sign
816 ? ki18np("<p>Your S/MIME signing certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
817 "<p>expires in less than a day.</p>",
818 "<p>Your S/MIME signing certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
819 "<p>expires in less than %1 days.</p>")
820 : ki18np("<p>Your S/MIME encryption certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
821 "<p>expires in less than a day.</p>",
822 "<p>Your S/MIME encryption certificate</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
823 "<p>expires in less than %1 days.</p>")
824 : ki18np("<p>The S/MIME certificate for</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
825 "<p>expires in less than a day.</p>",
826 "<p>The S/MIME certificate for</p><p align=\"center\"><b>%2</b> (serial number %3)</p>"
827 "<p>expires in less than %1 days.</p>" ) )
828 .subs( daysTillExpiry )
829 .subs( Kleo::DN( key.userID(0).id() ).prettyDN() )
830 .subs( QString::fromLatin1( key.issuerSerial() ) )
831 .toString() );
832 d->alreadyWarnedFingerprints.insert( subkey.fingerprint() );
833 if ( KMessageBox::warningContinueCancel( 0, msg,
834 key.protocol() == GpgME::OpenPGP
835 ? i18n("OpenPGP Key Expires Soon" )
836 : i18n("S/MIME Certificate Expires Soon" ),
837 KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
838 QLatin1String( dontAskAgainName ) )
839 == KMessageBox::Cancel )
840 return Kpgp::Canceled;
843 if ( key.isRoot() )
844 return Kpgp::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 Kpgp::Ok;
849 else
850 return checkKeyNearExpiry( issuer.front(), dontAskAgainName, mine, sign,
851 true, recur_limit-1, ca ? orig : key );
853 return Kpgp::Ok;
856 Kpgp::Result Kleo::KeyResolver::setEncryptToSelfKeys( const QStringList & fingerprints ) {
857 if ( !encryptToSelf() )
858 return Kpgp::Ok;
860 std::vector<GpgME::Key> keys = lookup( fingerprints );
861 std::remove_copy_if( keys.begin(), keys.end(),
862 std::back_inserter( d->mOpenPGPEncryptToSelfKeys ),
863 NotValidTrustedOpenPGPEncryptionKey ); // -= trusted?
864 std::remove_copy_if( keys.begin(), keys.end(),
865 std::back_inserter( d->mSMIMEEncryptToSelfKeys ),
866 NotValidTrustedSMIMEEncryptionKey ); // -= trusted?
868 if ( d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size()
869 < keys.size() ) {
870 // too few keys remain...
871 const QString msg = i18n("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( 0, msg, i18n("Unusable Encryption Keys"),
880 KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
881 QLatin1String("unusable own encryption key warning") )
882 == KMessageBox::Continue ? Kpgp::Ok : Kpgp::Canceled ;
885 // check for near-expiry:
887 for ( std::vector<GpgME::Key>::const_iterator it = d->mOpenPGPEncryptToSelfKeys.begin() ; it != d->mOpenPGPEncryptToSelfKeys.end() ; ++it ) {
888 const Kpgp::Result r = checkKeyNearExpiry( *it, "own encryption key expires soon warning",
889 true, false );
890 if ( r != Kpgp::Ok )
891 return r;
894 for ( std::vector<GpgME::Key>::const_iterator it = d->mSMIMEEncryptToSelfKeys.begin() ; it != d->mSMIMEEncryptToSelfKeys.end() ; ++it ) {
895 const Kpgp::Result r = checkKeyNearExpiry( *it, "own encryption key expires soon warning",
896 true, false );
897 if ( r != Kpgp::Ok )
898 return r;
901 return Kpgp::Ok;
904 Kpgp::Result Kleo::KeyResolver::setSigningKeys( const QStringList & fingerprints ) {
905 std::vector<GpgME::Key> keys = lookup( fingerprints, true ); // secret keys
906 std::remove_copy_if( keys.begin(), keys.end(),
907 std::back_inserter( d->mOpenPGPSigningKeys ),
908 NotValidOpenPGPSigningKey );
909 std::remove_copy_if( keys.begin(), keys.end(),
910 std::back_inserter( d->mSMIMESigningKeys ),
911 NotValidSMIMESigningKey );
913 if ( d->mOpenPGPSigningKeys.size() + d->mSMIMESigningKeys.size() < keys.size() ) {
914 // too few keys remain...
915 const QString msg = i18n("One or more of your configured OpenPGP signing keys "
916 "or S/MIME signing certificates is not usable for "
917 "signing. Please reconfigure your signing keys "
918 "and certificates for this identity in the identity "
919 "configuration dialog.\n"
920 "If you choose to continue, and the keys are needed "
921 "later on, you will be prompted to specify the keys "
922 "to use.");
923 return KMessageBox::warningContinueCancel( 0, msg, i18n("Unusable Signing Keys"),
924 KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
925 QLatin1String("unusable signing key warning") )
926 == KMessageBox::Continue ? Kpgp::Ok : Kpgp::Canceled ;
929 // check for near expiry:
931 for ( std::vector<GpgME::Key>::const_iterator it = d->mOpenPGPSigningKeys.begin() ; it != d->mOpenPGPSigningKeys.end() ; ++it ) {
932 const Kpgp::Result r = checkKeyNearExpiry( *it, "signing key expires soon warning",
933 true, true );
934 if ( r != Kpgp::Ok )
935 return r;
938 for ( std::vector<GpgME::Key>::const_iterator it = d->mSMIMESigningKeys.begin() ; it != d->mSMIMESigningKeys.end() ; ++it ) {
939 const Kpgp::Result r = checkKeyNearExpiry( *it, "signing key expires soon warning",
940 true, true );
941 if ( r != Kpgp::Ok )
942 return r;
945 return Kpgp::Ok;
948 void Kleo::KeyResolver::setPrimaryRecipients( const QStringList & addresses ) {
949 d->mPrimaryEncryptionKeys = getEncryptionItems( addresses );
952 void Kleo::KeyResolver::setSecondaryRecipients( const QStringList & addresses ) {
953 d->mSecondaryEncryptionKeys = getEncryptionItems( addresses );
956 std::vector<Kleo::KeyResolver::Item> Kleo::KeyResolver::getEncryptionItems( const QStringList & addresses ) {
957 std::vector<Item> items;
958 items.reserve( addresses.size() );
959 for ( QStringList::const_iterator it = addresses.begin() ; it != addresses.end() ; ++it ) {
960 QString addr = canonicalAddress( *it ).toLower();
961 const ContactPreferences pref = lookupContactPreferences( addr );
963 items.push_back( Item( *it, /*getEncryptionKeys( *it, true ),*/
964 pref.encryptionPreference,
965 pref.signingPreference,
966 pref.cryptoMessageFormat ) );
968 return items;
971 static Kleo::Action action( bool doit, bool ask, bool donot, bool requested ) {
972 if ( requested && !donot )
973 return Kleo::DoIt;
974 if ( doit && !ask && !donot )
975 return Kleo::DoIt;
976 if ( !doit && ask && !donot )
977 return Kleo::Ask;
978 if ( !doit && !ask && donot )
979 return requested ? Kleo::Conflict : Kleo::DontDoIt ;
980 if ( !doit && !ask && !donot )
981 return Kleo::DontDoIt ;
982 return Kleo::Conflict;
985 Kleo::Action Kleo::KeyResolver::checkSigningPreferences( bool signingRequested ) const {
987 if ( signingRequested && d->mOpenPGPSigningKeys.empty() && d->mSMIMESigningKeys.empty() )
988 return Impossible;
990 SigningPreferenceCounter count;
991 count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
992 count );
993 count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
994 count );
996 unsigned int sign = count.numAlwaysSign();
997 unsigned int ask = count.numAlwaysAskForSigning();
998 const unsigned int dontSign = count.numNeverSign();
999 if ( signingPossible() ) {
1000 sign += count.numAlwaysSignIfPossible();
1001 ask += count.numAskSigningWheneverPossible();
1004 return action( sign, ask, dontSign, signingRequested );
1007 bool Kleo::KeyResolver::signingPossible() const {
1008 return !d->mOpenPGPSigningKeys.empty() || !d->mSMIMESigningKeys.empty() ;
1011 Kleo::Action Kleo::KeyResolver::checkEncryptionPreferences( bool encryptionRequested ) const {
1013 if ( d->mPrimaryEncryptionKeys.empty() && d->mSecondaryEncryptionKeys.empty() )
1014 return DontDoIt;
1016 if ( encryptionRequested && encryptToSelf() &&
1017 d->mOpenPGPEncryptToSelfKeys.empty() && d->mSMIMEEncryptToSelfKeys.empty() )
1018 return Impossible;
1020 if ( !encryptionRequested && !mOpportunisticEncyption ) {
1021 // try to minimize crypto ops (including key lookups) by only
1022 // looking up keys when at least one the the encryption
1023 // preferences needs it:
1024 EncryptionPreferenceCounter count( 0, UnknownPreference );
1025 count.process( d->mPrimaryEncryptionKeys );
1026 count.process( d->mSecondaryEncryptionKeys );
1027 if ( !count.numAlwaysEncrypt() &&
1028 !count.numAlwaysAskForEncryption() && // this guy might not need a lookup, when declined, but it's too complex to implement that here
1029 !count.numAlwaysEncryptIfPossible() &&
1030 !count.numAskWheneverPossible() )
1031 return DontDoIt;
1034 EncryptionPreferenceCounter count( this, mOpportunisticEncyption ? AskWheneverPossible : UnknownPreference );
1035 count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
1036 count );
1037 count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
1038 count );
1040 unsigned int encrypt = count.numAlwaysEncrypt();
1041 unsigned int ask = count.numAlwaysAskForEncryption();
1042 const unsigned int dontEncrypt = count.numNeverEncrypt() + count.numNoKey();
1043 if ( encryptionPossible() ) {
1044 encrypt += count.numAlwaysEncryptIfPossible();
1045 ask += count.numAskWheneverPossible();
1048 const Action act = action( encrypt, ask, dontEncrypt, encryptionRequested );
1049 if ( act != Ask ||
1050 std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
1051 std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
1052 EncryptionPreferenceCounter( this, UnknownPreference ) ) ).numAlwaysAskForEncryption() )
1053 return act;
1054 else
1055 return AskOpportunistic;
1058 bool Kleo::KeyResolver::encryptionPossible() const {
1059 return std::find_if( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
1060 EmptyKeyList ) == d->mPrimaryEncryptionKeys.end()
1061 && std::find_if( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
1062 EmptyKeyList ) == d->mSecondaryEncryptionKeys.end() ;
1065 Kpgp::Result Kleo::KeyResolver::resolveAllKeys( bool& signingRequested, bool& encryptionRequested ) {
1066 if ( !encryptionRequested && !signingRequested ) {
1067 // make a dummy entry with all recipients, but no signing or
1068 // encryption keys to avoid special-casing on the caller side:
1069 dump();
1070 d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.push_back( SplitInfo( allRecipients() ) );
1071 dump();
1072 return Kpgp::Ok;
1074 Kpgp::Result result = Kpgp::Ok;
1075 if ( encryptionRequested )
1076 result = resolveEncryptionKeys( signingRequested );
1077 if ( result != Kpgp::Ok )
1078 return result;
1079 if ( signingRequested ) {
1080 if ( encryptionRequested ) {
1081 result = resolveSigningKeysForEncryption();
1083 else {
1084 result = resolveSigningKeysForSigningOnly();
1085 if ( result == Kpgp::Failure ) {
1086 signingRequested = false;
1087 return Kpgp::Ok;
1091 return result;
1094 Kpgp::Result Kleo::KeyResolver::resolveEncryptionKeys( bool signingRequested ) {
1096 // 1. Get keys for all recipients:
1098 kDebug() << "resolving enc keys";
1099 for ( std::vector<Item>::iterator it = d->mPrimaryEncryptionKeys.begin() ; it != d->mPrimaryEncryptionKeys.end() ; ++it ) {
1100 kDebug() << "checking primary:" << it->address;
1101 if ( !it->needKeys )
1102 continue;
1103 it->keys = getEncryptionKeys( it->address, false );
1104 kDebug() << "got # keys:" << it->keys.size();
1105 if ( it->keys.empty() )
1106 return Kpgp::Canceled;
1107 QString addr = canonicalAddress( it->address ).toLower();
1108 const ContactPreferences pref = lookupContactPreferences( addr );
1109 it->pref = pref.encryptionPreference;
1110 it->signPref = pref.signingPreference;
1111 it->format = pref.cryptoMessageFormat;
1112 kDebug() << "set key data:" << it->pref << it->signPref << it->format;
1115 for ( std::vector<Item>::iterator it = d->mSecondaryEncryptionKeys.begin() ; it != d->mSecondaryEncryptionKeys.end() ; ++it ) {
1116 if ( !it->needKeys )
1117 continue;
1118 it->keys = getEncryptionKeys( it->address, false );
1119 if ( it->keys.empty() )
1120 return Kpgp::Canceled;
1121 QString addr = canonicalAddress( it->address ).toLower();
1122 const ContactPreferences pref = lookupContactPreferences( addr );
1123 it->pref = pref.encryptionPreference;
1124 it->signPref = pref.signingPreference;
1125 it->format = pref.cryptoMessageFormat;
1128 // 1a: Present them to the user
1130 const Kpgp::Result res = showKeyApprovalDialog();
1131 if ( res != Kpgp::Ok )
1132 return res;
1135 // 2. Check what the primary recipients need
1138 // 2a. Try to find a common format for all primary recipients,
1139 // else use as many formats as needed
1141 const EncryptionFormatPreferenceCounter primaryCount
1142 = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
1143 EncryptionFormatPreferenceCounter() );
1145 CryptoMessageFormat commonFormat = AutoFormat;
1146 for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
1147 if ( !( concreteCryptoMessageFormats[i] & mCryptoMessageFormats ) )
1148 continue;
1149 if ( signingRequested && signingKeysFor( concreteCryptoMessageFormats[i] ).empty() )
1150 continue;
1151 if ( encryptToSelf() && encryptToSelfKeysFor( concreteCryptoMessageFormats[i] ).empty() )
1152 continue;
1153 if ( primaryCount.numOf( concreteCryptoMessageFormats[i] ) == primaryCount.numTotal() ) {
1154 commonFormat = concreteCryptoMessageFormats[i];
1155 break;
1158 kDebug() << "got commonFormat for primary recipients:" << commonFormat;
1159 if ( commonFormat != AutoFormat )
1160 addKeys( d->mPrimaryEncryptionKeys, commonFormat );
1161 else
1162 addKeys( d->mPrimaryEncryptionKeys );
1164 collapseAllSplitInfos(); // these can be encrypted together
1166 // 2b. Just try to find _something_ for each secondary recipient,
1167 // with a preference to a common format (if that exists)
1169 const EncryptionFormatPreferenceCounter secondaryCount
1170 = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
1171 EncryptionFormatPreferenceCounter() );
1173 if ( commonFormat != AutoFormat &&
1174 secondaryCount.numOf( commonFormat ) == secondaryCount.numTotal() )
1175 addKeys( d->mSecondaryEncryptionKeys, commonFormat );
1176 else
1177 addKeys( d->mSecondaryEncryptionKeys );
1179 // 3. Check for expiry:
1181 for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
1182 const std::vector<SplitInfo> si_list = encryptionItems( concreteCryptoMessageFormats[i] );
1183 for ( std::vector<SplitInfo>::const_iterator sit = si_list.begin() ; sit != si_list.end() ; ++sit )
1184 for ( std::vector<GpgME::Key>::const_iterator kit = sit->keys.begin() ; kit != sit->keys.end() ; ++kit ) {
1185 const Kpgp::Result r = checkKeyNearExpiry( *kit, "other encryption key near expiry warning",
1186 false, false );
1187 if ( r != Kpgp::Ok )
1188 return r;
1192 // 4. Check that we have the right keys for encryptToSelf()
1194 if ( !encryptToSelf() )
1195 return Kpgp::Ok;
1197 // 4a. Check for OpenPGP keys
1199 kDebug() << "sizes of encryption items:" << encryptionItems( InlineOpenPGPFormat ).size() << encryptionItems( OpenPGPMIMEFormat ).size() << encryptionItems( SMIMEFormat ).size() << encryptionItems( SMIMEOpaqueFormat ).size();
1200 if ( !encryptionItems( InlineOpenPGPFormat ).empty() ||
1201 !encryptionItems( OpenPGPMIMEFormat ).empty() ) {
1202 // need them
1203 if ( d->mOpenPGPEncryptToSelfKeys.empty() ) {
1204 const QString msg = i18n("Examination of recipient's encryption preferences "
1205 "yielded that the message should be encrypted using "
1206 "OpenPGP, at least for some recipients;\n"
1207 "however, you have not configured valid trusted "
1208 "OpenPGP encryption keys for this identity.\n"
1209 "You may continue without encrypting to yourself, "
1210 "but be aware that you will not be able to read your "
1211 "own messages if you do so.");
1212 if ( KMessageBox::warningContinueCancel( 0, msg,
1213 i18n("Unusable Encryption Keys"),
1214 KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
1215 QLatin1String("encrypt-to-self will fail warning") )
1216 == KMessageBox::Cancel )
1217 return Kpgp::Canceled;
1218 // FIXME: Allow selection
1220 addToAllSplitInfos( d->mOpenPGPEncryptToSelfKeys,
1221 InlineOpenPGPFormat|OpenPGPMIMEFormat );
1224 // 4b. Check for S/MIME certs:
1226 if ( !encryptionItems( SMIMEFormat ).empty() ||
1227 !encryptionItems( SMIMEOpaqueFormat ).empty() ) {
1228 // need them
1229 if ( d->mSMIMEEncryptToSelfKeys.empty() ) {
1230 // don't have one
1231 const QString msg = i18n("Examination of recipient's encryption preferences "
1232 "yielded that the message should be encrypted using "
1233 "S/MIME, at least for some recipients;\n"
1234 "however, you have not configured valid "
1235 "S/MIME encryption certificates for this identity.\n"
1236 "You may continue without encrypting to yourself, "
1237 "but be aware that you will not be able to read your "
1238 "own messages if you do so.");
1239 if ( KMessageBox::warningContinueCancel( 0, msg,
1240 i18n("Unusable Encryption Keys"),
1241 KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
1242 QLatin1String("encrypt-to-self will fail warning") )
1243 == KMessageBox::Cancel )
1244 return Kpgp::Canceled;
1245 // FIXME: Allow selection
1247 addToAllSplitInfos( d->mSMIMEEncryptToSelfKeys,
1248 SMIMEFormat|SMIMEOpaqueFormat );
1251 // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
1252 // are missing.
1254 return Kpgp::Ok;
1257 Kpgp::Result Kleo::KeyResolver::resolveSigningKeysForEncryption() {
1258 if ( ( !encryptionItems( InlineOpenPGPFormat ).empty() ||
1259 !encryptionItems( OpenPGPMIMEFormat ).empty() )
1260 && d->mOpenPGPSigningKeys.empty() ) {
1261 const QString msg = i18n("Examination of recipient's signing preferences "
1262 "yielded that the message should be signed using "
1263 "OpenPGP, at least for some recipients;\n"
1264 "however, you have not configured valid "
1265 "OpenPGP signing certificates for this identity.");
1266 if ( KMessageBox::warningContinueCancel( 0, msg,
1267 i18n("Unusable Signing Keys"),
1268 KGuiItem(i18n("Do Not OpenPGP-Sign")),
1269 KStandardGuiItem::cancel(),
1270 QLatin1String("signing will fail warning") )
1271 == KMessageBox::Cancel )
1272 return Kpgp::Canceled;
1273 // FIXME: Allow selection
1275 if ( ( !encryptionItems( SMIMEFormat ).empty() ||
1276 !encryptionItems( SMIMEOpaqueFormat ).empty() )
1277 && d->mSMIMESigningKeys.empty() ) {
1278 const QString msg = i18n("Examination of recipient's signing preferences "
1279 "yielded that the message should be signed using "
1280 "S/MIME, at least for some recipients;\n"
1281 "however, you have not configured valid "
1282 "S/MIME signing certificates for this identity.");
1283 if ( KMessageBox::warningContinueCancel( 0, msg,
1284 i18n("Unusable Signing Keys"),
1285 KGuiItem(i18n("Do Not S/MIME-Sign")),
1286 KStandardGuiItem::cancel(),
1287 QLatin1String("signing will fail warning") )
1288 == KMessageBox::Cancel )
1289 return Kpgp::Canceled;
1290 // FIXME: Allow selection
1293 // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
1294 // are missing.
1296 for ( std::map<CryptoMessageFormat,FormatInfo>::iterator it = d->mFormatInfoMap.begin() ; it != d->mFormatInfoMap.end() ; ++it )
1297 if ( !it->second.splitInfos.empty() ) {
1298 dump();
1299 it->second.signKeys = signingKeysFor( it->first );
1300 dump();
1303 return Kpgp::Ok;
1306 Kpgp::Result Kleo::KeyResolver::resolveSigningKeysForSigningOnly() {
1308 // we don't need to distinguish between primary and secondary
1309 // recipients here:
1311 SigningFormatPreferenceCounter count;
1312 count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
1313 count );
1314 count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
1315 count );
1317 // try to find a common format that works for all (and that we have signing keys for):
1319 CryptoMessageFormat commonFormat = AutoFormat;
1321 for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
1322 if ( !(mCryptoMessageFormats & concreteCryptoMessageFormats[i]) )
1323 continue; // skip
1324 if ( signingKeysFor( concreteCryptoMessageFormats[i] ).empty() )
1325 continue; // skip
1326 if ( count.numOf( concreteCryptoMessageFormats[i] ) == count.numTotal() ) {
1327 commonFormat = concreteCryptoMessageFormats[i];
1328 break;
1332 if ( commonFormat != AutoFormat ) { // found
1333 dump();
1334 FormatInfo & fi = d->mFormatInfoMap[ commonFormat ];
1335 fi.signKeys = signingKeysFor( commonFormat );
1336 fi.splitInfos.resize( 1 );
1337 fi.splitInfos.front() = SplitInfo( allRecipients() );
1338 dump();
1339 return Kpgp::Ok;
1342 const QString msg = i18n("Examination of recipient's signing preferences "
1343 "showed no common type of signature matching your "
1344 "available signing keys.\n"
1345 "Send message without signing?" );
1346 if ( KMessageBox::warningContinueCancel( 0, msg, i18n("No signing possible"),
1347 KStandardGuiItem::cont() )
1348 == KMessageBox::Continue ) {
1349 d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.push_back( SplitInfo( allRecipients() ) );
1350 return Kpgp::Failure; // means "Ok, but without signing"
1352 return Kpgp::Canceled;
1355 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeysFor( CryptoMessageFormat f ) const {
1356 if ( isOpenPGP( f ) )
1357 return d->mOpenPGPSigningKeys;
1358 if ( isSMIME( f ) )
1359 return d->mSMIMESigningKeys;
1360 return std::vector<GpgME::Key>();
1363 std::vector<GpgME::Key> Kleo::KeyResolver::encryptToSelfKeysFor( CryptoMessageFormat f ) const {
1364 if ( isOpenPGP( f ) )
1365 return d->mOpenPGPEncryptToSelfKeys;
1366 if ( isSMIME( f ) )
1367 return d->mSMIMEEncryptToSelfKeys;
1368 return std::vector<GpgME::Key>();
1371 QStringList Kleo::KeyResolver::allRecipients() const {
1372 QStringList result;
1373 std::transform( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
1374 std::back_inserter( result ), ItemDotAddress );
1375 std::transform( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
1376 std::back_inserter( result ), ItemDotAddress );
1377 return result;
1380 void Kleo::KeyResolver::collapseAllSplitInfos() {
1381 dump();
1382 for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
1383 std::map<CryptoMessageFormat,FormatInfo>::iterator pos =
1384 d->mFormatInfoMap.find( concreteCryptoMessageFormats[i] );
1385 if ( pos == d->mFormatInfoMap.end() )
1386 continue;
1387 std::vector<SplitInfo> & v = pos->second.splitInfos;
1388 if ( v.size() < 2 )
1389 continue;
1390 SplitInfo & si = v.front();
1391 for ( std::vector<SplitInfo>::const_iterator it = v.begin() + 1; it != v.end() ; ++it ) {
1392 si.keys.insert( si.keys.end(), it->keys.begin(), it->keys.end() );
1393 qCopy( it->recipients.begin(), it->recipients.end(), std::back_inserter( si.recipients ) );
1395 v.resize( 1 );
1397 dump();
1400 void Kleo::KeyResolver::addToAllSplitInfos( const std::vector<GpgME::Key> & keys, unsigned int f ) {
1401 dump();
1402 if ( !f || keys.empty() )
1403 return;
1404 for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
1405 if ( !( f & concreteCryptoMessageFormats[i] ) )
1406 continue;
1407 std::map<CryptoMessageFormat,FormatInfo>::iterator pos =
1408 d->mFormatInfoMap.find( concreteCryptoMessageFormats[i] );
1409 if ( pos == d->mFormatInfoMap.end() )
1410 continue;
1411 std::vector<SplitInfo> & v = pos->second.splitInfos;
1412 for ( std::vector<SplitInfo>::iterator it = v.begin() ; it != v.end() ; ++it )
1413 it->keys.insert( it->keys.end(), keys.begin(), keys.end() );
1415 dump();
1418 void Kleo::KeyResolver::dump() const {
1419 #ifndef NDEBUG
1420 if ( d->mFormatInfoMap.empty() )
1421 std::cerr << "Keyresolver: Format info empty" << std::endl;
1422 for ( std::map<CryptoMessageFormat,FormatInfo>::const_iterator it = d->mFormatInfoMap.begin() ; it != d->mFormatInfoMap.end() ; ++it ) {
1423 std::cerr << "Format info for " << Kleo::cryptoMessageFormatToString( it->first )
1424 << ":" << std::endl
1425 << " Signing keys: ";
1426 for ( std::vector<GpgME::Key>::const_iterator sit = it->second.signKeys.begin() ; sit != it->second.signKeys.end() ; ++sit )
1427 std::cerr << sit->shortKeyID() << " ";
1428 std::cerr << std::endl;
1429 unsigned int i = 0;
1430 for ( std::vector<SplitInfo>::const_iterator sit = it->second.splitInfos.begin() ; sit != it->second.splitInfos.end() ; ++sit, ++i ) {
1431 std::cerr << " SplitInfo #" << i << " encryption keys: ";
1432 for ( std::vector<GpgME::Key>::const_iterator kit = sit->keys.begin() ; kit != sit->keys.end() ; ++kit )
1433 std::cerr << kit->shortKeyID() << " ";
1434 std::cerr << std::endl
1435 << " SplitInfo #" << i << " recipients: "
1436 << qPrintable(sit->recipients.join( QLatin1String(", ") )) << std::endl;
1439 #endif
1442 Kpgp::Result Kleo::KeyResolver::showKeyApprovalDialog() {
1443 const bool showKeysForApproval = showApprovalDialog()
1444 || std::find_if( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
1445 ApprovalNeeded ) != d->mPrimaryEncryptionKeys.end()
1446 || std::find_if( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
1447 ApprovalNeeded ) != d->mSecondaryEncryptionKeys.end() ;
1449 if ( !showKeysForApproval )
1450 return Kpgp::Ok;
1452 std::vector<Kleo::KeyApprovalDialog::Item> items;
1453 items.reserve( d->mPrimaryEncryptionKeys.size() +
1454 d->mSecondaryEncryptionKeys.size() );
1455 std::copy( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
1456 std::back_inserter( items ) );
1457 std::copy( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
1458 std::back_inserter( items ) );
1460 std::vector<GpgME::Key> senderKeys;
1461 senderKeys.reserve( d->mOpenPGPEncryptToSelfKeys.size() +
1462 d->mSMIMEEncryptToSelfKeys.size() );
1463 std::copy( d->mOpenPGPEncryptToSelfKeys.begin(), d->mOpenPGPEncryptToSelfKeys.end(),
1464 std::back_inserter( senderKeys ) );
1465 std::copy( d->mSMIMEEncryptToSelfKeys.begin(), d->mSMIMEEncryptToSelfKeys.end(),
1466 std::back_inserter( senderKeys ) );
1468 const MessageViewer::KCursorSaver idle( MessageViewer::KBusyPtr::idle() );
1470 #ifdef QT_QT3SUPPORT_FOUND
1471 Kleo::KeyApprovalDialog dlg( items, senderKeys );
1473 if ( dlg.exec() == QDialog::Rejected )
1474 return Kpgp::Canceled;
1476 items = dlg.items();
1477 senderKeys = dlg.senderKeys();
1479 if ( dlg.preferencesChanged() ) {
1480 for ( uint i = 0; i < items.size(); ++i ) {
1481 ContactPreferences pref = lookupContactPreferences( items[i].address );
1482 pref.encryptionPreference = items[i].pref;
1483 pref.pgpKeyFingerprints.clear();
1484 pref.smimeCertFingerprints.clear();
1485 const std::vector<GpgME::Key> & keys = items[i].keys;
1486 for ( std::vector<GpgME::Key>::const_iterator it = keys.begin(), end = keys.end() ; it != end ; ++it ) {
1487 if ( it->protocol() == GpgME::OpenPGP ) {
1488 if ( const char * fpr = it->primaryFingerprint() )
1489 pref.pgpKeyFingerprints.push_back( QLatin1String( fpr ) );
1490 } else if ( it->protocol() == GpgME::CMS ) {
1491 if ( const char * fpr = it->primaryFingerprint() )
1492 pref.smimeCertFingerprints.push_back( QLatin1String( fpr ) );
1495 saveContactPreference( items[i].address, pref );
1498 #endif
1500 // show a warning if the user didn't select an encryption key for
1501 // herself:
1502 if ( encryptToSelf() && senderKeys.empty() ) {
1503 const QString msg = i18n("You did not select an encryption key for yourself "
1504 "(encrypt to self). You will not be able to decrypt "
1505 "your own message if you encrypt it.");
1506 if ( KMessageBox::warningContinueCancel( 0, msg,
1507 i18n("Missing Key Warning"),
1508 KGuiItem(i18n("&Encrypt")) )
1509 == KMessageBox::Cancel )
1510 return Kpgp::Canceled;
1511 else
1512 mEncryptToSelf = false;
1515 // count empty key ID lists
1516 const unsigned int emptyListCount =
1517 std::count_if( items.begin(), items.end(), EmptyKeyList );
1519 // show a warning if the user didn't select an encryption key for
1520 // some of the recipients
1521 if ( items.size() == emptyListCount ) {
1522 const QString msg = ( d->mPrimaryEncryptionKeys.size() +
1523 d->mSecondaryEncryptionKeys.size() == 1 )
1524 ? i18n("You did not select an encryption key for the "
1525 "recipient of this message; therefore, the message "
1526 "will not be encrypted.")
1527 : i18n("You did not select an encryption key for any of the "
1528 "recipients of this message; therefore, the message "
1529 "will not be encrypted.");
1530 if ( KMessageBox::warningContinueCancel( 0, msg,
1531 i18n("Missing Key Warning"),
1532 KGuiItem(i18n("Send &Unencrypted")) )
1533 == KMessageBox::Cancel )
1534 return Kpgp::Canceled;
1535 } else if ( emptyListCount > 0 ) {
1536 const QString msg = ( emptyListCount == 1 )
1537 ? i18n("You did not select an encryption key for one of "
1538 "the recipients: this person will not be able to "
1539 "decrypt the message if you encrypt it.")
1540 : i18n("You did not select encryption keys for some of "
1541 "the recipients: these persons will not be able to "
1542 "decrypt the message if you encrypt it." );
1543 MessageViewer::KCursorSaver idle( MessageViewer::KBusyPtr::idle() );
1544 if ( KMessageBox::warningContinueCancel( 0, msg,
1545 i18n("Missing Key Warning"),
1546 KGuiItem(i18n("&Encrypt")) )
1547 == KMessageBox::Cancel )
1548 return Kpgp::Canceled;
1551 std::transform( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
1552 items.begin(),
1553 d->mPrimaryEncryptionKeys.begin(),
1554 CopyKeysAndEncryptionPreferences );
1555 std::transform( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
1556 items.begin() + d->mPrimaryEncryptionKeys.size(),
1557 d->mSecondaryEncryptionKeys.begin(),
1558 CopyKeysAndEncryptionPreferences );
1560 d->mOpenPGPEncryptToSelfKeys.clear();
1561 d->mSMIMEEncryptToSelfKeys.clear();
1563 std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
1564 std::back_inserter( d->mOpenPGPEncryptToSelfKeys ),
1565 NotValidTrustedOpenPGPEncryptionKey ); // -= trusted (see above, too)?
1566 std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
1567 std::back_inserter( d->mSMIMEEncryptToSelfKeys ),
1568 NotValidTrustedSMIMEEncryptionKey ); // -= trusted (see above, too)?
1570 return Kpgp::Ok;
1573 std::vector<Kleo::KeyResolver::SplitInfo> Kleo::KeyResolver::encryptionItems( Kleo::CryptoMessageFormat f ) const {
1574 dump();
1575 std::map<CryptoMessageFormat,FormatInfo>::const_iterator it =
1576 d->mFormatInfoMap.find( f );
1577 return it != d->mFormatInfoMap.end() ? it->second.splitInfos : std::vector<SplitInfo>() ;
1580 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeys( CryptoMessageFormat f ) const {
1581 dump();
1582 std::map<CryptoMessageFormat,FormatInfo>::const_iterator it =
1583 d->mFormatInfoMap.find( f );
1584 return it != d->mFormatInfoMap.end() ? it->second.signKeys : std::vector<GpgME::Key>() ;
1589 // Private helper methods below:
1594 std::vector<GpgME::Key> Kleo::KeyResolver::selectKeys( const QString & person, const QString & msg, const std::vector<GpgME::Key> & selectedKeys ) const {
1595 const bool opgp = containsOpenPGP( mCryptoMessageFormats );
1596 const bool x509 = containsSMIME( mCryptoMessageFormats );
1598 #ifdef QT_QT3SUPPORT_FOUND
1599 Kleo::KeySelectionDialog dlg( i18n("Encryption Key Selection"),
1600 msg, selectedKeys,
1601 Kleo::KeySelectionDialog::ValidEncryptionKeys
1602 & ~(opgp ? 0 : Kleo::KeySelectionDialog::OpenPGPKeys)
1603 & ~(x509 ? 0 : Kleo::KeySelectionDialog::SMIMEKeys),
1604 true, true ); // multi-selection and "remember choice" box
1606 if ( dlg.exec() != QDialog::Accepted )
1607 return std::vector<GpgME::Key>();
1608 std::vector<GpgME::Key> keys = dlg.selectedKeys();
1609 keys.erase( std::remove_if( keys.begin(), keys.end(),
1610 NotValidTrustedEncryptionKey ), // -= trusted?
1611 keys.end() );
1612 if ( !keys.empty() && dlg.rememberSelection() )
1613 setKeysForAddress( person, dlg.pgpKeyFingerprints(), dlg.smimeFingerprints() );
1614 return keys;
1615 #else
1616 return std::vector<GpgME::Key>();
1617 #endif
1621 std::vector<GpgME::Key> Kleo::KeyResolver::getEncryptionKeys( const QString & person, bool quiet ) const {
1623 const QString address = canonicalAddress( person ).toLower();
1625 // First look for this person's address in the address->key dictionary
1626 const QStringList fingerprints = keysForAddress( address );
1628 if ( !fingerprints.empty() ) {
1629 kDebug() << "Using encryption keys 0x"
1630 << fingerprints.join( QLatin1String(", 0x") )
1631 << "for" << person;
1632 std::vector<GpgME::Key> keys = lookup( fingerprints );
1633 if ( !keys.empty() ) {
1634 // Check if all of the keys are trusted and valid encryption keys
1635 if ( std::find_if( keys.begin(), keys.end(),
1636 NotValidTrustedEncryptionKey ) != keys.end() ) { // -= trusted?
1637 // not ok, let the user select: this is not conditional on !quiet,
1638 // since it's a bug in the configuration and the user should be
1639 // notified about it as early as possible:
1640 keys = selectKeys( person,
1641 i18nc("if in your language something like "
1642 "'key(s)' is not possible please "
1643 "use the plural in the translation",
1644 "There is a problem with the "
1645 "encryption key(s) for \"%1\".\n\n"
1646 "Please re-select the key(s) which should "
1647 "be used for this recipient.", person),
1648 keys );
1650 bool canceled = false;
1651 keys = trustedOrConfirmed( keys, address, canceled );
1652 if ( canceled )
1653 return std::vector<GpgME::Key>();
1655 if ( !keys.empty() )
1656 return keys;
1657 // keys.empty() is considered cancel by callers, so go on
1661 // Now search all public keys for matching keys
1662 std::vector<GpgME::Key> matchingKeys = lookup( QStringList( person ) );
1663 matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
1664 NotValidEncryptionKey ), matchingKeys.end() );
1665 // if no keys match the complete address look for keys which match
1666 // the canonical mail address
1667 if ( matchingKeys.empty() ) {
1668 matchingKeys = lookup( QStringList( address ) );
1669 matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
1670 NotValidEncryptionKey ),
1671 matchingKeys.end() );
1674 // if called with quite == true (from EncryptionPreferenceCounter), we only want to
1675 // check if there are keys for this recipients, not (yet) their validity, so
1676 // don't show the untrusted encryption key warning in that case
1677 bool canceled = false;
1678 if ( !quiet )
1679 matchingKeys = trustedOrConfirmed( matchingKeys, address, canceled );
1680 if ( canceled )
1681 return std::vector<GpgME::Key>();
1682 if ( quiet || matchingKeys.size() == 1 )
1683 return matchingKeys;
1685 // no match until now, or more than one key matches; let the user
1686 // choose the key(s)
1687 // FIXME: let user get the key from keyserver
1688 return trustedOrConfirmed( selectKeys( person,
1689 matchingKeys.empty()
1690 ? i18nc("if in your language something like "
1691 "'key(s)' is not possible please "
1692 "use the plural in the translation",
1693 "<qt>No valid and trusted encryption key was "
1694 "found for \"%1\".<br/><br/>"
1695 "Select the key(s) which should "
1696 "be used for this recipient. If there is no suitable key in the list "
1697 "you can also <a href=\"%2\">search for external keys</a>.</qt>",
1698 Qt::escape( person ),
1699 QLatin1String( QUrl::toPercentEncoding( KPIMUtils::extractEmailAddress( person) ) ) )
1700 : i18nc("if in your language something like "
1701 "'key(s)' is not possible please "
1702 "use the plural in the translation",
1703 "More than one key matches \"%1\".\n\n"
1704 "Select the key(s) which should "
1705 "be used for this recipient.", person),
1706 matchingKeys ), address, canceled );
1707 // we can ignore 'canceled' here, since trustedOrConfirmed() returns
1708 // an empty vector when canceled == true, and we'd just do the same
1712 std::vector<GpgME::Key> Kleo::KeyResolver::lookup( const QStringList & patterns, bool secret ) const {
1713 if ( patterns.empty() )
1714 return std::vector<GpgME::Key>();
1715 kDebug() << "( \"" << patterns.join( QLatin1String("\", \"") ) << "\"," << secret << ")";
1716 std::vector<GpgME::Key> result;
1717 if ( mCryptoMessageFormats & (InlineOpenPGPFormat|OpenPGPMIMEFormat) )
1718 if ( const Kleo::CryptoBackend::Protocol * p = Kleo::CryptoBackendFactory::instance()->openpgp() ) {
1719 std::auto_ptr<Kleo::KeyListJob> job( p->keyListJob( false, false, true ) ); // use validating keylisting
1720 if ( job.get() ) {
1721 std::vector<GpgME::Key> keys;
1722 job->exec( patterns, secret, keys );
1723 result.insert( result.end(), keys.begin(), keys.end() );
1726 if ( mCryptoMessageFormats & (SMIMEFormat|SMIMEOpaqueFormat) )
1727 if ( const Kleo::CryptoBackend::Protocol * p = Kleo::CryptoBackendFactory::instance()->smime() ) {
1728 std::auto_ptr<Kleo::KeyListJob> job( p->keyListJob( false, false, true ) ); // use validating keylisting
1729 if ( job.get() ) {
1730 std::vector<GpgME::Key> keys;
1731 job->exec( patterns, secret, keys );
1732 result.insert( result.end(), keys.begin(), keys.end() );
1735 kDebug() << " returned" << result.size() << "keys";
1736 return result;
1739 void Kleo::KeyResolver::addKeys( const std::vector<Item> & items, CryptoMessageFormat f ) {
1740 dump();
1741 for ( std::vector<Item>::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
1742 SplitInfo si( QStringList( it->address ) );
1743 std::remove_copy_if( it->keys.begin(), it->keys.end(),
1744 std::back_inserter( si.keys ), IsNotForFormat( f ) );
1745 dump();
1746 kWarning( si.keys.empty() )
1747 << "Kleo::KeyResolver::addKeys(): Fix EncryptionFormatPreferenceCounter."
1748 << "It detected a common format, but the list of such keys for recipient \""
1749 << it->address << "\" is empty!";
1750 d->mFormatInfoMap[ f ].splitInfos.push_back( si );
1752 dump();
1755 void Kleo::KeyResolver::addKeys( const std::vector<Item> & items ) {
1756 dump();
1757 for ( std::vector<Item>::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
1758 SplitInfo si( QStringList( it->address ) );
1759 CryptoMessageFormat f = AutoFormat;
1760 for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
1761 const CryptoMessageFormat fmt = concreteCryptoMessageFormats[i];
1762 if ( ( fmt & it->format ) &&
1763 kdtools::any( it->keys.begin(), it->keys.end(), IsForFormat( fmt ) ) )
1765 f = fmt;
1766 break;
1769 if ( f == AutoFormat )
1770 kWarning() << "Something went wrong. Didn't find a format for \""
1771 << it->address << "\"";
1772 else
1773 std::remove_copy_if( it->keys.begin(), it->keys.end(),
1774 std::back_inserter( si.keys ), IsNotForFormat( f ) );
1775 d->mFormatInfoMap[ f ].splitInfos.push_back( si );
1777 dump();
1780 Kleo::KeyResolver::ContactPreferences Kleo::KeyResolver::lookupContactPreferences( const QString& address ) const
1782 const Private::ContactPreferencesMap::iterator it =
1783 d->mContactPreferencesMap.find( address );
1784 if ( it != d->mContactPreferencesMap.end() )
1785 return it->second;
1787 Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob();
1788 job->setLimit( 1 );
1789 job->setQuery( Akonadi::ContactSearchJob::Email, address );
1790 job->exec();
1792 const KABC::Addressee::List res = job->contacts();
1793 ContactPreferences pref;
1794 if ( !res.isEmpty() ) {
1795 KABC::Addressee addr = res.first();
1796 QString encryptPref = addr.custom( QLatin1String("KADDRESSBOOK"), QLatin1String("CRYPTOENCRYPTPREF") );
1797 pref.encryptionPreference = Kleo::stringToEncryptionPreference( encryptPref );
1798 QString signPref = addr.custom( QLatin1String("KADDRESSBOOK"), QLatin1String("CRYPTOSIGNPREF") );
1799 pref.signingPreference = Kleo::stringToSigningPreference( signPref );
1800 QString cryptoFormats = addr.custom( QLatin1String("KADDRESSBOOK"), QLatin1String("CRYPTOPROTOPREF") );
1801 pref.cryptoMessageFormat = Kleo::stringToCryptoMessageFormat( cryptoFormats );
1802 pref.pgpKeyFingerprints = addr.custom( QLatin1String("KADDRESSBOOK"), QLatin1String("OPENPGPFP") ).split( QLatin1Char(','), QString::SkipEmptyParts );
1803 pref.smimeCertFingerprints = addr.custom( QLatin1String("KADDRESSBOOK"), QLatin1String("SMIMEFP") ).split( QLatin1Char(','), QString::SkipEmptyParts );
1805 // insert into map and grab resulting iterator
1806 d->mContactPreferencesMap.insert( std::make_pair( address, pref ) );
1807 return pref;
1810 void Kleo::KeyResolver::writeCustomContactProperties( KABC::Addressee &contact, const ContactPreferences& pref ) const
1812 contact.insertCustom( QLatin1String("KADDRESSBOOK"), QLatin1String("CRYPTOENCRYPTPREF"), QLatin1String( Kleo::encryptionPreferenceToString( pref.encryptionPreference ) ) );
1813 contact.insertCustom( QLatin1String("KADDRESSBOOK"), QLatin1String("CRYPTOSIGNPREF"), QLatin1String( Kleo::signingPreferenceToString( pref.signingPreference ) ) );
1814 contact.insertCustom( QLatin1String("KADDRESSBOOK"), QLatin1String("CRYPTOPROTOPREF"), QLatin1String( cryptoMessageFormatToString( pref.cryptoMessageFormat ) ) );
1815 contact.insertCustom( QLatin1String("KADDRESSBOOK"), QLatin1String("OPENPGPFP"), pref.pgpKeyFingerprints.join( QLatin1String(",") ) );
1816 contact.insertCustom( QLatin1String("KADDRESSBOOK"), QLatin1String("SMIMEFP"), pref.smimeCertFingerprints.join( QLatin1String(",") ) );
1819 void Kleo::KeyResolver::saveContactPreference( const QString& email, const ContactPreferences& pref ) const
1821 d->mContactPreferencesMap.insert( std::make_pair( email, pref ) );
1823 Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob();
1824 job->setLimit( 1 );
1825 job->setQuery( Akonadi::ContactSearchJob::Email, email );
1826 job->exec();
1828 const Akonadi::Item::List items = job->items();
1830 if ( items.isEmpty() ) {
1831 bool ok = true;
1832 const QString fullName = KInputDialog::getText( i18n( "Name Selection" ), i18n( "Which name shall the contact '%1' have in your address book?", email ), QString(), &ok );
1833 if ( !ok )
1834 return;
1836 Akonadi::CollectionDialog dlg;
1837 dlg.setMimeTypeFilter( QStringList() << KABC::Addressee::mimeType() );
1838 dlg.setAccessRightsFilter( Akonadi::Collection::CanCreateItem );
1839 dlg.setDescription( i18n( "Select the address book folder to store the new contact in:" ) );
1840 if ( !dlg.exec() )
1841 return;
1843 const Akonadi::Collection targetCollection = dlg.selectedCollection();
1845 KABC::Addressee contact;
1846 contact.setNameFromString( fullName );
1847 contact.insertEmail( email, true );
1848 writeCustomContactProperties( contact, pref );
1850 Akonadi::Item item( KABC::Addressee::mimeType() );
1851 item.setPayload<KABC::Addressee>( contact );
1853 new Akonadi::ItemCreateJob( item, targetCollection );
1854 } else {
1855 Akonadi::Item item = items.first();
1857 KABC::Addressee contact = item.payload<KABC::Addressee>();
1858 writeCustomContactProperties( contact, pref );
1860 item.setPayload<KABC::Addressee>( contact );
1862 new Akonadi::ItemModifyJob( item );
1865 // Assumption: 'pref' comes from d->mContactPreferencesMap already, no need to update that
1868 Kleo::KeyResolver::ContactPreferences::ContactPreferences()
1869 : encryptionPreference( UnknownPreference ),
1870 signingPreference( UnknownSigningPreference ),
1871 cryptoMessageFormat( AutoFormat )
1875 QStringList Kleo::KeyResolver::keysForAddress( const QString & address ) const {
1876 if( address.isEmpty() ) {
1877 return QStringList();
1879 QString addr = canonicalAddress( address ).toLower();
1880 const ContactPreferences pref = lookupContactPreferences( addr );
1881 return pref.pgpKeyFingerprints + pref.smimeCertFingerprints;
1884 void Kleo::KeyResolver::setKeysForAddress( const QString& address, const QStringList& pgpKeyFingerprints, const QStringList& smimeCertFingerprints ) const {
1885 if( address.isEmpty() ) {
1886 return;
1888 QString addr = canonicalAddress( address ).toLower();
1889 ContactPreferences pref = lookupContactPreferences( addr );
1890 pref.pgpKeyFingerprints = pgpKeyFingerprints;
1891 pref.smimeCertFingerprints = smimeCertFingerprints;
1892 saveContactPreference( addr, pref );