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 <kprocess.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 
86 static 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 
155 static 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 
162 static inline TQString time_t2string( time_t t ) {
163  TQDateTime dt;
164  dt.setTime_t( t );
165  return dt.toString();
166 }
167 
168 namespace {
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 
301 static const int sCheckSelectionDelay = 250;
302 
303 Kleo::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 
322 Kleo::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 
344 Kleo::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 
364 void 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 ( kapp ) {
453  KWin::setIcons( winId(), kapp->icon(), kapp->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 
462 Kleo::KeySelectionDialog::~KeySelectionDialog() {
463  TDEConfigGroup dialogConfig( TDEGlobal::config(), "Key Selection Dialog" );
464  dialogConfig.writeEntry( "Dialog size", size() );
465  dialogConfig.sync();
466 }
467 
468 
469 void 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 
478 void 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 
487 const GpgME::Key & Kleo::KeySelectionDialog::selectedKey() const {
488  if ( mKeyListView->isMultiSelection() || !mKeyListView->selectedItem() )
489  return GpgME::Key::null;
490  return mKeyListView->selectedItem()->key();
491 }
492 
493 TQString Kleo::KeySelectionDialog::fingerprint() const {
494  return selectedKey().primaryFingerprint();
495 }
496 
497 TQStringList 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 
505 TQStringList 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 
514 TQStringList 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 
523 void 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 
548 void Kleo::KeySelectionDialog::slotHelp()
549 {
550  emit helpClicked();
551 }
552 
553 void 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__
570 static 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 
581 namespace {
582  struct ExtractFingerprint {
583  TQString operator()( const GpgME::Key & key ) {
584  return key.primaryFingerprint();
585  }
586  };
587 }
588 
589 void 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 
616 static 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 
625 void 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 
665 void 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 
674 namespace {
675  struct AlreadyChecked {
676  bool operator()( const GpgME::Key & key ) const {
677  return key.keyListMode() & GpgME::Context::Validate ;
678  }
679  };
680 }
681 
682 void 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 
715 void 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 
749 bool Kleo::KeySelectionDialog::rememberSelection() const {
750  return mRememberCB && mRememberCB->isChecked() ;
751 }
752 
753 void 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 
763 void Kleo::KeySelectionDialog::slotRecheckKey() {
764  if ( !mCurrentContextMenuItem || mCurrentContextMenuItem->key().isNull() )
765  return;
766 
767  mKeysToCheck.clear();
768  mKeysToCheck.push_back( mCurrentContextMenuItem->key() );
769 }
770 
771 void Kleo::KeySelectionDialog::slotTryOk() {
772  if ( actionButton( Ok )->isEnabled() )
773  slotOk();
774 }
775 
776 void 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 
787 void Kleo::KeySelectionDialog::slotCancel() {
788  mCheckSelectionTimer->stop();
789  mStartSearchTimer->stop();
790  reject();
791 }
792 
793 void Kleo::KeySelectionDialog::slotSearch( const TQString & text ) {
794  mSearchText = text.stripWhiteSpace().upper();
795  slotSearch();
796 }
797 
798 void Kleo::KeySelectionDialog::slotSearch() {
799  mStartSearchTimer->start( sCheckSelectionDelay, true /*single-shot*/ );
800 }
801 
802 void 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 
823 void 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 
833 static 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 
844 void 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 
855 void 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 
866 void 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