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"