certmanager Library API Documentation

keyselectiondialog.cpp

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