summaryrefslogtreecommitdiffstats
path: root/kmail/keyresolver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmail/keyresolver.cpp')
-rw-r--r--kmail/keyresolver.cpp1615
1 files changed, 1615 insertions, 0 deletions
diff --git a/kmail/keyresolver.cpp b/kmail/keyresolver.cpp
new file mode 100644
index 000000000..2161031fb
--- /dev/null
+++ b/kmail/keyresolver.cpp
@@ -0,0 +1,1615 @@
+/* -*- c++ -*-
+ keyresolver.cpp
+
+ This file is part of libkleopatra, the KDE keymanagement library
+ Copyright (c) 2004 Klarälvdalens Datakonsult AB
+
+ Based on kpgp.cpp
+ Copyright (C) 2001,2002 the KPGP authors
+ See file libkdenetwork/AUTHORS.kpgp for details
+
+ Libkleopatra is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ Libkleopatra is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ Qt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "keyresolver.h"
+
+#include "kcursorsaver.h"
+#include "kleo_util.h"
+
+#include <libemailfunctions/email.h>
+#include <ui/keyselectiondialog.h>
+#include <kleo/cryptobackendfactory.h>
+#include <kleo/keylistjob.h>
+#include <kleo/dn.h>
+
+#include <gpgmepp/key.h>
+#include <gpgmepp/keylistresult.h>
+
+#include <kabc/stdaddressbook.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+
+#include <qstringlist.h>
+#include <qtl.h>
+
+#include <time.h>
+
+#include <algorithm>
+#include <memory>
+#include <iterator>
+#include <functional>
+#include <map>
+#include <set>
+#include <iostream>
+#include <cassert>
+
+
+//
+// some predicates to be used in STL algorithms:
+//
+
+static inline bool EmptyKeyList( const Kleo::KeyApprovalDialog::Item & item ) {
+ return item.keys.empty();
+}
+
+static inline QString ItemDotAddress( const Kleo::KeyResolver::Item & item ) {
+ return item.address;
+}
+
+static inline bool ApprovalNeeded( const Kleo::KeyResolver::Item & item ) {
+ return item.pref == Kleo::UnknownPreference || item.pref == Kleo::NeverEncrypt || item.keys.empty() ;
+}
+
+static inline Kleo::KeyResolver::Item
+CopyKeysAndEncryptionPreferences( const Kleo::KeyResolver::Item & oldItem,
+ const Kleo::KeyApprovalDialog::Item & newItem ) {
+ return Kleo::KeyResolver::Item( oldItem.address, newItem.keys, newItem.pref, oldItem.signPref, oldItem.format );
+}
+
+static inline bool ByKeyID( const GpgME::Key & left, const GpgME::Key & right ) {
+ return qstrcmp( left.keyID(), right.keyID() ) < 0 ;
+}
+
+static inline bool WithRespectToKeyID( const GpgME::Key & left, const GpgME::Key & right ) {
+ return qstrcmp( left.keyID(), right.keyID() ) == 0 ;
+}
+
+static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
+ if ( key.protocol() != GpgME::Context::OpenPGP ) {
+ return false;
+ }
+#if 0
+ if ( key.isRevoked() )
+ kdWarning() << " is revoked" << endl;
+ if ( key.isExpired() )
+ kdWarning() << " is expired" << endl;
+ if ( key.isDisabled() )
+ kdWarning() << " is disabled" << endl;
+ if ( !key.canEncrypt() )
+ kdWarning() << " can't encrypt" << endl;
+#endif
+ if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() )
+ return false;
+ const std::vector<GpgME::UserID> uids = key.userIDs();
+ for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it ) {
+ if ( !it->isRevoked() && it->validity() != GpgME::UserID::Marginal )
+ return true;
+#if 0
+ else
+ if ( it->isRevoked() )
+ kdWarning() << "a userid is revoked" << endl;
+ else
+ kdWarning() << "bad validity " << it->validity() << endl;
+#endif
+ }
+ return false;
+}
+
+static bool ValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
+ if ( key.protocol() != GpgME::Context::CMS )
+ return false;
+ if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() )
+ return false;
+ return true;
+}
+
+static inline bool ValidTrustedEncryptionKey( const GpgME::Key & key ) {
+ switch ( key.protocol() ) {
+ case GpgME::Context::OpenPGP:
+ return ValidTrustedOpenPGPEncryptionKey( key );
+ case GpgME::Context::CMS:
+ return ValidTrustedSMIMEEncryptionKey( key );
+ default:
+ return false;
+ }
+}
+
+static inline bool ValidSigningKey( const GpgME::Key & key ) {
+ if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canSign() )
+ return false;
+ return key.hasSecret();
+}
+
+static inline bool ValidOpenPGPSigningKey( const GpgME::Key & key ) {
+ return key.protocol() == GpgME::Context::OpenPGP && ValidSigningKey( key );
+}
+
+static inline bool ValidSMIMESigningKey( const GpgME::Key & key ) {
+ return key.protocol() == GpgME::Context::CMS && ValidSigningKey( key );
+}
+
+static inline bool NotValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
+ return !ValidTrustedOpenPGPEncryptionKey( key );
+}
+
+static inline bool NotValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
+ return !ValidTrustedSMIMEEncryptionKey( key );
+}
+
+static inline bool NotValidTrustedEncryptionKey( const GpgME::Key & key ) {
+ return !ValidTrustedEncryptionKey( key );
+}
+
+static inline bool NotValidSigningKey( const GpgME::Key & key ) {
+ return !ValidSigningKey( key );
+}
+
+static inline bool NotValidOpenPGPSigningKey( const GpgME::Key & key ) {
+ return !ValidOpenPGPSigningKey( key );
+}
+
+static inline bool NotValidSMIMESigningKey( const GpgME::Key & key ) {
+ return !ValidSMIMESigningKey( key );
+}
+
+static QStringList keysAsStrings( const std::vector<GpgME::Key>& keys ) {
+ QStringList strings;
+ for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it ) {
+ assert( !(*it).userID(0).isNull() );
+ QString keyLabel = QString::fromUtf8( (*it).userID(0).email() );
+ if ( keyLabel.isEmpty() )
+ keyLabel = QString::fromUtf8( (*it).userID(0).name() );
+ if ( keyLabel.isEmpty() )
+ keyLabel = QString::fromUtf8( (*it).userID(0).id() );
+ strings.append( keyLabel );
+ }
+ return strings;
+}
+
+static inline std::vector<GpgME::Key> TrustedOrConfirmed( const std::vector<GpgME::Key> & keys ) {
+
+ std::vector<GpgME::Key> fishies;
+ std::vector<GpgME::Key> ickies;
+ std::vector<GpgME::Key>::const_iterator it = keys.begin();
+ const std::vector<GpgME::Key>::const_iterator end = keys.end();
+ for ( ; it != end ; it++ ) {
+ const GpgME::Key key = *it;
+ assert( ValidTrustedEncryptionKey( key ) );
+ const std::vector<GpgME::UserID> uids = key.userIDs();
+ for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it ) {
+ if ( !it->isRevoked() && it->validity() == GpgME::UserID::Marginal ) {
+ fishies.push_back( key );
+ break;
+ }
+ if ( !it->isRevoked() && it->validity() < GpgME::UserID::Never ) {
+ ickies.push_back( key );
+ break;
+ }
+ }
+ }
+
+ if ( fishies.empty() && ickies.empty() )
+ return keys;
+
+ // if some keys are not fully trusted, let the user confirm their use
+ QString msg = i18n("One or more of your configured OpenPGP encryption "
+ "keys or S/MIME certificates is not fully trusted "
+ "for encryption.");
+
+ if ( !fishies.empty() ) {
+ // certificates can't have marginal trust
+ msg += i18n( "\nThe following keys are only marginally trusted: \n");
+ msg += keysAsStrings( fishies ).join(",");
+ }
+ if ( !ickies.empty() ) {
+ msg += i18n( "\nThe following keys or certificates have unknown trust level: \n");
+ msg += keysAsStrings( ickies ).join(",");
+ }
+
+ if( KMessageBox::warningContinueCancel( 0, msg, i18n("Not Fully Trusted Encryption Keys"),
+ KStdGuiItem::cont(),
+ "not fully trusted encryption key warning" )
+ == KMessageBox::Continue )
+ return keys;
+ else
+ return std::vector<GpgME::Key>();
+}
+
+namespace {
+ struct IsNotForFormat : public std::unary_function<GpgME::Key,bool> {
+ IsNotForFormat( Kleo::CryptoMessageFormat f ) : format( f ) {}
+
+ bool operator()( const GpgME::Key & key ) const {
+ return
+ ( isOpenPGP( format ) && key.protocol() != GpgME::Context::OpenPGP ) ||
+ ( isSMIME( format ) && key.protocol() != GpgME::Context::CMS );
+ }
+
+ const Kleo::CryptoMessageFormat format;
+ };
+}
+
+
+
+class Kleo::KeyResolver::SigningPreferenceCounter : public std::unary_function<Kleo::KeyResolver::Item,void> {
+public:
+ SigningPreferenceCounter()
+ : mTotal( 0 ),
+ mUnknownSigningPreference( 0 ),
+ mNeverSign( 0 ),
+ mAlwaysSign( 0 ),
+ mAlwaysSignIfPossible( 0 ),
+ mAlwaysAskForSigning( 0 ),
+ mAskSigningWheneverPossible( 0 )
+ {
+
+ }
+ void operator()( const Kleo::KeyResolver::Item & item );
+#define make_int_accessor(x) unsigned int num##x() const { return m##x; }
+ make_int_accessor(UnknownSigningPreference)
+ make_int_accessor(NeverSign)
+ make_int_accessor(AlwaysSign)
+ make_int_accessor(AlwaysSignIfPossible)
+ make_int_accessor(AlwaysAskForSigning)
+ make_int_accessor(AskSigningWheneverPossible)
+ make_int_accessor(Total)
+#undef make_int_accessor
+private:
+ unsigned int mTotal;
+ unsigned int mUnknownSigningPreference, mNeverSign, mAlwaysSign,
+ mAlwaysSignIfPossible, mAlwaysAskForSigning, mAskSigningWheneverPossible;
+};
+
+void Kleo::KeyResolver::SigningPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
+ switch ( item.signPref ) {
+#define CASE(x) case x: ++m##x; break
+ CASE(UnknownSigningPreference);
+ CASE(NeverSign);
+ CASE(AlwaysSign);
+ CASE(AlwaysSignIfPossible);
+ CASE(AlwaysAskForSigning);
+ CASE(AskSigningWheneverPossible);
+#undef CASE
+ }
+ ++mTotal;
+}
+
+
+
+class Kleo::KeyResolver::EncryptionPreferenceCounter : public std::unary_function<Item,void> {
+ const Kleo::KeyResolver * _this;
+public:
+ EncryptionPreferenceCounter( const Kleo::KeyResolver * kr, EncryptionPreference defaultPreference )
+ : _this( kr ),
+ mDefaultPreference( defaultPreference ),
+ mTotal( 0 ),
+ mNoKey( 0 ),
+ mNeverEncrypt( 0 ),
+ mUnknownPreference( 0 ),
+ mAlwaysEncrypt( 0 ),
+ mAlwaysEncryptIfPossible( 0 ),
+ mAlwaysAskForEncryption( 0 ),
+ mAskWheneverPossible( 0 )
+ {
+
+ }
+ void operator()( Item & item );
+
+#define make_int_accessor(x) unsigned int num##x() const { return m##x; }
+ make_int_accessor(NoKey)
+ make_int_accessor(NeverEncrypt)
+ make_int_accessor(UnknownPreference)
+ make_int_accessor(AlwaysEncrypt)
+ make_int_accessor(AlwaysEncryptIfPossible)
+ make_int_accessor(AlwaysAskForEncryption)
+ make_int_accessor(AskWheneverPossible)
+ make_int_accessor(Total)
+#undef make_int_accessor
+private:
+ EncryptionPreference mDefaultPreference;
+ unsigned int mTotal;
+ unsigned int mNoKey;
+ unsigned int mNeverEncrypt, mUnknownPreference, mAlwaysEncrypt,
+ mAlwaysEncryptIfPossible, mAlwaysAskForEncryption, mAskWheneverPossible;
+};
+
+void Kleo::KeyResolver::EncryptionPreferenceCounter::operator()( Item & item ) {
+ if ( item.needKeys )
+ item.keys = _this->getEncryptionKeys( item.address, true );
+ if ( item.keys.empty() ) {
+ ++mNoKey;
+ return;
+ }
+ switch ( !item.pref ? mDefaultPreference : item.pref ) {
+#define CASE(x) case Kleo::x: ++m##x; break
+ CASE(NeverEncrypt);
+ CASE(UnknownPreference);
+ CASE(AlwaysEncrypt);
+ CASE(AlwaysEncryptIfPossible);
+ CASE(AlwaysAskForEncryption);
+ CASE(AskWheneverPossible);
+#undef CASE
+ }
+ ++mTotal;
+}
+
+namespace {
+
+ class FormatPreferenceCounterBase : public std::unary_function<Kleo::KeyResolver::Item,void> {
+ public:
+ FormatPreferenceCounterBase()
+ : mTotal( 0 ),
+ mInlineOpenPGP( 0 ),
+ mOpenPGPMIME( 0 ),
+ mSMIME( 0 ),
+ mSMIMEOpaque( 0 )
+ {
+
+ }
+
+#define make_int_accessor(x) unsigned int num##x() const { return m##x; }
+ make_int_accessor(Total)
+ make_int_accessor(InlineOpenPGP)
+ make_int_accessor(OpenPGPMIME)
+ make_int_accessor(SMIME)
+ make_int_accessor(SMIMEOpaque)
+#undef make_int_accessor
+
+ unsigned int numOf( Kleo::CryptoMessageFormat f ) const {
+ switch ( f ) {
+#define CASE(x) case Kleo::x##Format: return m##x
+ CASE(InlineOpenPGP);
+ CASE(OpenPGPMIME);
+ CASE(SMIME);
+ CASE(SMIMEOpaque);
+#undef CASE
+ default: return 0;
+ }
+ }
+
+ protected:
+ unsigned int mTotal;
+ unsigned int mInlineOpenPGP, mOpenPGPMIME, mSMIME, mSMIMEOpaque;
+ };
+
+ class EncryptionFormatPreferenceCounter : public FormatPreferenceCounterBase {
+ public:
+ EncryptionFormatPreferenceCounter() : FormatPreferenceCounterBase() {}
+ void operator()( const Kleo::KeyResolver::Item & item );
+ };
+
+ class SigningFormatPreferenceCounter : public FormatPreferenceCounterBase {
+ public:
+ SigningFormatPreferenceCounter() : FormatPreferenceCounterBase() {}
+ void operator()( const Kleo::KeyResolver::Item & item );
+ };
+
+#define CASE(x) if ( item.format & Kleo::x##Format ) ++m##x;
+ void EncryptionFormatPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
+ if ( item.format & (Kleo::InlineOpenPGPFormat|Kleo::OpenPGPMIMEFormat) &&
+ std::find_if( item.keys.begin(), item.keys.end(),
+ ValidTrustedOpenPGPEncryptionKey ) != item.keys.end() ) {
+ CASE(OpenPGPMIME);
+ CASE(InlineOpenPGP);
+ }
+ if ( item.format & (Kleo::SMIMEFormat|Kleo::SMIMEOpaqueFormat) &&
+ std::find_if( item.keys.begin(), item.keys.end(),
+ ValidTrustedSMIMEEncryptionKey ) != item.keys.end() ) {
+ CASE(SMIME);
+ CASE(SMIMEOpaque);
+ }
+ ++mTotal;
+ }
+
+ void SigningFormatPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
+ CASE(InlineOpenPGP);
+ CASE(OpenPGPMIME);
+ CASE(SMIME);
+ CASE(SMIMEOpaque);
+ ++mTotal;
+ }
+#undef CASE
+
+} // anon namespace
+
+static QString canonicalAddress( const QString & _address ) {
+ const QString address = KPIM::getEmailAddress( _address );
+ if ( address.find('@') == -1 ) {
+ // local address
+ //char hostname[1024];
+ //gethostname(hostname,1024);
+ //return address + '@' + hostname;
+ return address + "@localdomain";
+ }
+ else
+ return address;
+}
+
+
+struct FormatInfo {
+ std::vector<Kleo::KeyResolver::SplitInfo> splitInfos;
+ std::vector<GpgME::Key> signKeys;
+};
+
+struct Kleo::KeyResolver::Private {
+ std::set<QCString> alreadyWarnedFingerprints;
+
+ std::vector<GpgME::Key> mOpenPGPSigningKeys; // signing
+ std::vector<GpgME::Key> mSMIMESigningKeys; // signing
+
+ std::vector<GpgME::Key> mOpenPGPEncryptToSelfKeys; // encryption to self
+ std::vector<GpgME::Key> mSMIMEEncryptToSelfKeys; // encryption to self
+
+ std::vector<Item> mPrimaryEncryptionKeys; // encryption to To/CC
+ std::vector<Item> mSecondaryEncryptionKeys; // encryption to BCC
+
+ std::map<CryptoMessageFormat,FormatInfo> mFormatInfoMap;
+
+ // key=email address, value=crypto preferences for this contact (from kabc)
+ typedef std::map<QString, ContactPreferences> ContactPreferencesMap;
+ ContactPreferencesMap mContactPreferencesMap;
+};
+
+
+Kleo::KeyResolver::KeyResolver( bool encToSelf, bool showApproval, bool oppEncryption,
+ unsigned int f,
+ int encrWarnThresholdKey, int signWarnThresholdKey,
+ int encrWarnThresholdRootCert, int signWarnThresholdRootCert,
+ int encrWarnThresholdChainCert, int signWarnThresholdChainCert )
+ : mEncryptToSelf( encToSelf ),
+ mShowApprovalDialog( showApproval ),
+ mOpportunisticEncyption( oppEncryption ),
+ mCryptoMessageFormats( f ),
+ mEncryptKeyNearExpiryWarningThreshold( encrWarnThresholdKey ),
+ mSigningKeyNearExpiryWarningThreshold( signWarnThresholdKey ),
+ mEncryptRootCertNearExpiryWarningThreshold( encrWarnThresholdRootCert ),
+ mSigningRootCertNearExpiryWarningThreshold( signWarnThresholdRootCert ),
+ mEncryptChainCertNearExpiryWarningThreshold( encrWarnThresholdChainCert ),
+ mSigningChainCertNearExpiryWarningThreshold( signWarnThresholdChainCert )
+{
+ d = new Private();
+}
+
+Kleo::KeyResolver::~KeyResolver() {
+ delete d; d = 0;
+}
+
+Kpgp::Result Kleo::KeyResolver::checkKeyNearExpiry( const GpgME::Key & key, const char * dontAskAgainName,
+ bool mine, bool sign, bool ca,
+ int recur_limit, const GpgME::Key & orig ) const {
+ if ( recur_limit <= 0 ) {
+ kdDebug() << "Kleo::KeyResolver::checkKeyNearExpiry(): key chain too long (>100 certs)" << endl;
+ return Kpgp::Ok;
+ }
+ const GpgME::Subkey subkey = key.subkey(0);
+ if ( d->alreadyWarnedFingerprints.count( subkey.fingerprint() ) )
+ return Kpgp::Ok; // already warned about this one (and so about it's issuers)
+ d->alreadyWarnedFingerprints.insert( subkey.fingerprint() );
+
+ if ( subkey.neverExpires() )
+ return Kpgp::Ok;
+ static const double secsPerDay = 24 * 60 * 60;
+ const int daysTillExpiry =
+ 1 + int( ::difftime( subkey.expirationTime(), time(0) ) / secsPerDay );
+ kdDebug() << "Key 0x" << key.shortKeyID() << " expires in less than "
+ << daysTillExpiry << " days" << endl;
+ const int threshold =
+ ca
+ ? ( key.isRoot()
+ ? ( sign
+ ? signingRootCertNearExpiryWarningThresholdInDays()
+ : encryptRootCertNearExpiryWarningThresholdInDays() )
+ : ( sign
+ ? signingChainCertNearExpiryWarningThresholdInDays()
+ : encryptChainCertNearExpiryWarningThresholdInDays() ) )
+ : ( sign
+ ? signingKeyNearExpiryWarningThresholdInDays()
+ : encryptKeyNearExpiryWarningThresholdInDays() );
+ if ( threshold > -1 && daysTillExpiry <= threshold ) {
+ const QString msg =
+ key.protocol() == GpgME::Context::OpenPGP
+ ? ( mine ? sign
+ ? i18n("<p>Your OpenPGP signing key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>Your OpenPGP signing key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry )
+ : i18n("<p>Your OpenPGP encryption key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>Your OpenPGP encryption key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry )
+ : i18n("<p>The OpenPGP key for</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>The OpenPGP key for</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry ) ).arg( QString::fromUtf8( key.userID(0).id() ),
+ key.shortKeyID() )
+ : ( ca
+ ? ( key.isRoot()
+ ? ( mine ? sign
+ ? i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry )
+ : i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry )
+ : i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>The root certificate</p><p align=center><b>%3</b></p>"
+ "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry ) )
+ : ( mine ? sign
+ ? i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry )
+ : i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry )
+ : i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
+ "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry ) ) ).arg( Kleo::DN( orig.userID(0).id() ).prettyDN(),
+ orig.issuerSerial(),
+ Kleo::DN( key.userID(0).id() ).prettyDN() )
+ : ( mine ? sign
+ ? i18n("<p>Your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>Your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry )
+ : i18n("<p>Your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>Your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry )
+ : i18n("<p>The S/MIME certificate for</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than a day.</p>",
+ "<p>The S/MIME certificate for</p><p align=center><b>%1</b> (serial number %2)</p>"
+ "<p>expires in less than %n days.</p>",
+ daysTillExpiry ) ).arg( Kleo::DN( key.userID(0).id() ).prettyDN(),
+ key.issuerSerial() ) );
+ if ( KMessageBox::warningContinueCancel( 0, msg,
+ key.protocol() == GpgME::Context::OpenPGP
+ ? i18n("OpenPGP Key Expires Soon" )
+ : i18n("S/MIME Certificate Expires Soon" ),
+ KStdGuiItem::cont(), dontAskAgainName )
+ == KMessageBox::Cancel )
+ return Kpgp::Canceled;
+ }
+ if ( key.isRoot() )
+ return Kpgp::Ok;
+ else if ( const char * chain_id = key.chainID() ) {
+ const std::vector<GpgME::Key> issuer = lookup( chain_id, false );
+ if ( issuer.empty() )
+ return Kpgp::Ok;
+ else
+ return checkKeyNearExpiry( issuer.front(), dontAskAgainName, mine, sign,
+ true, recur_limit-1, ca ? orig : key );
+ }
+ return Kpgp::Ok;
+}
+
+Kpgp::Result Kleo::KeyResolver::setEncryptToSelfKeys( const QStringList & fingerprints ) {
+ if ( !encryptToSelf() )
+ return Kpgp::Ok;
+
+ std::vector<GpgME::Key> keys = lookup( fingerprints );
+ std::remove_copy_if( keys.begin(), keys.end(),
+ std::back_inserter( d->mOpenPGPEncryptToSelfKeys ),
+ NotValidTrustedOpenPGPEncryptionKey );
+ std::remove_copy_if( keys.begin(), keys.end(),
+ std::back_inserter( d->mSMIMEEncryptToSelfKeys ),
+ NotValidTrustedSMIMEEncryptionKey );
+
+ if ( d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size()
+ < keys.size() ) {
+ // too few keys remain...
+ const QString msg = i18n("One or more of your configured OpenPGP encryption "
+ "keys or S/MIME certificates is not usable for "
+ "encryption. Please reconfigure your encryption keys "
+ "and certificates for this identity in the identity "
+ "configuration dialog.\n"
+ "If you choose to continue, and the keys are needed "
+ "later on, you will be prompted to specify the keys "
+ "to use.");
+ return KMessageBox::warningContinueCancel( 0, msg, i18n("Unusable Encryption Keys"),
+ KStdGuiItem::cont(),
+ "unusable own encryption key warning" )
+ == KMessageBox::Continue ? Kpgp::Ok : Kpgp::Canceled ;
+ }
+
+ // check for near-expiry:
+
+ for ( std::vector<GpgME::Key>::const_iterator it = d->mOpenPGPEncryptToSelfKeys.begin() ; it != d->mOpenPGPEncryptToSelfKeys.end() ; ++it ) {
+ const Kpgp::Result r = checkKeyNearExpiry( *it, "own encryption key expires soon warning",
+ true, false );
+ if ( r != Kpgp::Ok )
+ return r;
+ }
+
+ for ( std::vector<GpgME::Key>::const_iterator it = d->mSMIMEEncryptToSelfKeys.begin() ; it != d->mSMIMEEncryptToSelfKeys.end() ; ++it ) {
+ const Kpgp::Result r = checkKeyNearExpiry( *it, "own encryption key expires soon warning",
+ true, false );
+ if ( r != Kpgp::Ok )
+ return r;
+ }
+
+ return Kpgp::Ok;
+}
+
+Kpgp::Result Kleo::KeyResolver::setSigningKeys( const QStringList & fingerprints ) {
+ std::vector<GpgME::Key> keys = lookup( fingerprints, true ); // secret keys
+ std::remove_copy_if( keys.begin(), keys.end(),
+ std::back_inserter( d->mOpenPGPSigningKeys ),
+ NotValidOpenPGPSigningKey );
+ std::remove_copy_if( keys.begin(), keys.end(),
+ std::back_inserter( d->mSMIMESigningKeys ),
+ NotValidSMIMESigningKey );
+
+ if ( d->mOpenPGPSigningKeys.size() + d->mSMIMESigningKeys.size() < keys.size() ) {
+ // too few keys remain...
+ const QString msg = i18n("One or more of your configured OpenPGP signing keys "
+ "or S/MIME signing certificates is not usable for "
+ "signing. Please reconfigure your signing keys "
+ "and certificates for this identity in the identity "
+ "configuration dialog.\n"
+ "If you choose to continue, and the keys are needed "
+ "later on, you will be prompted to specify the keys "
+ "to use.");
+ return KMessageBox::warningContinueCancel( 0, msg, i18n("Unusable Signing Keys"),
+ KStdGuiItem::cont(),
+ "unusable signing key warning" )
+ == KMessageBox::Continue ? Kpgp::Ok : Kpgp::Canceled ;
+ }
+
+ // check for near expiry:
+
+ for ( std::vector<GpgME::Key>::const_iterator it = d->mOpenPGPSigningKeys.begin() ; it != d->mOpenPGPSigningKeys.end() ; ++it ) {
+ const Kpgp::Result r = checkKeyNearExpiry( *it, "signing key expires soon warning",
+ true, true );
+ if ( r != Kpgp::Ok )
+ return r;
+ }
+
+ for ( std::vector<GpgME::Key>::const_iterator it = d->mSMIMESigningKeys.begin() ; it != d->mSMIMESigningKeys.end() ; ++it ) {
+ const Kpgp::Result r = checkKeyNearExpiry( *it, "signing key expires soon warning",
+ true, true );
+ if ( r != Kpgp::Ok )
+ return r;
+ }
+
+ return Kpgp::Ok;
+}
+
+void Kleo::KeyResolver::setPrimaryRecipients( const QStringList & addresses ) {
+ d->mPrimaryEncryptionKeys = getEncryptionItems( addresses );
+}
+
+void Kleo::KeyResolver::setSecondaryRecipients( const QStringList & addresses ) {
+ d->mSecondaryEncryptionKeys = getEncryptionItems( addresses );
+}
+
+std::vector<Kleo::KeyResolver::Item> Kleo::KeyResolver::getEncryptionItems( const QStringList & addresses ) {
+ std::vector<Item> items;
+ items.reserve( addresses.size() );
+ for ( QStringList::const_iterator it = addresses.begin() ; it != addresses.end() ; ++it ) {
+ QString addr = canonicalAddress( *it ).lower();
+ const ContactPreferences pref = lookupContactPreferences( addr );
+
+ items.push_back( Item( *it, /*getEncryptionKeys( *it, true ),*/
+ pref.encryptionPreference,
+ pref.signingPreference,
+ pref.cryptoMessageFormat ) );
+ }
+ return items;
+}
+
+static Kleo::Action action( bool doit, bool ask, bool dont, bool requested ) {
+ if ( requested && !dont )
+ return Kleo::DoIt;
+ if ( doit && !ask && !dont )
+ return Kleo::DoIt;
+ if ( !doit && ask && !dont )
+ return Kleo::Ask;
+ if ( !doit && !ask && dont )
+ return requested ? Kleo::Conflict : Kleo::DontDoIt ;
+ if ( !doit && !ask && !dont )
+ return Kleo::DontDoIt ;
+ return Kleo::Conflict;
+}
+
+Kleo::Action Kleo::KeyResolver::checkSigningPreferences( bool signingRequested ) const {
+
+ if ( signingRequested && d->mOpenPGPSigningKeys.empty() && d->mSMIMESigningKeys.empty() )
+ return Impossible;
+
+ SigningPreferenceCounter count;
+ count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ count );
+ count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ count );
+
+ unsigned int sign = count.numAlwaysSign();
+ unsigned int ask = count.numAlwaysAskForSigning();
+ const unsigned int dontSign = count.numNeverSign();
+ if ( signingPossible() ) {
+ sign += count.numAlwaysSignIfPossible();
+ ask += count.numAskSigningWheneverPossible();
+ }
+
+ return action( sign, ask, dontSign, signingRequested );
+}
+
+bool Kleo::KeyResolver::signingPossible() const {
+ return !d->mOpenPGPSigningKeys.empty() || !d->mSMIMESigningKeys.empty() ;
+}
+
+Kleo::Action Kleo::KeyResolver::checkEncryptionPreferences( bool encryptionRequested ) const {
+
+ if ( d->mPrimaryEncryptionKeys.empty() && d->mSecondaryEncryptionKeys.empty() )
+ return DontDoIt;
+
+ if ( encryptionRequested && encryptToSelf() &&
+ d->mOpenPGPEncryptToSelfKeys.empty() && d->mSMIMEEncryptToSelfKeys.empty() )
+ return Impossible;
+
+ EncryptionPreferenceCounter count( this, mOpportunisticEncyption ? AskWheneverPossible : UnknownPreference );
+ count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ count );
+ count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ count );
+
+ unsigned int encrypt = count.numAlwaysEncrypt();
+ unsigned int ask = count.numAlwaysAskForEncryption();
+ const unsigned int dontEncrypt = count.numNeverEncrypt() + count.numNoKey();
+ if ( encryptionPossible() ) {
+ encrypt += count.numAlwaysEncryptIfPossible();
+ ask += count.numAskWheneverPossible();
+ }
+
+ const Action act = action( encrypt, ask, dontEncrypt, encryptionRequested );
+ if ( act != Ask ||
+ std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ EncryptionPreferenceCounter( this, UnknownPreference ) ) ).numAlwaysAskForEncryption() )
+ return act;
+ else
+ return AskOpportunistic;
+}
+
+bool Kleo::KeyResolver::encryptionPossible() const {
+ return std::find_if( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ EmptyKeyList ) == d->mPrimaryEncryptionKeys.end()
+ && std::find_if( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ EmptyKeyList ) == d->mSecondaryEncryptionKeys.end() ;
+}
+
+Kpgp::Result Kleo::KeyResolver::resolveAllKeys( bool& signingRequested, bool& encryptionRequested ) {
+ if ( !encryptionRequested && !signingRequested ) {
+ // make a dummy entry with all recipients, but no signing or
+ // encryption keys to avoid special-casing on the caller side:
+ dump();
+ d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.push_back( SplitInfo( allRecipients() ) );
+ dump();
+ return Kpgp::Ok;
+ }
+ Kpgp::Result result = Kpgp::Ok;
+ if ( encryptionRequested )
+ result = resolveEncryptionKeys( signingRequested );
+ if ( result != Kpgp::Ok )
+ return result;
+ if ( signingRequested )
+ if ( encryptionRequested )
+ result = resolveSigningKeysForEncryption();
+ else {
+ result = resolveSigningKeysForSigningOnly();
+ if ( result == Kpgp::Failure ) {
+ signingRequested = false;
+ return Kpgp::Ok;
+ }
+ }
+ return result;
+}
+
+Kpgp::Result Kleo::KeyResolver::resolveEncryptionKeys( bool signingRequested ) {
+ //
+ // 1. Get keys for all recipients:
+ //
+
+ for ( std::vector<Item>::iterator it = d->mPrimaryEncryptionKeys.begin() ; it != d->mPrimaryEncryptionKeys.end() ; ++it ) {
+ if ( !it->needKeys )
+ continue;
+ it->keys = getEncryptionKeys( it->address, false );
+ if ( it->keys.empty() )
+ return Kpgp::Canceled;
+ QString addr = canonicalAddress( it->address ).lower();
+ const ContactPreferences pref = lookupContactPreferences( addr );
+ it->pref = pref.encryptionPreference;
+ it->signPref = pref.signingPreference;
+ it->format = pref.cryptoMessageFormat;
+ }
+
+ for ( std::vector<Item>::iterator it = d->mSecondaryEncryptionKeys.begin() ; it != d->mSecondaryEncryptionKeys.end() ; ++it ) {
+ if ( !it->needKeys )
+ continue;
+ it->keys = getEncryptionKeys( it->address, false );
+ if ( it->keys.empty() )
+ return Kpgp::Canceled;
+ QString addr = canonicalAddress( it->address ).lower();
+ const ContactPreferences pref = lookupContactPreferences( addr );
+ it->pref = pref.encryptionPreference;
+ it->signPref = pref.signingPreference;
+ it->format = pref.cryptoMessageFormat;
+ }
+
+ // 1a: Present them to the user
+
+ const Kpgp::Result res = showKeyApprovalDialog();
+ if ( res != Kpgp::Ok )
+ return res;
+
+ //
+ // 2. Check what the primary recipients need
+ //
+
+ // 2a. Try to find a common format for all primary recipients,
+ // else use as many formats as needed
+
+ const EncryptionFormatPreferenceCounter primaryCount
+ = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ EncryptionFormatPreferenceCounter() );
+
+ CryptoMessageFormat commonFormat = AutoFormat;
+ for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
+ if ( !( concreteCryptoMessageFormats[i] & mCryptoMessageFormats ) )
+ continue;
+ if ( signingRequested && signingKeysFor( concreteCryptoMessageFormats[i] ).empty() )
+ continue;
+ if ( encryptToSelf() && encryptToSelfKeysFor( concreteCryptoMessageFormats[i] ).empty() )
+ continue;
+ if ( primaryCount.numOf( concreteCryptoMessageFormats[i] ) == primaryCount.numTotal() ) {
+ commonFormat = concreteCryptoMessageFormats[i];
+ break;
+ }
+ }
+ if ( commonFormat != AutoFormat )
+ addKeys( d->mPrimaryEncryptionKeys, commonFormat );
+ else
+ addKeys( d->mPrimaryEncryptionKeys );
+
+ collapseAllSplitInfos(); // these can be encrypted together
+
+ // 2b. Just try to find _something_ for each secondary recipient,
+ // with a preference to a common format (if that exists)
+
+ const EncryptionFormatPreferenceCounter secondaryCount
+ = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ EncryptionFormatPreferenceCounter() );
+
+ if ( commonFormat != AutoFormat &&
+ secondaryCount.numOf( commonFormat ) == secondaryCount.numTotal() )
+ addKeys( d->mSecondaryEncryptionKeys, commonFormat );
+ else
+ addKeys( d->mSecondaryEncryptionKeys );
+
+ // 3. Check for expiry:
+
+ for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
+ const std::vector<SplitInfo> si_list = encryptionItems( concreteCryptoMessageFormats[i] );
+ for ( std::vector<SplitInfo>::const_iterator sit = si_list.begin() ; sit != si_list.end() ; ++sit )
+ for ( std::vector<GpgME::Key>::const_iterator kit = sit->keys.begin() ; kit != sit->keys.end() ; ++kit ) {
+ const Kpgp::Result r = checkKeyNearExpiry( *kit, "other encryption key near expiry warning",
+ false, false );
+ if ( r != Kpgp::Ok )
+ return r;
+ }
+ }
+
+ // 4. Check that we have the right keys for encryptToSelf()
+
+ if ( !encryptToSelf() )
+ return Kpgp::Ok;
+
+ // 4a. Check for OpenPGP keys
+
+ if ( !encryptionItems( InlineOpenPGPFormat ).empty() ||
+ !encryptionItems( OpenPGPMIMEFormat ).empty() ) {
+ // need them
+ if ( d->mOpenPGPEncryptToSelfKeys.empty() ) {
+ const QString msg = i18n("Examination of recipient's encryption preferences "
+ "yielded that the message should be encrypted using "
+ "OpenPGP, at least for some recipients;\n"
+ "however, you have not configured valid trusted "
+ "OpenPGP encryption keys for this identity.\n"
+ "You may continue without encrypting to yourself, "
+ "but be aware that you will not be able to read your "
+ "own messages if you do so.");
+ if ( KMessageBox::warningContinueCancel( 0, msg,
+ i18n("Unusable Encryption Keys"),
+ KStdGuiItem::cont(),
+ "encrypt-to-self will fail warning" )
+ == KMessageBox::Cancel )
+ return Kpgp::Canceled;
+ // FIXME: Allow selection
+ }
+ addToAllSplitInfos( d->mOpenPGPEncryptToSelfKeys,
+ InlineOpenPGPFormat|OpenPGPMIMEFormat );
+ }
+
+ // 4b. Check for S/MIME certs:
+
+ if ( !encryptionItems( SMIMEFormat ).empty() ||
+ !encryptionItems( SMIMEOpaqueFormat ).empty() ) {
+ // need them
+ if ( d->mSMIMEEncryptToSelfKeys.empty() ) {
+ // don't have one
+ const QString msg = i18n("Examination of recipient's encryption preferences "
+ "yielded that the message should be encrypted using "
+ "S/MIME, at least for some recipients;\n"
+ "however, you have not configured valid "
+ "S/MIME encryption certificates for this identity.\n"
+ "You may continue without encrypting to yourself, "
+ "but be aware that you will not be able to read your "
+ "own messages if you do so.");
+ if ( KMessageBox::warningContinueCancel( 0, msg,
+ i18n("Unusable Encryption Keys"),
+ KStdGuiItem::cont(),
+ "encrypt-to-self will fail warning" )
+ == KMessageBox::Cancel )
+ return Kpgp::Canceled;
+ // FIXME: Allow selection
+ }
+ addToAllSplitInfos( d->mSMIMEEncryptToSelfKeys,
+ SMIMEFormat|SMIMEOpaqueFormat );
+ }
+
+ // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
+ // are missing.
+
+ return Kpgp::Ok;
+}
+
+Kpgp::Result Kleo::KeyResolver::resolveSigningKeysForEncryption() {
+ if ( ( !encryptionItems( InlineOpenPGPFormat ).empty() ||
+ !encryptionItems( OpenPGPMIMEFormat ).empty() )
+ && d->mOpenPGPSigningKeys.empty() ) {
+ const QString msg = i18n("Examination of recipient's signing preferences "
+ "yielded that the message should be signed using "
+ "OpenPGP, at least for some recipients;\n"
+ "however, you have not configured valid "
+ "OpenPGP signing certificates for this identity.");
+ if ( KMessageBox::warningContinueCancel( 0, msg,
+ i18n("Unusable Signing Keys"),
+ i18n("Do Not OpenPGP-Sign"),
+ "signing will fail warning" )
+ == KMessageBox::Cancel )
+ return Kpgp::Canceled;
+ // FIXME: Allow selection
+ }
+ if ( ( !encryptionItems( SMIMEFormat ).empty() ||
+ !encryptionItems( SMIMEOpaqueFormat ).empty() )
+ && d->mSMIMESigningKeys.empty() ) {
+ const QString msg = i18n("Examination of recipient's signing preferences "
+ "yielded that the message should be signed using "
+ "S/MIME, at least for some recipients;\n"
+ "however, you have not configured valid "
+ "S/MIME signing certificates for this identity.");
+ if ( KMessageBox::warningContinueCancel( 0, msg,
+ i18n("Unusable Signing Keys"),
+ i18n("Do Not S/MIME-Sign"),
+ "signing will fail warning" )
+ == KMessageBox::Cancel )
+ return Kpgp::Canceled;
+ // FIXME: Allow selection
+ }
+
+ // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
+ // are missing.
+
+ for ( std::map<CryptoMessageFormat,FormatInfo>::iterator it = d->mFormatInfoMap.begin() ; it != d->mFormatInfoMap.end() ; ++it )
+ if ( !it->second.splitInfos.empty() ) {
+ dump();
+ it->second.signKeys = signingKeysFor( it->first );
+ dump();
+ }
+
+ return Kpgp::Ok;
+}
+
+Kpgp::Result Kleo::KeyResolver::resolveSigningKeysForSigningOnly() {
+ //
+ // we don't need to distinguish between primary and secondary
+ // recipients here:
+ //
+ SigningFormatPreferenceCounter count;
+ count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ count );
+ count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ count );
+
+ // try to find a common format that works for all (and that we have signing keys for):
+
+ CryptoMessageFormat commonFormat = AutoFormat;
+
+ for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
+ if ( !(mCryptoMessageFormats & concreteCryptoMessageFormats[i]) )
+ continue; // skip
+ if ( signingKeysFor( concreteCryptoMessageFormats[i] ).empty() )
+ continue; // skip
+ if ( count.numOf( concreteCryptoMessageFormats[i] ) == count.numTotal() ) {
+ commonFormat = concreteCryptoMessageFormats[i];
+ break;
+ }
+ }
+
+ if ( commonFormat != AutoFormat ) { // found
+ dump();
+ FormatInfo & fi = d->mFormatInfoMap[ commonFormat ];
+ fi.signKeys = signingKeysFor( commonFormat );
+ fi.splitInfos.resize( 1 );
+ fi.splitInfos.front() = SplitInfo( allRecipients() );
+ dump();
+ return Kpgp::Ok;
+ }
+
+ const QString msg = i18n("Examination of recipient's signing preferences "
+ "showed no common type of signature matching your "
+ "available signing keys.\n"
+ "Send message without signing?" );
+ if ( KMessageBox::warningContinueCancel( 0, msg, i18n("No signing possible"),
+ KStdGuiItem::cont() )
+ == KMessageBox::Continue ) {
+ d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.push_back( SplitInfo( allRecipients() ) );
+ return Kpgp::Failure; // means "Ok, but without signing"
+ }
+ return Kpgp::Canceled;
+}
+
+std::vector<GpgME::Key> Kleo::KeyResolver::signingKeysFor( CryptoMessageFormat f ) const {
+ if ( isOpenPGP( f ) )
+ return d->mOpenPGPSigningKeys;
+ if ( isSMIME( f ) )
+ return d->mSMIMESigningKeys;
+ return std::vector<GpgME::Key>();
+}
+
+std::vector<GpgME::Key> Kleo::KeyResolver::encryptToSelfKeysFor( CryptoMessageFormat f ) const {
+ if ( isOpenPGP( f ) )
+ return d->mOpenPGPEncryptToSelfKeys;
+ if ( isSMIME( f ) )
+ return d->mSMIMEEncryptToSelfKeys;
+ return std::vector<GpgME::Key>();
+}
+
+QStringList Kleo::KeyResolver::allRecipients() const {
+ QStringList result;
+ std::transform( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ std::back_inserter( result ), ItemDotAddress );
+ std::transform( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ std::back_inserter( result ), ItemDotAddress );
+ return result;
+}
+
+void Kleo::KeyResolver::collapseAllSplitInfos() {
+ dump();
+ for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
+ std::map<CryptoMessageFormat,FormatInfo>::iterator pos =
+ d->mFormatInfoMap.find( concreteCryptoMessageFormats[i] );
+ if ( pos == d->mFormatInfoMap.end() )
+ continue;
+ std::vector<SplitInfo> & v = pos->second.splitInfos;
+ if ( v.size() < 2 )
+ continue;
+ SplitInfo & si = v.front();
+ for ( std::vector<SplitInfo>::const_iterator it = v.begin() + 1; it != v.end() ; ++it ) {
+ si.keys.insert( si.keys.end(), it->keys.begin(), it->keys.end() );
+ qCopy( it->recipients.begin(), it->recipients.end(), std::back_inserter( si.recipients ) );
+ }
+ v.resize( 1 );
+ }
+ dump();
+}
+
+void Kleo::KeyResolver::addToAllSplitInfos( const std::vector<GpgME::Key> & keys, unsigned int f ) {
+ dump();
+ if ( !f || keys.empty() )
+ return;
+ for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
+ if ( !( f & concreteCryptoMessageFormats[i] ) )
+ continue;
+ std::map<CryptoMessageFormat,FormatInfo>::iterator pos =
+ d->mFormatInfoMap.find( concreteCryptoMessageFormats[i] );
+ if ( pos == d->mFormatInfoMap.end() )
+ continue;
+ std::vector<SplitInfo> & v = pos->second.splitInfos;
+ for ( std::vector<SplitInfo>::iterator it = v.begin() ; it != v.end() ; ++it )
+ it->keys.insert( it->keys.end(), keys.begin(), keys.end() );
+ }
+ dump();
+}
+
+void Kleo::KeyResolver::dump() const {
+#ifndef NDEBUG
+ if ( d->mFormatInfoMap.empty() )
+ std::cerr << "Keyresolver: Format info empty" << std::endl;
+ for ( std::map<CryptoMessageFormat,FormatInfo>::const_iterator it = d->mFormatInfoMap.begin() ; it != d->mFormatInfoMap.end() ; ++it ) {
+ std::cerr << "Format info for " << Kleo::cryptoMessageFormatToString( it->first )
+ << ":" << std::endl
+ << " Signing keys: ";
+ for ( std::vector<GpgME::Key>::const_iterator sit = it->second.signKeys.begin() ; sit != it->second.signKeys.end() ; ++sit )
+ std::cerr << sit->shortKeyID() << " ";
+ std::cerr << std::endl;
+ unsigned int i = 0;
+ for ( std::vector<SplitInfo>::const_iterator sit = it->second.splitInfos.begin() ; sit != it->second.splitInfos.end() ; ++sit, ++i ) {
+ std::cerr << " SplitInfo #" << i << " encryption keys: ";
+ for ( std::vector<GpgME::Key>::const_iterator kit = sit->keys.begin() ; kit != sit->keys.end() ; ++kit )
+ std::cerr << kit->shortKeyID() << " ";
+ std::cerr << std::endl
+ << " SplitInfo #" << i << " recipients: "
+ << sit->recipients.join(", ").utf8() << std::endl;
+ }
+ }
+#endif
+}
+
+Kpgp::Result Kleo::KeyResolver::showKeyApprovalDialog() {
+ const bool showKeysForApproval = showApprovalDialog()
+ || std::find_if( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ ApprovalNeeded ) != d->mPrimaryEncryptionKeys.end()
+ || std::find_if( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ ApprovalNeeded ) != d->mSecondaryEncryptionKeys.end() ;
+
+ if ( !showKeysForApproval )
+ return Kpgp::Ok;
+
+ std::vector<Kleo::KeyApprovalDialog::Item> items;
+ items.reserve( d->mPrimaryEncryptionKeys.size() +
+ d->mSecondaryEncryptionKeys.size() );
+ std::copy( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ std::back_inserter( items ) );
+ std::copy( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ std::back_inserter( items ) );
+
+ std::vector<GpgME::Key> senderKeys;
+ senderKeys.reserve( d->mOpenPGPEncryptToSelfKeys.size() +
+ d->mSMIMEEncryptToSelfKeys.size() );
+ std::copy( d->mOpenPGPEncryptToSelfKeys.begin(), d->mOpenPGPEncryptToSelfKeys.end(),
+ std::back_inserter( senderKeys ) );
+ std::copy( d->mSMIMEEncryptToSelfKeys.begin(), d->mSMIMEEncryptToSelfKeys.end(),
+ std::back_inserter( senderKeys ) );
+
+ const KCursorSaver idle( KBusyPtr::idle() );
+
+ Kleo::KeyApprovalDialog dlg( items, senderKeys );
+
+ if ( dlg.exec() == QDialog::Rejected )
+ return Kpgp::Canceled;
+
+ items = dlg.items();
+ senderKeys = dlg.senderKeys();
+
+ if ( dlg.preferencesChanged() ) {
+ for ( uint i = 0; i < items.size(); ++i ) {
+ ContactPreferences pref = lookupContactPreferences( items[i].address );
+ pref.encryptionPreference = items[i].pref;
+ pref.pgpKeyFingerprints.clear();
+ pref.smimeCertFingerprints.clear();
+ const std::vector<GpgME::Key> & keys = items[i].keys;
+ for ( std::vector<GpgME::Key>::const_iterator it = keys.begin(), end = keys.end() ; it != end ; ++it ) {
+ if ( it->protocol() == GpgME::Context::OpenPGP ) {
+ if ( const char * fpr = it->primaryFingerprint() )
+ pref.pgpKeyFingerprints.push_back( fpr );
+ } else if ( it->protocol() == GpgME::Context::CMS ) {
+ if ( const char * fpr = it->primaryFingerprint() )
+ pref.smimeCertFingerprints.push_back( fpr );
+ }
+ }
+ saveContactPreference( items[i].address, pref );
+ }
+ }
+
+ // show a warning if the user didn't select an encryption key for
+ // herself:
+ if ( encryptToSelf() && senderKeys.empty() ) {
+ const QString msg = i18n("You did not select an encryption key for yourself "
+ "(encrypt to self). You will not be able to decrypt "
+ "your own message if you encrypt it.");
+ if ( KMessageBox::warningContinueCancel( 0, msg,
+ i18n("Missing Key Warning"),
+ i18n("&Encrypt") )
+ == KMessageBox::Cancel )
+ return Kpgp::Canceled;
+ else
+ mEncryptToSelf = false;
+ }
+
+ // count empty key ID lists
+ const unsigned int emptyListCount =
+ std::count_if( items.begin(), items.end(), EmptyKeyList );
+
+ // show a warning if the user didn't select an encryption key for
+ // some of the recipients
+ if ( items.size() == emptyListCount ) {
+ const QString msg = ( d->mPrimaryEncryptionKeys.size() +
+ d->mSecondaryEncryptionKeys.size() == 1 )
+ ? i18n("You did not select an encryption key for the "
+ "recipient of this message; therefore, the message "
+ "will not be encrypted.")
+ : i18n("You did not select an encryption key for any of the "
+ "recipients of this message; therefore, the message "
+ "will not be encrypted.");
+ if ( KMessageBox::warningContinueCancel( 0, msg,
+ i18n("Missing Key Warning"),
+ i18n("Send &Unencrypted") )
+ == KMessageBox::Cancel )
+ return Kpgp::Canceled;
+ } else if ( emptyListCount > 0 ) {
+ const QString msg = ( emptyListCount == 1 )
+ ? i18n("You did not select an encryption key for one of "
+ "the recipients: this person will not be able to "
+ "decrypt the message if you encrypt it.")
+ : i18n("You did not select encryption keys for some of "
+ "the recipients: these persons will not be able to "
+ "decrypt the message if you encrypt it." );
+ KCursorSaver idle( KBusyPtr::idle() );
+ if ( KMessageBox::warningContinueCancel( 0, msg,
+ i18n("Missing Key Warning"),
+ i18n("&Encrypt") )
+ == KMessageBox::Cancel )
+ return Kpgp::Canceled;
+ }
+
+ std::transform( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
+ items.begin(),
+ d->mPrimaryEncryptionKeys.begin(),
+ CopyKeysAndEncryptionPreferences );
+ std::transform( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
+ items.begin() + d->mPrimaryEncryptionKeys.size(),
+ d->mSecondaryEncryptionKeys.begin(),
+ CopyKeysAndEncryptionPreferences );
+
+ d->mOpenPGPEncryptToSelfKeys.clear();
+ d->mSMIMEEncryptToSelfKeys.clear();
+
+ std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
+ std::back_inserter( d->mOpenPGPEncryptToSelfKeys ),
+ NotValidTrustedOpenPGPEncryptionKey );
+ std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
+ std::back_inserter( d->mSMIMEEncryptToSelfKeys ),
+ NotValidTrustedSMIMEEncryptionKey );
+
+ return Kpgp::Ok;
+}
+
+std::vector<Kleo::KeyResolver::SplitInfo> Kleo::KeyResolver::encryptionItems( Kleo::CryptoMessageFormat f ) const {
+ dump();
+ std::map<CryptoMessageFormat,FormatInfo>::const_iterator it =
+ d->mFormatInfoMap.find( f );
+ return it != d->mFormatInfoMap.end() ? it->second.splitInfos : std::vector<SplitInfo>() ;
+}
+
+std::vector<GpgME::Key> Kleo::KeyResolver::signingKeys( CryptoMessageFormat f ) const {
+ dump();
+ std::map<CryptoMessageFormat,FormatInfo>::const_iterator it =
+ d->mFormatInfoMap.find( f );
+ return it != d->mFormatInfoMap.end() ? it->second.signKeys : std::vector<GpgME::Key>() ;
+}
+
+//
+//
+// Private helper methods below:
+//
+//
+
+
+std::vector<GpgME::Key> Kleo::KeyResolver::selectKeys( const QString & person, const QString & msg, const std::vector<GpgME::Key> & selectedKeys ) const {
+ Kleo::KeySelectionDialog dlg( i18n("Encryption Key Selection"),
+ msg, selectedKeys,
+ Kleo::KeySelectionDialog::ValidEncryptionKeys,
+ true, true ); // multi-selection and "remember choice" box
+
+ if ( dlg.exec() != QDialog::Accepted )
+ return std::vector<GpgME::Key>();
+ std::vector<GpgME::Key> keys = dlg.selectedKeys();
+ keys.erase( std::remove_if( keys.begin(), keys.end(),
+ NotValidTrustedEncryptionKey ),
+ keys.end() );
+ if ( !keys.empty() && dlg.rememberSelection() )
+ setKeysForAddress( person, dlg.pgpKeyFingerprints(), dlg.smimeFingerprints() );
+ return keys;
+}
+
+
+std::vector<GpgME::Key> Kleo::KeyResolver::getEncryptionKeys( const QString & person, bool quiet ) const {
+
+ const QString address = canonicalAddress( person ).lower();
+
+ // First look for this person's address in the address->key dictionary
+ const QStringList fingerprints = keysForAddress( address );
+
+ if ( !fingerprints.empty() ) {
+ kdDebug() << "Using encryption keys 0x"
+ << fingerprints.join( ", 0x" )
+ << " for " << person << endl;
+ std::vector<GpgME::Key> keys = lookup( fingerprints );
+ if ( !keys.empty() ) {
+ // Check if all of the keys are trusted and valid encryption keys
+ if ( std::find_if( keys.begin(), keys.end(),
+ NotValidTrustedEncryptionKey ) != keys.end() ) {
+
+ // not ok, let the user select: this is not conditional on !quiet,
+ // since it's a bug in the configuration and the user should be
+ // notified about it as early as possible:
+ keys = selectKeys( person,
+ i18n("if in your language something like "
+ "'key(s)' isn't possible please "
+ "use the plural in the translation",
+ "There is a problem with the "
+ "encryption key(s) for \"%1\".\n\n"
+ "Please re-select the key(s) which should "
+ "be used for this recipient.").arg(person),
+ keys );
+ }
+ keys = TrustedOrConfirmed( keys );
+
+ if ( !keys.empty() )
+ return keys;
+ // hmmm, should we not return the keys in any case here?
+ }
+ }
+
+ // Now search all public keys for matching keys
+ std::vector<GpgME::Key> matchingKeys = lookup( person );
+ matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
+ NotValidTrustedEncryptionKey ),
+ matchingKeys.end() );
+ // if no keys match the complete address look for keys which match
+ // the canonical mail address
+ if ( matchingKeys.empty() ) {
+ matchingKeys = lookup( address );
+ matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
+ NotValidTrustedEncryptionKey ),
+ matchingKeys.end() );
+ }
+
+ // if called with quite == true (from EncryptionPreferenceCounter), we only want to
+ // check if there are keys for this recipients, not (yet) their validity, so
+ // don't show the untrusted encryption key warning in that case
+ if ( !quiet )
+ matchingKeys = TrustedOrConfirmed( matchingKeys );
+ if ( quiet || matchingKeys.size() == 1 )
+ return matchingKeys;
+
+ // no match until now, or more than one key matches; let the user
+ // choose the key(s)
+ // FIXME: let user get the key from keyserver
+ return TrustedOrConfirmed( selectKeys( person,
+ matchingKeys.empty()
+ ? i18n("if in your language something like "
+ "'key(s)' isn't possible please "
+ "use the plural in the translation",
+ "No valid and trusted encryption key was "
+ "found for \"%1\".\n\n"
+ "Select the key(s) which should "
+ "be used for this recipient.").arg(person)
+ : i18n("if in your language something like "
+ "'key(s)' isn't possible please "
+ "use the plural in the translation",
+ "More than one key matches \"%1\".\n\n"
+ "Select the key(s) which should "
+ "be used for this recipient.").arg(person),
+ matchingKeys ) );
+}
+
+
+std::vector<GpgME::Key> Kleo::KeyResolver::lookup( const QStringList & patterns, bool secret ) const {
+ if ( patterns.empty() )
+ return std::vector<GpgME::Key>();
+ kdDebug() << "Kleo::KeyResolver::lookup( \"" << patterns.join( "\", \"" )
+ << "\", " << secret << " )" << endl;
+ std::vector<GpgME::Key> result;
+ if ( mCryptoMessageFormats & (InlineOpenPGPFormat|OpenPGPMIMEFormat) )
+ if ( const Kleo::CryptoBackend::Protocol * p = Kleo::CryptoBackendFactory::instance()->openpgp() ) {
+ std::auto_ptr<Kleo::KeyListJob> job( p->keyListJob( false, false, true ) ); // use validating keylisting
+ if ( job.get() ) {
+ std::vector<GpgME::Key> keys;
+ job->exec( patterns, secret, keys );
+ result.insert( result.end(), keys.begin(), keys.end() );
+ }
+ }
+ if ( mCryptoMessageFormats & (SMIMEFormat|SMIMEOpaqueFormat) )
+ if ( const Kleo::CryptoBackend::Protocol * p = Kleo::CryptoBackendFactory::instance()->smime() ) {
+ std::auto_ptr<Kleo::KeyListJob> job( p->keyListJob( false, false, true ) ); // use validating keylisting
+ if ( job.get() ) {
+ std::vector<GpgME::Key> keys;
+ job->exec( patterns, secret, keys );
+ result.insert( result.end(), keys.begin(), keys.end() );
+ }
+ }
+ kdDebug() << " returned " << result.size() << " keys" << endl;
+ return result;
+}
+
+void Kleo::KeyResolver::addKeys( const std::vector<Item> & items, CryptoMessageFormat f ) {
+ dump();
+ for ( std::vector<Item>::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
+ SplitInfo si( it->address );
+ std::remove_copy_if( it->keys.begin(), it->keys.end(),
+ std::back_inserter( si.keys ), IsNotForFormat( f ) );
+ dump();
+ kdWarning( si.keys.empty() )
+ << "Kleo::KeyResolver::addKeys(): Fix EncryptionFormatPreferenceCounter. "
+ << "It detected a common format, but the list of such keys for recipient \""
+ << it->address << "\" is empty!" << endl;
+ d->mFormatInfoMap[ f ].splitInfos.push_back( si );
+ }
+ dump();
+}
+
+void Kleo::KeyResolver::addKeys( const std::vector<Item> & items ) {
+ dump();
+ for ( std::vector<Item>::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
+ SplitInfo si( it->address );
+ CryptoMessageFormat f = AutoFormat;
+ for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
+ if ( concreteCryptoMessageFormats[i] & it->format ) {
+ f = concreteCryptoMessageFormats[i];
+ break;
+ }
+ }
+ if ( f == AutoFormat )
+ kdWarning() << "Kleo::KeyResolver::addKeys(): Something went wrong. Didn't find a format for \""
+ << it->address << "\"" << endl;
+ else
+ std::remove_copy_if( it->keys.begin(), it->keys.end(),
+ std::back_inserter( si.keys ), IsNotForFormat( f ) );
+ d->mFormatInfoMap[ f ].splitInfos.push_back( si );
+ }
+ dump();
+}
+
+Kleo::KeyResolver::ContactPreferences Kleo::KeyResolver::lookupContactPreferences( const QString& address ) const
+{
+ const Private::ContactPreferencesMap::iterator it =
+ d->mContactPreferencesMap.find( address );
+ if ( it != d->mContactPreferencesMap.end() )
+ return it->second;
+
+ KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
+ const KABC::Addressee::List res = ab->findByEmail( address );
+ ContactPreferences pref;
+ if ( !res.isEmpty() ) {
+ KABC::Addressee addr = res.first();
+ QString encryptPref = addr.custom( "KADDRESSBOOK", "CRYPTOENCRYPTPREF" );
+ pref.encryptionPreference = Kleo::stringToEncryptionPreference( encryptPref );
+ QString signPref = addr.custom( "KADDRESSBOOK", "CRYPTOSIGNPREF" );
+ pref.signingPreference = Kleo::stringToSigningPreference( signPref );
+ QString cryptoFormats = addr.custom( "KADDRESSBOOK", "CRYPTOPROTOPREF" );
+ pref.cryptoMessageFormat = Kleo::stringToCryptoMessageFormat( cryptoFormats );
+ pref.pgpKeyFingerprints = QStringList::split( ',', addr.custom( "KADDRESSBOOK", "OPENPGPFP" ) );
+ pref.smimeCertFingerprints = QStringList::split( ',', addr.custom( "KADDRESSBOOK", "SMIMEFP" ) );
+ }
+ // insert into map and grab resulting iterator
+ d->mContactPreferencesMap.insert( std::make_pair( address, pref ) );
+ return pref;
+}
+
+void Kleo::KeyResolver::saveContactPreference( const QString& email, const ContactPreferences& pref ) const
+{
+ d->mContactPreferencesMap.insert( std::make_pair( email, pref ) );
+ KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
+ KABC::Addressee::List res = ab->findByEmail( email );
+
+ KABC::Addressee addr;
+ if ( res.isEmpty() ) {
+ bool ok = true;
+ QString fullName = KInputDialog::getText( i18n( "Name Selection" ), i18n( "Which name shall the contact '%1' have in your addressbook?" ).arg( email ), QString::null, &ok );
+ if ( ok ) {
+ addr.setNameFromString( fullName );
+ addr.insertEmail( email, true );
+ } else
+ return;
+ } else
+ addr = res.first();
+
+ addr.insertCustom( "KADDRESSBOOK", "CRYPTOENCRYPTPREF", Kleo::encryptionPreferenceToString( pref.encryptionPreference ) );
+ addr.insertCustom( "KADDRESSBOOK", "CRYPTOSIGNPREF", Kleo::signingPreferenceToString( pref.signingPreference ) );
+ addr.insertCustom( "KADDRESSBOOK", "CRYPTOPROTOPREF", cryptoMessageFormatToString( pref.cryptoMessageFormat ) );
+ addr.insertCustom( "KADDRESSBOOK", "OPENPGPFP", pref.pgpKeyFingerprints.join( "," ) );
+ addr.insertCustom( "KADDRESSBOOK", "SMIMEFP", pref.smimeCertFingerprints.join( "," ) );
+
+ ab->insertAddressee( addr );
+ KABC::Ticket *ticket = ab->requestSaveTicket( addr.resource() );
+ if ( ticket )
+ ab->save( ticket );
+
+ // Assumption: 'pref' comes from d->mContactPreferencesMap already, no need to update that
+}
+
+Kleo::KeyResolver::ContactPreferences::ContactPreferences()
+ : encryptionPreference( UnknownPreference ),
+ signingPreference( UnknownSigningPreference ),
+ cryptoMessageFormat( AutoFormat )
+{
+}
+
+QStringList Kleo::KeyResolver::keysForAddress( const QString & address ) const {
+ if( address.isEmpty() ) {
+ return QStringList();
+ }
+ QString addr = canonicalAddress( address ).lower();
+ const ContactPreferences pref = lookupContactPreferences( addr );
+ return pref.pgpKeyFingerprints + pref.smimeCertFingerprints;
+}
+
+void Kleo::KeyResolver::setKeysForAddress( const QString& address, const QStringList& pgpKeyFingerprints, const QStringList& smimeCertFingerprints ) const {
+ if( address.isEmpty() ) {
+ return;
+ }
+ QString addr = canonicalAddress( address ).lower();
+ ContactPreferences pref = lookupContactPreferences( addr );
+ pref.pgpKeyFingerprints = pgpKeyFingerprints;
+ pref.smimeCertFingerprints = smimeCertFingerprints;
+ saveContactPreference( addr, pref );
+}