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