kmail

keyresolver.cpp

00001 /*  -*- c++ -*-
00002     keyresolver.cpp
00003 
00004     This file is part of libkleopatra, the KDE keymanagement library
00005     Copyright (c) 2004 Klarälvdalens Datakonsult AB
00006 
00007     Based on kpgp.cpp
00008     Copyright (C) 2001,2002 the KPGP authors
00009     See file libkdenetwork/AUTHORS.kpgp for details
00010 
00011     Libkleopatra is free software; you can redistribute it and/or
00012     modify it under the terms of the GNU General Public License as
00013     published by the Free Software Foundation; either version 2 of the
00014     License, or (at your option) any later version.
00015 
00016     Libkleopatra is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019     General Public License for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with this program; if not, write to the Free Software
00023     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00024 
00025     In addition, as a special exception, the copyright holders give
00026     permission to link the code of this program with any edition of
00027     the Qt library by Trolltech AS, Norway (or with modified versions
00028     of Qt that use the same license as Qt), and distribute linked
00029     combinations including the two.  You must obey the GNU General
00030     Public License in all respects for all of the code used other than
00031     Qt.  If you modify this file, you may extend this exception to
00032     your version of the file, but you are not obligated to do so.  If
00033     you do not wish to do so, delete this exception statement from
00034     your version.
00035 */
00036 
00037 #ifdef HAVE_CONFIG_H
00038 #include <config.h>
00039 #endif
00040 
00041 #include "keyresolver.h"
00042 
00043 #include "kcursorsaver.h"
00044 #include "kleo_util.h"
00045 
00046 #include <libemailfunctions/email.h>
00047 #include <ui/keyselectiondialog.h>
00048 #include <kleo/cryptobackendfactory.h>
00049 #include <kleo/keylistjob.h>
00050 #include <kleo/dn.h>
00051 
00052 #include <gpgmepp/key.h>
00053 #include <gpgmepp/keylistresult.h>
00054 
00055 #include <kabc/stdaddressbook.h>
00056 #include <klocale.h>
00057 #include <kdebug.h>
00058 #include <kinputdialog.h>
00059 #include <kmessagebox.h>
00060 
00061 #include <qstringlist.h>
00062 #include <qtl.h>
00063 
00064 #include <time.h>
00065 
00066 #include <algorithm>
00067 #include <memory>
00068 #include <iterator>
00069 #include <functional>
00070 #include <map>
00071 #include <set>
00072 #include <iostream>
00073 #include <cassert>
00074 
00075 
00076 //
00077 // some predicates to be used in STL algorithms:
00078 //
00079 
00080 static inline bool EmptyKeyList( const Kleo::KeyApprovalDialog::Item & item ) {
00081   return item.keys.empty();
00082 }
00083 
00084 static inline QString ItemDotAddress( const Kleo::KeyResolver::Item & item ) {
00085   return item.address;
00086 }
00087 
00088 static inline bool ApprovalNeeded( const Kleo::KeyResolver::Item & item ) {
00089   return item.pref == Kleo::UnknownPreference || item.pref == Kleo::NeverEncrypt || item.keys.empty() ;
00090 }
00091 
00092 static inline Kleo::KeyResolver::Item
00093 CopyKeysAndEncryptionPreferences( const Kleo::KeyResolver::Item & oldItem,
00094                   const Kleo::KeyApprovalDialog::Item & newItem ) {
00095   return Kleo::KeyResolver::Item( oldItem.address, newItem.keys, newItem.pref, oldItem.signPref, oldItem.format );
00096 }
00097 
00098 static inline bool ByKeyID( const GpgME::Key & left, const GpgME::Key & right ) {
00099   return qstrcmp( left.keyID(), right.keyID() ) < 0 ;
00100 }
00101 
00102 static inline bool WithRespectToKeyID( const GpgME::Key & left, const GpgME::Key & right ) {
00103   return qstrcmp( left.keyID(), right.keyID() ) == 0 ;
00104 }
00105 
00106 static bool ValidOpenPGPEncryptionKey( const GpgME::Key & key ) {
00107   if ( key.protocol() != GpgME::Context::OpenPGP ) {
00108     return false;
00109   }
00110 #if 0
00111   if ( key.isRevoked() )
00112     kdWarning() << " is revoked" << endl;
00113   if ( key.isExpired() )
00114     kdWarning() << " is expired" << endl;
00115   if ( key.isDisabled() )
00116     kdWarning() << " is disabled" << endl;
00117   if ( !key.canEncrypt() )
00118     kdWarning() << " can't encrypt" << endl;
00119 #endif
00120   if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() )
00121     return false;
00122   return true;
00123 }
00124 
00125 static bool ValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
00126     if ( !ValidOpenPGPEncryptionKey( key ) )
00127         return false;
00128   const std::vector<GpgME::UserID> uids = key.userIDs();
00129   for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it ) {
00130     if ( !it->isRevoked() && it->validity() >= GpgME::UserID::Marginal )
00131       return true;
00132 #if 0
00133     else
00134       if ( it->isRevoked() )
00135         kdWarning() << "a userid is revoked" << endl;
00136       else
00137         kdWarning() << "bad validity " << it->validity() << endl;
00138 #endif
00139   }
00140   return false;
00141 }
00142 
00143 static bool ValidSMIMEEncryptionKey( const GpgME::Key & key ) {
00144   if ( key.protocol() != GpgME::Context::CMS )
00145     return false;
00146   if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt() )
00147     return false;
00148   return true;
00149 }
00150 
00151 static bool ValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
00152   if ( !ValidSMIMEEncryptionKey( key ) )
00153     return false;
00154   return true;
00155 }
00156 
00157 static inline bool ValidTrustedEncryptionKey( const GpgME::Key & key ) {
00158   switch ( key.protocol() ) {
00159   case GpgME::Context::OpenPGP:
00160     return ValidTrustedOpenPGPEncryptionKey( key );
00161   case GpgME::Context::CMS:
00162     return ValidTrustedSMIMEEncryptionKey( key );
00163   default:
00164     return false;
00165   }
00166 }
00167 
00168 static inline bool ValidEncryptionKey( const GpgME::Key & key ) {
00169     switch ( key.protocol() ) {
00170     case GpgME::Context::OpenPGP:
00171         return ValidOpenPGPEncryptionKey( key );
00172     case GpgME::Context::CMS:
00173         return ValidSMIMEEncryptionKey( key );
00174     default:
00175         return false;
00176     }
00177 }
00178 
00179 static inline bool ValidSigningKey( const GpgME::Key & key ) {
00180   if ( key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canSign() )
00181     return false;
00182   return key.hasSecret();
00183 }
00184 
00185 static inline bool ValidOpenPGPSigningKey( const GpgME::Key & key ) {
00186   return key.protocol() == GpgME::Context::OpenPGP && ValidSigningKey( key );
00187 }
00188 
00189 static inline bool ValidSMIMESigningKey( const GpgME::Key & key ) {
00190   return key.protocol() == GpgME::Context::CMS && ValidSigningKey( key );
00191 }
00192 
00193 static inline bool NotValidTrustedOpenPGPEncryptionKey( const GpgME::Key & key ) {
00194   return !ValidTrustedOpenPGPEncryptionKey( key );
00195 }
00196 
00197 static inline bool NotValidOpenPGPEncryptionKey( const GpgME::Key & key ) {
00198   return !ValidOpenPGPEncryptionKey( key );
00199 }
00200 
00201 static inline bool NotValidTrustedSMIMEEncryptionKey( const GpgME::Key & key ) {
00202   return !ValidTrustedSMIMEEncryptionKey( key );
00203 }
00204 
00205 static inline bool NotValidSMIMEEncryptionKey( const GpgME::Key & key ) {
00206   return !ValidSMIMEEncryptionKey( key );
00207 }
00208 
00209 static inline bool NotValidTrustedEncryptionKey( const GpgME::Key & key ) {
00210   return !ValidTrustedEncryptionKey( key );
00211 }
00212 
00213 static inline bool NotValidEncryptionKey( const GpgME::Key & key ) {
00214   return !ValidEncryptionKey( key );
00215 }
00216 
00217 static inline bool NotValidSigningKey( const GpgME::Key & key ) {
00218   return !ValidSigningKey( key );
00219 }
00220 
00221 static inline bool NotValidOpenPGPSigningKey( const GpgME::Key & key ) {
00222   return !ValidOpenPGPSigningKey( key );
00223 }
00224 
00225 static inline bool NotValidSMIMESigningKey( const GpgME::Key & key ) {
00226   return !ValidSMIMESigningKey( key );
00227 }
00228 
00229 namespace {
00230     struct ByTrustScore {
00231         static int score( const GpgME::UserID & uid ) {
00232             return uid.isRevoked() || uid.isInvalid() ? -1 : uid.validity() ;
00233         }
00234         bool operator()( const GpgME::UserID & lhs, const GpgME::UserID & rhs ) const {
00235             return score( lhs ) < score( rhs ) ;
00236         }
00237     };
00238 }
00239 
00240 static std::vector<GpgME::UserID> matchingUIDs( const std::vector<GpgME::UserID> & uids, const QString & address ) {
00241     if ( address.isEmpty() )
00242         return std::vector<GpgME::UserID>();
00243 
00244     std::vector<GpgME::UserID> result;
00245     result.reserve( uids.size() );
00246     for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin(), end = uids.end() ; it != end ; ++it )
00247         // PENDING(marc) check DN for an EMAIL, too, in case of X.509 certs... :/
00248         if ( const char * email = it->email() )
00249             if ( *email && QString::fromUtf8( email ).stripWhiteSpace().lower() == address )
00250                 result.push_back( *it );
00251     return result;
00252 }
00253 
00254 static GpgME::UserID findBestMatchUID( const GpgME::Key & key, const QString & address ) {
00255     const std::vector<GpgME::UserID> all = key.userIDs();
00256     if ( all.empty() )
00257         return GpgME::UserID();
00258     const std::vector<GpgME::UserID> matching = matchingUIDs( all, address.lower() );
00259     const std::vector<GpgME::UserID> & v = matching.empty() ? all : matching ;
00260     return *std::max_element( v.begin(), v.end(), ByTrustScore() );
00261 }
00262 
00263 static QStringList keysAsStrings( const std::vector<GpgME::Key>& keys ) {
00264   QStringList strings;
00265   for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it ) {
00266     assert( !(*it).userID(0).isNull() );
00267     QString keyLabel = QString::fromUtf8( (*it).userID(0).email() );
00268     if ( keyLabel.isEmpty() )
00269       keyLabel = QString::fromUtf8( (*it).userID(0).name() );
00270     if ( keyLabel.isEmpty() )
00271       keyLabel = QString::fromUtf8( (*it).userID(0).id() );
00272     strings.append( keyLabel );
00273   }
00274   return strings;
00275 }
00276 
00277 static std::vector<GpgME::Key> TrustedOrConfirmed( const std::vector<GpgME::Key> & keys, const QString & address ) {
00278 
00279   // PENDING(marc) work on UserIDs here?
00280   std::vector<GpgME::Key> fishies;
00281   std::vector<GpgME::Key> ickies;
00282   std::vector<GpgME::Key> rewookies;
00283   std::vector<GpgME::Key>::const_iterator it = keys.begin();
00284   const std::vector<GpgME::Key>::const_iterator end = keys.end();
00285   for ( ; it != end ; it++ ) {
00286     const GpgME::Key & key = *it;
00287     assert( ValidEncryptionKey( key ) );
00288     const GpgME::UserID uid = findBestMatchUID( key, address );
00289     if ( uid.isRevoked() ) {
00290         rewookies.push_back( key );
00291     }
00292     if ( !uid.isRevoked()  && uid.validity() == GpgME::UserID::Marginal ) {
00293         fishies.push_back( key );
00294     }
00295     if ( !uid.isRevoked()  && uid.validity() < GpgME::UserID::Never ) {
00296         ickies.push_back( key );
00297     }
00298   }
00299 
00300   if ( fishies.empty() && ickies.empty() && rewookies.empty() )
00301     return keys;
00302 
00303   // if  some keys are not fully trusted, let the user confirm their use
00304   QString msg = address.isEmpty()
00305       ? i18n("One or more of your configured OpenPGP encryption "
00306              "keys or S/MIME certificates is not fully trusted "
00307              "for encryption.")
00308       : i18n("One or more of the OpenPGP encryption keys or S/MIME "
00309              "certificates for recipient \"%1\" is not fully trusted "
00310              "for encryption.").arg(address) ;
00311 
00312   if ( !fishies.empty() ) {
00313     // certificates can't have marginal trust
00314     msg += i18n( "\nThe following keys are only marginally trusted: \n");
00315     msg += keysAsStrings( fishies ).join(",");
00316   }
00317   if ( !ickies.empty() ) {
00318     msg += i18n( "\nThe following keys or certificates have unknown trust level: \n");
00319     msg += keysAsStrings( ickies ).join(",");
00320   }
00321   if ( !rewookies.empty() ) {
00322     msg += i18n( "\nThe following keys or certificates are <b>revoked</b>: \n");
00323     msg += keysAsStrings( rewookies ).join(",");
00324   }
00325 
00326   if( KMessageBox::warningContinueCancel( 0, msg, i18n("Not Fully Trusted Encryption Keys"),
00327                                               KStdGuiItem::cont(),
00328                                               "not fully trusted encryption key warning" )
00329           == KMessageBox::Continue )
00330     return keys;
00331   else
00332     return std::vector<GpgME::Key>();
00333 }
00334 
00335 namespace {
00336   struct IsNotForFormat : public std::unary_function<GpgME::Key,bool> {
00337     IsNotForFormat( Kleo::CryptoMessageFormat f ) : format( f ) {}
00338 
00339     bool operator()( const GpgME::Key & key ) const {
00340       return
00341     ( isOpenPGP( format ) && key.protocol() != GpgME::Context::OpenPGP ) ||
00342     ( isSMIME( format )   && key.protocol() != GpgME::Context::CMS );
00343     }
00344 
00345     const Kleo::CryptoMessageFormat format;
00346   };
00347 }
00348 
00349 
00350 
00351 class Kleo::KeyResolver::SigningPreferenceCounter : public std::unary_function<Kleo::KeyResolver::Item,void> {
00352 public:
00353   SigningPreferenceCounter()
00354     : mTotal( 0 ),
00355       mUnknownSigningPreference( 0 ),
00356       mNeverSign( 0 ),
00357       mAlwaysSign( 0 ),
00358       mAlwaysSignIfPossible( 0 ),
00359       mAlwaysAskForSigning( 0 ),
00360       mAskSigningWheneverPossible( 0 )
00361   {
00362 
00363   }
00364   void operator()( const Kleo::KeyResolver::Item & item );
00365 #define make_int_accessor(x) unsigned int num##x() const { return m##x; }
00366   make_int_accessor(UnknownSigningPreference)
00367   make_int_accessor(NeverSign)
00368   make_int_accessor(AlwaysSign)
00369   make_int_accessor(AlwaysSignIfPossible)
00370   make_int_accessor(AlwaysAskForSigning)
00371   make_int_accessor(AskSigningWheneverPossible)
00372   make_int_accessor(Total)
00373 #undef make_int_accessor
00374 private:
00375   unsigned int mTotal;
00376   unsigned int mUnknownSigningPreference, mNeverSign, mAlwaysSign,
00377     mAlwaysSignIfPossible, mAlwaysAskForSigning, mAskSigningWheneverPossible;
00378 };
00379 
00380 void Kleo::KeyResolver::SigningPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
00381   switch ( item.signPref ) {
00382 #define CASE(x) case x: ++m##x; break
00383     CASE(UnknownSigningPreference);
00384     CASE(NeverSign);
00385     CASE(AlwaysSign);
00386     CASE(AlwaysSignIfPossible);
00387     CASE(AlwaysAskForSigning);
00388     CASE(AskSigningWheneverPossible);
00389 #undef CASE
00390   }
00391   ++mTotal;
00392 }
00393 
00394 
00395 
00396 class Kleo::KeyResolver::EncryptionPreferenceCounter : public std::unary_function<Item,void> {
00397   const Kleo::KeyResolver * _this;
00398 public:
00399   EncryptionPreferenceCounter( const Kleo::KeyResolver * kr, EncryptionPreference defaultPreference )
00400     : _this( kr ),
00401       mDefaultPreference( defaultPreference ),
00402       mTotal( 0 ),
00403       mNoKey( 0 ),
00404       mNeverEncrypt( 0 ),
00405       mUnknownPreference( 0 ),
00406       mAlwaysEncrypt( 0 ),
00407       mAlwaysEncryptIfPossible( 0 ),
00408       mAlwaysAskForEncryption( 0 ),
00409       mAskWheneverPossible( 0 )
00410   {
00411 
00412   }
00413   void operator()( Item & item );
00414 
00415   template <typename Container>
00416   void process( Container & c ) {
00417     *this = std::for_each( c.begin(), c.end(), *this );
00418   }
00419 
00420 #define make_int_accessor(x) unsigned int num##x() const { return m##x; }
00421   make_int_accessor(NoKey)
00422   make_int_accessor(NeverEncrypt)
00423   make_int_accessor(UnknownPreference)
00424   make_int_accessor(AlwaysEncrypt)
00425   make_int_accessor(AlwaysEncryptIfPossible)
00426   make_int_accessor(AlwaysAskForEncryption)
00427   make_int_accessor(AskWheneverPossible)
00428   make_int_accessor(Total)
00429 #undef make_int_accessor
00430 private:
00431   EncryptionPreference mDefaultPreference;
00432   bool mNoOps;
00433   unsigned int mTotal;
00434   unsigned int mNoKey;
00435   unsigned int mNeverEncrypt, mUnknownPreference, mAlwaysEncrypt,
00436     mAlwaysEncryptIfPossible, mAlwaysAskForEncryption, mAskWheneverPossible;
00437 };
00438 
00439 void Kleo::KeyResolver::EncryptionPreferenceCounter::operator()( Item & item ) {
00440   if ( _this ) {
00441   if ( item.needKeys )
00442     item.keys = _this->getEncryptionKeys( item.address, true );
00443   if ( item.keys.empty() ) {
00444     ++mNoKey;
00445     return;
00446   }
00447   }
00448   switch ( !item.pref ? mDefaultPreference : item.pref ) {
00449 #define CASE(x) case Kleo::x: ++m##x; break
00450     CASE(NeverEncrypt);
00451     CASE(UnknownPreference);
00452     CASE(AlwaysEncrypt);
00453     CASE(AlwaysEncryptIfPossible);
00454     CASE(AlwaysAskForEncryption);
00455     CASE(AskWheneverPossible);
00456 #undef CASE
00457   }
00458   ++mTotal;
00459 }
00460 
00461 namespace {
00462 
00463   class FormatPreferenceCounterBase : public std::unary_function<Kleo::KeyResolver::Item,void> {
00464   public:
00465     FormatPreferenceCounterBase()
00466       : mTotal( 0 ),
00467     mInlineOpenPGP( 0 ),
00468     mOpenPGPMIME( 0 ),
00469     mSMIME( 0 ),
00470     mSMIMEOpaque( 0 )
00471     {
00472 
00473     }
00474 
00475 #define make_int_accessor(x) unsigned int num##x() const { return m##x; }
00476     make_int_accessor(Total)
00477     make_int_accessor(InlineOpenPGP)
00478     make_int_accessor(OpenPGPMIME)
00479     make_int_accessor(SMIME)
00480     make_int_accessor(SMIMEOpaque)
00481 #undef make_int_accessor
00482 
00483     unsigned int numOf( Kleo::CryptoMessageFormat f ) const {
00484       switch ( f ) {
00485 #define CASE(x) case Kleo::x##Format: return m##x
00486     CASE(InlineOpenPGP);
00487     CASE(OpenPGPMIME);
00488     CASE(SMIME);
00489     CASE(SMIMEOpaque);
00490 #undef CASE
00491       default: return 0;
00492       }
00493     }
00494 
00495   protected:
00496     unsigned int mTotal;
00497     unsigned int mInlineOpenPGP, mOpenPGPMIME, mSMIME, mSMIMEOpaque;
00498   };
00499 
00500   class EncryptionFormatPreferenceCounter : public FormatPreferenceCounterBase {
00501   public:
00502     EncryptionFormatPreferenceCounter() : FormatPreferenceCounterBase() {}
00503     void operator()( const Kleo::KeyResolver::Item & item );
00504   };
00505 
00506   class SigningFormatPreferenceCounter : public FormatPreferenceCounterBase {
00507   public:
00508     SigningFormatPreferenceCounter() : FormatPreferenceCounterBase() {}
00509     void operator()( const Kleo::KeyResolver::Item & item );
00510   };
00511 
00512 #define CASE(x) if ( item.format & Kleo::x##Format ) ++m##x;
00513   void EncryptionFormatPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
00514     if ( item.format & (Kleo::InlineOpenPGPFormat|Kleo::OpenPGPMIMEFormat) &&
00515      std::find_if( item.keys.begin(), item.keys.end(),
00516                ValidTrustedOpenPGPEncryptionKey ) != item.keys.end() ) {  // -= trusted?
00517       CASE(OpenPGPMIME);
00518       CASE(InlineOpenPGP);
00519     }
00520     if ( item.format & (Kleo::SMIMEFormat|Kleo::SMIMEOpaqueFormat) &&
00521      std::find_if( item.keys.begin(), item.keys.end(),
00522                ValidTrustedSMIMEEncryptionKey ) != item.keys.end() ) {    // -= trusted?
00523       CASE(SMIME);
00524       CASE(SMIMEOpaque);
00525     }
00526     ++mTotal;
00527   }
00528 
00529   void SigningFormatPreferenceCounter::operator()( const Kleo::KeyResolver::Item & item ) {
00530     CASE(InlineOpenPGP);
00531     CASE(OpenPGPMIME);
00532     CASE(SMIME);
00533     CASE(SMIMEOpaque);
00534     ++mTotal;
00535   }
00536 #undef CASE
00537 
00538 } // anon namespace
00539 
00540 static QString canonicalAddress( const QString & _address ) {
00541   const QString address = KPIM::getEmailAddress( _address );
00542   if ( address.find('@') == -1 ) {
00543     // local address
00544     //char hostname[1024];
00545     //gethostname(hostname,1024);
00546     //return address + '@' + hostname;
00547     return address + "@localdomain";
00548   }
00549   else
00550     return address;
00551 }
00552 
00553 
00554 struct FormatInfo {
00555   std::vector<Kleo::KeyResolver::SplitInfo> splitInfos;
00556   std::vector<GpgME::Key> signKeys;
00557 };
00558 
00559 struct Kleo::KeyResolver::Private {
00560   std::set<QCString> alreadyWarnedFingerprints;
00561 
00562   std::vector<GpgME::Key> mOpenPGPSigningKeys; // signing
00563   std::vector<GpgME::Key> mSMIMESigningKeys; // signing
00564 
00565   std::vector<GpgME::Key> mOpenPGPEncryptToSelfKeys; // encryption to self
00566   std::vector<GpgME::Key> mSMIMEEncryptToSelfKeys; // encryption to self
00567 
00568   std::vector<Item> mPrimaryEncryptionKeys; // encryption to To/CC
00569   std::vector<Item> mSecondaryEncryptionKeys; // encryption to BCC
00570 
00571   std::map<CryptoMessageFormat,FormatInfo> mFormatInfoMap;
00572 
00573   // key=email address, value=crypto preferences for this contact (from kabc)
00574   typedef std::map<QString, ContactPreferences> ContactPreferencesMap;
00575   ContactPreferencesMap mContactPreferencesMap;
00576 };
00577 
00578 
00579 Kleo::KeyResolver::KeyResolver( bool encToSelf, bool showApproval, bool oppEncryption,
00580                 unsigned int f,
00581                 int encrWarnThresholdKey, int signWarnThresholdKey,
00582                 int encrWarnThresholdRootCert, int signWarnThresholdRootCert,
00583                 int encrWarnThresholdChainCert, int signWarnThresholdChainCert )
00584   : mEncryptToSelf( encToSelf ),
00585     mShowApprovalDialog( showApproval ),
00586     mOpportunisticEncyption( oppEncryption ),
00587     mCryptoMessageFormats( f ),
00588     mEncryptKeyNearExpiryWarningThreshold( encrWarnThresholdKey ),
00589     mSigningKeyNearExpiryWarningThreshold( signWarnThresholdKey ),
00590     mEncryptRootCertNearExpiryWarningThreshold( encrWarnThresholdRootCert ),
00591     mSigningRootCertNearExpiryWarningThreshold( signWarnThresholdRootCert ),
00592     mEncryptChainCertNearExpiryWarningThreshold( encrWarnThresholdChainCert ),
00593     mSigningChainCertNearExpiryWarningThreshold( signWarnThresholdChainCert )
00594 {
00595   d = new Private();
00596 }
00597 
00598 Kleo::KeyResolver::~KeyResolver() {
00599   delete d; d = 0;
00600 }
00601 
00602 Kpgp::Result Kleo::KeyResolver::checkKeyNearExpiry( const GpgME::Key & key, const char * dontAskAgainName,
00603                             bool mine, bool sign, bool ca,
00604                             int recur_limit, const GpgME::Key & orig ) const {
00605   if ( recur_limit <= 0 ) {
00606     kdDebug() << "Kleo::KeyResolver::checkKeyNearExpiry(): key chain too long (>100 certs)" << endl;
00607     return Kpgp::Ok;
00608   }
00609   const GpgME::Subkey subkey = key.subkey(0);
00610   if ( d->alreadyWarnedFingerprints.count( subkey.fingerprint() ) )
00611     return Kpgp::Ok; // already warned about this one (and so about it's issuers)
00612 
00613   if ( subkey.neverExpires() )
00614     return Kpgp::Ok;
00615   static const double secsPerDay = 24 * 60 * 60;
00616   const double secsTillExpiry = ::difftime( subkey.expirationTime(), time(0) );
00617   if ( secsTillExpiry <= 0 ) {
00618       const int daysSinceExpiry = 1 + int( -secsTillExpiry / secsPerDay );
00619       kdDebug() << "Key 0x" << key.shortKeyID() << " expired less than "
00620                 << daysSinceExpiry << " days ago" << endl;
00621       const QString msg =
00622           key.protocol() == GpgME::Context::OpenPGP
00623           ? ( mine ? sign
00624               ? i18n("<p>Your OpenPGP signing key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00625                      "<p>expired less than a day ago.</p>",
00626                      "<p>Your OpenPGP signing key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00627                      "<p>expired %n days ago.</p>",
00628                      daysSinceExpiry )
00629               : i18n("<p>Your OpenPGP encryption key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00630                      "<p>expired less than a day ago.</p>",
00631                      "<p>Your OpenPGP encryption key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00632                      "<p>expired %n days ago.</p>",
00633                      daysSinceExpiry )
00634               : i18n("<p>The OpenPGP key for</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00635                      "<p>expired less than a day ago.</p>",
00636                      "<p>The OpenPGP key for</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00637                      "<p>expired %n days ago.</p>",
00638                      daysSinceExpiry ) ).arg( QString::fromUtf8( key.userID(0).id() ),
00639                      key.shortKeyID() )
00640           : ( ca
00641               ? ( key.isRoot()
00642                   ? ( mine ? sign
00643                       ? i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
00644                              "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00645                              "<p>expired less than a day ago.</p>",
00646                              "<p>The root certificate</p><p align=center><b>%3</b></p>"
00647                              "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00648                              "<p>expired %n days ago.</p>",
00649                              daysSinceExpiry )
00650                       : i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
00651                              "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00652                              "<p>expired less than a day ago.</p>",
00653                              "<p>The root certificate</p><p align=center><b>%3</b></p>"
00654                              "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00655                              "<p>expired %n days ago.</p>",
00656                              daysSinceExpiry )
00657                       : i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
00658                              "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00659                              "<p>expired less than a day ago.</p>",
00660                              "<p>The root certificate</p><p align=center><b>%3</b></p>"
00661                              "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00662                              "<p>expired %n days ago.</p>",
00663                              daysSinceExpiry ) )
00664                   : ( mine ? sign
00665                       ? i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00666                              "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00667                              "<p>expired less than a day ago.</p>",
00668                              "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00669                              "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00670                              "<p>expired %n days ago.</p>",
00671                              daysSinceExpiry )
00672                       : i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00673                              "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00674                              "<p>expired less than a day ago.</p>",
00675                              "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00676                              "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00677                              "<p>expired %n days ago.</p>",
00678                              daysSinceExpiry )
00679                       : i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00680                              "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00681                              "<p>expired less than a day ago.</p>",
00682                              "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00683                              "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00684                              "<p>expired %n days ago.</p>",
00685                              daysSinceExpiry ) ) ).arg( Kleo::DN( orig.userID(0).id() ).prettyDN(),
00686                                                        orig.issuerSerial(),
00687                                                        Kleo::DN( key.userID(0).id() ).prettyDN() )
00688               : ( mine ? sign
00689                   ? i18n("<p>Your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00690                          "<p>expired less than a day ago.</p>",
00691                          "<p>Your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00692                          "<p>expired %n days ago.</p>",
00693                          daysSinceExpiry )
00694                   : i18n("<p>Your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00695                          "<p>expired less than a day ago.</p>",
00696                          "<p>Your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00697                          "<p>expired %n days ago.</p>",
00698                          daysSinceExpiry )
00699                   : i18n("<p>The S/MIME certificate for</p><p align=center><b>%1</b> (serial number %2)</p>"
00700                          "<p>expired less than a day ago.</p>",
00701                          "<p>The S/MIME certificate for</p><p align=center><b>%1</b> (serial number %2)</p>"
00702                          "<p>expired %n days ago.</p>",
00703                          daysSinceExpiry ) ).arg( Kleo::DN( key.userID(0).id() ).prettyDN(),
00704                                                  key.issuerSerial() ) );
00705       d->alreadyWarnedFingerprints.insert( subkey.fingerprint() );
00706       if ( KMessageBox::warningContinueCancel( 0, msg,
00707                                                key.protocol() == GpgME::Context::OpenPGP
00708                                                ? i18n("OpenPGP Key Expired" )
00709                                                : i18n("S/MIME Certificate Expired" ),
00710                                                KStdGuiItem::cont(), dontAskAgainName ) == KMessageBox::Cancel )
00711           return Kpgp::Canceled;
00712   } else {
00713   const int daysTillExpiry = 1 + int( secsTillExpiry / secsPerDay );
00714   kdDebug() << "Key 0x" << key.shortKeyID() << " expires in less than "
00715         << daysTillExpiry << " days" << endl;
00716   const int threshold =
00717     ca
00718     ? ( key.isRoot()
00719     ? ( sign
00720         ? signingRootCertNearExpiryWarningThresholdInDays()
00721         : encryptRootCertNearExpiryWarningThresholdInDays() )
00722     : ( sign
00723         ? signingChainCertNearExpiryWarningThresholdInDays()
00724         : encryptChainCertNearExpiryWarningThresholdInDays() ) )
00725     : ( sign
00726     ? signingKeyNearExpiryWarningThresholdInDays()
00727     : encryptKeyNearExpiryWarningThresholdInDays() );
00728   if ( threshold > -1 && daysTillExpiry <= threshold ) {
00729     const QString msg =
00730       key.protocol() == GpgME::Context::OpenPGP
00731       ? ( mine ? sign
00732       ? i18n("<p>Your OpenPGP signing key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00733          "<p>expires in less than a day.</p>",
00734          "<p>Your OpenPGP signing key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00735          "<p>expires in less than %n days.</p>",
00736          daysTillExpiry )
00737       : i18n("<p>Your OpenPGP encryption key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00738          "<p>expires in less than a day.</p>",
00739          "<p>Your OpenPGP encryption key</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00740          "<p>expires in less than %n days.</p>",
00741          daysTillExpiry )
00742       : i18n("<p>The OpenPGP key for</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00743          "<p>expires in less than a day.</p>",
00744          "<p>The OpenPGP key for</p><p align=center><b>%1</b> (KeyID 0x%2)</p>"
00745          "<p>expires in less than %n days.</p>",
00746          daysTillExpiry ) ).arg( QString::fromUtf8( key.userID(0).id() ),
00747                      key.shortKeyID() )
00748       : ( ca
00749       ? ( key.isRoot()
00750           ? ( mine ? sign
00751           ? i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
00752              "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00753              "<p>expires in less than a day.</p>",
00754              "<p>The root certificate</p><p align=center><b>%3</b></p>"
00755              "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00756              "<p>expires in less than %n days.</p>",
00757              daysTillExpiry )
00758           : i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
00759              "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00760              "<p>expires in less than a day.</p>",
00761              "<p>The root certificate</p><p align=center><b>%3</b></p>"
00762              "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00763              "<p>expires in less than %n days.</p>",
00764              daysTillExpiry )
00765           : i18n("<p>The root certificate</p><p align=center><b>%3</b></p>"
00766              "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00767              "<p>expires in less than a day.</p>",
00768              "<p>The root certificate</p><p align=center><b>%3</b></p>"
00769              "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00770              "<p>expires in less than %n days.</p>",
00771              daysTillExpiry ) )
00772           : ( mine ? sign
00773           ? i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00774              "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00775              "<p>expires in less than a day.</p>",
00776              "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00777              "<p>for your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00778              "<p>expires in less than %n days.</p>",
00779              daysTillExpiry )
00780           : i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00781              "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00782              "<p>expires in less than a day.</p>",
00783              "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00784              "<p>for your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00785              "<p>expires in less than %n days.</p>",
00786              daysTillExpiry )
00787           : i18n("<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00788              "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00789              "<p>expires in less than a day.</p>",
00790              "<p>The intermediate CA certificate</p><p align=center><b>%3</b></p>"
00791              "<p>for S/MIME certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00792              "<p>expires in less than %n days.</p>",
00793              daysTillExpiry ) ) ).arg( Kleo::DN( orig.userID(0).id() ).prettyDN(),
00794                            orig.issuerSerial(),
00795                            Kleo::DN( key.userID(0).id() ).prettyDN() )
00796       : ( mine ? sign
00797           ? i18n("<p>Your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00798              "<p>expires in less than a day.</p>",
00799              "<p>Your S/MIME signing certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00800              "<p>expires in less than %n days.</p>",
00801              daysTillExpiry )
00802           : i18n("<p>Your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00803              "<p>expires in less than a day.</p>",
00804              "<p>Your S/MIME encryption certificate</p><p align=center><b>%1</b> (serial number %2)</p>"
00805              "<p>expires in less than %n days.</p>",
00806              daysTillExpiry )
00807           : i18n("<p>The S/MIME certificate for</p><p align=center><b>%1</b> (serial number %2)</p>"
00808              "<p>expires in less than a day.</p>",
00809              "<p>The S/MIME certificate for</p><p align=center><b>%1</b> (serial number %2)</p>"
00810              "<p>expires in less than %n days.</p>",
00811              daysTillExpiry ) ).arg( Kleo::DN( key.userID(0).id() ).prettyDN(),
00812                          key.issuerSerial() ) );
00813     d->alreadyWarnedFingerprints.insert( subkey.fingerprint() );
00814     if ( KMessageBox::warningContinueCancel( 0, msg,
00815                          key.protocol() == GpgME::Context::OpenPGP
00816                          ? i18n("OpenPGP Key Expires Soon" )
00817                          : i18n("S/MIME Certificate Expires Soon" ),
00818                          KStdGuiItem::cont(), dontAskAgainName )
00819      == KMessageBox::Cancel )
00820       return Kpgp::Canceled;
00821   }
00822   }
00823   if ( key.isRoot() )
00824     return Kpgp::Ok;
00825   else if ( const char * chain_id = key.chainID() ) {
00826     const std::vector<GpgME::Key> issuer = lookup( chain_id, false );
00827     if ( issuer.empty() )
00828       return Kpgp::Ok;
00829     else
00830       return checkKeyNearExpiry( issuer.front(), dontAskAgainName, mine, sign,
00831                  true, recur_limit-1, ca ? orig : key );
00832   }
00833   return Kpgp::Ok;
00834 }
00835 
00836 Kpgp::Result Kleo::KeyResolver::setEncryptToSelfKeys( const QStringList & fingerprints ) {
00837   if ( !encryptToSelf() )
00838     return Kpgp::Ok;
00839 
00840   std::vector<GpgME::Key> keys = lookup( fingerprints );
00841   std::remove_copy_if( keys.begin(), keys.end(),
00842                std::back_inserter( d->mOpenPGPEncryptToSelfKeys ),
00843                NotValidTrustedOpenPGPEncryptionKey ); // -= trusted?
00844   std::remove_copy_if( keys.begin(), keys.end(),
00845                std::back_inserter( d->mSMIMEEncryptToSelfKeys ),
00846                NotValidTrustedSMIMEEncryptionKey );   // -= trusted?
00847 
00848   if ( d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size()
00849        < keys.size() ) {
00850     // too few keys remain...
00851     const QString msg = i18n("One or more of your configured OpenPGP encryption "
00852                  "keys or S/MIME certificates is not usable for "
00853                  "encryption. Please reconfigure your encryption keys "
00854                  "and certificates for this identity in the identity "
00855                  "configuration dialog.\n"
00856                  "If you choose to continue, and the keys are needed "
00857                  "later on, you will be prompted to specify the keys "
00858                  "to use.");
00859     return KMessageBox::warningContinueCancel( 0, msg, i18n("Unusable Encryption Keys"),
00860                            KStdGuiItem::cont(),
00861                            "unusable own encryption key warning" )
00862       == KMessageBox::Continue ? Kpgp::Ok : Kpgp::Canceled ;
00863   }
00864 
00865   // check for near-expiry:
00866 
00867   for ( std::vector<GpgME::Key>::const_iterator it = d->mOpenPGPEncryptToSelfKeys.begin() ; it != d->mOpenPGPEncryptToSelfKeys.end() ; ++it ) {
00868     const Kpgp::Result r = checkKeyNearExpiry( *it, "own encryption key expires soon warning",
00869                            true, false );
00870     if ( r != Kpgp::Ok )
00871       return r;
00872   }
00873 
00874   for ( std::vector<GpgME::Key>::const_iterator it = d->mSMIMEEncryptToSelfKeys.begin() ; it != d->mSMIMEEncryptToSelfKeys.end() ; ++it ) {
00875     const Kpgp::Result r = checkKeyNearExpiry( *it, "own encryption key expires soon warning",
00876                            true, false );
00877     if ( r != Kpgp::Ok )
00878       return r;
00879   }
00880 
00881   return Kpgp::Ok;
00882 }
00883 
00884 Kpgp::Result Kleo::KeyResolver::setSigningKeys( const QStringList & fingerprints ) {
00885   std::vector<GpgME::Key> keys = lookup( fingerprints, true ); // secret keys
00886   std::remove_copy_if( keys.begin(), keys.end(),
00887                std::back_inserter( d->mOpenPGPSigningKeys ),
00888                NotValidOpenPGPSigningKey );
00889   std::remove_copy_if( keys.begin(), keys.end(),
00890                std::back_inserter( d->mSMIMESigningKeys ),
00891                NotValidSMIMESigningKey );
00892 
00893   if ( d->mOpenPGPSigningKeys.size() + d->mSMIMESigningKeys.size() < keys.size() ) {
00894     // too few keys remain...
00895     const QString msg = i18n("One or more of your configured OpenPGP signing keys "
00896                  "or S/MIME signing certificates is not usable for "
00897                  "signing. Please reconfigure your signing keys "
00898                  "and certificates for this identity in the identity "
00899                  "configuration dialog.\n"
00900                  "If you choose to continue, and the keys are needed "
00901                  "later on, you will be prompted to specify the keys "
00902                  "to use.");
00903     return KMessageBox::warningContinueCancel( 0, msg, i18n("Unusable Signing Keys"),
00904                            KStdGuiItem::cont(),
00905                            "unusable signing key warning" )
00906       == KMessageBox::Continue ? Kpgp::Ok : Kpgp::Canceled ;
00907   }
00908 
00909   // check for near expiry:
00910 
00911   for ( std::vector<GpgME::Key>::const_iterator it = d->mOpenPGPSigningKeys.begin() ; it != d->mOpenPGPSigningKeys.end() ; ++it ) {
00912     const Kpgp::Result r = checkKeyNearExpiry( *it, "signing key expires soon warning",
00913                            true, true );
00914     if ( r != Kpgp::Ok )
00915       return r;
00916   }
00917 
00918   for ( std::vector<GpgME::Key>::const_iterator it = d->mSMIMESigningKeys.begin() ; it != d->mSMIMESigningKeys.end() ; ++it ) {
00919     const Kpgp::Result r = checkKeyNearExpiry( *it, "signing key expires soon warning",
00920                            true, true );
00921     if ( r != Kpgp::Ok )
00922       return r;
00923   }
00924 
00925   return Kpgp::Ok;
00926 }
00927 
00928 void Kleo::KeyResolver::setPrimaryRecipients( const QStringList & addresses ) {
00929   d->mPrimaryEncryptionKeys = getEncryptionItems( addresses );
00930 }
00931 
00932 void Kleo::KeyResolver::setSecondaryRecipients( const QStringList & addresses ) {
00933   d->mSecondaryEncryptionKeys = getEncryptionItems( addresses );
00934 }
00935 
00936 std::vector<Kleo::KeyResolver::Item> Kleo::KeyResolver::getEncryptionItems( const QStringList & addresses ) {
00937   std::vector<Item> items;
00938   items.reserve( addresses.size() );
00939   for ( QStringList::const_iterator it = addresses.begin() ; it != addresses.end() ; ++it ) {
00940     QString addr = canonicalAddress( *it ).lower();
00941     const ContactPreferences pref = lookupContactPreferences( addr );
00942 
00943     items.push_back( Item( *it, /*getEncryptionKeys( *it, true ),*/
00944                pref.encryptionPreference,
00945                pref.signingPreference,
00946                pref.cryptoMessageFormat ) );
00947   }
00948   return items;
00949 }
00950 
00951 static Kleo::Action action( bool doit, bool ask, bool dont, bool requested ) {
00952   if ( requested && !dont )
00953     return Kleo::DoIt;
00954   if ( doit && !ask && !dont )
00955     return Kleo::DoIt;
00956   if ( !doit && ask && !dont )
00957     return Kleo::Ask;
00958   if ( !doit && !ask && dont )
00959     return requested ? Kleo::Conflict : Kleo::DontDoIt ;
00960   if ( !doit && !ask && !dont )
00961     return Kleo::DontDoIt ;
00962   return Kleo::Conflict;
00963 }
00964 
00965 Kleo::Action Kleo::KeyResolver::checkSigningPreferences( bool signingRequested ) const {
00966 
00967   if ( signingRequested && d->mOpenPGPSigningKeys.empty() && d->mSMIMESigningKeys.empty() )
00968     return Impossible;
00969 
00970   SigningPreferenceCounter count;
00971   count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
00972              count );
00973   count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
00974              count );
00975 
00976   unsigned int sign = count.numAlwaysSign();
00977   unsigned int ask = count.numAlwaysAskForSigning();
00978   const unsigned int dontSign = count.numNeverSign();
00979   if ( signingPossible() ) {
00980     sign += count.numAlwaysSignIfPossible();
00981     ask += count.numAskSigningWheneverPossible();
00982   }
00983 
00984   return action( sign, ask, dontSign, signingRequested );
00985 }
00986 
00987 bool Kleo::KeyResolver::signingPossible() const {
00988   return !d->mOpenPGPSigningKeys.empty() || !d->mSMIMESigningKeys.empty() ;
00989 }
00990 
00991 Kleo::Action Kleo::KeyResolver::checkEncryptionPreferences( bool encryptionRequested ) const {
00992 
00993   if ( d->mPrimaryEncryptionKeys.empty() && d->mSecondaryEncryptionKeys.empty() )
00994     return DontDoIt;
00995 
00996   if ( encryptionRequested && encryptToSelf() &&
00997        d->mOpenPGPEncryptToSelfKeys.empty() && d->mSMIMEEncryptToSelfKeys.empty() )
00998     return Impossible;
00999 
01000   if ( !encryptionRequested && !mOpportunisticEncyption ) {
01001     // try to minimize crypto ops (including key lookups) by only
01002     // looking up keys when at least one the the encryption
01003     // preferences needs it:
01004     EncryptionPreferenceCounter count( 0, UnknownPreference );
01005     count.process( d->mPrimaryEncryptionKeys );
01006     count.process( d->mSecondaryEncryptionKeys );
01007     if ( !count.numAlwaysEncrypt() &&
01008          !count.numAlwaysAskForEncryption() && // this guy might not need a lookup, when declined, but it's too complex to implement that here
01009          !count.numAlwaysEncryptIfPossible() &&
01010          !count.numAskWheneverPossible() )
01011         return DontDoIt;
01012   }
01013 
01014   EncryptionPreferenceCounter count( this, mOpportunisticEncyption ? AskWheneverPossible : UnknownPreference );
01015   count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01016              count );
01017   count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01018              count );
01019 
01020   unsigned int encrypt = count.numAlwaysEncrypt();
01021   unsigned int ask = count.numAlwaysAskForEncryption();
01022   const unsigned int dontEncrypt = count.numNeverEncrypt() + count.numNoKey();
01023   if ( encryptionPossible() ) {
01024     encrypt += count.numAlwaysEncryptIfPossible();
01025     ask += count.numAskWheneverPossible();
01026   }
01027 
01028   const Action act = action( encrypt, ask, dontEncrypt, encryptionRequested );
01029   if ( act != Ask ||
01030        std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01031        std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01032               EncryptionPreferenceCounter( this, UnknownPreference ) ) ).numAlwaysAskForEncryption() )
01033     return act;
01034   else
01035     return AskOpportunistic;
01036 }
01037 
01038 bool Kleo::KeyResolver::encryptionPossible() const {
01039   return std::find_if( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01040                EmptyKeyList ) == d->mPrimaryEncryptionKeys.end()
01041     &&   std::find_if( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01042                EmptyKeyList ) == d->mSecondaryEncryptionKeys.end() ;
01043 }
01044 
01045 Kpgp::Result Kleo::KeyResolver::resolveAllKeys( bool& signingRequested, bool& encryptionRequested ) {
01046   if ( !encryptionRequested && !signingRequested ) {
01047     // make a dummy entry with all recipients, but no signing or
01048     // encryption keys to avoid special-casing on the caller side:
01049     dump();
01050     d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.push_back( SplitInfo( allRecipients() ) );
01051     dump();
01052     return Kpgp::Ok;
01053   }
01054   Kpgp::Result result = Kpgp::Ok;
01055   if ( encryptionRequested )
01056     result = resolveEncryptionKeys( signingRequested );
01057   if ( result != Kpgp::Ok )
01058     return result;
01059   if ( signingRequested ) {
01060     if ( encryptionRequested ) {
01061       result = resolveSigningKeysForEncryption();
01062     }
01063     else {
01064       result = resolveSigningKeysForSigningOnly();
01065       if ( result == Kpgp::Failure ) {
01066         signingRequested = false;
01067         return Kpgp::Ok;
01068       }
01069     }
01070   }
01071   return result;
01072 }
01073 
01074 Kpgp::Result Kleo::KeyResolver::resolveEncryptionKeys( bool signingRequested ) {
01075   //
01076   // 1. Get keys for all recipients:
01077   //
01078 
01079   for ( std::vector<Item>::iterator it = d->mPrimaryEncryptionKeys.begin() ; it != d->mPrimaryEncryptionKeys.end() ; ++it ) {
01080     if ( !it->needKeys )
01081       continue;
01082     it->keys = getEncryptionKeys( it->address, false );
01083     if ( it->keys.empty() )
01084       return Kpgp::Canceled;
01085     QString addr = canonicalAddress( it->address ).lower();
01086     const ContactPreferences pref = lookupContactPreferences( addr );
01087     it->pref = pref.encryptionPreference;
01088     it->signPref = pref.signingPreference;
01089     it->format = pref.cryptoMessageFormat;
01090   }
01091 
01092   for ( std::vector<Item>::iterator it = d->mSecondaryEncryptionKeys.begin() ; it != d->mSecondaryEncryptionKeys.end() ; ++it ) {
01093     if ( !it->needKeys )
01094       continue;
01095     it->keys = getEncryptionKeys( it->address, false );
01096     if ( it->keys.empty() )
01097       return Kpgp::Canceled;
01098     QString addr = canonicalAddress( it->address ).lower();
01099     const ContactPreferences pref = lookupContactPreferences( addr );
01100     it->pref = pref.encryptionPreference;
01101     it->signPref = pref.signingPreference;
01102     it->format = pref.cryptoMessageFormat;
01103   }
01104 
01105   // 1a: Present them to the user
01106 
01107   const Kpgp::Result res = showKeyApprovalDialog();
01108   if ( res != Kpgp::Ok )
01109     return res;
01110 
01111   //
01112   // 2. Check what the primary recipients need
01113   //
01114 
01115   // 2a. Try to find a common format for all primary recipients,
01116   //     else use as many formats as needed
01117 
01118   const EncryptionFormatPreferenceCounter primaryCount
01119     = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01120              EncryptionFormatPreferenceCounter() );
01121 
01122   CryptoMessageFormat commonFormat = AutoFormat;
01123   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01124     if ( !( concreteCryptoMessageFormats[i] & mCryptoMessageFormats ) )
01125       continue;
01126     if ( signingRequested && signingKeysFor( concreteCryptoMessageFormats[i] ).empty() )
01127       continue;
01128     if ( encryptToSelf() && encryptToSelfKeysFor( concreteCryptoMessageFormats[i] ).empty() )
01129       continue;
01130     if ( primaryCount.numOf( concreteCryptoMessageFormats[i] ) == primaryCount.numTotal() ) {
01131       commonFormat = concreteCryptoMessageFormats[i];
01132       break;
01133     }
01134   }
01135   if ( commonFormat != AutoFormat )
01136     addKeys( d->mPrimaryEncryptionKeys, commonFormat );
01137   else
01138     addKeys( d->mPrimaryEncryptionKeys );
01139 
01140   collapseAllSplitInfos(); // these can be encrypted together
01141 
01142   // 2b. Just try to find _something_ for each secondary recipient,
01143   //     with a preference to a common format (if that exists)
01144 
01145   const EncryptionFormatPreferenceCounter secondaryCount
01146     = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01147              EncryptionFormatPreferenceCounter() );
01148 
01149   if ( commonFormat != AutoFormat &&
01150        secondaryCount.numOf( commonFormat ) == secondaryCount.numTotal() )
01151     addKeys( d->mSecondaryEncryptionKeys, commonFormat );
01152   else
01153     addKeys( d->mSecondaryEncryptionKeys );
01154 
01155   // 3. Check for expiry:
01156 
01157   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01158     const std::vector<SplitInfo> si_list = encryptionItems( concreteCryptoMessageFormats[i] );
01159     for ( std::vector<SplitInfo>::const_iterator sit = si_list.begin() ; sit != si_list.end() ; ++sit )
01160       for ( std::vector<GpgME::Key>::const_iterator kit = sit->keys.begin() ; kit != sit->keys.end() ; ++kit ) {
01161     const Kpgp::Result r = checkKeyNearExpiry( *kit, "other encryption key near expiry warning",
01162                            false, false );
01163     if ( r != Kpgp::Ok )
01164       return r;
01165       }
01166   }
01167 
01168   // 4. Check that we have the right keys for encryptToSelf()
01169 
01170   if ( !encryptToSelf() )
01171     return Kpgp::Ok;
01172 
01173   // 4a. Check for OpenPGP keys
01174 
01175   if ( !encryptionItems( InlineOpenPGPFormat ).empty() ||
01176        !encryptionItems( OpenPGPMIMEFormat ).empty() ) {
01177     // need them
01178     if ( d->mOpenPGPEncryptToSelfKeys.empty() ) {
01179       const QString msg = i18n("Examination of recipient's encryption preferences "
01180                    "yielded that the message should be encrypted using "
01181                    "OpenPGP, at least for some recipients;\n"
01182                    "however, you have not configured valid trusted "
01183                    "OpenPGP encryption keys for this identity.\n"
01184                    "You may continue without encrypting to yourself, "
01185                    "but be aware that you will not be able to read your "
01186                    "own messages if you do so.");
01187       if ( KMessageBox::warningContinueCancel( 0, msg,
01188                            i18n("Unusable Encryption Keys"),
01189                            KStdGuiItem::cont(),
01190                            "encrypt-to-self will fail warning" )
01191        == KMessageBox::Cancel )
01192     return Kpgp::Canceled;
01193       // FIXME: Allow selection
01194     }
01195     addToAllSplitInfos( d->mOpenPGPEncryptToSelfKeys,
01196             InlineOpenPGPFormat|OpenPGPMIMEFormat );
01197   }
01198 
01199   // 4b. Check for S/MIME certs:
01200 
01201   if ( !encryptionItems( SMIMEFormat ).empty() ||
01202        !encryptionItems( SMIMEOpaqueFormat ).empty() ) {
01203     // need them
01204     if ( d->mSMIMEEncryptToSelfKeys.empty() ) {
01205       // don't have one
01206       const QString msg = i18n("Examination of recipient's encryption preferences "
01207                    "yielded that the message should be encrypted using "
01208                    "S/MIME, at least for some recipients;\n"
01209                    "however, you have not configured valid "
01210                    "S/MIME encryption certificates for this identity.\n"
01211                    "You may continue without encrypting to yourself, "
01212                    "but be aware that you will not be able to read your "
01213                    "own messages if you do so.");
01214       if ( KMessageBox::warningContinueCancel( 0, msg,
01215                            i18n("Unusable Encryption Keys"),
01216                            KStdGuiItem::cont(),
01217                            "encrypt-to-self will fail warning" )
01218        == KMessageBox::Cancel )
01219     return Kpgp::Canceled;
01220       // FIXME: Allow selection
01221     }
01222     addToAllSplitInfos( d->mSMIMEEncryptToSelfKeys,
01223             SMIMEFormat|SMIMEOpaqueFormat );
01224   }
01225 
01226   // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
01227   // are missing.
01228 
01229   return Kpgp::Ok;
01230 }
01231 
01232 Kpgp::Result Kleo::KeyResolver::resolveSigningKeysForEncryption() {
01233   if ( ( !encryptionItems( InlineOpenPGPFormat ).empty() ||
01234      !encryptionItems( OpenPGPMIMEFormat ).empty() )
01235        && d->mOpenPGPSigningKeys.empty() ) {
01236     const QString msg = i18n("Examination of recipient's signing preferences "
01237                  "yielded that the message should be signed using "
01238                  "OpenPGP, at least for some recipients;\n"
01239                  "however, you have not configured valid "
01240                  "OpenPGP signing certificates for this identity.");
01241     if ( KMessageBox::warningContinueCancel( 0, msg,
01242                          i18n("Unusable Signing Keys"),
01243                          i18n("Do Not OpenPGP-Sign"),
01244                          "signing will fail warning" )
01245      == KMessageBox::Cancel )
01246       return Kpgp::Canceled;
01247     // FIXME: Allow selection
01248   }
01249   if ( ( !encryptionItems( SMIMEFormat ).empty() ||
01250      !encryptionItems( SMIMEOpaqueFormat ).empty() )
01251        && d->mSMIMESigningKeys.empty() ) {
01252     const QString msg = i18n("Examination of recipient's signing preferences "
01253                  "yielded that the message should be signed using "
01254                  "S/MIME, at least for some recipients;\n"
01255                  "however, you have not configured valid "
01256                  "S/MIME signing certificates for this identity.");
01257     if ( KMessageBox::warningContinueCancel( 0, msg,
01258                          i18n("Unusable Signing Keys"),
01259                          i18n("Do Not S/MIME-Sign"),
01260                          "signing will fail warning" )
01261      == KMessageBox::Cancel )
01262       return Kpgp::Canceled;
01263     // FIXME: Allow selection
01264   }
01265 
01266   // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
01267   // are missing.
01268 
01269   for ( std::map<CryptoMessageFormat,FormatInfo>::iterator it = d->mFormatInfoMap.begin() ; it != d->mFormatInfoMap.end() ; ++it )
01270     if ( !it->second.splitInfos.empty() ) {
01271       dump();
01272       it->second.signKeys = signingKeysFor( it->first );
01273       dump();
01274     }
01275 
01276   return Kpgp::Ok;
01277 }
01278 
01279 Kpgp::Result Kleo::KeyResolver::resolveSigningKeysForSigningOnly() {
01280   //
01281   // we don't need to distinguish between primary and secondary
01282   // recipients here:
01283   //
01284   SigningFormatPreferenceCounter count;
01285   count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01286              count );
01287   count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01288              count );
01289 
01290   // try to find a common format that works for all (and that we have signing keys for):
01291 
01292   CryptoMessageFormat commonFormat = AutoFormat;
01293 
01294   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01295     if ( !(mCryptoMessageFormats & concreteCryptoMessageFormats[i]) )
01296       continue; // skip
01297     if ( signingKeysFor( concreteCryptoMessageFormats[i] ).empty() )
01298       continue; // skip
01299     if ( count.numOf( concreteCryptoMessageFormats[i] ) == count.numTotal() ) {
01300       commonFormat = concreteCryptoMessageFormats[i];
01301       break;
01302     }
01303   }
01304 
01305   if ( commonFormat != AutoFormat ) { // found
01306     dump();
01307     FormatInfo & fi = d->mFormatInfoMap[ commonFormat ];
01308     fi.signKeys = signingKeysFor( commonFormat );
01309     fi.splitInfos.resize( 1 );
01310     fi.splitInfos.front() = SplitInfo( allRecipients() );
01311     dump();
01312     return Kpgp::Ok;
01313   }
01314 
01315   const QString msg = i18n("Examination of recipient's signing preferences "
01316                            "showed no common type of signature matching your "
01317                            "available signing keys.\n"
01318                            "Send message without signing?"  );
01319   if ( KMessageBox::warningContinueCancel( 0, msg, i18n("No signing possible"),
01320                                            KStdGuiItem::cont() )
01321        == KMessageBox::Continue ) {
01322     d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.push_back( SplitInfo( allRecipients() ) );
01323     return Kpgp::Failure; // means "Ok, but without signing"
01324   }
01325   return Kpgp::Canceled;
01326 }
01327 
01328 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeysFor( CryptoMessageFormat f ) const {
01329   if ( isOpenPGP( f ) )
01330     return d->mOpenPGPSigningKeys;
01331   if ( isSMIME( f ) )
01332     return d->mSMIMESigningKeys;
01333   return std::vector<GpgME::Key>();
01334 }
01335 
01336 std::vector<GpgME::Key> Kleo::KeyResolver::encryptToSelfKeysFor( CryptoMessageFormat f ) const {
01337   if ( isOpenPGP( f ) )
01338     return d->mOpenPGPEncryptToSelfKeys;
01339   if ( isSMIME( f ) )
01340     return d->mSMIMEEncryptToSelfKeys;
01341   return std::vector<GpgME::Key>();
01342 }
01343 
01344 QStringList Kleo::KeyResolver::allRecipients() const {
01345   QStringList result;
01346   std::transform( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01347           std::back_inserter( result ), ItemDotAddress );
01348   std::transform( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01349           std::back_inserter( result ), ItemDotAddress );
01350   return result;
01351 }
01352 
01353 void Kleo::KeyResolver::collapseAllSplitInfos() {
01354   dump();
01355   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01356     std::map<CryptoMessageFormat,FormatInfo>::iterator pos =
01357       d->mFormatInfoMap.find( concreteCryptoMessageFormats[i] );
01358     if ( pos == d->mFormatInfoMap.end() )
01359       continue;
01360     std::vector<SplitInfo> & v = pos->second.splitInfos;
01361     if ( v.size() < 2 )
01362       continue;
01363     SplitInfo & si = v.front();
01364     for ( std::vector<SplitInfo>::const_iterator it = v.begin() + 1; it != v.end() ; ++it ) {
01365       si.keys.insert( si.keys.end(), it->keys.begin(), it->keys.end() );
01366       qCopy( it->recipients.begin(), it->recipients.end(), std::back_inserter( si.recipients ) );
01367     }
01368     v.resize( 1 );
01369   }
01370   dump();
01371 }
01372 
01373 void Kleo::KeyResolver::addToAllSplitInfos( const std::vector<GpgME::Key> & keys, unsigned int f ) {
01374   dump();
01375   if ( !f || keys.empty() )
01376     return;
01377   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01378     if ( !( f & concreteCryptoMessageFormats[i] ) )
01379       continue;
01380     std::map<CryptoMessageFormat,FormatInfo>::iterator pos =
01381       d->mFormatInfoMap.find( concreteCryptoMessageFormats[i] );
01382     if ( pos == d->mFormatInfoMap.end() )
01383       continue;
01384     std::vector<SplitInfo> & v = pos->second.splitInfos;
01385     for ( std::vector<SplitInfo>::iterator it = v.begin() ; it != v.end() ; ++it )
01386       it->keys.insert( it->keys.end(), keys.begin(), keys.end() );
01387   }
01388   dump();
01389 }
01390 
01391 void Kleo::KeyResolver::dump() const {
01392 #ifndef NDEBUG
01393   if ( d->mFormatInfoMap.empty() )
01394     std::cerr << "Keyresolver: Format info empty" << std::endl;
01395   for ( std::map<CryptoMessageFormat,FormatInfo>::const_iterator it = d->mFormatInfoMap.begin() ; it != d->mFormatInfoMap.end() ; ++it ) {
01396     std::cerr << "Format info for " << Kleo::cryptoMessageFormatToString( it->first )
01397           << ":" << std::endl
01398           << "  Signing keys: ";
01399     for ( std::vector<GpgME::Key>::const_iterator sit = it->second.signKeys.begin() ; sit != it->second.signKeys.end() ; ++sit )
01400       std::cerr << sit->shortKeyID() << " ";
01401     std::cerr << std::endl;
01402     unsigned int i = 0;
01403     for ( std::vector<SplitInfo>::const_iterator sit = it->second.splitInfos.begin() ; sit != it->second.splitInfos.end() ; ++sit, ++i ) {
01404       std::cerr << "  SplitInfo #" << i << " encryption keys: ";
01405       for ( std::vector<GpgME::Key>::const_iterator kit = sit->keys.begin() ; kit != sit->keys.end() ; ++kit )
01406     std::cerr << kit->shortKeyID() << " ";
01407       std::cerr << std::endl
01408         << "  SplitInfo #" << i << " recipients: "
01409         << sit->recipients.join(", ").utf8() << std::endl;
01410     }
01411   }
01412 #endif
01413 }
01414 
01415 Kpgp::Result Kleo::KeyResolver::showKeyApprovalDialog() {
01416   const bool showKeysForApproval = showApprovalDialog()
01417     || std::find_if( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01418              ApprovalNeeded ) != d->mPrimaryEncryptionKeys.end()
01419     || std::find_if( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01420              ApprovalNeeded ) != d->mSecondaryEncryptionKeys.end() ;
01421 
01422   if ( !showKeysForApproval )
01423     return Kpgp::Ok;
01424 
01425   std::vector<Kleo::KeyApprovalDialog::Item> items;
01426   items.reserve( d->mPrimaryEncryptionKeys.size() +
01427              d->mSecondaryEncryptionKeys.size() );
01428   std::copy( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01429          std::back_inserter( items ) );
01430   std::copy( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01431          std::back_inserter( items ) );
01432 
01433   std::vector<GpgME::Key> senderKeys;
01434   senderKeys.reserve( d->mOpenPGPEncryptToSelfKeys.size() +
01435                   d->mSMIMEEncryptToSelfKeys.size() );
01436   std::copy( d->mOpenPGPEncryptToSelfKeys.begin(), d->mOpenPGPEncryptToSelfKeys.end(),
01437          std::back_inserter( senderKeys ) );
01438   std::copy( d->mSMIMEEncryptToSelfKeys.begin(), d->mSMIMEEncryptToSelfKeys.end(),
01439          std::back_inserter( senderKeys ) );
01440 
01441   const KCursorSaver idle( KBusyPtr::idle() );
01442 
01443   Kleo::KeyApprovalDialog dlg( items, senderKeys );
01444 
01445   if ( dlg.exec() == QDialog::Rejected )
01446     return Kpgp::Canceled;
01447 
01448   items = dlg.items();
01449   senderKeys = dlg.senderKeys();
01450 
01451   if ( dlg.preferencesChanged() ) {
01452     for ( uint i = 0; i < items.size(); ++i ) {
01453       ContactPreferences pref = lookupContactPreferences( items[i].address );
01454       pref.encryptionPreference = items[i].pref;
01455       pref.pgpKeyFingerprints.clear();
01456       pref.smimeCertFingerprints.clear();
01457       const std::vector<GpgME::Key> & keys = items[i].keys;
01458       for ( std::vector<GpgME::Key>::const_iterator it = keys.begin(), end = keys.end() ; it != end ; ++it ) {
01459         if ( it->protocol() == GpgME::Context::OpenPGP ) {
01460           if ( const char * fpr = it->primaryFingerprint() )
01461             pref.pgpKeyFingerprints.push_back( fpr );
01462         } else if ( it->protocol() == GpgME::Context::CMS ) {
01463           if ( const char * fpr = it->primaryFingerprint() )
01464             pref.smimeCertFingerprints.push_back( fpr );
01465         }
01466       }
01467       saveContactPreference( items[i].address, pref );
01468     }
01469   }
01470 
01471   // show a warning if the user didn't select an encryption key for
01472   // herself:
01473   if ( encryptToSelf() && senderKeys.empty() ) {
01474     const QString msg = i18n("You did not select an encryption key for yourself "
01475                  "(encrypt to self). You will not be able to decrypt "
01476                  "your own message if you encrypt it.");
01477     if ( KMessageBox::warningContinueCancel( 0, msg,
01478                          i18n("Missing Key Warning"),
01479                          i18n("&Encrypt") )
01480      == KMessageBox::Cancel )
01481       return Kpgp::Canceled;
01482     else
01483       mEncryptToSelf = false;
01484   }
01485 
01486   // count empty key ID lists
01487   const unsigned int emptyListCount =
01488     std::count_if( items.begin(), items.end(), EmptyKeyList );
01489 
01490   // show a warning if the user didn't select an encryption key for
01491   // some of the recipients
01492   if ( items.size() == emptyListCount  ) {
01493     const QString msg = ( d->mPrimaryEncryptionKeys.size() +
01494               d->mSecondaryEncryptionKeys.size() == 1 )
01495                   ? i18n("You did not select an encryption key for the "
01496                          "recipient of this message; therefore, the message "
01497                          "will not be encrypted.")
01498                   : i18n("You did not select an encryption key for any of the "
01499                          "recipients of this message; therefore, the message "
01500                          "will not be encrypted.");
01501     if ( KMessageBox::warningContinueCancel( 0, msg,
01502                          i18n("Missing Key Warning"),
01503                          i18n("Send &Unencrypted") )
01504      == KMessageBox::Cancel )
01505       return Kpgp::Canceled;
01506   } else if ( emptyListCount > 0 ) {
01507     const QString msg = ( emptyListCount == 1 )
01508                   ? i18n("You did not select an encryption key for one of "
01509                          "the recipients: this person will not be able to "
01510                          "decrypt the message if you encrypt it.")
01511                   : i18n("You did not select encryption keys for some of "
01512                          "the recipients: these persons will not be able to "
01513                          "decrypt the message if you encrypt it." );
01514     KCursorSaver idle( KBusyPtr::idle() );
01515     if ( KMessageBox::warningContinueCancel( 0, msg,
01516                          i18n("Missing Key Warning"),
01517                          i18n("&Encrypt") )
01518      == KMessageBox::Cancel )
01519       return Kpgp::Canceled;
01520   }
01521 
01522   std::transform( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01523           items.begin(),
01524           d->mPrimaryEncryptionKeys.begin(),
01525           CopyKeysAndEncryptionPreferences );
01526   std::transform( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01527           items.begin() + d->mPrimaryEncryptionKeys.size(),
01528           d->mSecondaryEncryptionKeys.begin(),
01529           CopyKeysAndEncryptionPreferences );
01530 
01531   d->mOpenPGPEncryptToSelfKeys.clear();
01532   d->mSMIMEEncryptToSelfKeys.clear();
01533 
01534   std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
01535                std::back_inserter( d->mOpenPGPEncryptToSelfKeys ),
01536                NotValidTrustedOpenPGPEncryptionKey ); // -= trusted (see above, too)?
01537   std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
01538                std::back_inserter( d->mSMIMEEncryptToSelfKeys ),
01539                NotValidTrustedSMIMEEncryptionKey );   // -= trusted (see above, too)?
01540 
01541   return Kpgp::Ok;
01542 }
01543 
01544 std::vector<Kleo::KeyResolver::SplitInfo> Kleo::KeyResolver::encryptionItems( Kleo::CryptoMessageFormat f ) const {
01545   dump();
01546   std::map<CryptoMessageFormat,FormatInfo>::const_iterator it =
01547     d->mFormatInfoMap.find( f );
01548   return it != d->mFormatInfoMap.end() ? it->second.splitInfos : std::vector<SplitInfo>() ;
01549 }
01550 
01551 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeys( CryptoMessageFormat f ) const {
01552   dump();
01553   std::map<CryptoMessageFormat,FormatInfo>::const_iterator it =
01554     d->mFormatInfoMap.find( f );
01555   return it != d->mFormatInfoMap.end() ? it->second.signKeys : std::vector<GpgME::Key>() ;
01556 }
01557 
01558 //
01559 //
01560 // Private helper methods below:
01561 //
01562 //
01563 
01564 
01565 std::vector<GpgME::Key> Kleo::KeyResolver::selectKeys( const QString & person, const QString & msg, const std::vector<GpgME::Key> & selectedKeys ) const {
01566   const bool opgp = containsOpenPGP( mCryptoMessageFormats );
01567   const bool x509 = containsSMIME( mCryptoMessageFormats );
01568 
01569   Kleo::KeySelectionDialog dlg( i18n("Encryption Key Selection"),
01570                 msg, selectedKeys,
01571                                 Kleo::KeySelectionDialog::ValidEncryptionKeys
01572                                 & ~(opgp ? 0 : Kleo::KeySelectionDialog::OpenPGPKeys)
01573                                 & ~(x509 ? 0 : Kleo::KeySelectionDialog::SMIMEKeys),
01574                 true, true ); // multi-selection and "remember choice" box
01575 
01576   if ( dlg.exec() != QDialog::Accepted )
01577     return std::vector<GpgME::Key>();
01578   std::vector<GpgME::Key> keys = dlg.selectedKeys();
01579   keys.erase( std::remove_if( keys.begin(), keys.end(),
01580                               NotValidTrustedEncryptionKey ), // -= trusted?
01581                               keys.end() );
01582   if ( !keys.empty() && dlg.rememberSelection() )
01583     setKeysForAddress( person, dlg.pgpKeyFingerprints(), dlg.smimeFingerprints() );
01584   return keys;
01585 }
01586 
01587 
01588 std::vector<GpgME::Key> Kleo::KeyResolver::getEncryptionKeys( const QString & person, bool quiet ) const {
01589 
01590   const QString address = canonicalAddress( person ).lower();
01591 
01592   // First look for this person's address in the address->key dictionary
01593   const QStringList fingerprints = keysForAddress( address );
01594 
01595   if ( !fingerprints.empty() ) {
01596     kdDebug() << "Using encryption keys 0x"
01597           << fingerprints.join( ", 0x" )
01598           << " for " << person << endl;
01599     std::vector<GpgME::Key> keys = lookup( fingerprints );
01600     if ( !keys.empty() ) {
01601       // Check if all of the keys are trusted and valid encryption keys
01602       if ( std::find_if( keys.begin(), keys.end(),
01603                          NotValidTrustedEncryptionKey ) != keys.end() ) { // -= trusted?
01604 
01605         // not ok, let the user select: this is not conditional on !quiet,
01606         // since it's a bug in the configuration and the user should be
01607         // notified about it as early as possible:
01608         keys = selectKeys( person,
01609             i18n("if in your language something like "
01610               "'key(s)' isn't possible please "
01611               "use the plural in the translation",
01612               "There is a problem with the "
01613               "encryption key(s) for \"%1\".\n\n"
01614               "Please re-select the key(s) which should "
01615               "be used for this recipient.").arg(person),
01616             keys );
01617       }
01618       keys = TrustedOrConfirmed( keys, address );
01619 
01620       if ( !keys.empty() )
01621         return keys;
01622       // keys.empty() is considered cancel by callers, so go on
01623     }
01624   }
01625 
01626   // Now search all public keys for matching keys
01627   std::vector<GpgME::Key> matchingKeys = lookup( person );
01628   matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
01629                       NotValidEncryptionKey ),
01630               matchingKeys.end() );
01631   // if no keys match the complete address look for keys which match
01632   // the canonical mail address
01633   if ( matchingKeys.empty() ) {
01634     matchingKeys = lookup( address );
01635     matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
01636                     NotValidEncryptionKey ),
01637             matchingKeys.end() );
01638   }
01639 
01640   // if called with quite == true (from EncryptionPreferenceCounter), we only want to
01641   // check if there are keys for this recipients, not (yet) their validity, so
01642   // don't show the untrusted encryption key warning in that case
01643   if ( !quiet )
01644       matchingKeys = TrustedOrConfirmed( matchingKeys, address );
01645   if ( quiet || matchingKeys.size() == 1 )
01646     return matchingKeys;
01647 
01648   // no match until now, or more than one key matches; let the user
01649   // choose the key(s)
01650   // FIXME: let user get the key from keyserver
01651   return TrustedOrConfirmed( selectKeys( person,
01652           matchingKeys.empty()
01653           ? i18n("if in your language something like "
01654               "'key(s)' isn't possible please "
01655               "use the plural in the translation",
01656               "<qt>No valid and trusted encryption key was "
01657               "found for \"%1\".<br/><br/>"
01658               "Select the key(s) which should "
01659               "be used for this recipient. If there is no suitable key in the list "
01660               "you can also <a href=\"%2\">search for external keys</a>.</qt>")
01661              .arg( QStyleSheet::escape(person), KURL::encode_string( KPIM::getEmailAddress(person) ) )
01662           : i18n("if in your language something like "
01663               "'key(s)' isn't possible please "
01664               "use the plural in the translation",
01665               "More than one key matches \"%1\".\n\n"
01666               "Select the key(s) which should "
01667               "be used for this recipient.").arg(person),
01668           matchingKeys ), address );
01669 }
01670 
01671 
01672 std::vector<GpgME::Key> Kleo::KeyResolver::lookup( const QStringList & patterns, bool secret ) const {
01673   if ( patterns.empty() )
01674     return std::vector<GpgME::Key>();
01675   kdDebug() << "Kleo::KeyResolver::lookup( \"" << patterns.join( "\", \"" )
01676         << "\", " << secret << " )" << endl;
01677   std::vector<GpgME::Key> result;
01678   if ( mCryptoMessageFormats & (InlineOpenPGPFormat|OpenPGPMIMEFormat) )
01679     if ( const Kleo::CryptoBackend::Protocol * p = Kleo::CryptoBackendFactory::instance()->openpgp() ) {
01680       std::auto_ptr<Kleo::KeyListJob> job( p->keyListJob( false, false, true ) ); // use validating keylisting
01681       if ( job.get() ) {
01682     std::vector<GpgME::Key> keys;
01683     job->exec( patterns, secret, keys );
01684     result.insert( result.end(), keys.begin(), keys.end() );
01685       }
01686     }
01687   if ( mCryptoMessageFormats & (SMIMEFormat|SMIMEOpaqueFormat) )
01688     if ( const Kleo::CryptoBackend::Protocol * p = Kleo::CryptoBackendFactory::instance()->smime() ) {
01689       std::auto_ptr<Kleo::KeyListJob> job( p->keyListJob( false, false, true ) ); // use validating keylisting
01690       if ( job.get() ) {
01691     std::vector<GpgME::Key> keys;
01692     job->exec( patterns, secret, keys );
01693     result.insert( result.end(), keys.begin(), keys.end() );
01694       }
01695     }
01696   kdDebug() << "  returned " << result.size() << " keys" << endl;
01697   return result;
01698 }
01699 
01700 void Kleo::KeyResolver::addKeys( const std::vector<Item> & items, CryptoMessageFormat f ) {
01701   dump();
01702   for ( std::vector<Item>::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
01703     SplitInfo si( it->address );
01704     std::remove_copy_if( it->keys.begin(), it->keys.end(),
01705              std::back_inserter( si.keys ), IsNotForFormat( f ) );
01706     dump();
01707     kdWarning( si.keys.empty() )
01708       << "Kleo::KeyResolver::addKeys(): Fix EncryptionFormatPreferenceCounter. "
01709       << "It detected a common format, but the list of such keys for recipient \""
01710       << it->address << "\" is empty!" << endl;
01711     d->mFormatInfoMap[ f ].splitInfos.push_back( si );
01712   }
01713   dump();
01714 }
01715 
01716 void Kleo::KeyResolver::addKeys( const std::vector<Item> & items ) {
01717   dump();
01718   for ( std::vector<Item>::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
01719     SplitInfo si( it->address );
01720     CryptoMessageFormat f = AutoFormat;
01721     for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01722       if ( concreteCryptoMessageFormats[i] & it->format ) {
01723         f = concreteCryptoMessageFormats[i];
01724         break;
01725       }
01726     }
01727     if ( f == AutoFormat )
01728       kdWarning() << "Kleo::KeyResolver::addKeys(): Something went wrong. Didn't find a format for \""
01729                   << it->address << "\"" << endl;
01730     else
01731       std::remove_copy_if( it->keys.begin(), it->keys.end(),
01732                            std::back_inserter( si.keys ), IsNotForFormat( f ) );
01733     d->mFormatInfoMap[ f ].splitInfos.push_back( si );
01734   }
01735   dump();
01736 }
01737 
01738 Kleo::KeyResolver::ContactPreferences Kleo::KeyResolver::lookupContactPreferences( const QString& address ) const
01739 {
01740   const Private::ContactPreferencesMap::iterator it =
01741     d->mContactPreferencesMap.find( address );
01742   if ( it != d->mContactPreferencesMap.end() )
01743     return it->second;
01744 
01745   KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
01746   const KABC::Addressee::List res = ab->findByEmail( address );
01747   ContactPreferences pref;
01748   if ( !res.isEmpty() ) {
01749     KABC::Addressee addr = res.first();
01750     QString encryptPref = addr.custom( "KADDRESSBOOK", "CRYPTOENCRYPTPREF" );
01751     pref.encryptionPreference = Kleo::stringToEncryptionPreference( encryptPref );
01752     QString signPref = addr.custom( "KADDRESSBOOK", "CRYPTOSIGNPREF" );
01753     pref.signingPreference = Kleo::stringToSigningPreference( signPref );
01754     QString cryptoFormats = addr.custom( "KADDRESSBOOK", "CRYPTOPROTOPREF" );
01755     pref.cryptoMessageFormat = Kleo::stringToCryptoMessageFormat( cryptoFormats );
01756     pref.pgpKeyFingerprints = QStringList::split( ',', addr.custom( "KADDRESSBOOK", "OPENPGPFP" ) );
01757     pref.smimeCertFingerprints = QStringList::split( ',', addr.custom( "KADDRESSBOOK", "SMIMEFP" ) );
01758   }
01759   // insert into map and grab resulting iterator
01760   d->mContactPreferencesMap.insert( std::make_pair( address, pref ) );
01761   return pref;
01762 }
01763 
01764 void Kleo::KeyResolver::saveContactPreference( const QString& email, const ContactPreferences& pref ) const
01765 {
01766   d->mContactPreferencesMap.insert( std::make_pair( email, pref ) );
01767   KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
01768   KABC::Addressee::List res = ab->findByEmail( email );
01769 
01770   KABC::Addressee addr;
01771   if ( res.isEmpty() ) {
01772      bool ok = true;
01773      QString fullName = KInputDialog::getText( i18n( "Name Selection" ), i18n( "Which name shall the contact '%1' have in your addressbook?" ).arg( email ), QString::null, &ok );
01774     if ( ok ) {
01775       addr.setNameFromString( fullName );
01776       addr.insertEmail( email, true );
01777     } else
01778       return;
01779   } else
01780     addr = res.first();
01781 
01782   addr.insertCustom( "KADDRESSBOOK", "CRYPTOENCRYPTPREF", Kleo::encryptionPreferenceToString( pref.encryptionPreference ) );
01783   addr.insertCustom( "KADDRESSBOOK", "CRYPTOSIGNPREF", Kleo::signingPreferenceToString( pref.signingPreference ) );
01784   addr.insertCustom( "KADDRESSBOOK", "CRYPTOPROTOPREF", cryptoMessageFormatToString( pref.cryptoMessageFormat ) );
01785   addr.insertCustom( "KADDRESSBOOK", "OPENPGPFP", pref.pgpKeyFingerprints.join( "," ) );
01786   addr.insertCustom( "KADDRESSBOOK", "SMIMEFP", pref.smimeCertFingerprints.join( "," ) );
01787 
01788   ab->insertAddressee( addr );
01789   KABC::Ticket *ticket = ab->requestSaveTicket( addr.resource() );
01790   if ( ticket )
01791     ab->save( ticket );
01792 
01793   // Assumption: 'pref' comes from d->mContactPreferencesMap already, no need to update that
01794 }
01795 
01796 Kleo::KeyResolver::ContactPreferences::ContactPreferences()
01797   : encryptionPreference( UnknownPreference ),
01798     signingPreference( UnknownSigningPreference ),
01799     cryptoMessageFormat( AutoFormat )
01800 {
01801 }
01802 
01803 QStringList Kleo::KeyResolver::keysForAddress( const QString & address ) const {
01804   if( address.isEmpty() ) {
01805     return QStringList();
01806   }
01807   QString addr = canonicalAddress( address ).lower();
01808   const ContactPreferences pref = lookupContactPreferences( addr );
01809   return pref.pgpKeyFingerprints + pref.smimeCertFingerprints;
01810 }
01811 
01812 void Kleo::KeyResolver::setKeysForAddress( const QString& address, const QStringList& pgpKeyFingerprints, const QStringList& smimeCertFingerprints ) const {
01813   if( address.isEmpty() ) {
01814     return;
01815   }
01816   QString addr = canonicalAddress( address ).lower();
01817   ContactPreferences pref = lookupContactPreferences( addr );
01818   pref.pgpKeyFingerprints = pgpKeyFingerprints;
01819   pref.smimeCertFingerprints = smimeCertFingerprints;
01820   saveContactPreference( addr, pref );
01821 }