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