certmanager/lib

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., 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 "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 
00064 // Qt
00065 #include <qcheckbox.h>
00066 #include <qtoolbutton.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       kdDebug() << "key is invalid - ignoring" << endl;
00088     if ( key.isExpired() ) {
00089       kdDebug() << "key is expired" << endl;
00090       return false;
00091     } else if ( key.isRevoked() ) {
00092       kdDebug() << "key is revoked" << endl;
00093       return false;
00094     } else if ( key.isDisabled() ) {
00095       kdDebug() << "key is disabled" << endl;
00096       return false;
00097     }
00098   }
00099 
00100   if ( keyUsage & Kleo::KeySelectionDialog::EncryptionKeys &&
00101        !key.canEncrypt() ) {
00102     kdDebug() << "key can't encrypt" << endl;
00103     return false;
00104   }
00105   if ( keyUsage & Kleo::KeySelectionDialog::SigningKeys &&
00106        !key.canSign() ) {
00107     kdDebug() << "key can't sign" << endl;
00108     return false;
00109   }
00110   if ( keyUsage & Kleo::KeySelectionDialog::CertificationKeys &&
00111        !key.canCertify() ) {
00112     kdDebug() << "key can't certify" << endl;
00113     return false;
00114   }
00115   if ( keyUsage & Kleo::KeySelectionDialog::AuthenticationKeys &&
00116        !key.canAuthenticate() ) {
00117     kdDebug() << "key can't authenticate" << endl;
00118     return false;
00119   }
00120 
00121   if ( keyUsage & Kleo::KeySelectionDialog::SecretKeys &&
00122        !( keyUsage & Kleo::KeySelectionDialog::PublicKeys ) &&
00123        !key.isSecret() ) {
00124     kdDebug() << "key isn't secret" << endl;
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     kdDebug() << "key has no UIDs with validity >= Marginal" << endl;
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, 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, 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   QVBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
00349 
00350   if ( !text.isEmpty() )
00351     topLayout->addWidget( new QLabel( text, page ) );
00352 
00353   QHBoxLayout * hlay = new QHBoxLayout( topLayout ); // inherits spacing
00354   QLineEdit * le = new QLineEdit( page );
00355   le->setText( initialQuery );
00356   QToolButton *clearButton = new QToolButton( page );
00357   clearButton->setIconSet( KGlobal::iconLoader()->loadIconSet(
00358               KApplication::reverseLayout() ? "clear_left":"locationbar_erase", KIcon::Small, 0 ) );
00359   hlay->addWidget( clearButton );
00360   hlay->addWidget( new QLabel( le, i18n("&Search for:"), page ) );
00361   hlay->addWidget( le, 1 );
00362   le->setFocus();
00363 
00364   connect( clearButton, SIGNAL( clicked() ), le, SLOT( clear() ) );
00365   connect( le, SIGNAL(textChanged(const QString&)),
00366        this, SLOT(slotSearch(const QString&)) );
00367   connect( mStartSearchTimer, SIGNAL(timeout()), SLOT(slotFilter()) );
00368 
00369   mKeyListView = new KeyListView( new ColumnStrategy( mKeyUsage ), 0, page, "mKeyListView" );
00370   mKeyListView->setResizeMode( QListView::LastColumn );
00371   mKeyListView->setRootIsDecorated( true );
00372   mKeyListView->setShowSortIndicator( true );
00373   mKeyListView->setSorting( 1, true ); // sort by User ID
00374   mKeyListView->setShowToolTips( true );
00375   if ( extendedSelection )
00376     mKeyListView->setSelectionMode( QListView::Extended );
00377   topLayout->addWidget( mKeyListView, 10 );
00378 
00379   if ( rememberChoice ) {
00380     mRememberCB = new QCheckBox( i18n("&Remember choice"), page );
00381     topLayout->addWidget( mRememberCB );
00382     QWhatsThis::add( mRememberCB,
00383              i18n("<qt><p>If you check this box your choice will "
00384               "be stored and you will not be asked again."
00385               "</p></qt>") );
00386   }
00387 
00388   connect( mCheckSelectionTimer, SIGNAL(timeout()),
00389        SLOT(slotCheckSelection()) );
00390   connectSignals();
00391 
00392   connect( mKeyListView,
00393        SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const QPoint&,int)),
00394        SLOT(slotTryOk()) );
00395   connect( mKeyListView,
00396        SIGNAL(contextMenu(Kleo::KeyListViewItem*,const QPoint&)),
00397            SLOT(slotRMB(Kleo::KeyListViewItem*,const QPoint&)) );
00398 
00399   setButtonText( KDialogBase::Default, i18n("&Reread Keys") );
00400   connect( this, SIGNAL(defaultClicked()),
00401            this, SLOT(slotRereadKeys()) );
00402 
00403   slotRereadKeys();
00404 }
00405 
00406 Kleo::KeySelectionDialog::~KeySelectionDialog() {
00407   KConfigGroup dialogConfig( KGlobal::config(), "Key Selection Dialog" );
00408   dialogConfig.writeEntry( "Dialog size", size() );
00409   dialogConfig.sync();
00410 }
00411 
00412 
00413 void Kleo::KeySelectionDialog::connectSignals() {
00414   if ( mKeyListView->isMultiSelection() )
00415     connect( mKeyListView, SIGNAL(selectionChanged()),
00416              SLOT(slotSelectionChanged()) );
00417   else
00418     connect( mKeyListView, SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
00419              SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
00420 }
00421 
00422 void Kleo::KeySelectionDialog::disconnectSignals() {
00423   if ( mKeyListView->isMultiSelection() )
00424     disconnect( mKeyListView, SIGNAL(selectionChanged()),
00425         this, SLOT(slotSelectionChanged()) );
00426   else
00427     disconnect( mKeyListView, SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
00428         this, SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
00429 }
00430 
00431 const GpgME::Key & Kleo::KeySelectionDialog::selectedKey() const {
00432   if ( mKeyListView->isMultiSelection() || !mKeyListView->selectedItem() )
00433     return GpgME::Key::null;
00434   return mKeyListView->selectedItem()->key();
00435 }
00436 
00437 QString Kleo::KeySelectionDialog::fingerprint() const {
00438   return selectedKey().primaryFingerprint();
00439 }
00440 
00441 QStringList Kleo::KeySelectionDialog::fingerprints() const {
00442   QStringList result;
00443   for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
00444     if ( const char * fpr = it->primaryFingerprint() )
00445       result.push_back( fpr );
00446   return result;
00447 }
00448 
00449 QStringList Kleo::KeySelectionDialog::pgpKeyFingerprints() const {
00450   QStringList result;
00451   for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
00452     if ( it->protocol() == GpgME::Context::OpenPGP )
00453       if ( const char * fpr = it->primaryFingerprint() )
00454         result.push_back( fpr );
00455   return result;
00456 }
00457 
00458 QStringList Kleo::KeySelectionDialog::smimeFingerprints() const {
00459   QStringList result;
00460   for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
00461     if ( it->protocol() == GpgME::Context::CMS )
00462       if ( const char * fpr = it->primaryFingerprint() )
00463         result.push_back( fpr );
00464   return result;
00465 }
00466 
00467 void Kleo::KeySelectionDialog::slotRereadKeys() {
00468   mKeyListView->clear();
00469   mListJobCount = 0;
00470   mTruncated = 0;
00471   mSavedOffsetY = mKeyListView->contentsY();
00472 
00473   disconnectSignals();
00474   mKeyListView->setEnabled( false );
00475 
00476   // FIXME: save current selection
00477   if ( mOpenPGPBackend )
00478     startKeyListJobForBackend( mOpenPGPBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
00479   if ( mSMIMEBackend )
00480     startKeyListJobForBackend( mSMIMEBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
00481 
00482   if ( mListJobCount == 0 ) {
00483     mKeyListView->setEnabled( true );
00484     KMessageBox::information( this,
00485                   i18n("No backends found for listing keys. "
00486                    "Check your installation."),
00487                   i18n("Key Listing Failed") );
00488     connectSignals();
00489   }
00490 }
00491 
00492 #ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
00493 #define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
00494 static void showKeyListError( QWidget * parent, const GpgME::Error & err ) {
00495   assert( err );
00496   const QString msg = i18n( "<qt><p>An error occurred while fetching "
00497                 "the keys from the backend:</p>"
00498                 "<p><b>%1</b></p></qt>" )
00499     .arg( QString::fromLocal8Bit( err.asString() ) );
00500 
00501   KMessageBox::error( parent, msg, i18n( "Key Listing Failed" ) );
00502 }
00503 #endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
00504 
00505 namespace {
00506   struct ExtractFingerprint {
00507     QString operator()( const GpgME::Key & key ) {
00508       return key.primaryFingerprint();
00509     }
00510   };
00511 }
00512 
00513 void Kleo::KeySelectionDialog::startKeyListJobForBackend( const CryptoBackend::Protocol * backend, const std::vector<GpgME::Key> & keys, bool validate ) {
00514   assert( backend );
00515   KeyListJob * job = backend->keyListJob( false, false, validate ); // local, w/o sigs, validation as givem
00516   if ( !job )
00517     return;
00518 
00519   connect( job, SIGNAL(result(const GpgME::KeyListResult&)),
00520        SLOT(slotKeyListResult(const GpgME::KeyListResult&)) );
00521   connect( job, SIGNAL(nextKey(const GpgME::Key&)),
00522        mKeyListView, validate ?
00523        SLOT(slotRefreshKey(const GpgME::Key&)) :
00524        SLOT(slotAddKey(const GpgME::Key&)) );
00525 
00526   QStringList fprs;
00527   std::transform( keys.begin(), keys.end(), std::back_inserter( fprs ), ExtractFingerprint() );
00528   const GpgME::Error err = job->start( fprs, mKeyUsage & SecretKeys && !( mKeyUsage & PublicKeys ) );
00529 
00530   if ( err )
00531     return showKeyListError( this, err );
00532 
00533   // FIXME: create a MultiProgressDialog:
00534   (void)new ProgressDialog( job, validate ? i18n( "Checking selected keys..." ) : i18n( "Fetching keys..." ), this );
00535   ++mListJobCount;
00536 }
00537 
00538 static void selectKeys( Kleo::KeyListView * klv, const std::vector<GpgME::Key> & selectedKeys ) {
00539   klv->clearSelection();
00540   if ( selectedKeys.empty() )
00541     return;
00542   for ( std::vector<GpgME::Key>::const_iterator it = selectedKeys.begin() ; it != selectedKeys.end() ; ++it )
00543     if ( Kleo::KeyListViewItem * item = klv->itemByFingerprint( it->primaryFingerprint() ) )
00544       item->setSelected( true );
00545 }
00546 
00547 void Kleo::KeySelectionDialog::slotKeyListResult( const GpgME::KeyListResult & res ) {
00548   if ( res.error() )
00549     showKeyListError( this, res.error() );
00550   else if ( res.isTruncated() )
00551     ++mTruncated;
00552 
00553   if ( --mListJobCount > 0 )
00554     return; // not yet finished...
00555 
00556   if ( mTruncated > 0 )
00557     KMessageBox::information( this,
00558                   i18n("<qt>One backend returned truncated output.<br>"
00559                    "Not all available keys are shown</qt>",
00560                        "<qt>%n backends returned truncated output.<br>"
00561                    "Not all available keys are shown</qt>",
00562                    mTruncated),
00563                   i18n("Key List Result") );
00564 
00565   mKeyListView->flushKeys();
00566 
00567   mKeyListView->setEnabled( true );
00568   mListJobCount = mTruncated = 0;
00569   mKeysToCheck.clear();
00570 
00571   selectKeys( mKeyListView, mSelectedKeys );
00572 
00573   slotFilter();
00574 
00575   connectSignals();
00576 
00577   slotSelectionChanged();
00578 
00579   // restore the saved position of the contents
00580   mKeyListView->setContentsPos( 0, mSavedOffsetY ); mSavedOffsetY = 0;
00581 }
00582 
00583 void Kleo::KeySelectionDialog::slotSelectionChanged() {
00584   kdDebug(5150) << "KeySelectionDialog::slotSelectionChanged()" << endl;
00585 
00586   // (re)start the check selection timer. Checking the selection is delayed
00587   // because else drag-selection doesn't work very good (checking key trust
00588   // is slow).
00589   mCheckSelectionTimer->start( sCheckSelectionDelay );
00590 }
00591 
00592 namespace {
00593   struct AlreadyChecked {
00594     bool operator()( const GpgME::Key & key ) const {
00595       return key.keyListMode() & GpgME::Context::Validate ;
00596     }
00597   };
00598 }
00599 
00600 void Kleo::KeySelectionDialog::slotCheckSelection( KeyListViewItem * item ) {
00601   kdDebug(5150) << "KeySelectionDialog::slotCheckSelection()\n";
00602 
00603   mCheckSelectionTimer->stop();
00604 
00605   mSelectedKeys.clear();
00606 
00607   if ( !mKeyListView->isMultiSelection() ) {
00608     if ( item )
00609       mSelectedKeys.push_back( item->key() );
00610   }
00611 
00612   for ( KeyListViewItem * it = mKeyListView->firstChild() ; it ; it = it->nextSibling() )
00613     if ( it->isSelected() )
00614       mSelectedKeys.push_back( it->key() );
00615 
00616   mKeysToCheck.clear();
00617   std::remove_copy_if( mSelectedKeys.begin(), mSelectedKeys.end(),
00618                std::back_inserter( mKeysToCheck ),
00619                AlreadyChecked() );
00620   if ( mKeysToCheck.empty() ) {
00621     enableButtonOK( !mSelectedKeys.empty() &&
00622             checkKeyUsage( mSelectedKeys, mKeyUsage ) );
00623     return;
00624   }
00625 
00626   // performed all fast checks - now for validating key listing:
00627   startValidatingKeyListing();
00628 }
00629 
00630 void Kleo::KeySelectionDialog::startValidatingKeyListing() {
00631   if ( mKeysToCheck.empty() )
00632     return;
00633 
00634   mListJobCount = 0;
00635   mTruncated = 0;
00636   mSavedOffsetY = mKeyListView->contentsY();
00637 
00638   disconnectSignals();
00639   mKeyListView->setEnabled( false );
00640 
00641   std::vector<GpgME::Key> smime, openpgp;
00642   for ( std::vector<GpgME::Key>::const_iterator it = mKeysToCheck.begin() ; it != mKeysToCheck.end() ; ++it )
00643     if ( it->protocol() == GpgME::Context::OpenPGP )
00644       openpgp.push_back( *it );
00645     else
00646       smime.push_back( *it );
00647 
00648   if ( !openpgp.empty() ) {
00649     assert( mOpenPGPBackend );
00650     startKeyListJobForBackend( mOpenPGPBackend, openpgp, true /*validate*/ );
00651   }
00652   if ( !smime.empty() ) {
00653     assert( mSMIMEBackend );
00654     startKeyListJobForBackend( mSMIMEBackend, smime, true /*validate*/ );
00655   }
00656 
00657   assert( mListJobCount > 0 );
00658 }
00659 
00660 bool Kleo::KeySelectionDialog::rememberSelection() const {
00661   return mRememberCB && mRememberCB->isChecked() ;
00662 }
00663 
00664 void Kleo::KeySelectionDialog::slotRMB( Kleo::KeyListViewItem * item, const QPoint & p ) {
00665   if ( !item ) return;
00666 
00667   mCurrentContextMenuItem = item;
00668 
00669   QPopupMenu menu;
00670   menu.insertItem( i18n( "Recheck Key" ), this, SLOT(slotRecheckKey()) );
00671   menu.exec( p );
00672 }
00673 
00674 void Kleo::KeySelectionDialog::slotRecheckKey() {
00675   if ( !mCurrentContextMenuItem || mCurrentContextMenuItem->key().isNull() )
00676     return;
00677 
00678   mKeysToCheck.clear();
00679   mKeysToCheck.push_back( mCurrentContextMenuItem->key() );
00680 }
00681 
00682 void Kleo::KeySelectionDialog::slotTryOk() {
00683   if ( actionButton( Ok )->isEnabled() )
00684     slotOk();
00685 }
00686 
00687 void Kleo::KeySelectionDialog::slotOk() {
00688   if ( mCheckSelectionTimer->isActive() )
00689     slotCheckSelection();
00690   // button could be disabled again after checking the selected key
00691   if ( !actionButton( Ok )->isEnabled() )
00692     return;
00693   mStartSearchTimer->stop();
00694   accept();
00695 }
00696 
00697 
00698 void Kleo::KeySelectionDialog::slotCancel() {
00699   mCheckSelectionTimer->stop();
00700   mStartSearchTimer->stop();
00701   reject();
00702 }
00703 
00704 void Kleo::KeySelectionDialog::slotSearch( const QString & text ) {
00705   mSearchText = text.stripWhiteSpace().upper();
00706   slotSearch();
00707 }
00708 
00709 void Kleo::KeySelectionDialog::slotSearch() {
00710   mStartSearchTimer->start( sCheckSelectionDelay, true /*single-shot*/ );
00711 }
00712 
00713 void Kleo::KeySelectionDialog::slotFilter() {
00714   if ( mSearchText.isEmpty() ) {
00715     showAllItems();
00716     return;
00717   }
00718 
00719   // OK, so we need to filter:
00720   QRegExp keyIdRegExp( "(?:0x)?[A-F0-9]{1,8}", false /*case-insens.*/ );
00721   if ( keyIdRegExp.exactMatch( mSearchText ) ) {
00722     if ( mSearchText.startsWith( "0X" ) )
00723       // search for keyID only:
00724       filterByKeyID( mSearchText.mid( 2 ) );
00725     else
00726       // search for UID and keyID:
00727       filterByKeyIDOrUID( mSearchText );
00728   } else {
00729     // search in UID:
00730     filterByUID( mSearchText );
00731   }
00732 }
00733 
00734 void Kleo::KeySelectionDialog::filterByKeyID( const QString & keyID ) {
00735   assert( keyID.length() <= 8 );
00736   assert( !keyID.isEmpty() ); // regexp in slotFilter should prevent these
00737   if ( keyID.isEmpty() )
00738     showAllItems();
00739   else
00740     for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00741       item->setVisible( item->text( 0 ).upper().startsWith( keyID ) );
00742 }
00743 
00744 static bool anyUIDMatches( const Kleo::KeyListViewItem * item, QRegExp & rx ) {
00745   if ( !item )
00746     return false;
00747 
00748   const std::vector<GpgME::UserID> uids = item->key().userIDs();
00749   for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
00750     if ( it->id() && rx.search( QString::fromUtf8( it->id() ) ) >= 0 )
00751       return true;
00752   return false;
00753 }
00754 
00755 void Kleo::KeySelectionDialog::filterByKeyIDOrUID( const QString & str ) {
00756   assert( !str.isEmpty() );
00757 
00758   // match beginnings of words:
00759   QRegExp rx( "\\b" + QRegExp::escape( str ), false );
00760 
00761   for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00762     item->setVisible( item->text( 0 ).upper().startsWith( str ) || anyUIDMatches( item, rx ) );
00763 
00764 }
00765 
00766 void Kleo::KeySelectionDialog::filterByUID( 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( anyUIDMatches( item, rx ) );
00774 }
00775 
00776 
00777 void Kleo::KeySelectionDialog::showAllItems() {
00778   for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00779     item->setVisible( true );
00780 }
00781 
00782 #include "keyselectiondialog.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys