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