certmanager/lib

keyselectiondialog.cpp
1/*
2 keyselectiondialog.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 Copyright (c) 2004 Klarävdalens Datakonsult AB
6
7 Based on kpgpui.cpp
8 Copyright (C) 2001,2002 the KPGP authors
9 See file libtdenetwork/AUTHORS.kpgp for details
10
11 Libkleopatra is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version.
15
16 Libkleopatra is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24
25 In addition, as a special exception, the copyright holders give
26 permission to link the code of this program with any edition of
27 the TQt library by Trolltech AS, Norway (or with modified versions
28 of TQt that use the same license as TQt), and distribute linked
29 combinations including the two. You must obey the GNU General
30 Public License in all respects for all of the code used other than
31 TQt. If you modify this file, you may extend this exception to
32 your version of the file, but you are not obligated to do so. If
33 you do not wish to do so, delete this exception statement from
34 your version.
35*/
36
37#ifdef HAVE_CONFIG_H
38#include <config.h>
39#endif
40
41#include "keyselectiondialog.h"
42
43#include "keylistview.h"
44#include "progressdialog.h"
45
46#include <kleo/dn.h>
47#include <kleo/keylistjob.h>
48#include <kleo/cryptobackendfactory.h>
49
50// gpgme++
51#include <gpgmepp/key.h>
52#include <gpgmepp/keylistresult.h>
53
54// KDE
55#include <tdelocale.h>
56#include <tdeapplication.h>
57#include <tdeglobal.h>
58#include <kiconloader.h>
59#include <kdebug.h>
60#include <twin.h>
61#include <tdeconfig.h>
62#include <tdemessagebox.h>
63#include <tdeprocess.h>
64#include <kactivelabel.h>
65#include <kurl.h>
66
67// TQt
68#include <tqcheckbox.h>
69#include <tqtoolbutton.h>
70#include <tqlabel.h>
71#include <tqpixmap.h>
72#include <tqtimer.h>
73#include <tqlayout.h>
74#include <tqlineedit.h>
75#include <tqwhatsthis.h>
76#include <tqpopupmenu.h>
77#include <tqregexp.h>
78#include <tqpushbutton.h>
79
80#include <algorithm>
81#include <iterator>
82
83#include <string.h>
84#include <assert.h>
85
86static bool checkKeyUsage( const GpgME::Key & key, unsigned int keyUsage ) {
87
88 if ( keyUsage & Kleo::KeySelectionDialog::ValidKeys ) {
89 if ( key.isInvalid() ) {
90 if ( key.keyListMode() & GpgME::Context::Validate ) {
91 kdDebug() << "key is invalid" << endl;
92 return false;
93 } else {
94 kdDebug() << "key is invalid - ignoring" << endl;
95 }
96 }
97 if ( key.isExpired() ) {
98 kdDebug() << "key is expired" << endl;
99 return false;
100 } else if ( key.isRevoked() ) {
101 kdDebug() << "key is revoked" << endl;
102 return false;
103 } else if ( key.isDisabled() ) {
104 kdDebug() << "key is disabled" << endl;
105 return false;
106 }
107 }
108
109 if ( keyUsage & Kleo::KeySelectionDialog::EncryptionKeys &&
110 !key.canEncrypt() ) {
111 kdDebug() << "key can't encrypt" << endl;
112 return false;
113 }
114 if ( keyUsage & Kleo::KeySelectionDialog::SigningKeys &&
115 !key.canSign() ) {
116 kdDebug() << "key can't sign" << endl;
117 return false;
118 }
119 if ( keyUsage & Kleo::KeySelectionDialog::CertificationKeys &&
120 !key.canCertify() ) {
121 kdDebug() << "key can't certify" << endl;
122 return false;
123 }
124 if ( keyUsage & Kleo::KeySelectionDialog::AuthenticationKeys &&
125 !key.canAuthenticate() ) {
126 kdDebug() << "key can't authenticate" << endl;
127 return false;
128 }
129
130 if ( keyUsage & Kleo::KeySelectionDialog::SecretKeys &&
131 !( keyUsage & Kleo::KeySelectionDialog::PublicKeys ) &&
132 !key.isSecret() ) {
133 kdDebug() << "key isn't secret" << endl;
134 return false;
135 }
136
137 if ( keyUsage & Kleo::KeySelectionDialog::TrustedKeys &&
138 key.protocol() == GpgME::Context::OpenPGP &&
139 // only check this for secret keys for now.
140 // Seems validity isn't checked for secret keylistings...
141 !key.isSecret() ) {
142 std::vector<GpgME::UserID> uids = key.userIDs();
143 for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
144 if ( !it->isRevoked() && it->validity() >= GpgME::UserID::Marginal )
145 return true;
146 kdDebug() << "key has no UIDs with validity >= Marginal" << endl;
147 return false;
148 }
149 // X.509 keys are always trusted, else they won't be the keybox.
150 // PENDING(marc) check that this ^ is correct
151
152 return true;
153}
154
155static bool checkKeyUsage( const std::vector<GpgME::Key> & keys, unsigned int keyUsage ) {
156 for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it )
157 if ( !checkKeyUsage( *it, keyUsage ) )
158 return false;
159 return true;
160}
161
162static inline TQString time_t2string( time_t t ) {
163 TQDateTime dt;
164 dt.setTime_t( t );
165 return dt.toString();
166}
167
168namespace {
169
170 class ColumnStrategy : public Kleo::KeyListView::ColumnStrategy {
171 public:
172 ColumnStrategy( unsigned int keyUsage );
173
174 TQString title( int col ) const;
175 int width( int col, const TQFontMetrics & fm ) const;
176
177 TQString text( const GpgME::Key & key, int col ) const;
178 TQString toolTip( const GpgME::Key & key, int col ) const;
179 const TQPixmap * pixmap( const GpgME::Key & key, int col ) const;
180
181 private:
182 const TQPixmap mKeyGoodPix, mKeyBadPix, mKeyUnknownPix, mKeyValidPix;
183 const unsigned int mKeyUsage;
184 };
185
186 ColumnStrategy::ColumnStrategy( unsigned int keyUsage )
187 : Kleo::KeyListView::ColumnStrategy(),
188 mKeyGoodPix( UserIcon( "key_ok" ) ),
189 mKeyBadPix( UserIcon( "key_bad" ) ),
190 mKeyUnknownPix( UserIcon( "key_unknown" ) ),
191 mKeyValidPix( UserIcon( "key" ) ),
192 mKeyUsage( keyUsage )
193 {
194 kdWarning( keyUsage == 0, 5150 )
195 << "KeySelectionDialog: keyUsage == 0. You want to use AllKeys instead." << endl;
196 }
197
198 TQString ColumnStrategy::title( int col ) const {
199 switch ( col ) {
200 case 0: return i18n("Key ID");
201 case 1: return i18n("User ID");
202 default: return TQString();
203 }
204 }
205
206 int ColumnStrategy::width( int col, const TQFontMetrics & fm ) const {
207 if ( col == 0 ) {
208 static const char hexchars[] = "0123456789ABCDEF";
209 int maxWidth = 0;
210 for ( unsigned int i = 0 ; i < 16 ; ++i )
211 maxWidth = kMax( fm.width( TQChar( hexchars[i] ) ), maxWidth );
212 return 8 * maxWidth + 2 * mKeyGoodPix.width();
213 }
214 return Kleo::KeyListView::ColumnStrategy::width( col, fm );
215 }
216
217 TQString ColumnStrategy::text( const GpgME::Key & key, int col ) const {
218 switch ( col ) {
219 case 0:
220 {
221 if ( key.shortKeyID() )
222 return TQString::fromUtf8( key.shortKeyID() );
223 else
224 return i18n("<unknown>");
225 }
226 break;
227 case 1:
228 {
229 const char * uid = key.userID(0).id();
230 if ( key.protocol() == GpgME::Context::OpenPGP )
231 return uid && *uid ? TQString::fromUtf8( uid ) : TQString() ;
232 else // CMS
233 return Kleo::DN( uid ).prettyDN();
234 }
235 break;
236 default: return TQString();
237 }
238 }
239
240 TQString ColumnStrategy::toolTip( const GpgME::Key & key, int ) const {
241 const char * uid = key.userID(0).id();
242 const char * fpr = key.primaryFingerprint();
243 const char * issuer = key.issuerName();
244 const GpgME::Subkey subkey = key.subkey(0);
245 const TQString expiry = subkey.neverExpires() ? i18n("never") : time_t2string( subkey.expirationTime() ) ;
246 const TQString creation = time_t2string( subkey.creationTime() );
247 if ( key.protocol() == GpgME::Context::OpenPGP )
248 return i18n( "OpenPGP key for %1\n"
249 "Created: %2\n"
250 "Expiry: %3\n"
251 "Fingerprint: %4" )
252 .arg( uid ? TQString::fromUtf8( uid ) : i18n("unknown"),
253 creation, expiry,
254 fpr ? TQString::fromLatin1( fpr ) : i18n("unknown") );
255 else
256 return i18n( "S/MIME key for %1\n"
257 "Created: %2\n"
258 "Expiry: %3\n"
259 "Fingerprint: %4\n"
260 "Issuer: %5" )
261 .arg( uid ? Kleo::DN( uid ).prettyDN() : i18n("unknown"),
262 creation, expiry,
263 fpr ? TQString::fromLatin1( fpr ) : i18n("unknown") )
264 .arg( issuer ? Kleo::DN( issuer ).prettyDN() : i18n("unknown") );
265 }
266
267 const TQPixmap * ColumnStrategy::pixmap( const GpgME::Key & key, int col ) const {
268 if ( col != 0 ) {
269 return 0;
270 }
271 // this key did not undergo a validating keylisting yet:
272 if ( !( key.keyListMode() & GpgME::Context::Validate ) ) {
273 return &mKeyUnknownPix;
274 }
275
276 if ( !checkKeyUsage( key, mKeyUsage ) ) {
277 return &mKeyBadPix;
278 }
279
280 if ( key.protocol() == GpgME::Context::CMS ) {
281 return &mKeyGoodPix;
282 }
283
284 switch ( key.userID(0).validity() ) {
285 default:
286 case GpgME::UserID::Unknown:
287 case GpgME::UserID::Undefined:
288 return &mKeyUnknownPix;
289 case GpgME::UserID::Never:
290 return &mKeyValidPix;
291 case GpgME::UserID::Marginal:
292 case GpgME::UserID::Full:
293 case GpgME::UserID::Ultimate:
294 return &mKeyGoodPix;
295 }
296 }
297
298}
299
300
301static const int sCheckSelectionDelay = 250;
302
303Kleo::KeySelectionDialog::KeySelectionDialog( const TQString & title,
304 const TQString & text,
305 const std::vector<GpgME::Key> & selectedKeys,
306 unsigned int keyUsage,
307 bool extendedSelection,
308 bool rememberChoice,
309 TQWidget * parent, const char * name,
310 bool modal )
311 : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ),
312 mOpenPGPBackend( 0 ),
313 mSMIMEBackend( 0 ),
314 mRememberCB( 0 ),
315 mSelectedKeys( selectedKeys ),
316 mKeyUsage( keyUsage ),
317 mCurrentContextMenuItem( 0 )
318{
319 init( rememberChoice, extendedSelection, text, TQString() );
320}
321
322Kleo::KeySelectionDialog::KeySelectionDialog( const TQString & title,
323 const TQString & text,
324 const TQString & initialQuery,
325 const std::vector<GpgME::Key> & selectedKeys,
326 unsigned int keyUsage,
327 bool extendedSelection,
328 bool rememberChoice,
329 TQWidget * parent, const char * name,
330 bool modal )
331 : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ),
332 mOpenPGPBackend( 0 ),
333 mSMIMEBackend( 0 ),
334 mRememberCB( 0 ),
335 mSelectedKeys( selectedKeys ),
336 mKeyUsage( keyUsage ),
337 mSearchText( initialQuery ),
338 mInitialQuery( initialQuery ),
339 mCurrentContextMenuItem( 0 )
340{
341 init( rememberChoice, extendedSelection, text, initialQuery );
342}
343
344Kleo::KeySelectionDialog::KeySelectionDialog( const TQString & title,
345 const TQString & text,
346 const TQString & initialQuery,
347 unsigned int keyUsage,
348 bool extendedSelection,
349 bool rememberChoice,
350 TQWidget * parent, const char * name,
351 bool modal )
352 : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ),
353 mOpenPGPBackend( 0 ),
354 mSMIMEBackend( 0 ),
355 mRememberCB( 0 ),
356 mKeyUsage( keyUsage ),
357 mSearchText( initialQuery ),
358 mInitialQuery( initialQuery ),
359 mCurrentContextMenuItem( 0 )
360{
361 init( rememberChoice, extendedSelection, text, initialQuery );
362}
363
364void Kleo::KeySelectionDialog::init( bool rememberChoice, bool extendedSelection,
365 const TQString & text, const TQString & initialQuery ) {
366 if ( mKeyUsage & OpenPGPKeys )
367 mOpenPGPBackend = Kleo::CryptoBackendFactory::instance()->openpgp();
368 if ( mKeyUsage & SMIMEKeys )
369 mSMIMEBackend = Kleo::CryptoBackendFactory::instance()->smime();
370
371 mCheckSelectionTimer = new TQTimer( this );
372 mStartSearchTimer = new TQTimer( this );
373
374 TQFrame *page = makeMainWidget();
375 mTopLayout = new TQVBoxLayout( page, 0, spacingHint() );
376
377 if ( !text.isEmpty() ) {
378 if ( text.startsWith( "<qt>" ) ) {
379 KActiveLabel *textLabel = new KActiveLabel( text, page );
380 disconnect( textLabel, TQ_SIGNAL(linkClicked(const TQString&)), textLabel, TQ_SLOT(openLink(const TQString&)) );
381 connect( textLabel, TQ_SIGNAL(linkClicked(const TQString&)), TQ_SLOT(slotStartCertificateManager(const TQString&)) );
382 textLabel->setAlignment( textLabel->alignment() | TQt::WordBreak );
383 mTopLayout->addWidget( textLabel );
384 } else {
385 KActiveLabel *textLabel = new KActiveLabel( text, page );
386 textLabel->setAlignment( textLabel->alignment() | TQt::WordBreak );
387 mTopLayout->addWidget( textLabel );
388 }
389 }
390
391 TQPushButton * const searchExternalPB
392 = new TQPushButton( i18n("Search for &External Certificates"), page );
393 mTopLayout->addWidget( searchExternalPB, 0, TQt::AlignLeft );
394 connect( searchExternalPB, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotStartSearchForExternalCertificates()) );
395 if ( initialQuery.isEmpty() )
396 searchExternalPB->hide();
397
398 TQHBoxLayout * hlay = new TQHBoxLayout( mTopLayout ); // inherits spacing
399 TQLineEdit * le = new TQLineEdit( page );
400 le->setText( initialQuery );
401 TQToolButton *clearButton = new TQToolButton( page );
402 clearButton->setIconSet( TDEGlobal::iconLoader()->loadIconSet(
403 TDEApplication::reverseLayout() ? "clear_left":"locationbar_erase", TDEIcon::Small, 0 ) );
404 hlay->addWidget( clearButton );
405 hlay->addWidget( new TQLabel( le, i18n("&Search for:"), page ) );
406 hlay->addWidget( le, 1 );
407 le->setFocus();
408
409 connect( clearButton, TQ_SIGNAL( clicked() ), le, TQ_SLOT( clear() ) );
410 connect( le, TQ_SIGNAL(textChanged(const TQString&)),
411 this, TQ_SLOT(slotSearch(const TQString&)) );
412 connect( mStartSearchTimer, TQ_SIGNAL(timeout()), TQ_SLOT(slotFilter()) );
413
414 mKeyListView = new KeyListView( new ColumnStrategy( mKeyUsage ), 0, page, "mKeyListView" );
415 mKeyListView->setResizeMode( TQListView::LastColumn );
416 mKeyListView->setRootIsDecorated( true );
417 mKeyListView->setShowSortIndicator( true );
418 mKeyListView->setSorting( 1, true ); // sort by User ID
419 mKeyListView->setShowToolTips( true );
420 if ( extendedSelection )
421 mKeyListView->setSelectionMode( TQListView::Extended );
422 mTopLayout->addWidget( mKeyListView, 10 );
423
424 if ( rememberChoice ) {
425 mRememberCB = new TQCheckBox( i18n("&Remember choice"), page );
426 mTopLayout->addWidget( mRememberCB );
427 TQWhatsThis::add( mRememberCB,
428 i18n("<qt><p>If you check this box your choice will "
429 "be stored and you will not be asked again."
430 "</p></qt>") );
431 }
432
433 connect( mCheckSelectionTimer, TQ_SIGNAL(timeout()),
434 TQ_SLOT(slotCheckSelection()) );
435 connectSignals();
436
437 connect( mKeyListView,
438 TQ_SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const TQPoint&,int)),
439 TQ_SLOT(slotTryOk()) );
440 connect( mKeyListView,
441 TQ_SIGNAL(contextMenu(Kleo::KeyListViewItem*,const TQPoint&)),
442 TQ_SLOT(slotRMB(Kleo::KeyListViewItem*,const TQPoint&)) );
443
444 setButtonText( KDialogBase::Default, i18n("&Reread Keys") );
445 setButtonGuiItem( KDialogBase::Help, i18n("&Start Certificate Manager") );
446 connect( this, TQ_SIGNAL(defaultClicked()), this, TQ_SLOT(slotRereadKeys()) );
447 connect( this, TQ_SIGNAL(helpClicked()), this, TQ_SLOT(slotStartCertificateManager()) );
448
449 slotRereadKeys();
450 mTopLayout->activate();
451
452 if ( tdeApp ) {
453 KWin::setIcons( winId(), tdeApp->icon(), tdeApp->miniIcon() );
454 TQSize dialogSize( 500, 400 );
455
456 TDEConfigGroup dialogConfig( TDEGlobal::config(), "Key Selection Dialog" );
457 dialogSize = dialogConfig.readSizeEntry( "Dialog size", &dialogSize );
458 resize( dialogSize );
459 }
460}
461
462Kleo::KeySelectionDialog::~KeySelectionDialog() {
463 TDEConfigGroup dialogConfig( TDEGlobal::config(), "Key Selection Dialog" );
464 dialogConfig.writeEntry( "Dialog size", size() );
465 dialogConfig.sync();
466}
467
468
469void Kleo::KeySelectionDialog::connectSignals() {
470 if ( mKeyListView->isMultiSelection() )
471 connect( mKeyListView, TQ_SIGNAL(selectionChanged()),
472 TQ_SLOT(slotSelectionChanged()) );
473 else
474 connect( mKeyListView, TQ_SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
475 TQ_SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
476}
477
478void Kleo::KeySelectionDialog::disconnectSignals() {
479 if ( mKeyListView->isMultiSelection() )
480 disconnect( mKeyListView, TQ_SIGNAL(selectionChanged()),
481 this, TQ_SLOT(slotSelectionChanged()) );
482 else
483 disconnect( mKeyListView, TQ_SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
484 this, TQ_SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
485}
486
487const GpgME::Key & Kleo::KeySelectionDialog::selectedKey() const {
488 if ( mKeyListView->isMultiSelection() || !mKeyListView->selectedItem() )
489 return GpgME::Key::null;
490 return mKeyListView->selectedItem()->key();
491}
492
493TQString Kleo::KeySelectionDialog::fingerprint() const {
494 return selectedKey().primaryFingerprint();
495}
496
497TQStringList Kleo::KeySelectionDialog::fingerprints() const {
498 TQStringList result;
499 for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
500 if ( const char * fpr = it->primaryFingerprint() )
501 result.push_back( fpr );
502 return result;
503}
504
505TQStringList Kleo::KeySelectionDialog::pgpKeyFingerprints() const {
506 TQStringList result;
507 for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
508 if ( it->protocol() == GpgME::Context::OpenPGP )
509 if ( const char * fpr = it->primaryFingerprint() )
510 result.push_back( fpr );
511 return result;
512}
513
514TQStringList Kleo::KeySelectionDialog::smimeFingerprints() const {
515 TQStringList result;
516 for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
517 if ( it->protocol() == GpgME::Context::CMS )
518 if ( const char * fpr = it->primaryFingerprint() )
519 result.push_back( fpr );
520 return result;
521}
522
523void Kleo::KeySelectionDialog::slotRereadKeys() {
524 mKeyListView->clear();
525 mListJobCount = 0;
526 mTruncated = 0;
527 mSavedOffsetY = mKeyListView->contentsY();
528
529 disconnectSignals();
530 mKeyListView->setEnabled( false );
531
532 // FIXME: save current selection
533 if ( mOpenPGPBackend )
534 startKeyListJobForBackend( mOpenPGPBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
535 if ( mSMIMEBackend )
536 startKeyListJobForBackend( mSMIMEBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
537
538 if ( mListJobCount == 0 ) {
539 mKeyListView->setEnabled( true );
540 KMessageBox::information( this,
541 i18n("No backends found for listing keys. "
542 "Check your installation."),
543 i18n("Key Listing Failed") );
544 connectSignals();
545 }
546}
547
548void Kleo::KeySelectionDialog::slotHelp()
549{
550 emit helpClicked();
551}
552
553void Kleo::KeySelectionDialog::slotStartCertificateManager( const TQString &query )
554{
555 TDEProcess certManagerProc;
556 certManagerProc << "kleopatra";
557 if ( !query.isEmpty() )
558 certManagerProc << "--external" << "--query" << KURL::decode_string( query );
559
560 if( !certManagerProc.start( TDEProcess::DontCare ) )
561 KMessageBox::error( this, i18n( "Could not start certificate manager; "
562 "please check your installation." ),
563 i18n( "Certificate Manager Error" ) );
564 else
565 kdDebug(5006) << "\nslotStartCertManager(): certificate manager started.\n" << endl;
566}
567
568#ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
569#define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
570static void showKeyListError( TQWidget * parent, const GpgME::Error & err ) {
571 assert( err );
572 const TQString msg = i18n( "<qt><p>An error occurred while fetching "
573 "the keys from the backend:</p>"
574 "<p><b>%1</b></p></qt>" )
575 .arg( TQString::fromLocal8Bit( err.asString() ) );
576
577 KMessageBox::error( parent, msg, i18n( "Key Listing Failed" ) );
578}
579#endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
580
581namespace {
582 struct ExtractFingerprint {
583 TQString operator()( const GpgME::Key & key ) {
584 return key.primaryFingerprint();
585 }
586 };
587}
588
589void Kleo::KeySelectionDialog::startKeyListJobForBackend( const CryptoBackend::Protocol * backend, const std::vector<GpgME::Key> & keys, bool validate ) {
590 assert( backend );
591 KeyListJob * job = backend->keyListJob( false, false, validate ); // local, w/o sigs, validation as givem
592 if ( !job ) {
593 return;
594 }
595
596 connect( job, TQ_SIGNAL(result(const GpgME::KeyListResult&)),
597 TQ_SLOT(slotKeyListResult(const GpgME::KeyListResult&)) );
598 connect( job, TQ_SIGNAL(nextKey(const GpgME::Key&)),
599 mKeyListView, validate ?
600 TQ_SLOT(slotRefreshKey(const GpgME::Key&)) :
601 TQ_SLOT(slotAddKey(const GpgME::Key&)) );
602
603 TQStringList fprs;
604 std::transform( keys.begin(), keys.end(), std::back_inserter( fprs ), ExtractFingerprint() );
605 const GpgME::Error err = job->start( fprs, mKeyUsage & SecretKeys && !( mKeyUsage & PublicKeys ) );
606
607 if ( err ) {
608 return showKeyListError( this, err );
609 }
610
611 // FIXME: create a MultiProgressDialog:
612 (void)new ProgressDialog( job, validate ? i18n( "Checking selected keys..." ) : i18n( "Fetching keys..." ), this );
613 ++mListJobCount;
614}
615
616static void selectKeys( Kleo::KeyListView * klv, const std::vector<GpgME::Key> & selectedKeys ) {
617 klv->clearSelection();
618 if ( selectedKeys.empty() )
619 return;
620 for ( std::vector<GpgME::Key>::const_iterator it = selectedKeys.begin() ; it != selectedKeys.end() ; ++it )
621 if ( Kleo::KeyListViewItem * item = klv->itemByFingerprint( it->primaryFingerprint() ) )
622 item->setSelected( true );
623}
624
625void Kleo::KeySelectionDialog::slotKeyListResult( const GpgME::KeyListResult & res ) {
626 if ( res.error() ) {
627 showKeyListError( this, res.error() );
628 }
629 else if ( res.isTruncated() ) {
630 ++mTruncated;
631 }
632
633 if ( --mListJobCount > 0 ) {
634 return; // not yet finished...
635 }
636
637 if ( mTruncated > 0 ) {
638 KMessageBox::information( this,
639 i18n("<qt>One backend returned truncated output.<br>"
640 "Not all available keys are shown</qt>",
641 "<qt>%n backends returned truncated output.<br>"
642 "Not all available keys are shown</qt>",
643 mTruncated),
644 i18n("Key List Result") );
645 }
646
647 mKeyListView->flushKeys();
648
649 mKeyListView->setEnabled( true );
650 mListJobCount = mTruncated = 0;
651 mKeysToCheck.clear();
652
653 selectKeys( mKeyListView, mSelectedKeys );
654
655 slotFilter();
656
657 connectSignals();
658
659 slotSelectionChanged();
660
661 // restore the saved position of the contents
662 mKeyListView->setContentsPos( 0, mSavedOffsetY ); mSavedOffsetY = 0;
663}
664
665void Kleo::KeySelectionDialog::slotSelectionChanged() {
666 kdDebug(5150) << "KeySelectionDialog::slotSelectionChanged()" << endl;
667
668 // (re)start the check selection timer. Checking the selection is delayed
669 // because else drag-selection doesn't work very good (checking key trust
670 // is slow).
671 mCheckSelectionTimer->start( sCheckSelectionDelay );
672}
673
674namespace {
675 struct AlreadyChecked {
676 bool operator()( const GpgME::Key & key ) const {
677 return key.keyListMode() & GpgME::Context::Validate ;
678 }
679 };
680}
681
682void Kleo::KeySelectionDialog::slotCheckSelection( KeyListViewItem * item ) {
683 kdDebug(5150) << "KeySelectionDialog::slotCheckSelection()\n";
684
685 mCheckSelectionTimer->stop();
686
687 mSelectedKeys.clear();
688
689 if ( !mKeyListView->isMultiSelection() ) {
690 if ( item ) {
691 mSelectedKeys.push_back( item->key() );
692 }
693 }
694
695 for ( KeyListViewItem * it = mKeyListView->firstChild() ; it ; it = it->nextSibling() ) {
696 if ( it->isSelected() ) {
697 mSelectedKeys.push_back( it->key() );
698 }
699 }
700
701 mKeysToCheck.clear();
702 std::remove_copy_if( mSelectedKeys.begin(), mSelectedKeys.end(),
703 std::back_inserter( mKeysToCheck ),
704 AlreadyChecked() );
705 if ( mKeysToCheck.empty() ) {
706 enableButtonOK( !mSelectedKeys.empty() &&
707 checkKeyUsage( mSelectedKeys, mKeyUsage ) );
708 return;
709 }
710
711 // performed all fast checks - now for validating key listing:
712 startValidatingKeyListing();
713}
714
715void Kleo::KeySelectionDialog::startValidatingKeyListing() {
716 if ( mKeysToCheck.empty() ) {
717 return;
718 }
719
720 mListJobCount = 0;
721 mTruncated = 0;
722 mSavedOffsetY = mKeyListView->contentsY();
723
724 disconnectSignals();
725 mKeyListView->setEnabled( false );
726
727 std::vector<GpgME::Key> smime, openpgp;
728 for ( std::vector<GpgME::Key>::const_iterator it = mKeysToCheck.begin() ; it != mKeysToCheck.end() ; ++it ) {
729 if ( it->protocol() == GpgME::Context::OpenPGP ) {
730 openpgp.push_back( *it );
731 }
732 else {
733 smime.push_back( *it );
734 }
735 }
736
737 if ( !openpgp.empty() ) {
738 assert( mOpenPGPBackend );
739 startKeyListJobForBackend( mOpenPGPBackend, openpgp, true /*validate*/ );
740 }
741 if ( !smime.empty() ) {
742 assert( mSMIMEBackend );
743 startKeyListJobForBackend( mSMIMEBackend, smime, true /*validate*/ );
744 }
745
746 assert( mListJobCount > 0 );
747}
748
749bool Kleo::KeySelectionDialog::rememberSelection() const {
750 return mRememberCB && mRememberCB->isChecked() ;
751}
752
753void Kleo::KeySelectionDialog::slotRMB( Kleo::KeyListViewItem * item, const TQPoint & p ) {
754 if ( !item ) return;
755
756 mCurrentContextMenuItem = item;
757
758 TQPopupMenu menu;
759 menu.insertItem( i18n( "Recheck Key" ), this, TQ_SLOT(slotRecheckKey()) );
760 menu.exec( p );
761}
762
763void Kleo::KeySelectionDialog::slotRecheckKey() {
764 if ( !mCurrentContextMenuItem || mCurrentContextMenuItem->key().isNull() )
765 return;
766
767 mKeysToCheck.clear();
768 mKeysToCheck.push_back( mCurrentContextMenuItem->key() );
769}
770
771void Kleo::KeySelectionDialog::slotTryOk() {
772 if ( actionButton( Ok )->isEnabled() )
773 slotOk();
774}
775
776void Kleo::KeySelectionDialog::slotOk() {
777 if ( mCheckSelectionTimer->isActive() )
778 slotCheckSelection();
779 // button could be disabled again after checking the selected key
780 if ( !actionButton( Ok )->isEnabled() )
781 return;
782 mStartSearchTimer->stop();
783 accept();
784}
785
786
787void Kleo::KeySelectionDialog::slotCancel() {
788 mCheckSelectionTimer->stop();
789 mStartSearchTimer->stop();
790 reject();
791}
792
793void Kleo::KeySelectionDialog::slotSearch( const TQString & text ) {
794 mSearchText = text.stripWhiteSpace().upper();
795 slotSearch();
796}
797
798void Kleo::KeySelectionDialog::slotSearch() {
799 mStartSearchTimer->start( sCheckSelectionDelay, true /*single-shot*/ );
800}
801
802void Kleo::KeySelectionDialog::slotFilter() {
803 if ( mSearchText.isEmpty() ) {
804 showAllItems();
805 return;
806 }
807
808 // OK, so we need to filter:
809 TQRegExp keyIdRegExp( "(?:0x)?[A-F0-9]{1,8}", false /*case-insens.*/ );
810 if ( keyIdRegExp.exactMatch( mSearchText ) ) {
811 if ( mSearchText.startsWith( "0X" ) )
812 // search for keyID only:
813 filterByKeyID( mSearchText.mid( 2 ) );
814 else
815 // search for UID and keyID:
816 filterByKeyIDOrUID( mSearchText );
817 } else {
818 // search in UID:
819 filterByUID( mSearchText );
820 }
821}
822
823void Kleo::KeySelectionDialog::filterByKeyID( const TQString & keyID ) {
824 assert( keyID.length() <= 8 );
825 assert( !keyID.isEmpty() ); // regexp in slotFilter should prevent these
826 if ( keyID.isEmpty() )
827 showAllItems();
828 else
829 for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
830 item->setVisible( item->text( 0 ).upper().startsWith( keyID ) );
831}
832
833static bool anyUIDMatches( const Kleo::KeyListViewItem * item, TQRegExp & rx ) {
834 if ( !item )
835 return false;
836
837 const std::vector<GpgME::UserID> uids = item->key().userIDs();
838 for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
839 if ( it->id() && rx.search( TQString::fromUtf8( it->id() ) ) >= 0 )
840 return true;
841 return false;
842}
843
844void Kleo::KeySelectionDialog::filterByKeyIDOrUID( const TQString & str ) {
845 assert( !str.isEmpty() );
846
847 // match beginnings of words:
848 TQRegExp rx( "\\b" + TQRegExp::escape( str ), false );
849
850 for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
851 item->setVisible( item->text( 0 ).upper().startsWith( str ) || anyUIDMatches( item, rx ) );
852
853}
854
855void Kleo::KeySelectionDialog::filterByUID( const TQString & str ) {
856 assert( !str.isEmpty() );
857
858 // match beginnings of words:
859 TQRegExp rx( "\\b" + TQRegExp::escape( str ), false );
860
861 for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
862 item->setVisible( anyUIDMatches( item, rx ) );
863}
864
865
866void Kleo::KeySelectionDialog::showAllItems() {
867 for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
868 item->setVisible( true );
869}
870
871#include "keyselectiondialog.moc"
DN parser and reorderer.
Definition: dn.h:76
TQString prettyDN() const
Definition: dn.cpp:379