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 <tdestandarddirs.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>
101namespace {
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
163CertManager::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
214CertManager::~CertManager() {
215 writeConfig();
216 delete mDirmngrProc; mDirmngrProc = 0;
217 delete mHierarchyAnalyser; mHierarchyAnalyser = 0;
218}
219
220void 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
229void CertManager::writeConfig() {
230 TDEConfig config( "kleopatrarc" );
231 config.setGroup( "Display Options" );
232 config.writeEntry( "hierarchicalView", mKeyListView->hierarchical() );
233 config.writeEntry( "startInRemoteMode", mNextFindRemote );
234}
235
236void 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
246static inline void connectEnableOperationSignal( TQObject * s, TQObject * d ) {
247 TQObject::connect( s, TQ_SIGNAL(enableOperations(bool)),
248 d, TQ_SLOT(setEnabled(bool)) );
249}
250
251
252void 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
379void CertManager::updateImportActions( bool enable ) {
380 mImportCRLFromFileAction->setEnabled( mDirMngrFound && enable );
381 mImportCertFromFileAction->setEnabled( enable );
382}
383
384void CertManager::slotEditKeybindings() {
385 KKeyDialog::configure( actionCollection(), true );
386}
387
388void CertManager::slotShowConfigurationDialog() {
389 ConfigureDialog dlg( this );
390 connect( &dlg, TQ_SIGNAL( configCommitted() ), TQ_SLOT( slotRepaint() ) );
391 dlg.exec();
392}
393
394void 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 tdeApp->dcopClient()->emitDCOPSignal( "KPIM::CryptoConfig", "changed()", TQByteArray() );
409 }
410 }
411}
412
413void CertManager::slotRepaint()
414{
415 mKeyListView->repaintContents();
416}
417
418void CertManager::slotToggleRemote( int idx ) {
419 mNextFindRemote = idx != 0;
420}
421
422void 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
438void CertManager::slotExpandAll() {
439 for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
440 it.current()->setOpen( true );
441}
442
443void CertManager::slotCollapseAll() {
444 for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
445 it.current()->setOpen( false );
446}
447
448void 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
463void 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
475void 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
490static 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
498static 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
507void 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
520void 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
530static 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
540void CertManager::slotSearch() {
541 mPreviouslySelectedFingerprints.clear();
542 // Clear display
543 mKeyListView->clear();
544 mCurrentQuery = mLineEditAction->text();
545 startKeyListing( false, false, mCurrentQuery );
546}
547
548void 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
556void CertManager::startKeyListing( bool validating, bool refresh, const std::set<std::string> & patterns ) {
557 startKeyListing( validating, refresh, stringlistFromSet( patterns ) );
558}
559
560void 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
589static 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
599void 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
622void 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
632void CertManager::newCertificate()
633{
634 CertificateWizardImpl wizard( this );
635 wizard.exec();
636}
637
642void CertManager::revokeCertificate()
643{
644 tqDebug("Not Yet Implemented");
645}
646
651void CertManager::extendCertificate()
652{
653 tqDebug("Not Yet Implemented");
654}
655
656
657//
658//
659// Downloading / Importing Certificates
660//
661//
662
663
667void 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
675void 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
691void 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
703static 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
714void 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
724void 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
746TQString 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!
760void 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
771static 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
781void 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
800void 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
874void 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
891void 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
915void 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
928bool 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
944void 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
957void 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
965void CertManager::slotStderr( TDEProcess*, char* buf, int len ) {
966 mErrorbuffer += TQString::fromLocal8Bit( buf, len );
967}
968
972void CertManager::importCRLFromLDAP()
973{
974 tqDebug("Not Yet Implemented");
975}
976
977void CertManager::slotViewCRLs() {
978 if ( !mCrlView )
979 mCrlView = new CRLView( this );
980
981 mCrlView->show();
982 mCrlView->slotUpdateView();
983}
984
985
986void CertManager::slotClearCRLs() {
987 startClearCRLs();
988}
989
990void 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
1001static 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
1010static bool ByFingerprint( const GpgME::Key & left, const GpgME::Key & right ) {
1011 return tqstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) < 0 ;
1012}
1013
1014static bool WithRespectToFingerprints( const GpgME::Key & left, const GpgME::Key & right ) {
1015 return tqstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) == 0;
1016}
1017
1018void 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
1125void 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
1149void 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
1164void 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
1175void 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
1190void 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
1204static 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
1213void 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
1235static 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
1251void 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
1279void 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
1299static 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
1308void 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
1371void 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
1397void CertManager::slotUploadResult( TDEIO::Job* job )
1398{
1399 if ( job->error() )
1400 job->showErrorDialog();
1401}
1402
1403void CertManager::slotDropped(const KURL::List& lst)
1404{
1405 mURLsToImport = lst;
1406 if ( !lst.empty() )
1407 importNextURLOrRedisplay();
1408}
1409
1410void 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
1424void 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.