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     else {
01063       result = resolveSigningKeysForSigningOnly();
01064       if ( result == Kpgp::Failure ) {
01065         signingRequested = false;
01066         return Kpgp::Ok;
01067       }
01068     }
01069   return result;
01070 }
01071 
01072 Kpgp::Result Kleo::KeyResolver::resolveEncryptionKeys( bool signingRequested ) {
01073   //
01074   // 1. Get keys for all recipients:
01075   //
01076 
01077   for ( std::vector<Item>::iterator it = d->mPrimaryEncryptionKeys.begin() ; it != d->mPrimaryEncryptionKeys.end() ; ++it ) {
01078     if ( !it->needKeys )
01079       continue;
01080     it->keys = getEncryptionKeys( it->address, false );
01081     if ( it->keys.empty() )
01082       return Kpgp::Canceled;
01083     QString addr = canonicalAddress( it->address ).lower();
01084     const ContactPreferences pref = lookupContactPreferences( addr );
01085     it->pref = pref.encryptionPreference;
01086     it->signPref = pref.signingPreference;
01087     it->format = pref.cryptoMessageFormat;
01088   }
01089 
01090   for ( std::vector<Item>::iterator it = d->mSecondaryEncryptionKeys.begin() ; it != d->mSecondaryEncryptionKeys.end() ; ++it ) {
01091     if ( !it->needKeys )
01092       continue;
01093     it->keys = getEncryptionKeys( it->address, false );
01094     if ( it->keys.empty() )
01095       return Kpgp::Canceled;
01096     QString addr = canonicalAddress( it->address ).lower();
01097     const ContactPreferences pref = lookupContactPreferences( addr );
01098     it->pref = pref.encryptionPreference;
01099     it->signPref = pref.signingPreference;
01100     it->format = pref.cryptoMessageFormat;
01101   }
01102 
01103   // 1a: Present them to the user
01104 
01105   const Kpgp::Result res = showKeyApprovalDialog();
01106   if ( res != Kpgp::Ok )
01107     return res;
01108 
01109   //
01110   // 2. Check what the primary recipients need
01111   //
01112 
01113   // 2a. Try to find a common format for all primary recipients,
01114   //     else use as many formats as needed
01115 
01116   const EncryptionFormatPreferenceCounter primaryCount
01117     = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01118              EncryptionFormatPreferenceCounter() );
01119 
01120   CryptoMessageFormat commonFormat = AutoFormat;
01121   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01122     if ( !( concreteCryptoMessageFormats[i] & mCryptoMessageFormats ) )
01123       continue;
01124     if ( signingRequested && signingKeysFor( concreteCryptoMessageFormats[i] ).empty() )
01125       continue;
01126     if ( encryptToSelf() && encryptToSelfKeysFor( concreteCryptoMessageFormats[i] ).empty() )
01127       continue;
01128     if ( primaryCount.numOf( concreteCryptoMessageFormats[i] ) == primaryCount.numTotal() ) {
01129       commonFormat = concreteCryptoMessageFormats[i];
01130       break;
01131     }
01132   }
01133   if ( commonFormat != AutoFormat )
01134     addKeys( d->mPrimaryEncryptionKeys, commonFormat );
01135   else
01136     addKeys( d->mPrimaryEncryptionKeys );
01137 
01138   collapseAllSplitInfos(); // these can be encrypted together
01139 
01140   // 2b. Just try to find _something_ for each secondary recipient,
01141   //     with a preference to a common format (if that exists)
01142 
01143   const EncryptionFormatPreferenceCounter secondaryCount
01144     = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01145              EncryptionFormatPreferenceCounter() );
01146 
01147   if ( commonFormat != AutoFormat &&
01148        secondaryCount.numOf( commonFormat ) == secondaryCount.numTotal() )
01149     addKeys( d->mSecondaryEncryptionKeys, commonFormat );
01150   else
01151     addKeys( d->mSecondaryEncryptionKeys );
01152 
01153   // 3. Check for expiry:
01154 
01155   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01156     const std::vector<SplitInfo> si_list = encryptionItems( concreteCryptoMessageFormats[i] );
01157     for ( std::vector<SplitInfo>::const_iterator sit = si_list.begin() ; sit != si_list.end() ; ++sit )
01158       for ( std::vector<GpgME::Key>::const_iterator kit = sit->keys.begin() ; kit != sit->keys.end() ; ++kit ) {
01159     const Kpgp::Result r = checkKeyNearExpiry( *kit, "other encryption key near expiry warning",
01160                            false, false );
01161     if ( r != Kpgp::Ok )
01162       return r;
01163       }
01164   }
01165 
01166   // 4. Check that we have the right keys for encryptToSelf()
01167 
01168   if ( !encryptToSelf() )
01169     return Kpgp::Ok;
01170 
01171   // 4a. Check for OpenPGP keys
01172 
01173   if ( !encryptionItems( InlineOpenPGPFormat ).empty() ||
01174        !encryptionItems( OpenPGPMIMEFormat ).empty() ) {
01175     // need them
01176     if ( d->mOpenPGPEncryptToSelfKeys.empty() ) {
01177       const QString msg = i18n("Examination of recipient's encryption preferences "
01178                    "yielded that the message should be encrypted using "
01179                    "OpenPGP, at least for some recipients;\n"
01180                    "however, you have not configured valid trusted "
01181                    "OpenPGP encryption keys for this identity.\n"
01182                    "You may continue without encrypting to yourself, "
01183                    "but be aware that you will not be able to read your "
01184                    "own messages if you do so.");
01185       if ( KMessageBox::warningContinueCancel( 0, msg,
01186                            i18n("Unusable Encryption Keys"),
01187                            KStdGuiItem::cont(),
01188                            "encrypt-to-self will fail warning" )
01189        == KMessageBox::Cancel )
01190     return Kpgp::Canceled;
01191       // FIXME: Allow selection
01192     }
01193     addToAllSplitInfos( d->mOpenPGPEncryptToSelfKeys,
01194             InlineOpenPGPFormat|OpenPGPMIMEFormat );
01195   }
01196 
01197   // 4b. Check for S/MIME certs:
01198 
01199   if ( !encryptionItems( SMIMEFormat ).empty() ||
01200        !encryptionItems( SMIMEOpaqueFormat ).empty() ) {
01201     // need them
01202     if ( d->mSMIMEEncryptToSelfKeys.empty() ) {
01203       // don't have one
01204       const QString msg = i18n("Examination of recipient's encryption preferences "
01205                    "yielded that the message should be encrypted using "
01206                    "S/MIME, at least for some recipients;\n"
01207                    "however, you have not configured valid "
01208                    "S/MIME encryption certificates for this identity.\n"
01209                    "You may continue without encrypting to yourself, "
01210                    "but be aware that you will not be able to read your "
01211                    "own messages if you do so.");
01212       if ( KMessageBox::warningContinueCancel( 0, msg,
01213                            i18n("Unusable Encryption Keys"),
01214                            KStdGuiItem::cont(),
01215                            "encrypt-to-self will fail warning" )
01216        == KMessageBox::Cancel )
01217     return Kpgp::Canceled;
01218       // FIXME: Allow selection
01219     }
01220     addToAllSplitInfos( d->mSMIMEEncryptToSelfKeys,
01221             SMIMEFormat|SMIMEOpaqueFormat );
01222   }
01223 
01224   // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
01225   // are missing.
01226 
01227   return Kpgp::Ok;
01228 }
01229 
01230 Kpgp::Result Kleo::KeyResolver::resolveSigningKeysForEncryption() {
01231   if ( ( !encryptionItems( InlineOpenPGPFormat ).empty() ||
01232      !encryptionItems( OpenPGPMIMEFormat ).empty() )
01233        && d->mOpenPGPSigningKeys.empty() ) {
01234     const QString msg = i18n("Examination of recipient's signing preferences "
01235                  "yielded that the message should be signed using "
01236                  "OpenPGP, at least for some recipients;\n"
01237                  "however, you have not configured valid "
01238                  "OpenPGP signing certificates for this identity.");
01239     if ( KMessageBox::warningContinueCancel( 0, msg,
01240                          i18n("Unusable Signing Keys"),
01241                          i18n("Do Not OpenPGP-Sign"),
01242                          "signing will fail warning" )
01243      == KMessageBox::Cancel )
01244       return Kpgp::Canceled;
01245     // FIXME: Allow selection
01246   }
01247   if ( ( !encryptionItems( SMIMEFormat ).empty() ||
01248      !encryptionItems( SMIMEOpaqueFormat ).empty() )
01249        && d->mSMIMESigningKeys.empty() ) {
01250     const QString msg = i18n("Examination of recipient's signing preferences "
01251                  "yielded that the message should be signed using "
01252                  "S/MIME, at least for some recipients;\n"
01253                  "however, you have not configured valid "
01254                  "S/MIME signing certificates for this identity.");
01255     if ( KMessageBox::warningContinueCancel( 0, msg,
01256                          i18n("Unusable Signing Keys"),
01257                          i18n("Do Not S/MIME-Sign"),
01258                          "signing will fail warning" )
01259      == KMessageBox::Cancel )
01260       return Kpgp::Canceled;
01261     // FIXME: Allow selection
01262   }
01263 
01264   // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
01265   // are missing.
01266 
01267   for ( std::map<CryptoMessageFormat,FormatInfo>::iterator it = d->mFormatInfoMap.begin() ; it != d->mFormatInfoMap.end() ; ++it )
01268     if ( !it->second.splitInfos.empty() ) {
01269       dump();
01270       it->second.signKeys = signingKeysFor( it->first );
01271       dump();
01272     }
01273 
01274   return Kpgp::Ok;
01275 }
01276 
01277 Kpgp::Result Kleo::KeyResolver::resolveSigningKeysForSigningOnly() {
01278   //
01279   // we don't need to distinguish between primary and secondary
01280   // recipients here:
01281   //
01282   SigningFormatPreferenceCounter count;
01283   count = std::for_each( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01284              count );
01285   count = std::for_each( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01286              count );
01287 
01288   // try to find a common format that works for all (and that we have signing keys for):
01289 
01290   CryptoMessageFormat commonFormat = AutoFormat;
01291 
01292   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01293     if ( !(mCryptoMessageFormats & concreteCryptoMessageFormats[i]) )
01294       continue; // skip
01295     if ( signingKeysFor( concreteCryptoMessageFormats[i] ).empty() )
01296       continue; // skip
01297     if ( count.numOf( concreteCryptoMessageFormats[i] ) == count.numTotal() ) {
01298       commonFormat = concreteCryptoMessageFormats[i];
01299       break;
01300     }
01301   }
01302 
01303   if ( commonFormat != AutoFormat ) { // found
01304     dump();
01305     FormatInfo & fi = d->mFormatInfoMap[ commonFormat ];
01306     fi.signKeys = signingKeysFor( commonFormat );
01307     fi.splitInfos.resize( 1 );
01308     fi.splitInfos.front() = SplitInfo( allRecipients() );
01309     dump();
01310     return Kpgp::Ok;
01311   }
01312 
01313   const QString msg = i18n("Examination of recipient's signing preferences "
01314                            "showed no common type of signature matching your "
01315                            "available signing keys.\n"
01316                            "Send message without signing?"  );
01317   if ( KMessageBox::warningContinueCancel( 0, msg, i18n("No signing possible"),
01318                                            KStdGuiItem::cont() )
01319        == KMessageBox::Continue ) {
01320     d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.push_back( SplitInfo( allRecipients() ) );
01321     return Kpgp::Failure; // means "Ok, but without signing"
01322   }
01323   return Kpgp::Canceled;
01324 }
01325 
01326 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeysFor( CryptoMessageFormat f ) const {
01327   if ( isOpenPGP( f ) )
01328     return d->mOpenPGPSigningKeys;
01329   if ( isSMIME( f ) )
01330     return d->mSMIMESigningKeys;
01331   return std::vector<GpgME::Key>();
01332 }
01333 
01334 std::vector<GpgME::Key> Kleo::KeyResolver::encryptToSelfKeysFor( CryptoMessageFormat f ) const {
01335   if ( isOpenPGP( f ) )
01336     return d->mOpenPGPEncryptToSelfKeys;
01337   if ( isSMIME( f ) )
01338     return d->mSMIMEEncryptToSelfKeys;
01339   return std::vector<GpgME::Key>();
01340 }
01341 
01342 QStringList Kleo::KeyResolver::allRecipients() const {
01343   QStringList result;
01344   std::transform( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01345           std::back_inserter( result ), ItemDotAddress );
01346   std::transform( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01347           std::back_inserter( result ), ItemDotAddress );
01348   return result;
01349 }
01350 
01351 void Kleo::KeyResolver::collapseAllSplitInfos() {
01352   dump();
01353   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01354     std::map<CryptoMessageFormat,FormatInfo>::iterator pos =
01355       d->mFormatInfoMap.find( concreteCryptoMessageFormats[i] );
01356     if ( pos == d->mFormatInfoMap.end() )
01357       continue;
01358     std::vector<SplitInfo> & v = pos->second.splitInfos;
01359     if ( v.size() < 2 )
01360       continue;
01361     SplitInfo & si = v.front();
01362     for ( std::vector<SplitInfo>::const_iterator it = v.begin() + 1; it != v.end() ; ++it ) {
01363       si.keys.insert( si.keys.end(), it->keys.begin(), it->keys.end() );
01364       qCopy( it->recipients.begin(), it->recipients.end(), std::back_inserter( si.recipients ) );
01365     }
01366     v.resize( 1 );
01367   }
01368   dump();
01369 }
01370 
01371 void Kleo::KeyResolver::addToAllSplitInfos( const std::vector<GpgME::Key> & keys, unsigned int f ) {
01372   dump();
01373   if ( !f || keys.empty() )
01374     return;
01375   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01376     if ( !( f & concreteCryptoMessageFormats[i] ) )
01377       continue;
01378     std::map<CryptoMessageFormat,FormatInfo>::iterator pos =
01379       d->mFormatInfoMap.find( concreteCryptoMessageFormats[i] );
01380     if ( pos == d->mFormatInfoMap.end() )
01381       continue;
01382     std::vector<SplitInfo> & v = pos->second.splitInfos;
01383     for ( std::vector<SplitInfo>::iterator it = v.begin() ; it != v.end() ; ++it )
01384       it->keys.insert( it->keys.end(), keys.begin(), keys.end() );
01385   }
01386   dump();
01387 }
01388 
01389 void Kleo::KeyResolver::dump() const {
01390 #ifndef NDEBUG
01391   if ( d->mFormatInfoMap.empty() )
01392     std::cerr << "Keyresolver: Format info empty" << std::endl;
01393   for ( std::map<CryptoMessageFormat,FormatInfo>::const_iterator it = d->mFormatInfoMap.begin() ; it != d->mFormatInfoMap.end() ; ++it ) {
01394     std::cerr << "Format info for " << Kleo::cryptoMessageFormatToString( it->first )
01395           << ":" << std::endl
01396           << "  Signing keys: ";
01397     for ( std::vector<GpgME::Key>::const_iterator sit = it->second.signKeys.begin() ; sit != it->second.signKeys.end() ; ++sit )
01398       std::cerr << sit->shortKeyID() << " ";
01399     std::cerr << std::endl;
01400     unsigned int i = 0;
01401     for ( std::vector<SplitInfo>::const_iterator sit = it->second.splitInfos.begin() ; sit != it->second.splitInfos.end() ; ++sit, ++i ) {
01402       std::cerr << "  SplitInfo #" << i << " encryption keys: ";
01403       for ( std::vector<GpgME::Key>::const_iterator kit = sit->keys.begin() ; kit != sit->keys.end() ; ++kit )
01404     std::cerr << kit->shortKeyID() << " ";
01405       std::cerr << std::endl
01406         << "  SplitInfo #" << i << " recipients: "
01407         << sit->recipients.join(", ").utf8() << std::endl;
01408     }
01409   }
01410 #endif
01411 }
01412 
01413 Kpgp::Result Kleo::KeyResolver::showKeyApprovalDialog() {
01414   const bool showKeysForApproval = showApprovalDialog()
01415     || std::find_if( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01416              ApprovalNeeded ) != d->mPrimaryEncryptionKeys.end()
01417     || std::find_if( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01418              ApprovalNeeded ) != d->mSecondaryEncryptionKeys.end() ;
01419 
01420   if ( !showKeysForApproval )
01421     return Kpgp::Ok;
01422 
01423   std::vector<Kleo::KeyApprovalDialog::Item> items;
01424   items.reserve( d->mPrimaryEncryptionKeys.size() +
01425              d->mSecondaryEncryptionKeys.size() );
01426   std::copy( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01427          std::back_inserter( items ) );
01428   std::copy( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01429          std::back_inserter( items ) );
01430 
01431   std::vector<GpgME::Key> senderKeys;
01432   senderKeys.reserve( d->mOpenPGPEncryptToSelfKeys.size() +
01433                   d->mSMIMEEncryptToSelfKeys.size() );
01434   std::copy( d->mOpenPGPEncryptToSelfKeys.begin(), d->mOpenPGPEncryptToSelfKeys.end(),
01435          std::back_inserter( senderKeys ) );
01436   std::copy( d->mSMIMEEncryptToSelfKeys.begin(), d->mSMIMEEncryptToSelfKeys.end(),
01437          std::back_inserter( senderKeys ) );
01438 
01439   const KCursorSaver idle( KBusyPtr::idle() );
01440 
01441   Kleo::KeyApprovalDialog dlg( items, senderKeys );
01442 
01443   if ( dlg.exec() == QDialog::Rejected )
01444     return Kpgp::Canceled;
01445 
01446   items = dlg.items();
01447   senderKeys = dlg.senderKeys();
01448 
01449   if ( dlg.preferencesChanged() ) {
01450     for ( uint i = 0; i < items.size(); ++i ) {
01451       ContactPreferences pref = lookupContactPreferences( items[i].address );
01452       pref.encryptionPreference = items[i].pref;
01453       pref.pgpKeyFingerprints.clear();
01454       pref.smimeCertFingerprints.clear();
01455       const std::vector<GpgME::Key> & keys = items[i].keys;
01456       for ( std::vector<GpgME::Key>::const_iterator it = keys.begin(), end = keys.end() ; it != end ; ++it ) {
01457         if ( it->protocol() == GpgME::Context::OpenPGP ) {
01458           if ( const char * fpr = it->primaryFingerprint() )
01459             pref.pgpKeyFingerprints.push_back( fpr );
01460         } else if ( it->protocol() == GpgME::Context::CMS ) {
01461           if ( const char * fpr = it->primaryFingerprint() )
01462             pref.smimeCertFingerprints.push_back( fpr );
01463         }
01464       }
01465       saveContactPreference( items[i].address, pref );
01466     }
01467   }
01468 
01469   // show a warning if the user didn't select an encryption key for
01470   // herself:
01471   if ( encryptToSelf() && senderKeys.empty() ) {
01472     const QString msg = i18n("You did not select an encryption key for yourself "
01473                  "(encrypt to self). You will not be able to decrypt "
01474                  "your own message if you encrypt it.");
01475     if ( KMessageBox::warningContinueCancel( 0, msg,
01476                          i18n("Missing Key Warning"),
01477                          i18n("&Encrypt") )
01478      == KMessageBox::Cancel )
01479       return Kpgp::Canceled;
01480     else
01481       mEncryptToSelf = false;
01482   }
01483 
01484   // count empty key ID lists
01485   const unsigned int emptyListCount =
01486     std::count_if( items.begin(), items.end(), EmptyKeyList );
01487 
01488   // show a warning if the user didn't select an encryption key for
01489   // some of the recipients
01490   if ( items.size() == emptyListCount  ) {
01491     const QString msg = ( d->mPrimaryEncryptionKeys.size() +
01492               d->mSecondaryEncryptionKeys.size() == 1 )
01493                   ? i18n("You did not select an encryption key for the "
01494                          "recipient of this message; therefore, the message "
01495                          "will not be encrypted.")
01496                   : i18n("You did not select an encryption key for any of the "
01497                          "recipients of this message; therefore, the message "
01498                          "will not be encrypted.");
01499     if ( KMessageBox::warningContinueCancel( 0, msg,
01500                          i18n("Missing Key Warning"),
01501                          i18n("Send &Unencrypted") )
01502      == KMessageBox::Cancel )
01503       return Kpgp::Canceled;
01504   } else if ( emptyListCount > 0 ) {
01505     const QString msg = ( emptyListCount == 1 )
01506                   ? i18n("You did not select an encryption key for one of "
01507                          "the recipients: this person will not be able to "
01508                          "decrypt the message if you encrypt it.")
01509                   : i18n("You did not select encryption keys for some of "
01510                          "the recipients: these persons will not be able to "
01511                          "decrypt the message if you encrypt it." );
01512     KCursorSaver idle( KBusyPtr::idle() );
01513     if ( KMessageBox::warningContinueCancel( 0, msg,
01514                          i18n("Missing Key Warning"),
01515                          i18n("&Encrypt") )
01516      == KMessageBox::Cancel )
01517       return Kpgp::Canceled;
01518   }
01519 
01520   std::transform( d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(),
01521           items.begin(),
01522           d->mPrimaryEncryptionKeys.begin(),
01523           CopyKeysAndEncryptionPreferences );
01524   std::transform( d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(),
01525           items.begin() + d->mPrimaryEncryptionKeys.size(),
01526           d->mSecondaryEncryptionKeys.begin(),
01527           CopyKeysAndEncryptionPreferences );
01528 
01529   d->mOpenPGPEncryptToSelfKeys.clear();
01530   d->mSMIMEEncryptToSelfKeys.clear();
01531 
01532   std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
01533                std::back_inserter( d->mOpenPGPEncryptToSelfKeys ),
01534                NotValidTrustedOpenPGPEncryptionKey ); // -= trusted (see above, too)?
01535   std::remove_copy_if( senderKeys.begin(), senderKeys.end(),
01536                std::back_inserter( d->mSMIMEEncryptToSelfKeys ),
01537                NotValidTrustedSMIMEEncryptionKey );   // -= trusted (see above, too)?
01538 
01539   return Kpgp::Ok;
01540 }
01541 
01542 std::vector<Kleo::KeyResolver::SplitInfo> Kleo::KeyResolver::encryptionItems( Kleo::CryptoMessageFormat f ) const {
01543   dump();
01544   std::map<CryptoMessageFormat,FormatInfo>::const_iterator it =
01545     d->mFormatInfoMap.find( f );
01546   return it != d->mFormatInfoMap.end() ? it->second.splitInfos : std::vector<SplitInfo>() ;
01547 }
01548 
01549 std::vector<GpgME::Key> Kleo::KeyResolver::signingKeys( CryptoMessageFormat f ) const {
01550   dump();
01551   std::map<CryptoMessageFormat,FormatInfo>::const_iterator it =
01552     d->mFormatInfoMap.find( f );
01553   return it != d->mFormatInfoMap.end() ? it->second.signKeys : std::vector<GpgME::Key>() ;
01554 }
01555 
01556 //
01557 //
01558 // Private helper methods below:
01559 //
01560 //
01561 
01562 
01563 std::vector<GpgME::Key> Kleo::KeyResolver::selectKeys( const QString & person, const QString & msg, const std::vector<GpgME::Key> & selectedKeys ) const {
01564   const bool opgp = containsOpenPGP( mCryptoMessageFormats );
01565   const bool x509 = containsSMIME( mCryptoMessageFormats );
01566 
01567   Kleo::KeySelectionDialog dlg( i18n("Encryption Key Selection"),
01568                 msg, selectedKeys,
01569                                 Kleo::KeySelectionDialog::ValidEncryptionKeys
01570                                 & ~(opgp ? 0 : Kleo::KeySelectionDialog::OpenPGPKeys)
01571                                 & ~(x509 ? 0 : Kleo::KeySelectionDialog::SMIMEKeys),
01572                 true, true ); // multi-selection and "remember choice" box
01573 
01574   if ( dlg.exec() != QDialog::Accepted )
01575     return std::vector<GpgME::Key>();
01576   std::vector<GpgME::Key> keys = dlg.selectedKeys();
01577   keys.erase( std::remove_if( keys.begin(), keys.end(),
01578                               NotValidTrustedEncryptionKey ), // -= trusted?
01579                               keys.end() );
01580   if ( !keys.empty() && dlg.rememberSelection() )
01581     setKeysForAddress( person, dlg.pgpKeyFingerprints(), dlg.smimeFingerprints() );
01582   return keys;
01583 }
01584 
01585 
01586 std::vector<GpgME::Key> Kleo::KeyResolver::getEncryptionKeys( const QString & person, bool quiet ) const {
01587 
01588   const QString address = canonicalAddress( person ).lower();
01589 
01590   // First look for this person's address in the address->key dictionary
01591   const QStringList fingerprints = keysForAddress( address );
01592 
01593   if ( !fingerprints.empty() ) {
01594     kdDebug() << "Using encryption keys 0x"
01595           << fingerprints.join( ", 0x" )
01596           << " for " << person << endl;
01597     std::vector<GpgME::Key> keys = lookup( fingerprints );
01598     if ( !keys.empty() ) {
01599       // Check if all of the keys are trusted and valid encryption keys
01600       if ( std::find_if( keys.begin(), keys.end(),
01601                          NotValidTrustedEncryptionKey ) != keys.end() ) { // -= trusted?
01602 
01603         // not ok, let the user select: this is not conditional on !quiet,
01604         // since it's a bug in the configuration and the user should be
01605         // notified about it as early as possible:
01606         keys = selectKeys( person,
01607             i18n("if in your language something like "
01608               "'key(s)' isn't possible please "
01609               "use the plural in the translation",
01610               "There is a problem with the "
01611               "encryption key(s) for \"%1\".\n\n"
01612               "Please re-select the key(s) which should "
01613               "be used for this recipient.").arg(person),
01614             keys );
01615       }
01616       keys = TrustedOrConfirmed( keys, address );
01617 
01618       if ( !keys.empty() )
01619         return keys;
01620       // keys.empty() is considered cancel by callers, so go on
01621     }
01622   }
01623 
01624   // Now search all public keys for matching keys
01625   std::vector<GpgME::Key> matchingKeys = lookup( person );
01626   matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
01627                       NotValidEncryptionKey ),
01628               matchingKeys.end() );
01629   // if no keys match the complete address look for keys which match
01630   // the canonical mail address
01631   if ( matchingKeys.empty() ) {
01632     matchingKeys = lookup( address );
01633     matchingKeys.erase( std::remove_if( matchingKeys.begin(), matchingKeys.end(),
01634                     NotValidEncryptionKey ),
01635             matchingKeys.end() );
01636   }
01637 
01638   // if called with quite == true (from EncryptionPreferenceCounter), we only want to
01639   // check if there are keys for this recipients, not (yet) their validity, so
01640   // don't show the untrusted encryption key warning in that case
01641   if ( !quiet )
01642       matchingKeys = TrustedOrConfirmed( matchingKeys, address );
01643   if ( quiet || matchingKeys.size() == 1 )
01644     return matchingKeys;
01645 
01646   // no match until now, or more than one key matches; let the user
01647   // choose the key(s)
01648   // FIXME: let user get the key from keyserver
01649   return TrustedOrConfirmed( selectKeys( person,
01650           matchingKeys.empty()
01651           ? i18n("if in your language something like "
01652               "'key(s)' isn't possible please "
01653               "use the plural in the translation",
01654               "<qt>No valid and trusted encryption key was "
01655               "found for \"%1\".<br/><br/>"
01656               "Select the key(s) which should "
01657               "be used for this recipient. If there is no suitable key in the list "
01658               "you can also <a href=\"%2\">search for external keys</a>.</qt>")
01659              .arg( QStyleSheet::escape(person), KURL::encode_string( KPIM::getEmailAddress(person) ) )
01660           : i18n("if in your language something like "
01661               "'key(s)' isn't possible please "
01662               "use the plural in the translation",
01663               "More than one key matches \"%1\".\n\n"
01664               "Select the key(s) which should "
01665               "be used for this recipient.").arg(person),
01666           matchingKeys ), address );
01667 }
01668 
01669 
01670 std::vector<GpgME::Key> Kleo::KeyResolver::lookup( const QStringList & patterns, bool secret ) const {
01671   if ( patterns.empty() )
01672     return std::vector<GpgME::Key>();
01673   kdDebug() << "Kleo::KeyResolver::lookup( \"" << patterns.join( "\", \"" )
01674         << "\", " << secret << " )" << endl;
01675   std::vector<GpgME::Key> result;
01676   if ( mCryptoMessageFormats & (InlineOpenPGPFormat|OpenPGPMIMEFormat) )
01677     if ( const Kleo::CryptoBackend::Protocol * p = Kleo::CryptoBackendFactory::instance()->openpgp() ) {
01678       std::auto_ptr<Kleo::KeyListJob> job( p->keyListJob( false, false, true ) ); // use validating keylisting
01679       if ( job.get() ) {
01680     std::vector<GpgME::Key> keys;
01681     job->exec( patterns, secret, keys );
01682     result.insert( result.end(), keys.begin(), keys.end() );
01683       }
01684     }
01685   if ( mCryptoMessageFormats & (SMIMEFormat|SMIMEOpaqueFormat) )
01686     if ( const Kleo::CryptoBackend::Protocol * p = Kleo::CryptoBackendFactory::instance()->smime() ) {
01687       std::auto_ptr<Kleo::KeyListJob> job( p->keyListJob( false, false, true ) ); // use validating keylisting
01688       if ( job.get() ) {
01689     std::vector<GpgME::Key> keys;
01690     job->exec( patterns, secret, keys );
01691     result.insert( result.end(), keys.begin(), keys.end() );
01692       }
01693     }
01694   kdDebug() << "  returned " << result.size() << " keys" << endl;
01695   return result;
01696 }
01697 
01698 void Kleo::KeyResolver::addKeys( const std::vector<Item> & items, CryptoMessageFormat f ) {
01699   dump();
01700   for ( std::vector<Item>::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
01701     SplitInfo si( it->address );
01702     std::remove_copy_if( it->keys.begin(), it->keys.end(),
01703              std::back_inserter( si.keys ), IsNotForFormat( f ) );
01704     dump();
01705     kdWarning( si.keys.empty() )
01706       << "Kleo::KeyResolver::addKeys(): Fix EncryptionFormatPreferenceCounter. "
01707       << "It detected a common format, but the list of such keys for recipient \""
01708       << it->address << "\" is empty!" << endl;
01709     d->mFormatInfoMap[ f ].splitInfos.push_back( si );
01710   }
01711   dump();
01712 }
01713 
01714 void Kleo::KeyResolver::addKeys( const std::vector<Item> & items ) {
01715   dump();
01716   for ( std::vector<Item>::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
01717     SplitInfo si( it->address );
01718     CryptoMessageFormat f = AutoFormat;
01719     for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
01720       if ( concreteCryptoMessageFormats[i] & it->format ) {
01721         f = concreteCryptoMessageFormats[i];
01722         break;
01723       }
01724     }
01725     if ( f == AutoFormat )
01726       kdWarning() << "Kleo::KeyResolver::addKeys(): Something went wrong. Didn't find a format for \""
01727                   << it->address << "\"" << endl;
01728     else
01729       std::remove_copy_if( it->keys.begin(), it->keys.end(),
01730                            std::back_inserter( si.keys ), IsNotForFormat( f ) );
01731     d->mFormatInfoMap[ f ].splitInfos.push_back( si );
01732   }
01733   dump();
01734 }
01735 
01736 Kleo::KeyResolver::ContactPreferences Kleo::KeyResolver::lookupContactPreferences( const QString& address ) const
01737 {
01738   const Private::ContactPreferencesMap::iterator it =
01739     d->mContactPreferencesMap.find( address );
01740   if ( it != d->mContactPreferencesMap.end() )
01741     return it->second;
01742 
01743   KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
01744   const KABC::Addressee::List res = ab->findByEmail( address );
01745   ContactPreferences pref;
01746   if ( !res.isEmpty() ) {
01747     KABC::Addressee addr = res.first();
01748     QString encryptPref = addr.custom( "KADDRESSBOOK", "CRYPTOENCRYPTPREF" );
01749     pref.encryptionPreference = Kleo::stringToEncryptionPreference( encryptPref );
01750     QString signPref = addr.custom( "KADDRESSBOOK", "CRYPTOSIGNPREF" );
01751     pref.signingPreference = Kleo::stringToSigningPreference( signPref );
01752     QString cryptoFormats = addr.custom( "KADDRESSBOOK", "CRYPTOPROTOPREF" );
01753     pref.cryptoMessageFormat = Kleo::stringToCryptoMessageFormat( cryptoFormats );
01754     pref.pgpKeyFingerprints = QStringList::split( ',', addr.custom( "KADDRESSBOOK", "OPENPGPFP" ) );
01755     pref.smimeCertFingerprints = QStringList::split( ',', addr.custom( "KADDRESSBOOK", "SMIMEFP" ) );
01756   }
01757   // insert into map and grab resulting iterator
01758   d->mContactPreferencesMap.insert( std::make_pair( address, pref ) );
01759   return pref;
01760 }
01761 
01762 void Kleo::KeyResolver::saveContactPreference( const QString& email, const ContactPreferences& pref ) const
01763 {
01764   d->mContactPreferencesMap.insert( std::make_pair( email, pref ) );
01765   KABC::AddressBook *ab = KABC::StdAddressBook::self( true );
01766   KABC::Addressee::List res = ab->findByEmail( email );
01767 
01768   KABC::Addressee addr;
01769   if ( res.isEmpty() ) {
01770      bool ok = true;
01771      QString fullName = KInputDialog::getText( i18n( "Name Selection" ), i18n( "Which name shall the contact '%1' have in your addressbook?" ).arg( email ), QString::null, &ok );
01772     if ( ok ) {
01773       addr.setNameFromString( fullName );
01774       addr.insertEmail( email, true );
01775     } else
01776       return;
01777   } else
01778     addr = res.first();
01779 
01780   addr.insertCustom( "KADDRESSBOOK", "CRYPTOENCRYPTPREF", Kleo::encryptionPreferenceToString( pref.encryptionPreference ) );
01781   addr.insertCustom( "KADDRESSBOOK", "CRYPTOSIGNPREF", Kleo::signingPreferenceToString( pref.signingPreference ) );
01782   addr.insertCustom( "KADDRESSBOOK", "CRYPTOPROTOPREF", cryptoMessageFormatToString( pref.cryptoMessageFormat ) );
01783   addr.insertCustom( "KADDRESSBOOK", "OPENPGPFP", pref.pgpKeyFingerprints.join( "," ) );
01784   addr.insertCustom( "KADDRESSBOOK", "SMIMEFP", pref.smimeCertFingerprints.join( "," ) );
01785 
01786   ab->insertAddressee( addr );
01787   KABC::Ticket *ticket = ab->requestSaveTicket( addr.resource() );
01788   if ( ticket )
01789     ab->save( ticket );
01790 
01791   // Assumption: 'pref' comes from d->mContactPreferencesMap already, no need to update that
01792 }
01793 
01794 Kleo::KeyResolver::ContactPreferences::ContactPreferences()
01795   : encryptionPreference( UnknownPreference ),
01796     signingPreference( UnknownSigningPreference ),
01797     cryptoMessageFormat( AutoFormat )
01798 {
01799 }
01800 
01801 QStringList Kleo::KeyResolver::keysForAddress( const QString & address ) const {
01802   if( address.isEmpty() ) {
01803     return QStringList();
01804   }
01805   QString addr = canonicalAddress( address ).lower();
01806   const ContactPreferences pref = lookupContactPreferences( addr );
01807   return pref.pgpKeyFingerprints + pref.smimeCertFingerprints;
01808 }
01809 
01810 void Kleo::KeyResolver::setKeysForAddress( const QString& address, const QStringList& pgpKeyFingerprints, const QStringList& smimeCertFingerprints ) const {
01811   if( address.isEmpty() ) {
01812     return;
01813   }
01814   QString addr = canonicalAddress( address ).lower();
01815   ContactPreferences pref = lookupContactPreferences( addr );
01816   pref.pgpKeyFingerprints = pgpKeyFingerprints;
01817   pref.smimeCertFingerprints = smimeCertFingerprints;
01818   saveContactPreference( addr, pref );
01819 }