certmanager

certmanager.cpp
1 /*
2  certmanager.cpp
3 
4  This file is part of Kleopatra, the KDE keymanager
5  Copyright (c) 2001,2002,2004 Klarälvdalens Datakonsult AB
6 
7  Kleopatra is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  Kleopatra is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 
21  In addition, as a special exception, the copyright holders give
22  permission to link the code of this program with any edition of
23  the TQt library by Trolltech AS, Norway (or with modified versions
24  of TQt that use the same license as TQt), and distribute linked
25  combinations including the two. You must obey the GNU General
26  Public License in all respects for all of the code used other than
27  TQt. If you modify this file, you may extend this exception to
28  your version of the file, but you are not obligated to do so. If
29  you do not wish to do so, delete this exception statement from
30  your version.
31 */
32 
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36 
37 #include "certmanager.h"
38 
39 #include "certlistview.h"
40 #include "certificatewizardimpl.h"
41 #include "certificateinfowidgetimpl.h"
42 #include "crlview.h"
43 #include "customactions.h"
44 #include "hierarchyanalyser.h"
45 #include "storedtransferjob.h"
46 #include "conf/configuredialog.h"
47 
48 // libkleopatra
49 #include <kleo/cryptobackendfactory.h>
50 #include <kleo/downloadjob.h>
51 #include <kleo/importjob.h>
52 #include <kleo/exportjob.h>
53 #include <kleo/multideletejob.h>
54 #include <kleo/deletejob.h>
55 #include <kleo/keylistjob.h>
56 #include <kleo/dn.h>
57 #include <kleo/keyfilter.h>
58 #include <kleo/keyfiltermanager.h>
59 #include <kleo/hierarchicalkeylistjob.h>
60 #include <kleo/refreshkeysjob.h>
61 #include <kleo/cryptoconfig.h>
62 
63 #include <ui/progressdialog.h>
64 #include <ui/progressbar.h>
65 #include <ui/keyselectiondialog.h>
66 #include <ui/cryptoconfigdialog.h>
67 
68 // GPGME++
69 #include <gpgmepp/importresult.h>
70 #include <gpgmepp/keylistresult.h>
71 #include <gpgmepp/key.h>
72 
73 // KDE
74 #include <tdefiledialog.h>
75 #include <tdeprocess.h>
76 #include <tdeaction.h>
77 #include <tdeapplication.h>
78 #include <tdelocale.h>
79 #include <tdemessagebox.h>
80 #include <dcopclient.h>
81 #include <tdetoolbar.h>
82 #include <kstatusbar.h>
83 #include <kstandarddirs.h>
84 #include <kdebug.h>
85 #include <kdialogbase.h>
86 #include <kkeydialog.h>
87 #include <tdetempfile.h>
88 #include <tdeio/job.h>
89 #include <tdeio/netaccess.h>
90 #include <tdestdaccel.h>
91 
92 // TQt
93 #include <tqfontmetrics.h>
94 #include <tqpopupmenu.h>
95 
96 // other
97 #include <algorithm>
98 #include <assert.h>
99 #include <tdemacros.h>
100 #include <kinputdialog.h>
101 namespace {
102 
103  class TDE_EXPORT DisplayStrategy : public Kleo::KeyListView::DisplayStrategy{
104  public:
105  ~DisplayStrategy() {}
106 
107  virtual TQFont keyFont( const GpgME::Key& key, const TQFont& font ) const {
108  const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key );
109  return filter ? filter->font( font ) : font;
110  }
111  virtual TQColor keyForeground( const GpgME::Key& key, const TQColor& c ) const {
112  const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key );
113  if ( filter && filter->fgColor().isValid() )
114  return filter->fgColor();
115  return c;
116  }
117  virtual TQColor keyBackground( const GpgME::Key& key, const TQColor& c ) const {
118  const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key );
119  if ( filter && filter->bgColor().isValid() )
120  return filter->bgColor();
121  return c;
122  }
123  };
124 
125  class TDE_EXPORT ColumnStrategy : public Kleo::KeyListView::ColumnStrategy {
126  public:
127  ~ColumnStrategy() {}
128 
129  TQString title( int col ) const;
130  TQString text( const GpgME::Key & key, int col ) const;
131  int width( int col, const TQFontMetrics & fm ) const;
132  };
133 
134  TQString ColumnStrategy::title( int col ) const {
135  switch ( col ) {
136  case 0: return i18n("Subject");
137  case 1: return i18n("Issuer");
138  case 2: return i18n("Serial");
139  default: return TQString();
140  }
141  }
142 
143  TQString ColumnStrategy::text( const GpgME::Key & key, int col ) const {
144  switch ( col ) {
145  case 0: return Kleo::DN( key.userID(0).id() ).prettyDN();
146  case 1: return Kleo::DN( key.issuerName() ).prettyDN();
147  case 2: return key.issuerSerial() ? TQString::fromUtf8( key.issuerSerial() ) : TQString() ;
148  default: return TQString();
149  }
150  }
151 
152  int ColumnStrategy::width( int col, const TQFontMetrics & fm ) const {
153  int factor = -1;
154  switch ( col ) {
155  case 0: factor = 6; break;
156  case 1: factor = 4; break;
157  default: return -1;
158  }
159  return fm.width( title( col ) ) * factor;
160  }
161 } // anon namespace
162 
163 CertManager::CertManager( bool remote, const TQString& query, const TQString & import,
164  TQWidget* parent, const char* name, WFlags f )
165  : TDEMainWindow( parent, name, f|WDestructiveClose ),
166  mCrlView( 0 ),
167  mDirmngrProc( 0 ),
168  mHierarchyAnalyser( 0 ),
169  mLineEditAction( 0 ),
170  mComboAction( 0 ),
171  mFindAction( 0 ),
172  mImportCertFromFileAction( 0 ),
173  mImportCRLFromFileAction( 0 ),
174  mNextFindRemote( remote ),
175  mRemote( remote ),
176  mDirMngrFound( false )
177 {
178  readConfig( query.isEmpty() );
179  createStatusBar();
180  createActions();
181 
182  createGUI();
183  setAutoSaveSettings();
184 
185  // Main Window --------------------------------------------------
186  mKeyListView = new CertKeyListView( new ColumnStrategy(), new DisplayStrategy(), this, "mKeyListView" );
187  mKeyListView->setSelectionMode( TQListView::Extended );
188  setCentralWidget( mKeyListView );
189 
190  connect( mKeyListView, TQ_SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const TQPoint&,int)),
191  TQ_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) );
192  connect( mKeyListView, TQ_SIGNAL(returnPressed(Kleo::KeyListViewItem*)),
193  TQ_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) );
194  connect( mKeyListView, TQ_SIGNAL(selectionChanged()),
195  TQ_SLOT(slotSelectionChanged()) );
196  connect( mKeyListView, TQ_SIGNAL(contextMenu(Kleo::KeyListViewItem*, const TQPoint&)),
197  TQ_SLOT(slotContextMenu(Kleo::KeyListViewItem*, const TQPoint&)) );
198 
199  connect( mKeyListView, TQ_SIGNAL(dropped(const KURL::List&) ),
200  TQ_SLOT( slotDropped(const KURL::List&) ) );
201 
202  mLineEditAction->setText(query);
203  if ( !mRemote && !mNextFindRemote || !query.isEmpty() )
204  slotSearch();
205 
206  if ( !import.isEmpty() )
207  slotImportCertFromFile( KURL( import ) );
208 
209  slotToggleHierarchicalView( mHierarchicalView );
210  updateStatusBarLabels();
211  slotSelectionChanged(); // initial state for selection-dependent actions
212 }
213 
214 CertManager::~CertManager() {
215  writeConfig();
216  delete mDirmngrProc; mDirmngrProc = 0;
217  delete mHierarchyAnalyser; mHierarchyAnalyser = 0;
218 }
219 
220 void CertManager::readConfig( bool noQueryGiven ) {
221  TDEConfig config( "kleopatrarc" );
222  config.setGroup( "Display Options" );
223  mHierarchicalView = config.readBoolEntry( "hierarchicalView", false );
224  if ( noQueryGiven ) {
225  mNextFindRemote = config.readBoolEntry( "startInRemoteMode", false );
226  }
227 }
228 
229 void CertManager::writeConfig() {
230  TDEConfig config( "kleopatrarc" );
231  config.setGroup( "Display Options" );
232  config.writeEntry( "hierarchicalView", mKeyListView->hierarchical() );
233  config.writeEntry( "startInRemoteMode", mNextFindRemote );
234 }
235 
236 void CertManager::createStatusBar() {
237  KStatusBar * bar = statusBar();
238  mProgressBar = new Kleo::ProgressBar( bar, "mProgressBar" );
239  mProgressBar->reset();
240  mProgressBar->setFixedSize( TQSize( 100, mProgressBar->height() * 3 / 5 ) );
241  bar->addWidget( mProgressBar, 0, true );
242  mStatusLabel = new TQLabel( bar, "mStatusLabel" );
243  bar->addWidget( mStatusLabel, 1, false );
244 }
245 
246 static inline void connectEnableOperationSignal( TQObject * s, TQObject * d ) {
247  TQObject::connect( s, TQ_SIGNAL(enableOperations(bool)),
248  d, TQ_SLOT(setEnabled(bool)) );
249 }
250 
251 
252 void CertManager::createActions() {
253  TDEAction * action = 0;
254 
255  (void)KStdAction::quit( this, TQ_SLOT(close()), actionCollection() );
256 
257  action = KStdAction::redisplay( this, TQ_SLOT(slotRedisplay()), actionCollection() );
258  // work around the fact that the stdaction has no shortcut
259  TDEShortcut reloadShortcut = TDEStdAccel::shortcut(TDEStdAccel::Reload);
260  reloadShortcut.append(KKey(CTRL + Key_R));
261  action->setShortcut( reloadShortcut );
262 
263  connectEnableOperationSignal( this, action );
264 
265  action = new TDEAction( i18n("Stop Operation"), "process-stop", Key_Escape,
266  this, TQ_SIGNAL(stopOperations()),
267  actionCollection(), "view_stop_operations" );
268  action->setEnabled( false );
269 
270  (void) new TDEAction( i18n("New Key Pair..."), "document-new", 0,
271  this, TQ_SLOT(newCertificate()),
272  actionCollection(), "file_new_certificate" );
273 
274  connect( new TDEToggleAction( i18n("Hierarchical Key List"), 0,
275  actionCollection(), "view_hierarchical" ),
276  TQ_SIGNAL(toggled(bool)), TQ_SLOT(slotToggleHierarchicalView(bool)) );
277 
278  action = new TDEAction( i18n("Expand All"), 0, CTRL+Key_Period,
279  this, TQ_SLOT(slotExpandAll()),
280  actionCollection(), "view_expandall" );
281  action = new TDEAction( i18n("Collapse All"), 0, CTRL+Key_Comma,
282  this, TQ_SLOT(slotCollapseAll()),
283  actionCollection(), "view_collapseall" );
284 
285  (void) new TDEAction( i18n("Refresh CRLs"), 0, 0,
286  this, TQ_SLOT(slotRefreshKeys()),
287  actionCollection(), "certificates_refresh_clr" );
288 
289 #ifdef NOT_IMPLEMENTED_ANYWAY
290  mRevokeCertificateAction = new TDEAction( i18n("Revoke"), 0,
291  this, TQ_SLOT(revokeCertificate()),
292  actionCollection(), "edit_revoke_certificate" );
293  connectEnableOperationSignal( this, mRevokeCertificateAction );
294 
295  mExtendCertificateAction = new TDEAction( i18n("Extend"), 0,
296  this, TQ_SLOT(extendCertificate()),
297  actionCollection(), "edit_extend_certificate" );
298  connectEnableOperationSignal( this, mExtendCertificateAction );
299 #endif
300 
301  mDeleteCertificateAction = new TDEAction( i18n("Delete"), "edit-delete", Key_Delete,
302  this, TQ_SLOT(slotDeleteCertificate()),
303  actionCollection(), "edit_delete_certificate" );
304  connectEnableOperationSignal( this, mDeleteCertificateAction );
305 
306  mValidateCertificateAction = new TDEAction( i18n("Validate"), "reload", SHIFT + Key_F5,
307  this, TQ_SLOT(slotValidate()),
308  actionCollection(), "certificates_validate" );
309  connectEnableOperationSignal( this, mValidateCertificateAction );
310 
311  mImportCertFromFileAction = new TDEAction( i18n("Import Certificates..."), 0,
312  this, TQ_SLOT(slotImportCertFromFile()),
313  actionCollection(), "file_import_certificates" );
314  connectEnableOperationSignal( this, mImportCertFromFileAction );
315 
316  mImportCRLFromFileAction = new TDEAction( i18n("Import CRLs..."), 0,
317  this, TQ_SLOT(importCRLFromFile()),
318  actionCollection(), "file_import_crls" );
319  connectEnableOperationSignal( this, mImportCRLFromFileAction );
320 
321  mExportCertificateAction = new TDEAction( i18n("Export Certificates..."), "export", 0,
322  this, TQ_SLOT(slotExportCertificate()),
323  actionCollection(), "file_export_certificate" );
324 
325  mExportSecretKeyAction = new TDEAction( i18n("Export Secret Key..."), "export", 0,
326  this, TQ_SLOT(slotExportSecretKey()),
327  actionCollection(), "file_export_secret_keys" );
328  connectEnableOperationSignal( this, mExportSecretKeyAction );
329 
330  mViewCertDetailsAction = new TDEAction( i18n("Certificate Details..."), 0, 0,
331  this, TQ_SLOT(slotViewDetails()), actionCollection(),
332  "view_certificate_details" );
333  mDownloadCertificateAction = new TDEAction( i18n( "Download"), 0, 0,
334  this, TQ_SLOT(slotDownloadCertificate()), actionCollection(),
335  "download_certificate" );
336 
337  const TQString dirmngr = TDEStandardDirs::findExe( "gpgsm" );
338  mDirMngrFound = !dirmngr.isEmpty();
339 
340  action = new TDEAction( i18n("Dump CRL Cache..."), 0,
341  this, TQ_SLOT(slotViewCRLs()),
342  actionCollection(), "crl_dump_crl_cache" );
343  action->setEnabled( mDirMngrFound ); // we also need dirmngr for this
344 
345  action = new TDEAction( i18n("Clear CRL Cache..."), 0,
346  this, TQ_SLOT(slotClearCRLs()),
347  actionCollection(), "crl_clear_crl_cache" );
348  action->setEnabled( mDirMngrFound ); // we also need dirmngr for this
349 
350  action = new TDEAction( i18n("GnuPG Log Viewer..."), "pgp-keys", 0, this,
351  TQ_SLOT(slotStartWatchGnuPG()), actionCollection(), "tools_start_kwatchgnupg");
352  // disable action if no kwatchgnupg binary is around
353  if (TDEStandardDirs::findExe("kwatchgnupg").isEmpty()) action->setEnabled(false);
354 
355  (void)new LabelAction( i18n("Search:"), actionCollection(), "label_action" );
356 
357  mLineEditAction = new LineEditAction( TQString(), actionCollection(), this,
358  TQ_SLOT(slotSearch()),
359  "query_lineedit_action");
360 
361  TQStringList lst;
362  lst << i18n("In Local Certificates") << i18n("In External Certificates");
363  mComboAction = new ComboAction( lst, actionCollection(), this, TQ_SLOT( slotToggleRemote(int) ),
364  "location_combo_action", mNextFindRemote? 1 : 0 );
365 
366  mFindAction = new TDEAction( i18n("Find"), "edit-find", 0, this, TQ_SLOT(slotSearch()),
367  actionCollection(), "find" );
368 
369  KStdAction::keyBindings( this, TQ_SLOT(slotEditKeybindings()), actionCollection() );
370  KStdAction::preferences( this, TQ_SLOT(slotShowConfigurationDialog()), actionCollection() );
371 
372  new TDEAction( i18n( "Configure &GpgME Backend" ), 0, 0, this, TQ_SLOT(slotConfigureGpgME()),
373  actionCollection(), "configure_gpgme" );
374 
375  createStandardStatusBarAction();
376  updateImportActions( true );
377 }
378 
379 void CertManager::updateImportActions( bool enable ) {
380  mImportCRLFromFileAction->setEnabled( mDirMngrFound && enable );
381  mImportCertFromFileAction->setEnabled( enable );
382 }
383 
384 void CertManager::slotEditKeybindings() {
385  KKeyDialog::configure( actionCollection(), true );
386 }
387 
388 void CertManager::slotShowConfigurationDialog() {
389  ConfigureDialog dlg( this );
390  connect( &dlg, TQ_SIGNAL( configCommitted() ), TQ_SLOT( slotRepaint() ) );
391  dlg.exec();
392 }
393 
394 void CertManager::slotConfigureGpgME() {
395  Kleo::CryptoConfig* config = Kleo::CryptoBackendFactory::instance()->config();
396  if ( config ) {
397  Kleo::CryptoConfigDialog dlg( config );
398 
399  int result = dlg.exec();
400 
401  // Forget all data parsed from gpgconf, so that we show updated information
402  // when reopening the configuration dialog.
403  config->clear();
404 
405  if ( result == TQDialog::Accepted )
406  {
407  // Tell other apps (e.g. kmail) that the gpgconf data might have changed
408  kapp->dcopClient()->emitDCOPSignal( "KPIM::CryptoConfig", "changed()", TQByteArray() );
409  }
410  }
411 }
412 
413 void CertManager::slotRepaint()
414 {
415  mKeyListView->repaintContents();
416 }
417 
418 void CertManager::slotToggleRemote( int idx ) {
419  mNextFindRemote = idx != 0;
420 }
421 
422 void CertManager::slotToggleHierarchicalView( bool hier ) {
423  mHierarchicalView = hier;
424  mKeyListView->setHierarchical( hier );
425  mKeyListView->setRootIsDecorated( hier );
426  if ( TDEAction * act = action("view_expandall") )
427  act->setEnabled( hier );
428  if ( TDEAction * act = action("view_collapseall" ) )
429  act->setEnabled( hier );
430  if ( TDEToggleAction * act =
431  static_cast<TDEToggleAction*>( action("view_hierarchical") ) )
432  act->setChecked( hier );
433 
434  if ( hier && !mCurrentQuery.isEmpty() )
435  startRedisplay( false );
436 }
437 
438 void CertManager::slotExpandAll() {
439  for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
440  it.current()->setOpen( true );
441 }
442 
443 void CertManager::slotCollapseAll() {
444  for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
445  it.current()->setOpen( false );
446 }
447 
448 void CertManager::connectJobToStatusBarProgress( Kleo::Job * job, const TQString & initialText ) {
449  assert( mProgressBar );
450  if ( !job )
451  return;
452  if ( !initialText.isEmpty() )
453  statusBar()->message( initialText );
454  connect( job, TQ_SIGNAL(progress(const TQString&,int,int)),
455  mProgressBar, TQ_SLOT(slotProgress(const TQString&,int,int)) );
456  connect( job, TQ_SIGNAL(done()), mProgressBar, TQ_SLOT(reset()) );
457  connect( this, TQ_SIGNAL(stopOperations()), job, TQ_SLOT(slotCancel()) );
458 
459  action("view_stop_operations")->setEnabled( true );
460  emit enableOperations( false );
461 }
462 
463 void CertManager::disconnectJobFromStatusBarProgress( const GpgME::Error & err ) {
464  updateStatusBarLabels();
465  const TQString msg = err.isCanceled() ? i18n("Canceled.")
466  : err ? i18n("Failed.")
467  : i18n("Done.") ;
468  statusBar()->message( msg, 4000 );
469 
470  action("view_stop_operations")->setEnabled( false );
471  emit enableOperations( true );
472  slotSelectionChanged();
473 }
474 
475 void CertManager::updateStatusBarLabels() {
476  mKeyListView->flushKeys();
477  int total = 0;
478  for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
479  ++total;
480  mStatusLabel->setText( i18n( "%n Key.","%n Keys.", total ) );
481 }
482 
483 //
484 //
485 // Key Listing:
486 //
487 //
488 
489 
490 static std::set<std::string> extractKeyFingerprints( const TQPtrList<Kleo::KeyListViewItem> & items ) {
491  std::set<std::string> result;
492  for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it )
493  if ( const char * fpr = it.current()->key().primaryFingerprint() )
494  result.insert( fpr );
495  return result;
496 }
497 
498 static TQStringList stringlistFromSet( const std::set<std::string> & set ) {
499  // ARGH. This is madness. Shitty TQt containers don't support TQStringList( patterns.begin(), patterns.end() ) :/
500  TQStringList sl;
501  for ( std::set<std::string>::const_iterator it = set.begin() ; it != set.end() ; ++it )
502  // let's make extra sure, maybe someone tries to make TQt not support std::string->TQString conversion
503  sl.push_back( TQString::fromLatin1( it->c_str() ) );
504  return sl;
505 }
506 
507 void CertManager::slotRefreshKeys() {
508  const TQStringList keys = stringlistFromSet( extractKeyFingerprints( mKeyListView->selectedItems() ) );
509  Kleo::RefreshKeysJob * job = Kleo::CryptoBackendFactory::instance()->smime()->refreshKeysJob();
510  assert( job );
511 
512  connect( job, TQ_SIGNAL(result(const GpgME::Error&)),
513  this, TQ_SLOT(slotRefreshKeysResult(const GpgME::Error&)) );
514 
515  connectJobToStatusBarProgress( job, i18n("Refreshing keys...") );
516  if ( const GpgME::Error err = job->start( keys ) )
517  slotRefreshKeysResult( err );
518 }
519 
520 void CertManager::slotRefreshKeysResult( const GpgME::Error & err ) {
521  disconnectJobFromStatusBarProgress( err );
522  if ( err.isCanceled() )
523  return;
524  if ( err )
525  KMessageBox::error( this, i18n("An error occurred while trying to refresh "
526  "keys:\n%1").arg( TQString::fromLocal8Bit( err.asString() ) ),
527  i18n("Refreshing Keys Failed") );
528 }
529 
530 static void showKeyListError( TQWidget * parent, const GpgME::Error & err ) {
531  assert( err );
532  const TQString msg = i18n( "<qt><p>An error occurred while fetching "
533  "the certificates from the backend:</p>"
534  "<p><b>%1</b></p></qt>" )
535  .arg( TQString::fromLocal8Bit( err.asString() ) );
536 
537  KMessageBox::error( parent, msg, i18n( "Certificate Listing Failed" ) );
538 }
539 
540 void CertManager::slotSearch() {
541  mPreviouslySelectedFingerprints.clear();
542  // Clear display
543  mKeyListView->clear();
544  mCurrentQuery = mLineEditAction->text();
545  startKeyListing( false, false, mCurrentQuery );
546 }
547 
548 void CertManager::startRedisplay( bool validate ) {
549  mPreviouslySelectedFingerprints = extractKeyFingerprints( mKeyListView->selectedItems() );
550  if ( mPreviouslySelectedFingerprints.empty() )
551  startKeyListing( validate, true, mCurrentQuery );
552  else
553  startKeyListing( validate, true, mPreviouslySelectedFingerprints );
554 }
555 
556 void CertManager::startKeyListing( bool validating, bool refresh, const std::set<std::string> & patterns ) {
557  startKeyListing( validating, refresh, stringlistFromSet( patterns ) );
558 }
559 
560 void CertManager::startKeyListing( bool validating, bool refresh, const TQStringList & patterns ) {
561  mRemote = mNextFindRemote;
562  mLineEditAction->setEnabled( false );
563  mComboAction->setEnabled( false );
564  mFindAction->setEnabled( false );
565 
566  Kleo::KeyListJob * job = 0;
567  if ( !validating && !refresh && mKeyListView->hierarchical() && !patterns.empty() )
568  job = new Kleo::HierarchicalKeyListJob( Kleo::CryptoBackendFactory::instance()->smime(),
569  mRemote, false, validating );
570  else
571  job = Kleo::CryptoBackendFactory::instance()->smime()->keyListJob( mRemote, false, validating );
572  assert( job );
573 
574  connect( job, TQ_SIGNAL(nextKey(const GpgME::Key&)),
575  mKeyListView, refresh ? TQ_SLOT(slotRefreshKey(const GpgME::Key&)) : TQ_SLOT(slotAddKey(const GpgME::Key&)) );
576  connect( job, TQ_SIGNAL(result(const GpgME::KeyListResult&)),
577  this, TQ_SLOT(slotKeyListResult(const GpgME::KeyListResult&)) );
578 
579  connectJobToStatusBarProgress( job, i18n("Fetching keys...") );
580 
581  const GpgME::Error err = job->start( patterns ) ;
582  if ( err ) {
583  showKeyListError( this, err );
584  return;
585  }
586  mProgressBar->setProgress( 0, 0 ); // enable busy indicator
587 }
588 
589 static void selectKeys( Kleo::KeyListView * lv, const std::set<std::string> & fprs ) {
590  if ( !lv || fprs.empty() )
591  return;
592  for ( TQListViewItemIterator it( lv ) ; it.current() ; ++it )
593  if ( Kleo::KeyListViewItem * item = Kleo::lvi_cast<Kleo::KeyListViewItem>( it.current() ) ) {
594  const char * fpr = item->key().primaryFingerprint();
595  item->setSelected( fpr && fprs.find( fpr ) != fprs.end() );
596  }
597 }
598 
599 void CertManager::slotKeyListResult( const GpgME::KeyListResult & res ) {
600  if ( res.error() )
601  showKeyListError( this, res.error() );
602  else if ( res.isTruncated() )
603  KMessageBox::information( this,
604  i18n("The query result has been truncated.\n"
605  "Either the local or a remote limit on "
606  "the maximum number of returned hits has "
607  "been exceeded.\n"
608  "You can try to increase the local limit "
609  "in the configuration dialog, but if one "
610  "of the configured servers is the limiting "
611  "factor, you have to refine your search.") );
612 
613  mLineEditAction->setEnabled( true );
614  mComboAction->setEnabled( true );
615  mFindAction->setEnabled( true );
616 
617  mLineEditAction->focusAll();
618  disconnectJobFromStatusBarProgress( res.error() );
619  selectKeys( mKeyListView, mPreviouslySelectedFingerprints );
620 }
621 
622 void CertManager::slotContextMenu(Kleo::KeyListViewItem* item, const TQPoint& point) {
623  if ( !item )
624  return;
625  if ( TQPopupMenu * popup = static_cast<TQPopupMenu*>(factory()->container("listview_popup",this)) )
626  popup->exec( point );
627 }
628 
632 void CertManager::newCertificate()
633 {
634  CertificateWizardImpl wizard( this );
635  wizard.exec();
636 }
637 
642 void CertManager::revokeCertificate()
643 {
644  tqDebug("Not Yet Implemented");
645 }
646 
651 void CertManager::extendCertificate()
652 {
653  tqDebug("Not Yet Implemented");
654 }
655 
656 
657 //
658 //
659 // Downloading / Importing Certificates
660 //
661 //
662 
663 
667 void CertManager::slotImportCertFromFile()
668 {
669  const TQString filter = "application/x-x509-ca-cert application/x-pkcs12 application/pkcs7-mime";
670  //const TQString filter = TQString("*.pem *.der *.p7c *.p12|") + i18n("Certificates (*.pem *.der *.p7c *.p12)");
671  slotImportCertFromFile( KFileDialog::getOpenURL( TQString(), filter, this,
672  i18n( "Select Certificate File" ) ) );
673 }
674 
675 void CertManager::slotImportCertFromFile( const KURL & certURL )
676 {
677  if ( !certURL.isValid() ) // empty or malformed
678  return;
679 
680  mPreviouslySelectedFingerprints.clear();
681 
682  // Prevent two simultaneous imports
683  updateImportActions( false );
684 
685  // Download the cert
686  TDEIOext::StoredTransferJob* importJob = TDEIOext::storedGet( certURL );
687  importJob->setWindow( this );
688  connect( importJob, TQ_SIGNAL(result(TDEIO::Job*)), TQ_SLOT(slotImportResult(TDEIO::Job*)) );
689 }
690 
691 void CertManager::slotImportResult( TDEIO::Job* job )
692 {
693  if ( job->error() ) {
694  job->showErrorDialog();
695  } else {
696  TDEIOext::StoredTransferJob* trJob = static_cast<TDEIOext::StoredTransferJob *>( job );
697  startCertificateImport( trJob->data(), trJob->url().fileName() );
698  }
699 
700  updateImportActions( true );
701 }
702 
703 static void showCertificateDownloadError( TQWidget * parent, const GpgME::Error & err, const TQString& certDisplayName ) {
704  assert( err );
705  const TQString msg = i18n( "<qt><p>An error occurred while trying "
706  "to download the certificate %1:</p>"
707  "<p><b>%2</b></p></qt>" )
708  .arg( certDisplayName )
709  .arg( TQString::fromLocal8Bit( err.asString() ) );
710 
711  KMessageBox::error( parent, msg, i18n( "Certificate Download Failed" ) );
712 }
713 
714 void CertManager::slotDownloadCertificate() {
715  mPreviouslySelectedFingerprints.clear();
716  TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems();
717  for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it )
718  if ( !it.current()->key().isNull() )
719  if ( const char * fpr = it.current()->key().primaryFingerprint() )
720  slotStartCertificateDownload( fpr, it.current()->text(0) );
721 }
722 
723 // Called from slotDownloadCertificate and from the certificate-details widget
724 void CertManager::slotStartCertificateDownload( const TQString& fingerprint, const TQString& displayName ) {
725  if ( fingerprint.isEmpty() )
726  return;
727 
728  Kleo::DownloadJob * job =
729  Kleo::CryptoBackendFactory::instance()->smime()->downloadJob( false /* no armor */ );
730  assert( job );
731 
732  connect( job, TQ_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
733  TQ_SLOT(slotCertificateDownloadResult(const GpgME::Error&,const TQByteArray&)) );
734 
735  connectJobToStatusBarProgress( job, i18n("Fetching certificate from server...") );
736 
737  const GpgME::Error err = job->start( fingerprint );
738  if ( err )
739  showCertificateDownloadError( this, err, displayName );
740  else {
741  mProgressBar->setProgress( 0, 0 );
742  mJobsDisplayNameMap.insert( job, displayName );
743  }
744 }
745 
746 TQString CertManager::displayNameForJob( const Kleo::Job *job )
747 {
748  JobsDisplayNameMap::iterator it = mJobsDisplayNameMap.find( job );
749  TQString displayName;
750  if ( it != mJobsDisplayNameMap.end() ) {
751  displayName = *it;
752  mJobsDisplayNameMap.remove( it );
753  } else {
754  kdWarning() << "Job not found in map: " << job << endl;
755  }
756  return displayName;
757 }
758 
759 // Don't call directly!
760 void CertManager::slotCertificateDownloadResult( const GpgME::Error & err, const TQByteArray & keyData ) {
761 
762  TQString displayName = displayNameForJob( static_cast<const Kleo::Job *>( sender() ) );
763 
764  if ( err )
765  showCertificateDownloadError( this, err, displayName );
766  else
767  startCertificateImport( keyData, displayName );
768  disconnectJobFromStatusBarProgress( err );
769 }
770 
771 static void showCertificateImportError( TQWidget * parent, const GpgME::Error & err, const TQString& certDisplayName ) {
772  assert( err );
773  const TQString msg = i18n( "<qt><p>An error occurred while trying "
774  "to import the certificate %1:</p>"
775  "<p><b>%2</b></p></qt>" )
776  .arg( certDisplayName )
777  .arg( TQString::fromLocal8Bit( err.asString() ) );
778  KMessageBox::error( parent, msg, i18n( "Certificate Import Failed" ) );
779 }
780 
781 void CertManager::startCertificateImport( const TQByteArray & keyData, const TQString& certDisplayName ) {
782  Kleo::ImportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->importJob();
783  assert( job );
784 
785  connect( job, TQ_SIGNAL(result(const GpgME::ImportResult&)),
786  TQ_SLOT(slotCertificateImportResult(const GpgME::ImportResult&)) );
787 
788  connectJobToStatusBarProgress( job, i18n("Importing certificates...") );
789 
790  kdDebug() << "Importing certificate. keyData size:" << keyData.size() << endl;
791  const GpgME::Error err = job->start( keyData );
792  if ( err )
793  showCertificateImportError( this, err, certDisplayName );
794  else {
795  mProgressBar->setProgress( 0, 0 );
796  mJobsDisplayNameMap.insert( job, certDisplayName );
797  }
798 }
799 
800 void CertManager::slotCertificateImportResult( const GpgME::ImportResult & res ) {
801  TQString displayName = displayNameForJob( static_cast<const Kleo::Job *>( sender() ) );
802 
803  if ( res.error().isCanceled() ) {
804  // do nothing
805  } else if ( res.error() ) {
806  showCertificateImportError( this, res.error(), displayName );
807  } else {
808 
809  const TQString normalLine = i18n("<tr><td align=\"right\">%1</td><td>%2</td></tr>");
810  const TQString boldLine = i18n("<tr><td align=\"right\"><b>%1</b></td><td>%2</td></tr>");
811 
812  TQStringList lines;
813  lines.push_back( normalLine.arg( i18n("Total number processed:"),
814  TQString::number( res.numConsidered() ) ) );
815  lines.push_back( normalLine.arg( i18n("Imported:"),
816  TQString::number( res.numImported() ) ) );
817  if ( res.newSignatures() )
818  lines.push_back( normalLine.arg( i18n("New signatures:"),
819  TQString::number( res.newSignatures() ) ) );
820  if ( res.newUserIDs() )
821  lines.push_back( normalLine.arg( i18n("New user IDs:"),
822  TQString::number( res.newUserIDs() ) ) );
823  if ( res.numKeysWithoutUserID() )
824  lines.push_back( normalLine.arg( i18n("Keys without user IDs:"),
825  TQString::number( res.numKeysWithoutUserID() ) ) );
826  if ( res.newSubkeys() )
827  lines.push_back( normalLine.arg( i18n("New subkeys:"),
828  TQString::number( res.newSubkeys() ) ) );
829  if ( res.newRevocations() )
830  lines.push_back( boldLine.arg( i18n("Newly revoked:"),
831  TQString::number( res.newRevocations() ) ) );
832  if ( res.notImported() )
833  lines.push_back( boldLine.arg( i18n("Not imported:"),
834  TQString::number( res.notImported() ) ) );
835  if ( res.numUnchanged() )
836  lines.push_back( normalLine.arg( i18n("Unchanged:"),
837  TQString::number( res.numUnchanged() ) ) );
838  if ( res.numSecretKeysConsidered() )
839  lines.push_back( normalLine.arg( i18n("Secret keys processed:"),
840  TQString::number( res.numSecretKeysConsidered() ) ) );
841  if ( res.numSecretKeysImported() )
842  lines.push_back( normalLine.arg( i18n("Secret keys imported:"),
843  TQString::number( res.numSecretKeysImported() ) ) );
844  if ( res.numSecretKeysConsidered() - res.numSecretKeysImported() - res.numSecretKeysUnchanged() > 0 )
845  lines.push_back( boldLine.arg( i18n("Secret keys <em>not</em> imported:"),
846  TQString::number( res.numSecretKeysConsidered()
847  - res.numSecretKeysImported()
848  - res.numSecretKeysUnchanged() ) ) );
849  if ( res.numSecretKeysUnchanged() )
850  lines.push_back( normalLine.arg( i18n("Secret keys unchanged:"),
851  TQString::number( res.numSecretKeysUnchanged() ) ) );
852 
853  KMessageBox::information( this,
854  i18n( "<qt><p>Detailed results of importing %1:</p>"
855  "<table>%2</table></qt>" )
856  .arg( displayName ).arg( lines.join( TQString() ) ),
857  i18n( "Certificate Import Result" ) );
858 
859  disconnectJobFromStatusBarProgress( res.error() );
860  // save the fingerprints of imported certs for later selection:
861  const std::vector<GpgME::Import> imports = res.imports();
862  for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it )
863  mPreviouslySelectedFingerprints.insert( it->fingerprint() );
864  }
865  importNextURLOrRedisplay();
866 }
867 
868 
869 
874 void CertManager::slotDirmngrExited() {
875  if ( !mDirmngrProc->normalExit() )
876  KMessageBox::error( this, i18n( "The GpgSM process that tried to import the CRL file ended prematurely because of an unexpected error." ), i18n( "Certificate Manager Error" ) );
877  else if ( mDirmngrProc->exitStatus() )
878  KMessageBox::error( this, i18n( "An error occurred when trying to import the CRL file. The output from GpgSM was:\n%1").arg( mErrorbuffer ), i18n( "Certificate Manager Error" ) );
879  else
880  KMessageBox::information( this, i18n( "CRL file imported successfully." ), i18n( "Certificate Manager Information" ) );
881 
882  delete mDirmngrProc; mDirmngrProc = 0;
883  if ( !mImportCRLTempFile.isEmpty() )
884  TQFile::remove( mImportCRLTempFile );
885  updateImportActions( true );
886 }
887 
891 void CertManager::importCRLFromFile() {
892  // loadcrl can only work with DER encoded files (verified with dirmngr 1.0.3)
893  TQString filter = TQString("*.crl *.arl *-crl.der *-arl.der|") + i18n("Certificate Revocation List, DER encoded (*.crl *.arl *-crl.der *-arl.der)");
894  KURL url = KFileDialog::getOpenURL( TQString(),
895  filter,
896  this,
897  i18n( "Select CRL File" ) );
898  if ( url.isValid() ) {
899  updateImportActions( false );
900  if ( url.isLocalFile() ) {
901  startImportCRL( url.path(), false );
902  updateImportActions( true );
903  } else {
904  KTempFile tempFile;
905  KURL destURL;
906  destURL.setPath( tempFile.name() );
907  TDEIO::Job* copyJob = TDEIO::file_copy( url, destURL, 0600, true, false );
908  copyJob->setWindow( this );
909  connect( copyJob, TQ_SIGNAL( result( TDEIO::Job * ) ),
910  TQ_SLOT( slotImportCRLJobFinished( TDEIO::Job * ) ) );
911  }
912  }
913 }
914 
915 void CertManager::slotImportCRLJobFinished( TDEIO::Job *job )
916 {
917  TDEIO::FileCopyJob* fcjob = static_cast<TDEIO::FileCopyJob*>( job );
918  TQString tempFilePath = fcjob->destURL().path();
919  if ( job->error() ) {
920  job->showErrorDialog();
921  TQFile::remove( tempFilePath ); // unlink tempfile
922  updateImportActions( true );
923  return;
924  }
925  startImportCRL( tempFilePath, true );
926 }
927 
928 bool CertManager::connectAndStartDirmngr( const char * slot, const char * processname ) {
929  assert( slot );
930  assert( processname );
931  assert( mDirmngrProc );
932  mErrorbuffer = TQString();
933  connect( mDirmngrProc, TQ_SIGNAL(processExited(TDEProcess*)), slot );
934  connect( mDirmngrProc, TQ_SIGNAL(receivedStderr(TDEProcess*,char*,int) ),
935  this, TQ_SLOT(slotStderr(TDEProcess*,char*,int)) );
936  if( !mDirmngrProc->start( TDEProcess::NotifyOnExit, TDEProcess::Stderr ) ) {
937  delete mDirmngrProc; mDirmngrProc = 0;
938  KMessageBox::error( this, i18n( "Unable to start %1 process. Please check your installation." ).arg( processname ), i18n( "Certificate Manager Error" ) );
939  return false;
940  }
941  return true;
942 }
943 
944 void CertManager::startImportCRL( const TQString& filename, bool isTempFile )
945 {
946  assert( !mDirmngrProc );
947  mImportCRLTempFile = isTempFile ? filename : TQString();
948  mDirmngrProc = new TDEProcess();
949  *mDirmngrProc << "gpgsm" << "--call-dirmngr" << "loadcrl" << filename;
950  if ( !connectAndStartDirmngr( TQ_SLOT(slotDirmngrExited()), "gpgsm" ) ) {
951  updateImportActions( true );
952  if ( isTempFile )
953  TQFile::remove( mImportCRLTempFile ); // unlink tempfile
954  }
955 }
956 
957 void CertManager::startClearCRLs() {
958  assert( !mDirmngrProc );
959  mDirmngrProc = new TDEProcess();
960  *mDirmngrProc << "dirmngr" << "--flush";
961  //*mDirmngrProc << "gpgsm" << "--call-dimngr" << "flush"; // use this once it's implemented!
962  connectAndStartDirmngr( TQ_SLOT(slotClearCRLsResult()), "dirmngr" );
963 }
964 
965 void CertManager::slotStderr( TDEProcess*, char* buf, int len ) {
966  mErrorbuffer += TQString::fromLocal8Bit( buf, len );
967 }
968 
972 void CertManager::importCRLFromLDAP()
973 {
974  tqDebug("Not Yet Implemented");
975 }
976 
977 void CertManager::slotViewCRLs() {
978  if ( !mCrlView )
979  mCrlView = new CRLView( this );
980 
981  mCrlView->show();
982  mCrlView->slotUpdateView();
983 }
984 
985 
986 void CertManager::slotClearCRLs() {
987  startClearCRLs();
988 }
989 
990 void CertManager::slotClearCRLsResult() {
991  assert( mDirmngrProc );
992  if ( !mDirmngrProc->normalExit() )
993  KMessageBox::error( this, i18n( "The DirMngr process that tried to clear the CRL cache ended prematurely because of an unexpected error." ), i18n( "Certificate Manager Error" ) );
994  else if ( mDirmngrProc->exitStatus() )
995  KMessageBox::error( this, i18n( "An error occurred when trying to clear the CRL cache. The output from DirMngr was:\n%1").arg( mErrorbuffer ), i18n( "Certificate Manager Error" ) );
996  else
997  KMessageBox::information( this, i18n( "CRL cache cleared successfully." ), i18n( "Certificate Manager Information" ) );
998  delete mDirmngrProc; mDirmngrProc = 0;
999 }
1000 
1001 static void showDeleteError( TQWidget * parent, const GpgME::Error & err ) {
1002  assert( err );
1003  const TQString msg = i18n("<qt><p>An error occurred while trying to delete "
1004  "the certificates:</p>"
1005  "<p><b>%1</b></p></qt>")
1006  .arg( TQString::fromLocal8Bit( err.asString() ) );
1007  KMessageBox::error( parent, msg, i18n("Certificate Deletion Failed") );
1008 }
1009 
1010 static bool ByFingerprint( const GpgME::Key & left, const GpgME::Key & right ) {
1011  return tqstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) < 0 ;
1012 }
1013 
1014 static bool WithRespectToFingerprints( const GpgME::Key & left, const GpgME::Key & right ) {
1015  return tqstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) == 0;
1016 }
1017 
1018 void CertManager::slotDeleteCertificate() {
1019  mItemsToDelete = mKeyListView->selectedItems();
1020  if ( mItemsToDelete.isEmpty() )
1021  return;
1022  std::vector<GpgME::Key> keys;
1023  keys.reserve( mItemsToDelete.count() );
1024  TQStringList keyDisplayNames;
1025  for ( TQPtrListIterator<Kleo::KeyListViewItem> it( mItemsToDelete ) ; it.current() ; ++it )
1026  if ( !it.current()->key().isNull() ) {
1027  keys.push_back( it.current()->key() );
1028  keyDisplayNames.push_back( it.current()->text( 0 ) );
1029  }
1030  if ( keys.empty() )
1031  return;
1032 
1033  if ( !mHierarchyAnalyser ) {
1034  mHierarchyAnalyser = new HierarchyAnalyser( this, "mHierarchyAnalyser" );
1035  Kleo::KeyListJob * job = Kleo::CryptoBackendFactory::instance()->smime()->keyListJob();
1036  assert( job );
1037  connect( job, TQ_SIGNAL(nextKey(const GpgME::Key&)),
1038  mHierarchyAnalyser, TQ_SLOT(slotNextKey(const GpgME::Key&)) );
1039  connect( job, TQ_SIGNAL(result(const GpgME::KeyListResult&)),
1040  this, TQ_SLOT(slotDeleteCertificate()) );
1041  connectJobToStatusBarProgress( job, i18n("Checking key dependencies...") );
1042  if ( const GpgME::Error error = job->start( TQStringList() ) ) {
1043  showKeyListError( this, error );
1044  delete mHierarchyAnalyser; mHierarchyAnalyser = 0;
1045  }
1046  return;
1047  } else
1048  disconnectJobFromStatusBarProgress( 0 );
1049 
1050  std::vector<GpgME::Key> keysToDelete = keys;
1051  for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it )
1052  if ( !it->isNull() ) {
1053  const std::vector<GpgME::Key> subjects
1054  = mHierarchyAnalyser->subjectsForIssuerRecursive( it->primaryFingerprint() );
1055  keysToDelete.insert( keysToDelete.end(), subjects.begin(), subjects.end() );
1056  }
1057 
1058  std::sort( keysToDelete.begin(), keysToDelete.end(), ByFingerprint );
1059  keysToDelete.erase( std::unique( keysToDelete.begin(), keysToDelete.end(),
1060  WithRespectToFingerprints ),
1061  keysToDelete.end() );
1062 
1063  delete mHierarchyAnalyser; mHierarchyAnalyser = 0;
1064 
1065  if ( keysToDelete.size() > keys.size() )
1066  if ( KMessageBox::warningContinueCancel( this,
1067  i18n("Some or all of the selected "
1068  "certificates are issuers (CA certificates) "
1069  "for other, non-selected certificates.\n"
1070  "Deleting a CA certificate will also delete "
1071  "all certificates issued by it."),
1072  i18n("Deleting CA Certificates") )
1073  != KMessageBox::Continue )
1074  return;
1075 
1076  const TQString msg = keysToDelete.size() > keys.size()
1077  ? i18n("Do you really want to delete this certificate and the %1 certificates it certified?",
1078  "Do you really want to delete these %n certificates and the %1 certificates they certified?",
1079  keys.size() ).arg( keysToDelete.size() - keys.size() )
1080  : i18n("Do you really want to delete this certificate?",
1081  "Do you really want to delete these %n certificates?", keys.size() ) ;
1082 
1083  if ( KMessageBox::warningContinueCancelList( this, msg, keyDisplayNames,
1084  i18n( "Delete Certificates" ),
1085  KGuiItem( i18n( "Delete" ), "edit-delete" ),
1086  "ConfirmDeleteCert", KMessageBox::Dangerous )
1087  != KMessageBox::Continue )
1088  return;
1089 
1090  if ( Kleo::DeleteJob * job = Kleo::CryptoBackendFactory::instance()->smime()->deleteJob() )
1091  job->slotCancel();
1092  else {
1093  TQString str = keys.size() == 1
1094  ? i18n("<qt><p>An error occurred while trying to delete "
1095  "the certificate:</p>"
1096  "<p><b>%1</b><p></qt>" )
1097  : i18n( "<qt><p>An error occurred while trying to delete "
1098  "the certificates:</p>"
1099  "<p><b>%1</b><p></qt>" );
1100  KMessageBox::error( this,
1101  str.arg( i18n("Operation not supported by the backend.") ),
1102  i18n("Certificate Deletion Failed") );
1103  }
1104 
1105  mItemsToDelete.clear(); // re-create according to the real selection
1106  for ( std::vector<GpgME::Key>::const_iterator it = keysToDelete.begin() ; it != keysToDelete.end() ; ++it )
1107  if ( Kleo::KeyListViewItem * item = mKeyListView->itemByFingerprint( it->primaryFingerprint() ) )
1108  mItemsToDelete.append( item );
1109 
1110  Kleo::MultiDeleteJob * job = new Kleo::MultiDeleteJob( Kleo::CryptoBackendFactory::instance()->smime() );
1111  assert( job );
1112 
1113  connect( job, TQ_SIGNAL(result(const GpgME::Error&,const GpgME::Key&)),
1114  TQ_SLOT(slotDeleteResult(const GpgME::Error&,const GpgME::Key&)) );
1115 
1116  connectJobToStatusBarProgress( job, i18n("Deleting keys...") );
1117 
1118  const GpgME::Error err = job->start( keys, true );
1119  if ( err )
1120  showDeleteError( this, err );
1121  else
1122  mProgressBar->setProgress( 0, 0 );
1123 }
1124 
1125 void CertManager::slotDeleteResult( const GpgME::Error & err, const GpgME::Key & ) {
1126  if ( err )
1127  showDeleteError( this, err );
1128  else {
1129  const int infinity = 100; // infinite loop guard...
1130  mItemsToDelete.setAutoDelete( true );
1131  for ( int i = 0 ; i < infinity ; ++i ) {
1132  TQPtrListIterator<Kleo::KeyListViewItem> it( mItemsToDelete );
1133  while ( Kleo::KeyListViewItem * cur = it.current() ) {
1134  ++it;
1135  if ( cur->childCount() == 0 ) {
1136  mItemsToDelete.remove( cur );
1137  }
1138  }
1139  if ( mItemsToDelete.isEmpty() )
1140  break;
1141  }
1142  mItemsToDelete.setAutoDelete( false );
1143  Q_ASSERT( mItemsToDelete.isEmpty() );
1144  mItemsToDelete.clear();
1145  }
1146  disconnectJobFromStatusBarProgress( err );
1147 }
1148 
1149 void CertManager::slotViewDetails( Kleo::KeyListViewItem * item ) {
1150  if ( !item || item->key().isNull() )
1151  return;
1152 
1153  // <UGH>
1154  KDialogBase * dialog = new KDialogBase( this, "dialog", false, i18n("Additional Information for Key"), KDialogBase::Close, KDialogBase::Close );
1155 
1156  CertificateInfoWidgetImpl * top = new CertificateInfoWidgetImpl( item->key(), isRemote(), dialog );
1157  dialog->setMainWidget( top );
1158  // </UGH>
1159  connect( top, TQ_SIGNAL(requestCertificateDownload(const TQString&, const TQString&)),
1160  TQ_SLOT(slotStartCertificateDownload(const TQString&, const TQString&)) );
1161  dialog->show();
1162 }
1163 
1164 void CertManager::slotViewDetails()
1165 {
1166  TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems();
1167  if ( items.isEmpty() )
1168  return;
1169 
1170  // selectedItem() doesn't work in Extended mode.
1171  // But we only want to show the details of one item...
1172  slotViewDetails( items.first() );
1173 }
1174 
1175 void CertManager::slotSelectionChanged()
1176 {
1177  mKeyListView->flushKeys();
1178  bool b = mKeyListView->hasSelection();
1179  mExportCertificateAction->setEnabled( b );
1180  mViewCertDetailsAction->setEnabled( b );
1181  mDeleteCertificateAction->setEnabled( b );
1182 #ifdef NOT_IMPLEMENTED_ANYWAY
1183  mRevokeCertificateAction->setEnabled( b );
1184  mExtendCertificateAction->setEnabled( b );
1185 #endif
1186  mDownloadCertificateAction->setEnabled( b && mRemote );
1187  mValidateCertificateAction->setEnabled( !mRemote );
1188 }
1189 
1190 void CertManager::slotExportCertificate() {
1191  TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems();
1192  if ( items.isEmpty() )
1193  return;
1194 
1195  TQStringList fingerprints;
1196  for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it )
1197  if ( !it.current()->key().isNull() )
1198  if ( const char * fpr = it.current()->key().primaryFingerprint() )
1199  fingerprints.push_back( fpr );
1200 
1201  startCertificateExport( fingerprints );
1202 }
1203 
1204 static void showCertificateExportError( TQWidget * parent, const GpgME::Error & err ) {
1205  assert( err );
1206  const TQString msg = i18n("<qt><p>An error occurred while trying to export "
1207  "the certificate:</p>"
1208  "<p><b>%1</b></p></qt>")
1209  .arg( TQString::fromLocal8Bit( err.asString() ) );
1210  KMessageBox::error( parent, msg, i18n("Certificate Export Failed") );
1211 }
1212 
1213 void CertManager::startCertificateExport( const TQStringList & fingerprints ) {
1214  if ( fingerprints.empty() )
1215  return;
1216 
1217  // we need to use PEM (ascii armoured) format, since DER (binary)
1218  // can't transport more than one certificate *sigh* this is madness :/
1219  Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->publicKeyExportJob( true );
1220  assert( job );
1221 
1222  connect( job, TQ_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
1223  TQ_SLOT(slotCertificateExportResult(const GpgME::Error&,const TQByteArray&)) );
1224 
1225  connectJobToStatusBarProgress( job, i18n("Exporting certificate...") );
1226 
1227  const GpgME::Error err = job->start( fingerprints );
1228  if ( err )
1229  showCertificateExportError( this, err );
1230  else
1231  mProgressBar->setProgress( 0, 0 );
1232 }
1233 
1234 // return true if we should proceed, false if we should abort
1235 static bool checkOverwrite( const KURL& url, bool& overwrite, TQWidget* w )
1236 {
1237  if ( TDEIO::NetAccess::exists( url, false /*dest*/, w ) ) {
1238  if ( KMessageBox::Cancel ==
1239  KMessageBox::warningContinueCancel(
1240  w,
1241  i18n( "A file named \"%1\" already exists. "
1242  "Are you sure you want to overwrite it?" ).arg( url.prettyURL() ),
1243  i18n( "Overwrite File?" ),
1244  i18n( "&Overwrite" ) ) )
1245  return false;
1246  overwrite = true;
1247  }
1248  return true;
1249 }
1250 
1251 void CertManager::slotCertificateExportResult( const GpgME::Error & err, const TQByteArray & data ) {
1252  disconnectJobFromStatusBarProgress( err );
1253  if ( err ) {
1254  showCertificateExportError( this, err );
1255  return;
1256  }
1257 
1258  kdDebug() << "CertManager::slotCertificateExportResult(): got " << data.size() << " bytes" << endl;
1259 
1260  const TQString filter = TQString("*.pem|") + i18n("ASCII Armored Certificate Bundles (*.pem)");
1261  const KURL url = KFileDialog::getOpenURL( TQString(),
1262  filter,
1263  this,
1264  i18n( "Save Certificate" ) );
1265  if ( !url.isValid() )
1266  return;
1267 
1268  bool overwrite = false;
1269  if ( !checkOverwrite( url, overwrite, this ) )
1270  return;
1271 
1272  TDEIO::Job* uploadJob = TDEIOext::put( data, url, -1, overwrite, false /*resume*/ );
1273  uploadJob->setWindow( this );
1274  connect( uploadJob, TQ_SIGNAL( result( TDEIO::Job* ) ),
1275  this, TQ_SLOT( slotUploadResult( TDEIO::Job* ) ) );
1276 }
1277 
1278 
1279 void CertManager::slotExportSecretKey() {
1280  Kleo::KeySelectionDialog dlg( i18n("Secret Key Export"),
1281  "<qt>" +
1282  i18n("Select the secret key to export "
1283  "(<b>Warning: The PKCS#12 format is insecure; "
1284  "exporting secret keys is discouraged</b>):") +
1285  "</qt>",
1286  std::vector<GpgME::Key>(),
1287  Kleo::KeySelectionDialog::SecretKeys|Kleo::KeySelectionDialog::SMIMEKeys,
1288  false /* no multiple selection */,
1289  false /* no remember choice box */,
1290  this, "secret key export key selection dialog" );
1291  //dlg.setHideInvalidKeys( false );
1292 
1293  if ( dlg.exec() != TQDialog::Accepted )
1294  return;
1295 
1296  startSecretKeyExport( dlg.fingerprint() );
1297 }
1298 
1299 static void showSecretKeyExportError( TQWidget * parent, const GpgME::Error & err ) {
1300  assert( err );
1301  const TQString msg = i18n("<qt><p>An error occurred while trying to export "
1302  "the secret key:</p>"
1303  "<p><b>%1</b></p></qt>")
1304  .arg( TQString::fromLocal8Bit( err.asString() ) );
1305  KMessageBox::error( parent, msg, i18n("Secret-Key Export Failed") );
1306 }
1307 
1308 void CertManager::startSecretKeyExport( const TQString & fingerprint ) {
1309  if ( fingerprint.isEmpty() )
1310  return;
1311 
1312  // PENDING(marc): let user choose between binary and PEM format?
1313 
1314  // Check if gpgsm supports --p12-charset
1315  Kleo::CryptoConfig* config = Kleo::CryptoBackendFactory::instance()->config();
1316  TQString charset;
1317  if ( config && config->entry( "gpgsm", "Configuration", "p12-charset" ) ) {
1318  // This comes from gnupg's sources, agent/minip12.c
1319  // In fact, any charset supported by iconv would work, but we don't link to iconv directly...
1320  static const char *charsets[] = {
1321  "utf8",
1322  "iso-8859-1",
1323  "iso-8859-15",
1324  "iso-8859-2",
1325  "iso-8859-3",
1326  "iso-8859-4",
1327  "iso-8859-5",
1328  "iso-8859-6",
1329  "iso-8859-7",
1330  "iso-8859-8",
1331  "iso-8859-9",
1332  "koi8-r",
1333  "ibm437",
1334  "ibm850",
1335  "euc-jp",
1336  "big5",
1337  NULL
1338  };
1339  TQStringList charsetList;
1340  for ( const char** c = charsets; *c; ++c ) {
1341  charsetList.append( TQString::fromLatin1( *c ) );
1342  }
1343 
1344  // TODO this selection could be done in a derived KeySelectionDialog which would add a combobox,
1345  // it would be better integrated.
1346  bool ok;
1347  charset = KInputDialog::getItem( i18n("Exporting secret key..."),
1348  i18n("Choose a charset for encoding the pkcs#12 passphrase (utf8 is recommended)"),
1349  charsetList,
1350  0, false /*editable*/,
1351  &ok, this );
1352  if ( !ok )
1353  return;
1354  }
1355 
1356  Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->secretKeyExportJob( false, charset );
1357  assert( job );
1358 
1359  connect( job, TQ_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
1360  TQ_SLOT(slotSecretKeyExportResult(const GpgME::Error&,const TQByteArray&)) );
1361 
1362  connectJobToStatusBarProgress( job, i18n("Exporting secret key...") );
1363 
1364  const GpgME::Error err = job->start( fingerprint );
1365  if ( err )
1366  showSecretKeyExportError( this, err );
1367  else
1368  mProgressBar->setProgress( 0, 0 );
1369 }
1370 
1371 void CertManager::slotSecretKeyExportResult( const GpgME::Error & err, const TQByteArray & data ) {
1372  disconnectJobFromStatusBarProgress( err );
1373  if ( err ) {
1374  showSecretKeyExportError( this, err );
1375  return;
1376  }
1377 
1378  kdDebug() << "CertManager::slotSecretKeyExportResult(): got " << data.size() << " bytes" << endl;
1379  TQString filter = TQString("*.p12|") + i18n("PKCS#12 Key Bundle (*.p12)");
1380  KURL url = KFileDialog::getOpenURL( TQString(),
1381  filter,
1382  this,
1383  i18n( "Save Certificate" ) );
1384  if ( !url.isValid() )
1385  return;
1386 
1387  bool overwrite = false;
1388  if ( !checkOverwrite( url, overwrite, this ) )
1389  return;
1390 
1391  TDEIO::Job* uploadJob = TDEIOext::put( data, url, -1, overwrite, false /*resume*/ );
1392  uploadJob->setWindow( this );
1393  connect( uploadJob, TQ_SIGNAL( result( TDEIO::Job* ) ),
1394  this, TQ_SLOT( slotUploadResult( TDEIO::Job* ) ) );
1395 }
1396 
1397 void CertManager::slotUploadResult( TDEIO::Job* job )
1398 {
1399  if ( job->error() )
1400  job->showErrorDialog();
1401 }
1402 
1403 void CertManager::slotDropped(const KURL::List& lst)
1404 {
1405  mURLsToImport = lst;
1406  if ( !lst.empty() )
1407  importNextURLOrRedisplay();
1408 }
1409 
1410 void CertManager::importNextURLOrRedisplay()
1411 {
1412  if ( !mURLsToImport.empty() ) {
1413  // We can only import them one by one, otherwise the jobs would run into each other
1414  KURL url = mURLsToImport.front();
1415  mURLsToImport.pop_front();
1416  slotImportCertFromFile( url );
1417  } else {
1418  if ( isRemote() )
1419  return;
1420  startKeyListing( false, true, mPreviouslySelectedFingerprints );
1421  }
1422 }
1423 
1424 void CertManager::slotStartWatchGnuPG()
1425 {
1426  TDEProcess certManagerProc;
1427  certManagerProc << "kwatchgnupg";
1428 
1429  if( !certManagerProc.start( TDEProcess::DontCare ) )
1430  KMessageBox::error( this, i18n( "Could not start GnuPG LogViewer (kwatchgnupg). "
1431  "Please check your installation!" ),
1432  i18n( "Kleopatra Error" ) );
1433 }
1434 
1435 #include "certmanager.moc"
We need to derive from Kleo::KeyListView simply to add support for drop events.
Definition: certlistview.h:8
StoredTransferJob is a TransferJob (for downloading or uploading data) that also stores a TQByteArray...
TQByteArray data() const
Get hold of the downloaded data.