certmanager Library API Documentation

keyselectiondialog.cpp

00001 /*  -*- c++ -*-
00002     keyselectiondialog.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 kpgpui.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "keyselectiondialog.h"
00042 
00043 #include "keylistview.h"
00044 #include "progressdialog.h"
00045 
00046 #include <kleo/dn.h>
00047 #include <kleo/keylistjob.h>
00048 #include <kleo/cryptobackendfactory.h>
00049 
00050 // gpgme++
00051 #include <gpgmepp/key.h>
00052 #include <gpgmepp/keylistresult.h>
00053 
00054 // KDE
00055 #include <klocale.h>
00056 #include <kapplication.h>
00057 #include <kglobal.h>
00058 #include <kiconloader.h>
00059 #include <kdebug.h>
00060 #include <kwin.h>
00061 #include <kconfig.h>
00062 #include <kmessagebox.h>
00063 #include <kprocess.h>
00064 
00065 // Qt
00066 #include <qcheckbox.h>
00067 #include <qlabel.h>
00068 #include <qpixmap.h>
00069 #include <qtimer.h>
00070 #include <qlayout.h>
00071 #include <qlineedit.h>
00072 #include <qwhatsthis.h>
00073 #include <qpopupmenu.h>
00074 #include <qregexp.h>
00075 #include <qpushbutton.h>
00076 
00077 #include <algorithm>
00078 #include <iterator>
00079 
00080 #include <string.h>
00081 #include <assert.h>
00082 
00083 static bool checkKeyUsage( const GpgME::Key & key, unsigned int keyUsage ) {
00084 
00085   if ( keyUsage & Kleo::KeySelectionDialog::ValidKeys ) {
00086     if ( key.isInvalid() )
00087       qDebug( "key is invalid - ignoring" );
00088     if ( key.isExpired() ) {
00089       qDebug( "key is expired" );
00090       return false;
00091     } else if ( key.isRevoked() ) {
00092       qDebug( "key is revoked" );
00093       return false;
00094     } else if ( key.isDisabled() ) {
00095       qDebug( "key is disabled" );
00096       return false;
00097     }
00098   }
00099 
00100   if ( keyUsage & Kleo::KeySelectionDialog::EncryptionKeys &&
00101        !key.canEncrypt() ) {
00102     qDebug( "key can't encrypt" );
00103     return false;
00104   }
00105   if ( keyUsage & Kleo::KeySelectionDialog::SigningKeys &&
00106        !key.canSign() ) {
00107     qDebug( "key can't sign" );
00108     return false;
00109   }
00110   if ( keyUsage & Kleo::KeySelectionDialog::CertificationKeys &&
00111        !key.canCertify() ) {
00112     qDebug( "key can't certify" );
00113     return false;
00114   }
00115   if ( keyUsage & Kleo::KeySelectionDialog::AuthenticationKeys &&
00116        !key.canAuthenticate() ) {
00117     qDebug( "key can't authenticate" );
00118     return false;
00119   }
00120 
00121   if ( keyUsage & Kleo::KeySelectionDialog::SecretKeys &&
00122        !( keyUsage & Kleo::KeySelectionDialog::PublicKeys ) &&
00123        !key.isSecret() ) {
00124     qDebug( "key isn't secret" );
00125     return false;
00126   }
00127 
00128   if ( keyUsage & Kleo::KeySelectionDialog::TrustedKeys &&
00129        key.protocol() == GpgME::Context::OpenPGP &&
00130        // only check this for secret keys for now.
00131        // Seems validity isn't checked for secret keylistings...
00132        !key.isSecret() ) {
00133     std::vector<GpgME::UserID> uids = key.userIDs();
00134     for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
00135       if ( !it->isRevoked() && it->validity() >= GpgME::UserID::Marginal )
00136     return true;
00137     qDebug( "key has no UIDs with validity >= Marginal" );
00138     return false;
00139   }
00140   // X.509 keys are always trusted, else they won't be the keybox.
00141   // PENDING(marc) check that this ^ is correct
00142 
00143   return true;
00144 }
00145 
00146 static bool checkKeyUsage( const std::vector<GpgME::Key> & keys, unsigned int keyUsage ) {
00147   for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it )
00148     if ( !checkKeyUsage( *it, keyUsage ) )
00149       return false;
00150   return true;
00151 }
00152 
00153 static inline QString time_t2string( time_t t ) {
00154   QDateTime dt;
00155   dt.setTime_t( t );
00156   return dt.toString();
00157 }
00158 
00159 namespace {
00160 
00161   class ColumnStrategy : public Kleo::KeyListView::ColumnStrategy {
00162   public:
00163     ColumnStrategy( unsigned int keyUsage );
00164 
00165     QString title( int col ) const;
00166     int width( int col, const QFontMetrics & fm ) const;
00167 
00168     QString text( const GpgME::Key & key, int col ) const;
00169     QString toolTip( const GpgME::Key & key, int col ) const;
00170     const QPixmap * pixmap( const GpgME::Key & key, int col ) const;
00171 
00172   private:
00173     const QPixmap mKeyGoodPix, mKeyBadPix, mKeyUnknownPix, mKeyValidPix;
00174     const unsigned int mKeyUsage;
00175   };
00176 
00177   ColumnStrategy::ColumnStrategy( unsigned int keyUsage )
00178     : Kleo::KeyListView::ColumnStrategy(),
00179       mKeyGoodPix( UserIcon( "key_ok" ) ),
00180       mKeyBadPix( UserIcon( "key_bad" ) ),
00181       mKeyUnknownPix( UserIcon( "key_unknown" ) ),
00182       mKeyValidPix( UserIcon( "key" ) ),
00183       mKeyUsage( keyUsage )
00184   {
00185     kdWarning( keyUsage == 0, 5150 )
00186       << "KeySelectionDialog: keyUsage == 0. You want to use AllKeys instead." << endl;
00187   }
00188 
00189   QString ColumnStrategy::title( int col ) const {
00190     switch ( col ) {
00191     case 0: return i18n("Key ID");
00192     case 1: return i18n("User ID");
00193     default: return QString::null;
00194     }
00195   }
00196 
00197   int ColumnStrategy::width( int col, const QFontMetrics & fm ) const {
00198     if ( col == 0 ) {
00199       static const char hexchars[] = "0123456789ABCDEF";
00200       int maxWidth = 0;
00201       for ( unsigned int i = 0 ; i < 16 ; ++i )
00202     maxWidth = kMax( fm.width( QChar( hexchars[i] ) ), maxWidth );
00203       return 8 * maxWidth + 2 * mKeyGoodPix.width();
00204     }
00205     return Kleo::KeyListView::ColumnStrategy::width( col, fm );
00206   }
00207 
00208   QString ColumnStrategy::text( const GpgME::Key & key, int col ) const {
00209     switch ( col ) {
00210     case 0:
00211       {
00212     if ( key.shortKeyID() )
00213       return QString::fromUtf8( key.shortKeyID() );
00214     else
00215       return i18n("<unknown>");
00216       }
00217       break;
00218     case 1:
00219       {
00220     const char * uid = key.userID(0).id();
00221     if ( key.protocol() == GpgME::Context::OpenPGP )
00222       return uid && *uid ? QString::fromUtf8( uid ) : QString::null ;
00223     else // CMS
00224       return Kleo::DN( uid ).prettyDN();
00225       }
00226       break;
00227     default: return QString::null;
00228     }
00229   }
00230 
00231   QString ColumnStrategy::toolTip( const GpgME::Key & key, int ) const {
00232     const char * uid = key.userID(0).id();
00233     const char * fpr = key.primaryFingerprint();
00234     const char * issuer = key.issuerName();
00235     const GpgME::Subkey subkey = key.subkey(0);
00236     const QString expiry = subkey.neverExpires() ? i18n("never") : time_t2string( subkey.expirationTime() ) ;
00237     const QString creation = time_t2string( subkey.creationTime() );
00238     if ( key.protocol() == GpgME::Context::OpenPGP )
00239       return i18n( "OpenPGP key for %1\n"
00240            "Created: %2\n"
00241            "Expiry: %3\n"
00242            "Fingerprint: %4" )
00243     .arg( uid ? QString::fromUtf8( uid ) : i18n("unknown"),
00244           creation, expiry,
00245           fpr ? QString::fromLatin1( fpr ) : i18n("unknown") );
00246     else
00247       return i18n( "S/MIME key for %1\n"
00248            "Created: %2\n"
00249            "Expiry: %3\n"
00250            "Fingerprint: %4\n"
00251            "Issuer: %5" )
00252     .arg( uid ? Kleo::DN( uid ).prettyDN() : i18n("unknown"),
00253           creation, expiry,
00254           fpr ? QString::fromLatin1( fpr ) : i18n("unknown") )
00255     .arg( issuer ? Kleo::DN( issuer ).prettyDN() : i18n("unknown") );
00256   }
00257 
00258   const QPixmap * ColumnStrategy::pixmap( const GpgME::Key & key, int col ) const {
00259     if ( col != 0 )
00260       return 0;
00261     // this key did not undergo a validating keylisting yet:
00262     if ( !( key.keyListMode() & GpgME::Context::Validate ) )
00263       return &mKeyUnknownPix;
00264 
00265     if ( !checkKeyUsage( key, mKeyUsage ) )
00266       return &mKeyBadPix;
00267 
00268     if ( key.protocol() == GpgME::Context::CMS )
00269       return &mKeyGoodPix;
00270 
00271     switch ( key.userID(0).validity() ) {
00272     default:
00273     case GpgME::UserID::Unknown:
00274     case GpgME::UserID::Undefined:
00275       return &mKeyUnknownPix;
00276     case GpgME::UserID::Never:
00277       return &mKeyValidPix;
00278     case GpgME::UserID::Marginal:
00279     case GpgME::UserID::Full:
00280     case GpgME::UserID::Ultimate:
00281       return &mKeyGoodPix;
00282     }
00283   }
00284 
00285 }
00286 
00287 
00288 static const int sCheckSelectionDelay = 250;
00289 
00290 Kleo::KeySelectionDialog::KeySelectionDialog( const QString & title,
00291                           const QString & text,
00292                           const std::vector<GpgME::Key> & selectedKeys,
00293                           unsigned int keyUsage,
00294                           bool extendedSelection,
00295                           bool rememberChoice,
00296                           QWidget * parent, const char * name,
00297                           bool modal )
00298   : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ),
00299     mOpenPGPBackend( 0 ),
00300     mSMIMEBackend( 0 ),
00301     mRememberCB( 0 ),
00302     mSelectedKeys( selectedKeys ),
00303     mKeyUsage( keyUsage ),
00304     mCurrentContextMenuItem( 0 )
00305 {
00306   init( rememberChoice, extendedSelection, text, QString::null );
00307 }
00308 
00309 Kleo::KeySelectionDialog::KeySelectionDialog( const QString & title,
00310                           const QString & text,
00311                           const QString & initialQuery,
00312                           unsigned int keyUsage,
00313                           bool extendedSelection,
00314                           bool rememberChoice,
00315                           QWidget * parent, const char * name,
00316                           bool modal )
00317   : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ),
00318     mOpenPGPBackend( 0 ),
00319     mSMIMEBackend( 0 ),
00320     mRememberCB( 0 ),
00321     mKeyUsage( keyUsage ),
00322     mSearchText( initialQuery ),
00323     mCurrentContextMenuItem( 0 )
00324 {
00325   init( rememberChoice, extendedSelection, text, initialQuery );
00326 }
00327 
00328 void Kleo::KeySelectionDialog::init( bool rememberChoice, bool extendedSelection,
00329                      const QString & text, const QString & initialQuery ) {
00330   if ( mKeyUsage & OpenPGPKeys )
00331     mOpenPGPBackend = Kleo::CryptoBackendFactory::instance()->openpgp();
00332   if ( mKeyUsage & SMIMEKeys )
00333     mSMIMEBackend = Kleo::CryptoBackendFactory::instance()->smime();
00334 
00335   QSize dialogSize( 580, 400 );
00336   if ( kapp ) {
00337     KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
00338 
00339     KConfigGroup dialogConfig( KGlobal::config(), "Key Selection Dialog" );
00340     dialogSize = dialogConfig.readSizeEntry( "Dialog size", &dialogSize );
00341   }
00342   resize( dialogSize );
00343 
00344   mCheckSelectionTimer = new QTimer( this );
00345   mStartSearchTimer = new QTimer( this );
00346 
00347   QFrame *page = makeMainWidget();
00348   mTopLayout = new QVBoxLayout( page, 0, spacingHint() );
00349 
00350   if ( !text.isEmpty() )
00351     mTopLayout->addWidget( new QLabel( text, page ) );
00352 
00353   QHBoxLayout * hlay = new QHBoxLayout( mTopLayout ); // inherits spacing
00354   QLineEdit * le = new QLineEdit( page );
00355   le->setText( initialQuery );
00356   hlay->addWidget( new QLabel( le, i18n("&Search for:"), page ) );
00357   hlay->addWidget( le, 1 );
00358   le->setFocus();
00359 
00360   connect( le, SIGNAL(textChanged(const QString&)),
00361        this, SLOT(slotSearch(const QString&)) );
00362   connect( mStartSearchTimer, SIGNAL(timeout()), SLOT(slotFilter()) );
00363 
00364   mKeyListView = new KeyListView( new ColumnStrategy( mKeyUsage ), 0, page, "mKeyListView" );
00365   mKeyListView->setResizeMode( QListView::LastColumn );
00366   mKeyListView->setRootIsDecorated( true );
00367   mKeyListView->setShowSortIndicator( true );
00368   mKeyListView->setSorting( 1, true ); // sort by User ID
00369   mKeyListView->setShowToolTips( true );
00370   if ( extendedSelection )
00371     mKeyListView->setSelectionMode( QListView::Extended );
00372   mTopLayout->addWidget( mKeyListView, 10 );
00373 
00374   if ( rememberChoice ) {
00375     mRememberCB = new QCheckBox( i18n("&Remember choice"), page );
00376     mTopLayout->addWidget( mRememberCB );
00377     QWhatsThis::add( mRememberCB,
00378              i18n("<qt><p>If you check this box your choice will "
00379               "be stored and you will not be asked again."
00380               "</p></qt>") );
00381   }
00382 
00383   connect( mCheckSelectionTimer, SIGNAL(timeout()),
00384        SLOT(slotCheckSelection()) );
00385   connectSignals();
00386 
00387   connect( mKeyListView,
00388        SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const QPoint&,int)),
00389        SLOT(slotTryOk()) );
00390   connect( mKeyListView,
00391        SIGNAL(contextMenu(Kleo::KeyListViewItem*,const QPoint&)),
00392            SLOT(slotRMB(Kleo::KeyListViewItem*,const QPoint&)) );
00393 
00394   setButtonText( KDialogBase::Default, i18n("&Reread Keys") );
00395   setButtonText( KDialogBase::Help, i18n("&Start Certificate Manager") );
00396   connect( this, SIGNAL(defaultClicked()), this, SLOT(slotRereadKeys()) );
00397   connect( this, SIGNAL(helpClicked()), this, SLOT(slotStartCertificateManager()) );
00398 
00399   slotRereadKeys();
00400 }
00401 
00402 Kleo::KeySelectionDialog::~KeySelectionDialog() {
00403   KConfigGroup dialogConfig( KGlobal::config(), "Key Selection Dialog" );
00404   dialogConfig.writeEntry( "Dialog size", size() );
00405   dialogConfig.sync();
00406 }
00407 
00408 
00409 void Kleo::KeySelectionDialog::connectSignals() {
00410   if ( mKeyListView->isMultiSelection() )
00411     connect( mKeyListView, SIGNAL(selectionChanged()),
00412              SLOT(slotSelectionChanged()) );
00413   else
00414     connect( mKeyListView, SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
00415              SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
00416 }
00417 
00418 void Kleo::KeySelectionDialog::disconnectSignals() {
00419   if ( mKeyListView->isMultiSelection() )
00420     disconnect( mKeyListView, SIGNAL(selectionChanged()),
00421         this, SLOT(slotSelectionChanged()) );
00422   else
00423     disconnect( mKeyListView, SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
00424         this, SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
00425 }
00426 
00427 const GpgME::Key & Kleo::KeySelectionDialog::selectedKey() const {
00428   if ( mKeyListView->isMultiSelection() || !mKeyListView->selectedItem() )
00429     return GpgME::Key::null;
00430   return mKeyListView->selectedItem()->key();
00431 }
00432 
00433 QString Kleo::KeySelectionDialog::fingerprint() const {
00434   return selectedKey().primaryFingerprint();
00435 }
00436 
00437 QStringList Kleo::KeySelectionDialog::fingerprints() const {
00438   QStringList result;
00439   for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
00440     if ( const char * fpr = it->primaryFingerprint() )
00441       result.push_back( fpr );
00442   return result;
00443 }
00444 
00445 QStringList Kleo::KeySelectionDialog::pgpKeyFingerprints() const {
00446   QStringList result;
00447   for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
00448     if ( it->protocol() == GpgME::Context::OpenPGP )
00449       if ( const char * fpr = it->primaryFingerprint() )
00450         result.push_back( fpr );
00451   return result;
00452 }
00453 
00454 QStringList Kleo::KeySelectionDialog::smimeFingerprints() const {
00455   QStringList result;
00456   for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
00457     if ( it->protocol() == GpgME::Context::CMS )
00458       if ( const char * fpr = it->primaryFingerprint() )
00459         result.push_back( fpr );
00460   return result;
00461 }
00462 
00463 void Kleo::KeySelectionDialog::slotRereadKeys() {
00464   mKeyListView->clear();
00465   mListJobCount = 0;
00466   mTruncated = 0;
00467   mSavedOffsetY = mKeyListView->contentsY();
00468 
00469   disconnectSignals();
00470   mKeyListView->setEnabled( false );
00471 
00472   // FIXME: save current selection
00473   if ( mOpenPGPBackend )
00474     startKeyListJobForBackend( mOpenPGPBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
00475   if ( mSMIMEBackend )
00476     startKeyListJobForBackend( mSMIMEBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
00477 
00478   if ( mListJobCount == 0 ) {
00479     mKeyListView->setEnabled( true );
00480     KMessageBox::information( this,
00481                   i18n("No backends found for listing keys. "
00482                    "Check your installation."),
00483                   i18n("Key Listing Failed") );
00484     connectSignals();
00485   }
00486 }
00487 
00488 void Kleo::KeySelectionDialog::slotHelp()
00489 {
00490     emit helpClicked();
00491 }
00492 
00493 void Kleo::KeySelectionDialog::slotStartCertificateManager()
00494 {
00495   KProcess certManagerProc;
00496   certManagerProc << "kleopatra";
00497 
00498   if( !certManagerProc.start( KProcess::DontCare ) )
00499     KMessageBox::error( this, i18n( "Could not start certificate manager; "
00500                                     "please check your installation." ),
00501                                     i18n( "Certificate Manager Error" ) );
00502   else
00503     kdDebug(5006) << "\nslotStartCertManager(): certificate manager started.\n" << endl;
00504 }
00505 
00506 #ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
00507 #define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
00508 static void showKeyListError( QWidget * parent, const GpgME::Error & err ) {
00509   assert( err );
00510   const QString msg = i18n( "<qt><p>An error occurred while fetching "
00511                 "the keys from the backend:</p>"
00512                 "<p><b>%1</b></p></qt>" )
00513     .arg( QString::fromLocal8Bit( err.asString() ) );
00514 
00515   KMessageBox::error( parent, msg, i18n( "Key Listing Failed" ) );
00516 }
00517 #endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
00518 
00519 namespace {
00520   struct ExtractFingerprint {
00521     QString operator()( const GpgME::Key & key ) {
00522       return key.primaryFingerprint();
00523     }
00524   };
00525 }
00526 
00527 void Kleo::KeySelectionDialog::startKeyListJobForBackend( const CryptoBackend::Protocol * backend, const std::vector<GpgME::Key> & keys, bool validate ) {
00528   assert( backend );
00529   KeyListJob * job = backend->keyListJob( false, false, validate ); // local, w/o sigs, validation as givem
00530   if ( !job )
00531     return;
00532 
00533   connect( job, SIGNAL(result(const GpgME::KeyListResult&)),
00534        SLOT(slotKeyListResult(const GpgME::KeyListResult&)) );
00535   connect( job, SIGNAL(nextKey(const GpgME::Key&)),
00536        mKeyListView, validate ?
00537        SLOT(slotRefreshKey(const GpgME::Key&)) :
00538        SLOT(slotAddKey(const GpgME::Key&)) );
00539 
00540   QStringList fprs;
00541   std::transform( keys.begin(), keys.end(), std::back_inserter( fprs ), ExtractFingerprint() );
00542   const GpgME::Error err = job->start( fprs, mKeyUsage & SecretKeys && !( mKeyUsage & PublicKeys ) );
00543 
00544   if ( err )
00545     return showKeyListError( this, err );
00546 
00547   // FIXME: create a MultiProgressDialog:
00548   (void)new ProgressDialog( job, validate ? i18n( "Checking selected keys..." ) : i18n( "Fetching keys..." ), this );
00549   ++mListJobCount;
00550 }
00551 
00552 static void selectKeys( Kleo::KeyListView * klv, const std::vector<GpgME::Key> & selectedKeys ) {
00553   klv->clearSelection();
00554   if ( selectedKeys.empty() )
00555     return;
00556   for ( std::vector<GpgME::Key>::const_iterator it = selectedKeys.begin() ; it != selectedKeys.end() ; ++it )
00557     if ( Kleo::KeyListViewItem * item = klv->itemByFingerprint( it->primaryFingerprint() ) )
00558       item->setSelected( true );
00559 }
00560 
00561 void Kleo::KeySelectionDialog::slotKeyListResult( const GpgME::KeyListResult & res ) {
00562   if ( res.error() )
00563     showKeyListError( this, res.error() );
00564   else if ( res.isTruncated() )
00565     ++mTruncated;
00566 
00567   if ( --mListJobCount > 0 )
00568     return; // not yet finished...
00569 
00570   if ( mTruncated > 0 )
00571     KMessageBox::information( this,
00572                   i18n("<qt>One backend returned truncated output.<br>"
00573                    "Not all available keys are shown</qt>",
00574                        "<qt>%n backends returned truncated output.<br>"
00575                    "Not all available keys are shown</qt>",
00576                    mTruncated),
00577                   i18n("Key List Result") );
00578 
00579   mKeyListView->flushKeys();
00580 
00581   mKeyListView->setEnabled( true );
00582   mListJobCount = mTruncated = 0;
00583   mKeysToCheck.clear();
00584 
00585   selectKeys( mKeyListView, mSelectedKeys );
00586 
00587   slotFilter();
00588 
00589   connectSignals();
00590 
00591   slotSelectionChanged();
00592 
00593   // restore the saved position of the contents
00594   mKeyListView->setContentsPos( 0, mSavedOffsetY ); mSavedOffsetY = 0;
00595 }
00596 
00597 void Kleo::KeySelectionDialog::slotSelectionChanged() {
00598   kdDebug(5150) << "KeySelectionDialog::slotSelectionChanged()" << endl;
00599 
00600   // (re)start the check selection timer. Checking the selection is delayed
00601   // because else drag-selection doesn't work very good (checking key trust
00602   // is slow).
00603   mCheckSelectionTimer->start( sCheckSelectionDelay );
00604 }
00605 
00606 namespace {
00607   struct AlreadyChecked {
00608     bool operator()( const GpgME::Key & key ) const {
00609       return key.keyListMode() & GpgME::Context::Validate ;
00610     }
00611   };
00612 }
00613 
00614 void Kleo::KeySelectionDialog::slotCheckSelection( KeyListViewItem * item ) {
00615   kdDebug(5150) << "KeySelectionDialog::slotCheckSelection()\n";
00616 
00617   mCheckSelectionTimer->stop();
00618 
00619   mSelectedKeys.clear();
00620 
00621   if ( !mKeyListView->isMultiSelection() ) {
00622     if ( item )
00623       mSelectedKeys.push_back( item->key() );
00624   }
00625 
00626   for ( KeyListViewItem * it = mKeyListView->firstChild() ; it ; it = it->nextSibling() )
00627     if ( it->isSelected() )
00628       mSelectedKeys.push_back( it->key() );
00629 
00630   mKeysToCheck.clear();
00631   std::remove_copy_if( mSelectedKeys.begin(), mSelectedKeys.end(),
00632                std::back_inserter( mKeysToCheck ),
00633                AlreadyChecked() );
00634   if ( mKeysToCheck.empty() ) {
00635     enableButtonOK( !mSelectedKeys.empty() &&
00636             checkKeyUsage( mSelectedKeys, mKeyUsage ) );
00637     return;
00638   }
00639 
00640   // performed all fast checks - now for validating key listing:
00641   startValidatingKeyListing();
00642 }
00643 
00644 void Kleo::KeySelectionDialog::startValidatingKeyListing() {
00645   if ( mKeysToCheck.empty() )
00646     return;
00647 
00648   mListJobCount = 0;
00649   mTruncated = 0;
00650   mSavedOffsetY = mKeyListView->contentsY();
00651 
00652   disconnectSignals();
00653   mKeyListView->setEnabled( false );
00654 
00655   std::vector<GpgME::Key> smime, openpgp;
00656   for ( std::vector<GpgME::Key>::const_iterator it = mKeysToCheck.begin() ; it != mKeysToCheck.end() ; ++it )
00657     if ( it->protocol() == GpgME::Context::OpenPGP )
00658       openpgp.push_back( *it );
00659     else
00660       smime.push_back( *it );
00661 
00662   if ( !openpgp.empty() ) {
00663     assert( mOpenPGPBackend );
00664     startKeyListJobForBackend( mOpenPGPBackend, openpgp, true /*validate*/ );
00665   }
00666   if ( !smime.empty() ) {
00667     assert( mSMIMEBackend );
00668     startKeyListJobForBackend( mSMIMEBackend, smime, true /*validate*/ );
00669   }
00670 
00671   assert( mListJobCount > 0 );
00672 }
00673 
00674 bool Kleo::KeySelectionDialog::rememberSelection() const {
00675   return mRememberCB && mRememberCB->isChecked() ;
00676 }
00677 
00678 void Kleo::KeySelectionDialog::slotRMB( Kleo::KeyListViewItem * item, const QPoint & p ) {
00679   if ( !item ) return;
00680 
00681   mCurrentContextMenuItem = item;
00682 
00683   QPopupMenu menu;
00684   menu.insertItem( i18n( "Recheck Key" ), this, SLOT(slotRecheckKey()) );
00685   menu.exec( p );
00686 }
00687 
00688 void Kleo::KeySelectionDialog::slotRecheckKey() {
00689   if ( !mCurrentContextMenuItem || mCurrentContextMenuItem->key().isNull() )
00690     return;
00691 
00692   mKeysToCheck.clear();
00693   mKeysToCheck.push_back( mCurrentContextMenuItem->key() );
00694 }
00695 
00696 void Kleo::KeySelectionDialog::slotTryOk() {
00697   if ( actionButton( Ok )->isEnabled() )
00698     slotOk();
00699 }
00700 
00701 void Kleo::KeySelectionDialog::slotOk() {
00702   if ( mCheckSelectionTimer->isActive() )
00703     slotCheckSelection();
00704   mStartSearchTimer->stop();
00705   accept();
00706 }
00707 
00708 
00709 void Kleo::KeySelectionDialog::slotCancel() {
00710   mCheckSelectionTimer->stop();
00711   mStartSearchTimer->stop();
00712   reject();
00713 }
00714 
00715 void Kleo::KeySelectionDialog::slotSearch( const QString & text ) {
00716   mSearchText = text.stripWhiteSpace().upper();
00717   slotSearch();
00718 }
00719 
00720 void Kleo::KeySelectionDialog::slotSearch() {
00721   mStartSearchTimer->start( sCheckSelectionDelay, true /*single-shot*/ );
00722 }
00723 
00724 void Kleo::KeySelectionDialog::slotFilter() {
00725   if ( mSearchText.isEmpty() ) {
00726     showAllItems();
00727     return;
00728   }
00729 
00730   // OK, so we need to filter:
00731   QRegExp keyIdRegExp( "(?:0x)?[A-F0-9]{1,8}", false /*case-insens.*/ );
00732   if ( keyIdRegExp.exactMatch( mSearchText ) ) {
00733     if ( mSearchText.startsWith( "0X" ) )
00734       // search for keyID only:
00735       filterByKeyID( mSearchText.mid( 2 ) );
00736     else
00737       // search for UID and keyID:
00738       filterByKeyIDOrUID( mSearchText );
00739   } else {
00740     // search in UID:
00741     filterByUID( mSearchText );
00742   }
00743 }
00744 
00745 void Kleo::KeySelectionDialog::filterByKeyID( const QString & keyID ) {
00746   assert( keyID.length() <= 8 );
00747   assert( !keyID.isEmpty() ); // regexp in slotFilter should prevent these
00748   if ( keyID.isEmpty() )
00749     showAllItems();
00750   else
00751     for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00752       item->setVisible( item->text( 0 ).upper().startsWith( keyID ) );
00753 }
00754 
00755 static bool anyUIDMatches( const Kleo::KeyListViewItem * item, QRegExp & rx ) {
00756   if ( !item )
00757     return false;
00758 
00759   const std::vector<GpgME::UserID> uids = item->key().userIDs();
00760   for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
00761     if ( it->id() && rx.search( QString::fromUtf8( it->id() ) ) >= 0 )
00762       return true;
00763   return false;
00764 }
00765 
00766 void Kleo::KeySelectionDialog::filterByKeyIDOrUID( const QString & str ) {
00767   assert( !str.isEmpty() );
00768 
00769   // match beginnings of words:
00770   QRegExp rx( "\\b" + QRegExp::escape( str ), false );
00771 
00772   for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00773     item->setVisible( item->text( 0 ).upper().startsWith( str ) || anyUIDMatches( item, rx ) );
00774 
00775 }
00776 
00777 void Kleo::KeySelectionDialog::filterByUID( const QString & str ) {
00778   assert( !str.isEmpty() );
00779 
00780   // match beginnings of words:
00781   QRegExp rx( "\\b" + QRegExp::escape( str ), false );
00782 
00783   for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00784     item->setVisible( anyUIDMatches( item, rx ) );
00785 }
00786 
00787 
00788 void Kleo::KeySelectionDialog::showAllItems() {
00789   for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00790     item->setVisible( true );
00791 }
00792 
00793 #include "keyselectiondialog.moc"
KDE Logo
This file is part of the documentation for certmanager Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 4 14:40:06 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003