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