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 <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     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 TQString time_t2string( time_t t ) {
00162   TQDateTime 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     TQString title( int col ) const;
00174     int width( int col, const TQFontMetrics & fm ) const;
00175 
00176     TQString text( const GpgME::Key & key, int col ) const;
00177     TQString toolTip( const GpgME::Key & key, int col ) const;
00178     const TQPixmap * pixmap( const GpgME::Key & key, int col ) const;
00179 
00180   private:
00181     const TQPixmap 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   TQString 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 TQString::null;
00202     }
00203   }
00204 
00205   int ColumnStrategy::width( int col, const TQFontMetrics & 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( TQChar( hexchars[i] ) ), maxWidth );
00211       return 8 * maxWidth + 2 * mKeyGoodPix.width();
00212     }
00213     return Kleo::KeyListView::ColumnStrategy::width( col, fm );
00214   }
00215 
00216   TQString ColumnStrategy::text( const GpgME::Key & key, int col ) const {
00217     switch ( col ) {
00218     case 0:
00219       {
00220     if ( key.shortKeyID() )
00221       return TQString::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 ? TQString::fromUtf8( uid ) : TQString::null ;
00231     else // CMS
00232       return Kleo::DN( uid ).prettyDN();
00233       }
00234       break;
00235     default: return TQString::null;
00236     }
00237   }
00238 
00239   TQString 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 TQString expiry = subkey.neverExpires() ? i18n("never") : time_t2string( subkey.expirationTime() ) ;
00245     const TQString 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 ? TQString::fromUtf8( uid ) : i18n("unknown"),
00252           creation, expiry,
00253           fpr ? TQString::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 ? TQString::fromLatin1( fpr ) : i18n("unknown") )
00263     .arg( issuer ? Kleo::DN( issuer ).prettyDN() : i18n("unknown") );
00264   }
00265 
00266   const TQPixmap * 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 TQString & title,
00299                           const TQString & text,
00300                           const std::vector<GpgME::Key> & selectedKeys,
00301                           unsigned int keyUsage,
00302                           bool extendedSelection,
00303                           bool rememberChoice,
00304                           TQWidget * 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, TQString::null );
00315 }
00316 
00317 Kleo::KeySelectionDialog::KeySelectionDialog( const TQString & title,
00318                           const TQString & text,
00319                                               const TQString & initialQuery,
00320                           const std::vector<GpgME::Key> & selectedKeys,
00321                           unsigned int keyUsage,
00322                           bool extendedSelection,
00323                           bool rememberChoice,
00324                           TQWidget * parent, const char * name,
00325                           bool modal )
00326   : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ),
00327     mOpenPGPBackend( 0 ),
00328     mSMIMEBackend( 0 ),
00329     mRememberCB( 0 ),
00330     mSelectedKeys( selectedKeys ),
00331     mKeyUsage( keyUsage ),
00332     mSearchText( initialQuery ),
00333     mInitialQuery( initialQuery ),
00334     mCurrentContextMenuItem( 0 )
00335 {
00336   init( rememberChoice, extendedSelection, text, initialQuery );
00337 }
00338 
00339 Kleo::KeySelectionDialog::KeySelectionDialog( const TQString & title,
00340                           const TQString & text,
00341                           const TQString & initialQuery,
00342                           unsigned int keyUsage,
00343                           bool extendedSelection,
00344                           bool rememberChoice,
00345                           TQWidget * parent, const char * name,
00346                           bool modal )
00347   : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ),
00348     mOpenPGPBackend( 0 ),
00349     mSMIMEBackend( 0 ),
00350     mRememberCB( 0 ),
00351     mKeyUsage( keyUsage ),
00352     mSearchText( initialQuery ),
00353     mInitialQuery( initialQuery ),
00354     mCurrentContextMenuItem( 0 )
00355 {
00356   init( rememberChoice, extendedSelection, text, initialQuery );
00357 }
00358 
00359 void Kleo::KeySelectionDialog::init( bool rememberChoice, bool extendedSelection,
00360                      const TQString & text, const TQString & initialQuery ) {
00361   if ( mKeyUsage & OpenPGPKeys )
00362     mOpenPGPBackend = Kleo::CryptoBackendFactory::instance()->openpgp();
00363   if ( mKeyUsage & SMIMEKeys )
00364     mSMIMEBackend = Kleo::CryptoBackendFactory::instance()->smime();
00365 
00366   mCheckSelectionTimer = new TQTimer( this );
00367   mStartSearchTimer = new TQTimer( this );
00368 
00369   TQFrame *page = makeMainWidget();
00370   mTopLayout = new TQVBoxLayout( page, 0, spacingHint() );
00371 
00372   if ( !text.isEmpty() ) {
00373     if ( text.startsWith( "<qt>" ) ) {
00374       KActiveLabel *textLabel = new KActiveLabel( text, page );
00375       disconnect( textLabel, TQT_SIGNAL(linkClicked(const TQString&)), textLabel, TQT_SLOT(openLink(const TQString&)) );
00376       connect( textLabel, TQT_SIGNAL(linkClicked(const TQString&)), TQT_SLOT(slotStartCertificateManager(const TQString&)) );
00377       textLabel->setAlignment( textLabel->alignment() | TQt::WordBreak );
00378       mTopLayout->addWidget( textLabel );
00379     } else {
00380       KActiveLabel *textLabel = new KActiveLabel( text, page );
00381       textLabel->setAlignment( textLabel->alignment() | TQt::WordBreak );
00382       mTopLayout->addWidget( textLabel );
00383     }
00384   }
00385 
00386   TQPushButton * const searchExternalPB
00387       = new TQPushButton( i18n("Search for &External Certificates"), page );
00388   mTopLayout->addWidget( searchExternalPB, 0, TQt::AlignLeft );
00389   connect( searchExternalPB, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotStartSearchForExternalCertificates()) );
00390   if ( initialQuery.isEmpty() )
00391       searchExternalPB->hide();
00392 
00393   TQHBoxLayout * hlay = new TQHBoxLayout( mTopLayout ); // inherits spacing
00394   TQLineEdit * le = new TQLineEdit( page );
00395   le->setText( initialQuery );
00396   TQToolButton *clearButton = new TQToolButton( page );
00397   clearButton->setIconSet( KGlobal::iconLoader()->loadIconSet(
00398               KApplication::reverseLayout() ? "clear_left":"locationbar_erase", KIcon::Small, 0 ) );
00399   hlay->addWidget( clearButton );
00400   hlay->addWidget( new TQLabel( le, i18n("&Search for:"), page ) );
00401   hlay->addWidget( le, 1 );
00402   le->setFocus();
00403 
00404   connect( clearButton, TQT_SIGNAL( clicked() ), le, TQT_SLOT( clear() ) );
00405   connect( le, TQT_SIGNAL(textChanged(const TQString&)),
00406        this, TQT_SLOT(slotSearch(const TQString&)) );
00407   connect( mStartSearchTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotFilter()) );
00408 
00409   mKeyListView = new KeyListView( new ColumnStrategy( mKeyUsage ), 0, page, "mKeyListView" );
00410   mKeyListView->setResizeMode( TQListView::LastColumn );
00411   mKeyListView->setRootIsDecorated( true );
00412   mKeyListView->setShowSortIndicator( true );
00413   mKeyListView->setSorting( 1, true ); // sort by User ID
00414   mKeyListView->setShowToolTips( true );
00415   if ( extendedSelection )
00416     mKeyListView->setSelectionMode( TQListView::Extended );
00417   mTopLayout->addWidget( mKeyListView, 10 );
00418 
00419   if ( rememberChoice ) {
00420     mRememberCB = new TQCheckBox( i18n("&Remember choice"), page );
00421     mTopLayout->addWidget( mRememberCB );
00422     TQWhatsThis::add( mRememberCB,
00423              i18n("<qt><p>If you check this box your choice will "
00424               "be stored and you will not be asked again."
00425               "</p></qt>") );
00426   }
00427 
00428   connect( mCheckSelectionTimer, TQT_SIGNAL(timeout()),
00429        TQT_SLOT(slotCheckSelection()) );
00430   connectSignals();
00431 
00432   connect( mKeyListView,
00433        TQT_SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const TQPoint&,int)),
00434        TQT_SLOT(slotTryOk()) );
00435   connect( mKeyListView,
00436        TQT_SIGNAL(contextMenu(Kleo::KeyListViewItem*,const TQPoint&)),
00437            TQT_SLOT(slotRMB(Kleo::KeyListViewItem*,const TQPoint&)) );
00438 
00439   setButtonText( KDialogBase::Default, i18n("&Reread Keys") );
00440   setButtonGuiItem( KDialogBase::Help, i18n("&Start Certificate Manager") );
00441   connect( this, TQT_SIGNAL(defaultClicked()), this, TQT_SLOT(slotRereadKeys()) );
00442   connect( this, TQT_SIGNAL(helpClicked()), this, TQT_SLOT(slotStartCertificateManager()) );
00443 
00444   slotRereadKeys();
00445   mTopLayout->activate();
00446 
00447   if ( kapp ) {
00448     KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
00449     TQSize dialogSize( 500, 400 );
00450 
00451     KConfigGroup dialogConfig( KGlobal::config(), "Key Selection Dialog" );
00452     dialogSize = dialogConfig.readSizeEntry( "Dialog size", &dialogSize );
00453     resize( dialogSize );
00454   }
00455 }
00456 
00457 Kleo::KeySelectionDialog::~KeySelectionDialog() {
00458   KConfigGroup dialogConfig( KGlobal::config(), "Key Selection Dialog" );
00459   dialogConfig.writeEntry( "Dialog size", size() );
00460   dialogConfig.sync();
00461 }
00462 
00463 
00464 void Kleo::KeySelectionDialog::connectSignals() {
00465   if ( mKeyListView->isMultiSelection() )
00466     connect( mKeyListView, TQT_SIGNAL(selectionChanged()),
00467              TQT_SLOT(slotSelectionChanged()) );
00468   else
00469     connect( mKeyListView, TQT_SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
00470              TQT_SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
00471 }
00472 
00473 void Kleo::KeySelectionDialog::disconnectSignals() {
00474   if ( mKeyListView->isMultiSelection() )
00475     disconnect( mKeyListView, TQT_SIGNAL(selectionChanged()),
00476         this, TQT_SLOT(slotSelectionChanged()) );
00477   else
00478     disconnect( mKeyListView, TQT_SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
00479         this, TQT_SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
00480 }
00481 
00482 const GpgME::Key & Kleo::KeySelectionDialog::selectedKey() const {
00483   if ( mKeyListView->isMultiSelection() || !mKeyListView->selectedItem() )
00484     return GpgME::Key::null;
00485   return mKeyListView->selectedItem()->key();
00486 }
00487 
00488 TQString Kleo::KeySelectionDialog::fingerprint() const {
00489   return selectedKey().primaryFingerprint();
00490 }
00491 
00492 TQStringList Kleo::KeySelectionDialog::fingerprints() const {
00493   TQStringList result;
00494   for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
00495     if ( const char * fpr = it->primaryFingerprint() )
00496       result.push_back( fpr );
00497   return result;
00498 }
00499 
00500 TQStringList Kleo::KeySelectionDialog::pgpKeyFingerprints() const {
00501   TQStringList result;
00502   for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
00503     if ( it->protocol() == GpgME::Context::OpenPGP )
00504       if ( const char * fpr = it->primaryFingerprint() )
00505         result.push_back( fpr );
00506   return result;
00507 }
00508 
00509 TQStringList Kleo::KeySelectionDialog::smimeFingerprints() const {
00510   TQStringList result;
00511   for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
00512     if ( it->protocol() == GpgME::Context::CMS )
00513       if ( const char * fpr = it->primaryFingerprint() )
00514         result.push_back( fpr );
00515   return result;
00516 }
00517 
00518 void Kleo::KeySelectionDialog::slotRereadKeys() {
00519   mKeyListView->clear();
00520   mListJobCount = 0;
00521   mTruncated = 0;
00522   mSavedOffsetY = mKeyListView->contentsY();
00523 
00524   disconnectSignals();
00525   mKeyListView->setEnabled( false );
00526 
00527   // FIXME: save current selection
00528   if ( mOpenPGPBackend )
00529     startKeyListJobForBackend( mOpenPGPBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
00530   if ( mSMIMEBackend )
00531     startKeyListJobForBackend( mSMIMEBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
00532 
00533   if ( mListJobCount == 0 ) {
00534     mKeyListView->setEnabled( true );
00535     KMessageBox::information( this,
00536                   i18n("No backends found for listing keys. "
00537                    "Check your installation."),
00538                   i18n("Key Listing Failed") );
00539     connectSignals();
00540   }
00541 }
00542 
00543 void Kleo::KeySelectionDialog::slotHelp()
00544 {
00545     emit helpClicked();
00546 }
00547 
00548 void Kleo::KeySelectionDialog::slotStartCertificateManager( const TQString &query )
00549 {
00550   KProcess certManagerProc;
00551   certManagerProc << "kleopatra";
00552   if ( !query.isEmpty() )
00553     certManagerProc << "--external" << "--query" << KURL::decode_string( query );
00554 
00555   if( !certManagerProc.start( KProcess::DontCare ) )
00556     KMessageBox::error( this, i18n( "Could not start certificate manager; "
00557                                     "please check your installation." ),
00558                                     i18n( "Certificate Manager Error" ) );
00559   else
00560     kdDebug(5006) << "\nslotStartCertManager(): certificate manager started.\n" << endl;
00561 }
00562 
00563 #ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
00564 #define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
00565 static void showKeyListError( TQWidget * parent, const GpgME::Error & err ) {
00566   assert( err );
00567   const TQString msg = i18n( "<qt><p>An error occurred while fetching "
00568                 "the keys from the backend:</p>"
00569                 "<p><b>%1</b></p></qt>" )
00570     .arg( TQString::fromLocal8Bit( err.asString() ) );
00571 
00572   KMessageBox::error( parent, msg, i18n( "Key Listing Failed" ) );
00573 }
00574 #endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
00575 
00576 namespace {
00577   struct ExtractFingerprint {
00578     TQString operator()( const GpgME::Key & key ) {
00579       return key.primaryFingerprint();
00580     }
00581   };
00582 }
00583 
00584 void Kleo::KeySelectionDialog::startKeyListJobForBackend( const CryptoBackend::Protocol * backend, const std::vector<GpgME::Key> & keys, bool validate ) {
00585   assert( backend );
00586   KeyListJob * job = backend->keyListJob( false, false, validate ); // local, w/o sigs, validation as givem
00587   if ( !job )
00588     return;
00589 
00590   connect( job, TQT_SIGNAL(result(const GpgME::KeyListResult&)),
00591        TQT_SLOT(slotKeyListResult(const GpgME::KeyListResult&)) );
00592   connect( job, TQT_SIGNAL(nextKey(const GpgME::Key&)),
00593        mKeyListView, validate ?
00594        TQT_SLOT(slotRefreshKey(const GpgME::Key&)) :
00595        TQT_SLOT(slotAddKey(const GpgME::Key&)) );
00596 
00597   TQStringList fprs;
00598   std::transform( keys.begin(), keys.end(), std::back_inserter( fprs ), ExtractFingerprint() );
00599   const GpgME::Error err = job->start( fprs, mKeyUsage & SecretKeys && !( mKeyUsage & PublicKeys ) );
00600 
00601   if ( err )
00602     return showKeyListError( this, err );
00603 
00604   // FIXME: create a MultiProgressDialog:
00605   (void)new ProgressDialog( job, validate ? i18n( "Checking selected keys..." ) : i18n( "Fetching keys..." ), this );
00606   ++mListJobCount;
00607 }
00608 
00609 static void selectKeys( Kleo::KeyListView * klv, const std::vector<GpgME::Key> & selectedKeys ) {
00610   klv->clearSelection();
00611   if ( selectedKeys.empty() )
00612     return;
00613   for ( std::vector<GpgME::Key>::const_iterator it = selectedKeys.begin() ; it != selectedKeys.end() ; ++it )
00614     if ( Kleo::KeyListViewItem * item = klv->itemByFingerprint( it->primaryFingerprint() ) )
00615       item->setSelected( true );
00616 }
00617 
00618 void Kleo::KeySelectionDialog::slotKeyListResult( const GpgME::KeyListResult & res ) {
00619   if ( res.error() )
00620     showKeyListError( this, res.error() );
00621   else if ( res.isTruncated() )
00622     ++mTruncated;
00623 
00624   if ( --mListJobCount > 0 )
00625     return; // not yet finished...
00626 
00627   if ( mTruncated > 0 )
00628     KMessageBox::information( this,
00629                   i18n("<qt>One backend returned truncated output.<br>"
00630                    "Not all available keys are shown</qt>",
00631                        "<qt>%n backends returned truncated output.<br>"
00632                    "Not all available keys are shown</qt>",
00633                    mTruncated),
00634                   i18n("Key List Result") );
00635 
00636   mKeyListView->flushKeys();
00637 
00638   mKeyListView->setEnabled( true );
00639   mListJobCount = mTruncated = 0;
00640   mKeysToCheck.clear();
00641 
00642   selectKeys( mKeyListView, mSelectedKeys );
00643 
00644   slotFilter();
00645 
00646   connectSignals();
00647 
00648   slotSelectionChanged();
00649 
00650   // restore the saved position of the contents
00651   mKeyListView->setContentsPos( 0, mSavedOffsetY ); mSavedOffsetY = 0;
00652 }
00653 
00654 void Kleo::KeySelectionDialog::slotSelectionChanged() {
00655   kdDebug(5150) << "KeySelectionDialog::slotSelectionChanged()" << endl;
00656 
00657   // (re)start the check selection timer. Checking the selection is delayed
00658   // because else drag-selection doesn't work very good (checking key trust
00659   // is slow).
00660   mCheckSelectionTimer->start( sCheckSelectionDelay );
00661 }
00662 
00663 namespace {
00664   struct AlreadyChecked {
00665     bool operator()( const GpgME::Key & key ) const {
00666       return key.keyListMode() & GpgME::Context::Validate ;
00667     }
00668   };
00669 }
00670 
00671 void Kleo::KeySelectionDialog::slotCheckSelection( KeyListViewItem * item ) {
00672   kdDebug(5150) << "KeySelectionDialog::slotCheckSelection()\n";
00673 
00674   mCheckSelectionTimer->stop();
00675 
00676   mSelectedKeys.clear();
00677 
00678   if ( !mKeyListView->isMultiSelection() ) {
00679     if ( item )
00680       mSelectedKeys.push_back( item->key() );
00681   }
00682 
00683   for ( KeyListViewItem * it = mKeyListView->firstChild() ; it ; it = it->nextSibling() )
00684     if ( it->isSelected() )
00685       mSelectedKeys.push_back( it->key() );
00686 
00687   mKeysToCheck.clear();
00688   std::remove_copy_if( mSelectedKeys.begin(), mSelectedKeys.end(),
00689                std::back_inserter( mKeysToCheck ),
00690                AlreadyChecked() );
00691   if ( mKeysToCheck.empty() ) {
00692     enableButtonOK( !mSelectedKeys.empty() &&
00693             checkKeyUsage( mSelectedKeys, mKeyUsage ) );
00694     return;
00695   }
00696 
00697   // performed all fast checks - now for validating key listing:
00698   startValidatingKeyListing();
00699 }
00700 
00701 void Kleo::KeySelectionDialog::startValidatingKeyListing() {
00702   if ( mKeysToCheck.empty() )
00703     return;
00704 
00705   mListJobCount = 0;
00706   mTruncated = 0;
00707   mSavedOffsetY = mKeyListView->contentsY();
00708 
00709   disconnectSignals();
00710   mKeyListView->setEnabled( false );
00711 
00712   std::vector<GpgME::Key> smime, openpgp;
00713   for ( std::vector<GpgME::Key>::const_iterator it = mKeysToCheck.begin() ; it != mKeysToCheck.end() ; ++it )
00714     if ( it->protocol() == GpgME::Context::OpenPGP )
00715       openpgp.push_back( *it );
00716     else
00717       smime.push_back( *it );
00718 
00719   if ( !openpgp.empty() ) {
00720     assert( mOpenPGPBackend );
00721     startKeyListJobForBackend( mOpenPGPBackend, openpgp, true /*validate*/ );
00722   }
00723   if ( !smime.empty() ) {
00724     assert( mSMIMEBackend );
00725     startKeyListJobForBackend( mSMIMEBackend, smime, true /*validate*/ );
00726   }
00727 
00728   assert( mListJobCount > 0 );
00729 }
00730 
00731 bool Kleo::KeySelectionDialog::rememberSelection() const {
00732   return mRememberCB && mRememberCB->isChecked() ;
00733 }
00734 
00735 void Kleo::KeySelectionDialog::slotRMB( Kleo::KeyListViewItem * item, const TQPoint & p ) {
00736   if ( !item ) return;
00737 
00738   mCurrentContextMenuItem = item;
00739 
00740   TQPopupMenu menu;
00741   menu.insertItem( i18n( "Recheck Key" ), this, TQT_SLOT(slotRecheckKey()) );
00742   menu.exec( p );
00743 }
00744 
00745 void Kleo::KeySelectionDialog::slotRecheckKey() {
00746   if ( !mCurrentContextMenuItem || mCurrentContextMenuItem->key().isNull() )
00747     return;
00748 
00749   mKeysToCheck.clear();
00750   mKeysToCheck.push_back( mCurrentContextMenuItem->key() );
00751 }
00752 
00753 void Kleo::KeySelectionDialog::slotTryOk() {
00754   if ( actionButton( Ok )->isEnabled() )
00755     slotOk();
00756 }
00757 
00758 void Kleo::KeySelectionDialog::slotOk() {
00759   if ( mCheckSelectionTimer->isActive() )
00760     slotCheckSelection();
00761   // button could be disabled again after checking the selected key
00762   if ( !actionButton( Ok )->isEnabled() )
00763     return;
00764   mStartSearchTimer->stop();
00765   accept();
00766 }
00767 
00768 
00769 void Kleo::KeySelectionDialog::slotCancel() {
00770   mCheckSelectionTimer->stop();
00771   mStartSearchTimer->stop();
00772   reject();
00773 }
00774 
00775 void Kleo::KeySelectionDialog::slotSearch( const TQString & text ) {
00776   mSearchText = text.stripWhiteSpace().upper();
00777   slotSearch();
00778 }
00779 
00780 void Kleo::KeySelectionDialog::slotSearch() {
00781   mStartSearchTimer->start( sCheckSelectionDelay, true /*single-shot*/ );
00782 }
00783 
00784 void Kleo::KeySelectionDialog::slotFilter() {
00785   if ( mSearchText.isEmpty() ) {
00786     showAllItems();
00787     return;
00788   }
00789 
00790   // OK, so we need to filter:
00791   TQRegExp keyIdRegExp( "(?:0x)?[A-F0-9]{1,8}", false /*case-insens.*/ );
00792   if ( keyIdRegExp.exactMatch( mSearchText ) ) {
00793     if ( mSearchText.startsWith( "0X" ) )
00794       // search for keyID only:
00795       filterByKeyID( mSearchText.mid( 2 ) );
00796     else
00797       // search for UID and keyID:
00798       filterByKeyIDOrUID( mSearchText );
00799   } else {
00800     // search in UID:
00801     filterByUID( mSearchText );
00802   }
00803 }
00804 
00805 void Kleo::KeySelectionDialog::filterByKeyID( const TQString & keyID ) {
00806   assert( keyID.length() <= 8 );
00807   assert( !keyID.isEmpty() ); // regexp in slotFilter should prevent these
00808   if ( keyID.isEmpty() )
00809     showAllItems();
00810   else
00811     for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00812       item->setVisible( item->text( 0 ).upper().startsWith( keyID ) );
00813 }
00814 
00815 static bool anyUIDMatches( const Kleo::KeyListViewItem * item, TQRegExp & rx ) {
00816   if ( !item )
00817     return false;
00818 
00819   const std::vector<GpgME::UserID> uids = item->key().userIDs();
00820   for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
00821     if ( it->id() && rx.search( TQString::fromUtf8( it->id() ) ) >= 0 )
00822       return true;
00823   return false;
00824 }
00825 
00826 void Kleo::KeySelectionDialog::filterByKeyIDOrUID( const TQString & str ) {
00827   assert( !str.isEmpty() );
00828 
00829   // match beginnings of words:
00830   TQRegExp rx( "\\b" + TQRegExp::escape( str ), false );
00831 
00832   for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00833     item->setVisible( item->text( 0 ).upper().startsWith( str ) || anyUIDMatches( item, rx ) );
00834 
00835 }
00836 
00837 void Kleo::KeySelectionDialog::filterByUID( const TQString & str ) {
00838   assert( !str.isEmpty() );
00839 
00840   // match beginnings of words:
00841   TQRegExp rx( "\\b" + TQRegExp::escape( str ), false );
00842 
00843   for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00844     item->setVisible( anyUIDMatches( item, rx ) );
00845 }
00846 
00847 
00848 void Kleo::KeySelectionDialog::showAllItems() {
00849   for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
00850     item->setVisible( true );
00851 }
00852 
00853 #include "keyselectiondialog.moc"