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