certmanager.cpp
00001 /* 00002 certmanager.cpp 00003 00004 This file is part of Kleopatra, the KDE keymanager 00005 Copyright (c) 2001,2002,2004 Klarälvdalens Datakonsult AB 00006 00007 Kleopatra is free software; you can redistribute it and/or modify 00008 it under the terms of the GNU General Public License as published by 00009 the Free Software Foundation; either version 2 of the License, or 00010 (at your option) any later version. 00011 00012 Kleopatra is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 General Public License for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with this program; if not, write to the Free Software 00019 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 00021 In addition, as a special exception, the copyright holders give 00022 permission to link the code of this program with any edition of 00023 the TQt library by Trolltech AS, Norway (or with modified versions 00024 of TQt that use the same license as TQt), and distribute linked 00025 combinations including the two. You must obey the GNU General 00026 Public License in all respects for all of the code used other than 00027 TQt. If you modify this file, you may extend this exception to 00028 your version of the file, but you are not obligated to do so. If 00029 you do not wish to do so, delete this exception statement from 00030 your version. 00031 */ 00032 00033 #ifdef HAVE_CONFIG_H 00034 #include <config.h> 00035 #endif 00036 00037 #include "certmanager.h" 00038 00039 #include "certlistview.h" 00040 #include "certificatewizardimpl.h" 00041 #include "certificateinfowidgetimpl.h" 00042 #include "crlview.h" 00043 #include "customactions.h" 00044 #include "hierarchyanalyser.h" 00045 #include "storedtransferjob.h" 00046 #include "conf/configuredialog.h" 00047 00048 // libkleopatra 00049 #include <kleo/cryptobackendfactory.h> 00050 #include <kleo/downloadjob.h> 00051 #include <kleo/importjob.h> 00052 #include <kleo/exportjob.h> 00053 #include <kleo/multideletejob.h> 00054 #include <kleo/deletejob.h> 00055 #include <kleo/keylistjob.h> 00056 #include <kleo/dn.h> 00057 #include <kleo/keyfilter.h> 00058 #include <kleo/keyfiltermanager.h> 00059 #include <kleo/hierarchicalkeylistjob.h> 00060 #include <kleo/refreshkeysjob.h> 00061 #include <kleo/cryptoconfig.h> 00062 00063 #include <ui/progressdialog.h> 00064 #include <ui/progressbar.h> 00065 #include <ui/keyselectiondialog.h> 00066 #include <ui/cryptoconfigdialog.h> 00067 00068 // GPGME++ 00069 #include <gpgmepp/importresult.h> 00070 #include <gpgmepp/keylistresult.h> 00071 #include <gpgmepp/key.h> 00072 00073 // KDE 00074 #include <tdefiledialog.h> 00075 #include <kprocess.h> 00076 #include <tdeaction.h> 00077 #include <tdeapplication.h> 00078 #include <tdelocale.h> 00079 #include <tdemessagebox.h> 00080 #include <dcopclient.h> 00081 #include <tdetoolbar.h> 00082 #include <kstatusbar.h> 00083 #include <kstandarddirs.h> 00084 #include <kdebug.h> 00085 #include <kdialogbase.h> 00086 #include <kkeydialog.h> 00087 #include <tdetempfile.h> 00088 #include <tdeio/job.h> 00089 #include <tdeio/netaccess.h> 00090 #include <tdestdaccel.h> 00091 00092 // TQt 00093 #include <tqfontmetrics.h> 00094 #include <tqpopupmenu.h> 00095 00096 // other 00097 #include <algorithm> 00098 #include <assert.h> 00099 #include <tdepimmacros.h> 00100 #include <kinputdialog.h> 00101 namespace { 00102 00103 class KDE_EXPORT DisplayStrategy : public Kleo::KeyListView::DisplayStrategy{ 00104 public: 00105 ~DisplayStrategy() {} 00106 00107 virtual TQFont keyFont( const GpgME::Key& key, const TQFont& font ) const { 00108 const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key ); 00109 return filter ? filter->font( font ) : font; 00110 } 00111 virtual TQColor keyForeground( const GpgME::Key& key, const TQColor& c ) const { 00112 const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key ); 00113 if ( filter && filter->fgColor().isValid() ) 00114 return filter->fgColor(); 00115 return c; 00116 } 00117 virtual TQColor keyBackground( const GpgME::Key& key, const TQColor& c ) const { 00118 const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key ); 00119 if ( filter && filter->bgColor().isValid() ) 00120 return filter->bgColor(); 00121 return c; 00122 } 00123 }; 00124 00125 class KDE_EXPORT ColumnStrategy : public Kleo::KeyListView::ColumnStrategy { 00126 public: 00127 ~ColumnStrategy() {} 00128 00129 TQString title( int col ) const; 00130 TQString text( const GpgME::Key & key, int col ) const; 00131 int width( int col, const TQFontMetrics & fm ) const; 00132 }; 00133 00134 TQString ColumnStrategy::title( int col ) const { 00135 switch ( col ) { 00136 case 0: return i18n("Subject"); 00137 case 1: return i18n("Issuer"); 00138 case 2: return i18n("Serial"); 00139 default: return TQString(); 00140 } 00141 } 00142 00143 TQString ColumnStrategy::text( const GpgME::Key & key, int col ) const { 00144 switch ( col ) { 00145 case 0: return Kleo::DN( key.userID(0).id() ).prettyDN(); 00146 case 1: return Kleo::DN( key.issuerName() ).prettyDN(); 00147 case 2: return key.issuerSerial() ? TQString::fromUtf8( key.issuerSerial() ) : TQString() ; 00148 default: return TQString(); 00149 } 00150 } 00151 00152 int ColumnStrategy::width( int col, const TQFontMetrics & fm ) const { 00153 int factor = -1; 00154 switch ( col ) { 00155 case 0: factor = 6; break; 00156 case 1: factor = 4; break; 00157 default: return -1; 00158 } 00159 return fm.width( title( col ) ) * factor; 00160 } 00161 } // anon namespace 00162 00163 CertManager::CertManager( bool remote, const TQString& query, const TQString & import, 00164 TQWidget* parent, const char* name, WFlags f ) 00165 : TDEMainWindow( parent, name, f|WDestructiveClose ), 00166 mCrlView( 0 ), 00167 mDirmngrProc( 0 ), 00168 mHierarchyAnalyser( 0 ), 00169 mLineEditAction( 0 ), 00170 mComboAction( 0 ), 00171 mFindAction( 0 ), 00172 mImportCertFromFileAction( 0 ), 00173 mImportCRLFromFileAction( 0 ), 00174 mNextFindRemote( remote ), 00175 mRemote( remote ), 00176 mDirMngrFound( false ) 00177 { 00178 readConfig( query.isEmpty() ); 00179 createStatusBar(); 00180 createActions(); 00181 00182 createGUI(); 00183 setAutoSaveSettings(); 00184 00185 // Main Window -------------------------------------------------- 00186 mKeyListView = new CertKeyListView( new ColumnStrategy(), new DisplayStrategy(), this, "mKeyListView" ); 00187 mKeyListView->setSelectionMode( TQListView::Extended ); 00188 setCentralWidget( mKeyListView ); 00189 00190 connect( mKeyListView, TQT_SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const TQPoint&,int)), 00191 TQT_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) ); 00192 connect( mKeyListView, TQT_SIGNAL(returnPressed(Kleo::KeyListViewItem*)), 00193 TQT_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) ); 00194 connect( mKeyListView, TQT_SIGNAL(selectionChanged()), 00195 TQT_SLOT(slotSelectionChanged()) ); 00196 connect( mKeyListView, TQT_SIGNAL(contextMenu(Kleo::KeyListViewItem*, const TQPoint&)), 00197 TQT_SLOT(slotContextMenu(Kleo::KeyListViewItem*, const TQPoint&)) ); 00198 00199 connect( mKeyListView, TQT_SIGNAL(dropped(const KURL::List&) ), 00200 TQT_SLOT( slotDropped(const KURL::List&) ) ); 00201 00202 mLineEditAction->setText(query); 00203 if ( !mRemote && !mNextFindRemote || !query.isEmpty() ) 00204 slotSearch(); 00205 00206 if ( !import.isEmpty() ) 00207 slotImportCertFromFile( KURL( import ) ); 00208 00209 slotToggleHierarchicalView( mHierarchicalView ); 00210 updateStatusBarLabels(); 00211 slotSelectionChanged(); // initial state for selection-dependent actions 00212 } 00213 00214 CertManager::~CertManager() { 00215 writeConfig(); 00216 delete mDirmngrProc; mDirmngrProc = 0; 00217 delete mHierarchyAnalyser; mHierarchyAnalyser = 0; 00218 } 00219 00220 void CertManager::readConfig( bool noQueryGiven ) { 00221 TDEConfig config( "kleopatrarc" ); 00222 config.setGroup( "Display Options" ); 00223 mHierarchicalView = config.readBoolEntry( "hierarchicalView", false ); 00224 if ( noQueryGiven ) { 00225 mNextFindRemote = config.readBoolEntry( "startInRemoteMode", false ); 00226 } 00227 } 00228 00229 void CertManager::writeConfig() { 00230 TDEConfig config( "kleopatrarc" ); 00231 config.setGroup( "Display Options" ); 00232 config.writeEntry( "hierarchicalView", mKeyListView->hierarchical() ); 00233 config.writeEntry( "startInRemoteMode", mNextFindRemote ); 00234 } 00235 00236 void CertManager::createStatusBar() { 00237 KStatusBar * bar = statusBar(); 00238 mProgressBar = new Kleo::ProgressBar( bar, "mProgressBar" ); 00239 mProgressBar->reset(); 00240 mProgressBar->setFixedSize( TQSize( 100, mProgressBar->height() * 3 / 5 ) ); 00241 bar->addWidget( mProgressBar, 0, true ); 00242 mStatusLabel = new TQLabel( bar, "mStatusLabel" ); 00243 bar->addWidget( mStatusLabel, 1, false ); 00244 } 00245 00246 static inline void connectEnableOperationSignal( TQObject * s, TQObject * d ) { 00247 TQObject::connect( s, TQT_SIGNAL(enableOperations(bool)), 00248 d, TQT_SLOT(setEnabled(bool)) ); 00249 } 00250 00251 00252 void CertManager::createActions() { 00253 TDEAction * action = 0; 00254 00255 (void)KStdAction::quit( TQT_TQOBJECT(this), TQT_SLOT(close()), actionCollection() ); 00256 00257 action = KStdAction::redisplay( TQT_TQOBJECT(this), TQT_SLOT(slotRedisplay()), actionCollection() ); 00258 // work around the fact that the stdaction has no shortcut 00259 TDEShortcut reloadShortcut = TDEStdAccel::shortcut(TDEStdAccel::Reload); 00260 reloadShortcut.append(KKey(CTRL + Key_R)); 00261 action->setShortcut( reloadShortcut ); 00262 00263 connectEnableOperationSignal( TQT_TQOBJECT(this), action ); 00264 00265 action = new TDEAction( i18n("Stop Operation"), "process-stop", Key_Escape, 00266 TQT_TQOBJECT(this), TQT_SIGNAL(stopOperations()), 00267 actionCollection(), "view_stop_operations" ); 00268 action->setEnabled( false ); 00269 00270 (void) new TDEAction( i18n("New Key Pair..."), "document-new", 0, 00271 TQT_TQOBJECT(this), TQT_SLOT(newCertificate()), 00272 actionCollection(), "file_new_certificate" ); 00273 00274 connect( new TDEToggleAction( i18n("Hierarchical Key List"), 0, 00275 actionCollection(), "view_hierarchical" ), 00276 TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotToggleHierarchicalView(bool)) ); 00277 00278 action = new TDEAction( i18n("Expand All"), 0, CTRL+Key_Period, 00279 TQT_TQOBJECT(this), TQT_SLOT(slotExpandAll()), 00280 actionCollection(), "view_expandall" ); 00281 action = new TDEAction( i18n("Collapse All"), 0, CTRL+Key_Comma, 00282 TQT_TQOBJECT(this), TQT_SLOT(slotCollapseAll()), 00283 actionCollection(), "view_collapseall" ); 00284 00285 (void) new TDEAction( i18n("Refresh CRLs"), 0, 0, 00286 TQT_TQOBJECT(this), TQT_SLOT(slotRefreshKeys()), 00287 actionCollection(), "certificates_refresh_clr" ); 00288 00289 #ifdef NOT_IMPLEMENTED_ANYWAY 00290 mRevokeCertificateAction = new TDEAction( i18n("Revoke"), 0, 00291 TQT_TQOBJECT(this), TQT_SLOT(revokeCertificate()), 00292 actionCollection(), "edit_revoke_certificate" ); 00293 connectEnableOperationSignal( this, mRevokeCertificateAction ); 00294 00295 mExtendCertificateAction = new TDEAction( i18n("Extend"), 0, 00296 TQT_TQOBJECT(this), TQT_SLOT(extendCertificate()), 00297 actionCollection(), "edit_extend_certificate" ); 00298 connectEnableOperationSignal( this, mExtendCertificateAction ); 00299 #endif 00300 00301 mDeleteCertificateAction = new TDEAction( i18n("Delete"), "edit-delete", Key_Delete, 00302 TQT_TQOBJECT(this), TQT_SLOT(slotDeleteCertificate()), 00303 actionCollection(), "edit_delete_certificate" ); 00304 connectEnableOperationSignal( TQT_TQOBJECT(this), mDeleteCertificateAction ); 00305 00306 mValidateCertificateAction = new TDEAction( i18n("Validate"), "reload", SHIFT + Key_F5, 00307 TQT_TQOBJECT(this), TQT_SLOT(slotValidate()), 00308 actionCollection(), "certificates_validate" ); 00309 connectEnableOperationSignal( TQT_TQOBJECT(this), mValidateCertificateAction ); 00310 00311 mImportCertFromFileAction = new TDEAction( i18n("Import Certificates..."), 0, 00312 TQT_TQOBJECT(this), TQT_SLOT(slotImportCertFromFile()), 00313 actionCollection(), "file_import_certificates" ); 00314 connectEnableOperationSignal( TQT_TQOBJECT(this), mImportCertFromFileAction ); 00315 00316 mImportCRLFromFileAction = new TDEAction( i18n("Import CRLs..."), 0, 00317 TQT_TQOBJECT(this), TQT_SLOT(importCRLFromFile()), 00318 actionCollection(), "file_import_crls" ); 00319 connectEnableOperationSignal( TQT_TQOBJECT(this), mImportCRLFromFileAction ); 00320 00321 mExportCertificateAction = new TDEAction( i18n("Export Certificates..."), "export", 0, 00322 TQT_TQOBJECT(this), TQT_SLOT(slotExportCertificate()), 00323 actionCollection(), "file_export_certificate" ); 00324 00325 mExportSecretKeyAction = new TDEAction( i18n("Export Secret Key..."), "export", 0, 00326 TQT_TQOBJECT(this), TQT_SLOT(slotExportSecretKey()), 00327 actionCollection(), "file_export_secret_keys" ); 00328 connectEnableOperationSignal( TQT_TQOBJECT(this), mExportSecretKeyAction ); 00329 00330 mViewCertDetailsAction = new TDEAction( i18n("Certificate Details..."), 0, 0, 00331 TQT_TQOBJECT(this), TQT_SLOT(slotViewDetails()), actionCollection(), 00332 "view_certificate_details" ); 00333 mDownloadCertificateAction = new TDEAction( i18n( "Download"), 0, 0, 00334 TQT_TQOBJECT(this), TQT_SLOT(slotDownloadCertificate()), actionCollection(), 00335 "download_certificate" ); 00336 00337 const TQString dirmngr = TDEStandardDirs::findExe( "gpgsm" ); 00338 mDirMngrFound = !dirmngr.isEmpty(); 00339 00340 action = new TDEAction( i18n("Dump CRL Cache..."), 0, 00341 TQT_TQOBJECT(this), TQT_SLOT(slotViewCRLs()), 00342 actionCollection(), "crl_dump_crl_cache" ); 00343 action->setEnabled( mDirMngrFound ); // we also need dirmngr for this 00344 00345 action = new TDEAction( i18n("Clear CRL Cache..."), 0, 00346 TQT_TQOBJECT(this), TQT_SLOT(slotClearCRLs()), 00347 actionCollection(), "crl_clear_crl_cache" ); 00348 action->setEnabled( mDirMngrFound ); // we also need dirmngr for this 00349 00350 action = new TDEAction( i18n("GnuPG Log Viewer..."), "pgp-keys", 0, TQT_TQOBJECT(this), 00351 TQT_SLOT(slotStartWatchGnuPG()), actionCollection(), "tools_start_kwatchgnupg"); 00352 // disable action if no kwatchgnupg binary is around 00353 if (TDEStandardDirs::findExe("kwatchgnupg").isEmpty()) action->setEnabled(false); 00354 00355 (void)new LabelAction( i18n("Search:"), actionCollection(), "label_action" ); 00356 00357 mLineEditAction = new LineEditAction( TQString(), actionCollection(), TQT_TQOBJECT(this), 00358 TQT_SLOT(slotSearch()), 00359 "query_lineedit_action"); 00360 00361 TQStringList lst; 00362 lst << i18n("In Local Certificates") << i18n("In External Certificates"); 00363 mComboAction = new ComboAction( lst, actionCollection(), TQT_TQOBJECT(this), TQT_SLOT( slotToggleRemote(int) ), 00364 "location_combo_action", mNextFindRemote? 1 : 0 ); 00365 00366 mFindAction = new TDEAction( i18n("Find"), "edit-find", 0, TQT_TQOBJECT(this), TQT_SLOT(slotSearch()), 00367 actionCollection(), "find" ); 00368 00369 KStdAction::keyBindings( TQT_TQOBJECT(this), TQT_SLOT(slotEditKeybindings()), actionCollection() ); 00370 KStdAction::preferences( TQT_TQOBJECT(this), TQT_SLOT(slotShowConfigurationDialog()), actionCollection() ); 00371 00372 new TDEAction( i18n( "Configure &GpgME Backend" ), 0, 0, TQT_TQOBJECT(this), TQT_SLOT(slotConfigureGpgME()), 00373 actionCollection(), "configure_gpgme" ); 00374 00375 createStandardStatusBarAction(); 00376 updateImportActions( true ); 00377 } 00378 00379 void CertManager::updateImportActions( bool enable ) { 00380 mImportCRLFromFileAction->setEnabled( mDirMngrFound && enable ); 00381 mImportCertFromFileAction->setEnabled( enable ); 00382 } 00383 00384 void CertManager::slotEditKeybindings() { 00385 KKeyDialog::configure( actionCollection(), true ); 00386 } 00387 00388 void CertManager::slotShowConfigurationDialog() { 00389 ConfigureDialog dlg( this ); 00390 connect( &dlg, TQT_SIGNAL( configCommitted() ), TQT_SLOT( slotRepaint() ) ); 00391 dlg.exec(); 00392 } 00393 00394 void CertManager::slotConfigureGpgME() { 00395 Kleo::CryptoConfig* config = Kleo::CryptoBackendFactory::instance()->config(); 00396 if ( config ) { 00397 Kleo::CryptoConfigDialog dlg( config ); 00398 00399 int result = dlg.exec(); 00400 00401 // Forget all data parsed from gpgconf, so that we show updated information 00402 // when reopening the configuration dialog. 00403 config->clear(); 00404 00405 if ( result == TQDialog::Accepted ) 00406 { 00407 // Tell other apps (e.g. kmail) that the gpgconf data might have changed 00408 kapp->dcopClient()->emitDCOPSignal( "KPIM::CryptoConfig", "changed()", TQByteArray() ); 00409 } 00410 } 00411 } 00412 00413 void CertManager::slotRepaint() 00414 { 00415 mKeyListView->repaintContents(); 00416 } 00417 00418 void CertManager::slotToggleRemote( int idx ) { 00419 mNextFindRemote = idx != 0; 00420 } 00421 00422 void CertManager::slotToggleHierarchicalView( bool hier ) { 00423 mHierarchicalView = hier; 00424 mKeyListView->setHierarchical( hier ); 00425 mKeyListView->setRootIsDecorated( hier ); 00426 if ( TDEAction * act = action("view_expandall") ) 00427 act->setEnabled( hier ); 00428 if ( TDEAction * act = action("view_collapseall" ) ) 00429 act->setEnabled( hier ); 00430 if ( TDEToggleAction * act = 00431 static_cast<TDEToggleAction*>( action("view_hierarchical") ) ) 00432 act->setChecked( hier ); 00433 00434 if ( hier && !mCurrentQuery.isEmpty() ) 00435 startRedisplay( false ); 00436 } 00437 00438 void CertManager::slotExpandAll() { 00439 for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it ) 00440 it.current()->setOpen( true ); 00441 } 00442 00443 void CertManager::slotCollapseAll() { 00444 for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it ) 00445 it.current()->setOpen( false ); 00446 } 00447 00448 void CertManager::connectJobToStatusBarProgress( Kleo::Job * job, const TQString & initialText ) { 00449 assert( mProgressBar ); 00450 if ( !job ) 00451 return; 00452 if ( !initialText.isEmpty() ) 00453 statusBar()->message( initialText ); 00454 connect( job, TQT_SIGNAL(progress(const TQString&,int,int)), 00455 mProgressBar, TQT_SLOT(slotProgress(const TQString&,int,int)) ); 00456 connect( job, TQT_SIGNAL(done()), mProgressBar, TQT_SLOT(reset()) ); 00457 connect( this, TQT_SIGNAL(stopOperations()), job, TQT_SLOT(slotCancel()) ); 00458 00459 action("view_stop_operations")->setEnabled( true ); 00460 emit enableOperations( false ); 00461 } 00462 00463 void CertManager::disconnectJobFromStatusBarProgress( const GpgME::Error & err ) { 00464 updateStatusBarLabels(); 00465 const TQString msg = err.isCanceled() ? i18n("Canceled.") 00466 : err ? i18n("Failed.") 00467 : i18n("Done.") ; 00468 statusBar()->message( msg, 4000 ); 00469 00470 action("view_stop_operations")->setEnabled( false ); 00471 emit enableOperations( true ); 00472 slotSelectionChanged(); 00473 } 00474 00475 void CertManager::updateStatusBarLabels() { 00476 mKeyListView->flushKeys(); 00477 int total = 0; 00478 for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it ) 00479 ++total; 00480 mStatusLabel->setText( i18n( "%n Key.","%n Keys.", total ) ); 00481 } 00482 00483 // 00484 // 00485 // Key Listing: 00486 // 00487 // 00488 00489 00490 static std::set<std::string> extractKeyFingerprints( const TQPtrList<Kleo::KeyListViewItem> & items ) { 00491 std::set<std::string> result; 00492 for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it ) 00493 if ( const char * fpr = it.current()->key().primaryFingerprint() ) 00494 result.insert( fpr ); 00495 return result; 00496 } 00497 00498 static TQStringList stringlistFromSet( const std::set<std::string> & set ) { 00499 // ARGH. This is madness. Shitty TQt containers don't support TQStringList( patterns.begin(), patterns.end() ) :/ 00500 TQStringList sl; 00501 for ( std::set<std::string>::const_iterator it = set.begin() ; it != set.end() ; ++it ) 00502 // let's make extra sure, maybe someone tries to make TQt not support std::string->TQString conversion 00503 sl.push_back( TQString::fromLatin1( it->c_str() ) ); 00504 return sl; 00505 } 00506 00507 void CertManager::slotRefreshKeys() { 00508 const TQStringList keys = stringlistFromSet( extractKeyFingerprints( mKeyListView->selectedItems() ) ); 00509 Kleo::RefreshKeysJob * job = Kleo::CryptoBackendFactory::instance()->smime()->refreshKeysJob(); 00510 assert( job ); 00511 00512 connect( job, TQT_SIGNAL(result(const GpgME::Error&)), 00513 this, TQT_SLOT(slotRefreshKeysResult(const GpgME::Error&)) ); 00514 00515 connectJobToStatusBarProgress( job, i18n("Refreshing keys...") ); 00516 if ( const GpgME::Error err = job->start( keys ) ) 00517 slotRefreshKeysResult( err ); 00518 } 00519 00520 void CertManager::slotRefreshKeysResult( const GpgME::Error & err ) { 00521 disconnectJobFromStatusBarProgress( err ); 00522 if ( err.isCanceled() ) 00523 return; 00524 if ( err ) 00525 KMessageBox::error( this, i18n("An error occurred while trying to refresh " 00526 "keys:\n%1").arg( TQString::fromLocal8Bit( err.asString() ) ), 00527 i18n("Refreshing Keys Failed") ); 00528 } 00529 00530 static void showKeyListError( TQWidget * parent, const GpgME::Error & err ) { 00531 assert( err ); 00532 const TQString msg = i18n( "<qt><p>An error occurred while fetching " 00533 "the certificates from the backend:</p>" 00534 "<p><b>%1</b></p></qt>" ) 00535 .arg( TQString::fromLocal8Bit( err.asString() ) ); 00536 00537 KMessageBox::error( parent, msg, i18n( "Certificate Listing Failed" ) ); 00538 } 00539 00540 void CertManager::slotSearch() { 00541 mPreviouslySelectedFingerprints.clear(); 00542 // Clear display 00543 mKeyListView->clear(); 00544 mCurrentQuery = mLineEditAction->text(); 00545 startKeyListing( false, false, mCurrentQuery ); 00546 } 00547 00548 void CertManager::startRedisplay( bool validate ) { 00549 mPreviouslySelectedFingerprints = extractKeyFingerprints( mKeyListView->selectedItems() ); 00550 if ( mPreviouslySelectedFingerprints.empty() ) 00551 startKeyListing( validate, true, mCurrentQuery ); 00552 else 00553 startKeyListing( validate, true, mPreviouslySelectedFingerprints ); 00554 } 00555 00556 void CertManager::startKeyListing( bool validating, bool refresh, const std::set<std::string> & patterns ) { 00557 startKeyListing( validating, refresh, stringlistFromSet( patterns ) ); 00558 } 00559 00560 void CertManager::startKeyListing( bool validating, bool refresh, const TQStringList & patterns ) { 00561 mRemote = mNextFindRemote; 00562 mLineEditAction->setEnabled( false ); 00563 mComboAction->setEnabled( false ); 00564 mFindAction->setEnabled( false ); 00565 00566 Kleo::KeyListJob * job = 0; 00567 if ( !validating && !refresh && mKeyListView->hierarchical() && !patterns.empty() ) 00568 job = new Kleo::HierarchicalKeyListJob( Kleo::CryptoBackendFactory::instance()->smime(), 00569 mRemote, false, validating ); 00570 else 00571 job = Kleo::CryptoBackendFactory::instance()->smime()->keyListJob( mRemote, false, validating ); 00572 assert( job ); 00573 00574 connect( job, TQT_SIGNAL(nextKey(const GpgME::Key&)), 00575 mKeyListView, refresh ? TQT_SLOT(slotRefreshKey(const GpgME::Key&)) : TQT_SLOT(slotAddKey(const GpgME::Key&)) ); 00576 connect( job, TQT_SIGNAL(result(const GpgME::KeyListResult&)), 00577 this, TQT_SLOT(slotKeyListResult(const GpgME::KeyListResult&)) ); 00578 00579 connectJobToStatusBarProgress( job, i18n("Fetching keys...") ); 00580 00581 const GpgME::Error err = job->start( patterns ) ; 00582 if ( err ) { 00583 showKeyListError( this, err ); 00584 return; 00585 } 00586 mProgressBar->setProgress( 0, 0 ); // enable busy indicator 00587 } 00588 00589 static void selectKeys( Kleo::KeyListView * lv, const std::set<std::string> & fprs ) { 00590 if ( !lv || fprs.empty() ) 00591 return; 00592 for ( TQListViewItemIterator it( lv ) ; it.current() ; ++it ) 00593 if ( Kleo::KeyListViewItem * item = Kleo::lvi_cast<Kleo::KeyListViewItem>( it.current() ) ) { 00594 const char * fpr = item->key().primaryFingerprint(); 00595 item->setSelected( fpr && fprs.find( fpr ) != fprs.end() ); 00596 } 00597 } 00598 00599 void CertManager::slotKeyListResult( const GpgME::KeyListResult & res ) { 00600 if ( res.error() ) 00601 showKeyListError( this, res.error() ); 00602 else if ( res.isTruncated() ) 00603 KMessageBox::information( this, 00604 i18n("The query result has been truncated.\n" 00605 "Either the local or a remote limit on " 00606 "the maximum number of returned hits has " 00607 "been exceeded.\n" 00608 "You can try to increase the local limit " 00609 "in the configuration dialog, but if one " 00610 "of the configured servers is the limiting " 00611 "factor, you have to refine your search.") ); 00612 00613 mLineEditAction->setEnabled( true ); 00614 mComboAction->setEnabled( true ); 00615 mFindAction->setEnabled( true ); 00616 00617 mLineEditAction->focusAll(); 00618 disconnectJobFromStatusBarProgress( res.error() ); 00619 selectKeys( mKeyListView, mPreviouslySelectedFingerprints ); 00620 } 00621 00622 void CertManager::slotContextMenu(Kleo::KeyListViewItem* item, const TQPoint& point) { 00623 if ( !item ) 00624 return; 00625 if ( TQPopupMenu * popup = static_cast<TQPopupMenu*>(factory()->container("listview_popup",this)) ) 00626 popup->exec( point ); 00627 } 00628 00632 void CertManager::newCertificate() 00633 { 00634 CertificateWizardImpl wizard( this ); 00635 wizard.exec(); 00636 } 00637 00642 void CertManager::revokeCertificate() 00643 { 00644 tqDebug("Not Yet Implemented"); 00645 } 00646 00651 void CertManager::extendCertificate() 00652 { 00653 tqDebug("Not Yet Implemented"); 00654 } 00655 00656 00657 // 00658 // 00659 // Downloading / Importing Certificates 00660 // 00661 // 00662 00663 00667 void CertManager::slotImportCertFromFile() 00668 { 00669 const TQString filter = "application/x-x509-ca-cert application/x-pkcs12 application/pkcs7-mime"; 00670 //const TQString filter = TQString("*.pem *.der *.p7c *.p12|") + i18n("Certificates (*.pem *.der *.p7c *.p12)"); 00671 slotImportCertFromFile( KFileDialog::getOpenURL( TQString(), filter, this, 00672 i18n( "Select Certificate File" ) ) ); 00673 } 00674 00675 void CertManager::slotImportCertFromFile( const KURL & certURL ) 00676 { 00677 if ( !certURL.isValid() ) // empty or malformed 00678 return; 00679 00680 mPreviouslySelectedFingerprints.clear(); 00681 00682 // Prevent two simultaneous imports 00683 updateImportActions( false ); 00684 00685 // Download the cert 00686 TDEIOext::StoredTransferJob* importJob = TDEIOext::storedGet( certURL ); 00687 importJob->setWindow( this ); 00688 connect( importJob, TQT_SIGNAL(result(TDEIO::Job*)), TQT_SLOT(slotImportResult(TDEIO::Job*)) ); 00689 } 00690 00691 void CertManager::slotImportResult( TDEIO::Job* job ) 00692 { 00693 if ( job->error() ) { 00694 job->showErrorDialog(); 00695 } else { 00696 TDEIOext::StoredTransferJob* trJob = static_cast<TDEIOext::StoredTransferJob *>( job ); 00697 startCertificateImport( trJob->data(), trJob->url().fileName() ); 00698 } 00699 00700 updateImportActions( true ); 00701 } 00702 00703 static void showCertificateDownloadError( TQWidget * parent, const GpgME::Error & err, const TQString& certDisplayName ) { 00704 assert( err ); 00705 const TQString msg = i18n( "<qt><p>An error occurred while trying " 00706 "to download the certificate %1:</p>" 00707 "<p><b>%2</b></p></qt>" ) 00708 .arg( certDisplayName ) 00709 .arg( TQString::fromLocal8Bit( err.asString() ) ); 00710 00711 KMessageBox::error( parent, msg, i18n( "Certificate Download Failed" ) ); 00712 } 00713 00714 void CertManager::slotDownloadCertificate() { 00715 mPreviouslySelectedFingerprints.clear(); 00716 TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems(); 00717 for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it ) 00718 if ( !it.current()->key().isNull() ) 00719 if ( const char * fpr = it.current()->key().primaryFingerprint() ) 00720 slotStartCertificateDownload( fpr, it.current()->text(0) ); 00721 } 00722 00723 // Called from slotDownloadCertificate and from the certificate-details widget 00724 void CertManager::slotStartCertificateDownload( const TQString& fingerprint, const TQString& displayName ) { 00725 if ( fingerprint.isEmpty() ) 00726 return; 00727 00728 Kleo::DownloadJob * job = 00729 Kleo::CryptoBackendFactory::instance()->smime()->downloadJob( false /* no armor */ ); 00730 assert( job ); 00731 00732 connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)), 00733 TQT_SLOT(slotCertificateDownloadResult(const GpgME::Error&,const TQByteArray&)) ); 00734 00735 connectJobToStatusBarProgress( job, i18n("Fetching certificate from server...") ); 00736 00737 const GpgME::Error err = job->start( fingerprint ); 00738 if ( err ) 00739 showCertificateDownloadError( this, err, displayName ); 00740 else { 00741 mProgressBar->setProgress( 0, 0 ); 00742 mJobsDisplayNameMap.insert( job, displayName ); 00743 } 00744 } 00745 00746 TQString CertManager::displayNameForJob( const Kleo::Job *job ) 00747 { 00748 JobsDisplayNameMap::iterator it = mJobsDisplayNameMap.find( job ); 00749 TQString displayName; 00750 if ( it != mJobsDisplayNameMap.end() ) { 00751 displayName = *it; 00752 mJobsDisplayNameMap.remove( it ); 00753 } else { 00754 kdWarning() << "Job not found in map: " << job << endl; 00755 } 00756 return displayName; 00757 } 00758 00759 // Don't call directly! 00760 void CertManager::slotCertificateDownloadResult( const GpgME::Error & err, const TQByteArray & keyData ) { 00761 00762 TQString displayName = displayNameForJob( static_cast<const Kleo::Job *>( sender() ) ); 00763 00764 if ( err ) 00765 showCertificateDownloadError( this, err, displayName ); 00766 else 00767 startCertificateImport( keyData, displayName ); 00768 disconnectJobFromStatusBarProgress( err ); 00769 } 00770 00771 static void showCertificateImportError( TQWidget * parent, const GpgME::Error & err, const TQString& certDisplayName ) { 00772 assert( err ); 00773 const TQString msg = i18n( "<qt><p>An error occurred while trying " 00774 "to import the certificate %1:</p>" 00775 "<p><b>%2</b></p></qt>" ) 00776 .arg( certDisplayName ) 00777 .arg( TQString::fromLocal8Bit( err.asString() ) ); 00778 KMessageBox::error( parent, msg, i18n( "Certificate Import Failed" ) ); 00779 } 00780 00781 void CertManager::startCertificateImport( const TQByteArray & keyData, const TQString& certDisplayName ) { 00782 Kleo::ImportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->importJob(); 00783 assert( job ); 00784 00785 connect( job, TQT_SIGNAL(result(const GpgME::ImportResult&)), 00786 TQT_SLOT(slotCertificateImportResult(const GpgME::ImportResult&)) ); 00787 00788 connectJobToStatusBarProgress( job, i18n("Importing certificates...") ); 00789 00790 kdDebug() << "Importing certificate. keyData size:" << keyData.size() << endl; 00791 const GpgME::Error err = job->start( keyData ); 00792 if ( err ) 00793 showCertificateImportError( this, err, certDisplayName ); 00794 else { 00795 mProgressBar->setProgress( 0, 0 ); 00796 mJobsDisplayNameMap.insert( job, certDisplayName ); 00797 } 00798 } 00799 00800 void CertManager::slotCertificateImportResult( const GpgME::ImportResult & res ) { 00801 TQString displayName = displayNameForJob( static_cast<const Kleo::Job *>( sender() ) ); 00802 00803 if ( res.error().isCanceled() ) { 00804 // do nothing 00805 } else if ( res.error() ) { 00806 showCertificateImportError( this, res.error(), displayName ); 00807 } else { 00808 00809 const TQString normalLine = i18n("<tr><td align=\"right\">%1</td><td>%2</td></tr>"); 00810 const TQString boldLine = i18n("<tr><td align=\"right\"><b>%1</b></td><td>%2</td></tr>"); 00811 00812 TQStringList lines; 00813 lines.push_back( normalLine.arg( i18n("Total number processed:"), 00814 TQString::number( res.numConsidered() ) ) ); 00815 lines.push_back( normalLine.arg( i18n("Imported:"), 00816 TQString::number( res.numImported() ) ) ); 00817 if ( res.newSignatures() ) 00818 lines.push_back( normalLine.arg( i18n("New signatures:"), 00819 TQString::number( res.newSignatures() ) ) ); 00820 if ( res.newUserIDs() ) 00821 lines.push_back( normalLine.arg( i18n("New user IDs:"), 00822 TQString::number( res.newUserIDs() ) ) ); 00823 if ( res.numKeysWithoutUserID() ) 00824 lines.push_back( normalLine.arg( i18n("Keys without user IDs:"), 00825 TQString::number( res.numKeysWithoutUserID() ) ) ); 00826 if ( res.newSubkeys() ) 00827 lines.push_back( normalLine.arg( i18n("New subkeys:"), 00828 TQString::number( res.newSubkeys() ) ) ); 00829 if ( res.newRevocations() ) 00830 lines.push_back( boldLine.arg( i18n("Newly revoked:"), 00831 TQString::number( res.newRevocations() ) ) ); 00832 if ( res.notImported() ) 00833 lines.push_back( boldLine.arg( i18n("Not imported:"), 00834 TQString::number( res.notImported() ) ) ); 00835 if ( res.numUnchanged() ) 00836 lines.push_back( normalLine.arg( i18n("Unchanged:"), 00837 TQString::number( res.numUnchanged() ) ) ); 00838 if ( res.numSecretKeysConsidered() ) 00839 lines.push_back( normalLine.arg( i18n("Secret keys processed:"), 00840 TQString::number( res.numSecretKeysConsidered() ) ) ); 00841 if ( res.numSecretKeysImported() ) 00842 lines.push_back( normalLine.arg( i18n("Secret keys imported:"), 00843 TQString::number( res.numSecretKeysImported() ) ) ); 00844 if ( res.numSecretKeysConsidered() - res.numSecretKeysImported() - res.numSecretKeysUnchanged() > 0 ) 00845 lines.push_back( boldLine.arg( i18n("Secret keys <em>not</em> imported:"), 00846 TQString::number( res.numSecretKeysConsidered() 00847 - res.numSecretKeysImported() 00848 - res.numSecretKeysUnchanged() ) ) ); 00849 if ( res.numSecretKeysUnchanged() ) 00850 lines.push_back( normalLine.arg( i18n("Secret keys unchanged:"), 00851 TQString::number( res.numSecretKeysUnchanged() ) ) ); 00852 00853 KMessageBox::information( this, 00854 i18n( "<qt><p>Detailed results of importing %1:</p>" 00855 "<table>%2</table></qt>" ) 00856 .arg( displayName ).arg( lines.join( TQString() ) ), 00857 i18n( "Certificate Import Result" ) ); 00858 00859 disconnectJobFromStatusBarProgress( res.error() ); 00860 // save the fingerprints of imported certs for later selection: 00861 const std::vector<GpgME::Import> imports = res.imports(); 00862 for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) 00863 mPreviouslySelectedFingerprints.insert( it->fingerprint() ); 00864 } 00865 importNextURLOrRedisplay(); 00866 } 00867 00868 00869 00874 void CertManager::slotDirmngrExited() { 00875 if ( !mDirmngrProc->normalExit() ) 00876 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" ) ); 00877 else if ( mDirmngrProc->exitStatus() ) 00878 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" ) ); 00879 else 00880 KMessageBox::information( this, i18n( "CRL file imported successfully." ), i18n( "Certificate Manager Information" ) ); 00881 00882 delete mDirmngrProc; mDirmngrProc = 0; 00883 if ( !mImportCRLTempFile.isEmpty() ) 00884 TQFile::remove( mImportCRLTempFile ); 00885 updateImportActions( true ); 00886 } 00887 00891 void CertManager::importCRLFromFile() { 00892 // loadcrl can only work with DER encoded files (verified with dirmngr 1.0.3) 00893 TQString filter = TQString("*.crl *.arl *-crl.der *-arl.der|") + i18n("Certificate Revocation List, DER encoded (*.crl *.arl *-crl.der *-arl.der)"); 00894 KURL url = KFileDialog::getOpenURL( TQString(), 00895 filter, 00896 this, 00897 i18n( "Select CRL File" ) ); 00898 if ( url.isValid() ) { 00899 updateImportActions( false ); 00900 if ( url.isLocalFile() ) { 00901 startImportCRL( url.path(), false ); 00902 updateImportActions( true ); 00903 } else { 00904 KTempFile tempFile; 00905 KURL destURL; 00906 destURL.setPath( tempFile.name() ); 00907 TDEIO::Job* copyJob = TDEIO::file_copy( url, destURL, 0600, true, false ); 00908 copyJob->setWindow( this ); 00909 connect( copyJob, TQT_SIGNAL( result( TDEIO::Job * ) ), 00910 TQT_SLOT( slotImportCRLJobFinished( TDEIO::Job * ) ) ); 00911 } 00912 } 00913 } 00914 00915 void CertManager::slotImportCRLJobFinished( TDEIO::Job *job ) 00916 { 00917 TDEIO::FileCopyJob* fcjob = static_cast<TDEIO::FileCopyJob*>( job ); 00918 TQString tempFilePath = fcjob->destURL().path(); 00919 if ( job->error() ) { 00920 job->showErrorDialog(); 00921 TQFile::remove( tempFilePath ); // unlink tempfile 00922 updateImportActions( true ); 00923 return; 00924 } 00925 startImportCRL( tempFilePath, true ); 00926 } 00927 00928 bool CertManager::connectAndStartDirmngr( const char * slot, const char * processname ) { 00929 assert( slot ); 00930 assert( processname ); 00931 assert( mDirmngrProc ); 00932 mErrorbuffer = TQString(); 00933 connect( mDirmngrProc, TQT_SIGNAL(processExited(TDEProcess*)), slot ); 00934 connect( mDirmngrProc, TQT_SIGNAL(receivedStderr(TDEProcess*,char*,int) ), 00935 this, TQT_SLOT(slotStderr(TDEProcess*,char*,int)) ); 00936 if( !mDirmngrProc->start( TDEProcess::NotifyOnExit, TDEProcess::Stderr ) ) { 00937 delete mDirmngrProc; mDirmngrProc = 0; 00938 KMessageBox::error( this, i18n( "Unable to start %1 process. Please check your installation." ).arg( processname ), i18n( "Certificate Manager Error" ) ); 00939 return false; 00940 } 00941 return true; 00942 } 00943 00944 void CertManager::startImportCRL( const TQString& filename, bool isTempFile ) 00945 { 00946 assert( !mDirmngrProc ); 00947 mImportCRLTempFile = isTempFile ? filename : TQString(); 00948 mDirmngrProc = new TDEProcess(); 00949 *mDirmngrProc << "gpgsm" << "--call-dirmngr" << "loadcrl" << filename; 00950 if ( !connectAndStartDirmngr( TQT_SLOT(slotDirmngrExited()), "gpgsm" ) ) { 00951 updateImportActions( true ); 00952 if ( isTempFile ) 00953 TQFile::remove( mImportCRLTempFile ); // unlink tempfile 00954 } 00955 } 00956 00957 void CertManager::startClearCRLs() { 00958 assert( !mDirmngrProc ); 00959 mDirmngrProc = new TDEProcess(); 00960 *mDirmngrProc << "dirmngr" << "--flush"; 00961 //*mDirmngrProc << "gpgsm" << "--call-dimngr" << "flush"; // use this once it's implemented! 00962 connectAndStartDirmngr( TQT_SLOT(slotClearCRLsResult()), "dirmngr" ); 00963 } 00964 00965 void CertManager::slotStderr( TDEProcess*, char* buf, int len ) { 00966 mErrorbuffer += TQString::fromLocal8Bit( buf, len ); 00967 } 00968 00972 void CertManager::importCRLFromLDAP() 00973 { 00974 tqDebug("Not Yet Implemented"); 00975 } 00976 00977 void CertManager::slotViewCRLs() { 00978 if ( !mCrlView ) 00979 mCrlView = new CRLView( this ); 00980 00981 mCrlView->show(); 00982 mCrlView->slotUpdateView(); 00983 } 00984 00985 00986 void CertManager::slotClearCRLs() { 00987 startClearCRLs(); 00988 } 00989 00990 void CertManager::slotClearCRLsResult() { 00991 assert( mDirmngrProc ); 00992 if ( !mDirmngrProc->normalExit() ) 00993 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" ) ); 00994 else if ( mDirmngrProc->exitStatus() ) 00995 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" ) ); 00996 else 00997 KMessageBox::information( this, i18n( "CRL cache cleared successfully." ), i18n( "Certificate Manager Information" ) ); 00998 delete mDirmngrProc; mDirmngrProc = 0; 00999 } 01000 01001 static void showDeleteError( TQWidget * parent, const GpgME::Error & err ) { 01002 assert( err ); 01003 const TQString msg = i18n("<qt><p>An error occurred while trying to delete " 01004 "the certificates:</p>" 01005 "<p><b>%1</b></p></qt>") 01006 .arg( TQString::fromLocal8Bit( err.asString() ) ); 01007 KMessageBox::error( parent, msg, i18n("Certificate Deletion Failed") ); 01008 } 01009 01010 static bool ByFingerprint( const GpgME::Key & left, const GpgME::Key & right ) { 01011 return tqstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) < 0 ; 01012 } 01013 01014 static bool WithRespectToFingerprints( const GpgME::Key & left, const GpgME::Key & right ) { 01015 return tqstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) == 0; 01016 } 01017 01018 void CertManager::slotDeleteCertificate() { 01019 mItemsToDelete = mKeyListView->selectedItems(); 01020 if ( mItemsToDelete.isEmpty() ) 01021 return; 01022 std::vector<GpgME::Key> keys; 01023 keys.reserve( mItemsToDelete.count() ); 01024 TQStringList keyDisplayNames; 01025 for ( TQPtrListIterator<Kleo::KeyListViewItem> it( mItemsToDelete ) ; it.current() ; ++it ) 01026 if ( !it.current()->key().isNull() ) { 01027 keys.push_back( it.current()->key() ); 01028 keyDisplayNames.push_back( it.current()->text( 0 ) ); 01029 } 01030 if ( keys.empty() ) 01031 return; 01032 01033 if ( !mHierarchyAnalyser ) { 01034 mHierarchyAnalyser = new HierarchyAnalyser( TQT_TQOBJECT(this), "mHierarchyAnalyser" ); 01035 Kleo::KeyListJob * job = Kleo::CryptoBackendFactory::instance()->smime()->keyListJob(); 01036 assert( job ); 01037 connect( job, TQT_SIGNAL(nextKey(const GpgME::Key&)), 01038 mHierarchyAnalyser, TQT_SLOT(slotNextKey(const GpgME::Key&)) ); 01039 connect( job, TQT_SIGNAL(result(const GpgME::KeyListResult&)), 01040 this, TQT_SLOT(slotDeleteCertificate()) ); 01041 connectJobToStatusBarProgress( job, i18n("Checking key dependencies...") ); 01042 if ( const GpgME::Error error = job->start( TQStringList() ) ) { 01043 showKeyListError( this, error ); 01044 delete mHierarchyAnalyser; mHierarchyAnalyser = 0; 01045 } 01046 return; 01047 } else 01048 disconnectJobFromStatusBarProgress( 0 ); 01049 01050 std::vector<GpgME::Key> keysToDelete = keys; 01051 for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it ) 01052 if ( !it->isNull() ) { 01053 const std::vector<GpgME::Key> subjects 01054 = mHierarchyAnalyser->subjectsForIssuerRecursive( it->primaryFingerprint() ); 01055 keysToDelete.insert( keysToDelete.end(), subjects.begin(), subjects.end() ); 01056 } 01057 01058 std::sort( keysToDelete.begin(), keysToDelete.end(), ByFingerprint ); 01059 keysToDelete.erase( std::unique( keysToDelete.begin(), keysToDelete.end(), 01060 WithRespectToFingerprints ), 01061 keysToDelete.end() ); 01062 01063 delete mHierarchyAnalyser; mHierarchyAnalyser = 0; 01064 01065 if ( keysToDelete.size() > keys.size() ) 01066 if ( KMessageBox::warningContinueCancel( this, 01067 i18n("Some or all of the selected " 01068 "certificates are issuers (CA certificates) " 01069 "for other, non-selected certificates.\n" 01070 "Deleting a CA certificate will also delete " 01071 "all certificates issued by it."), 01072 i18n("Deleting CA Certificates") ) 01073 != KMessageBox::Continue ) 01074 return; 01075 01076 const TQString msg = keysToDelete.size() > keys.size() 01077 ? i18n("Do you really want to delete this certificate and the %1 certificates it certified?", 01078 "Do you really want to delete these %n certificates and the %1 certificates they certified?", 01079 keys.size() ).arg( keysToDelete.size() - keys.size() ) 01080 : i18n("Do you really want to delete this certificate?", 01081 "Do you really want to delete these %n certificates?", keys.size() ) ; 01082 01083 if ( KMessageBox::warningContinueCancelList( this, msg, keyDisplayNames, 01084 i18n( "Delete Certificates" ), 01085 KGuiItem( i18n( "Delete" ), "edit-delete" ), 01086 "ConfirmDeleteCert", KMessageBox::Dangerous ) 01087 != KMessageBox::Continue ) 01088 return; 01089 01090 if ( Kleo::DeleteJob * job = Kleo::CryptoBackendFactory::instance()->smime()->deleteJob() ) 01091 job->slotCancel(); 01092 else { 01093 TQString str = keys.size() == 1 01094 ? i18n("<qt><p>An error occurred while trying to delete " 01095 "the certificate:</p>" 01096 "<p><b>%1</b><p></qt>" ) 01097 : i18n( "<qt><p>An error occurred while trying to delete " 01098 "the certificates:</p>" 01099 "<p><b>%1</b><p></qt>" ); 01100 KMessageBox::error( this, 01101 str.arg( i18n("Operation not supported by the backend.") ), 01102 i18n("Certificate Deletion Failed") ); 01103 } 01104 01105 mItemsToDelete.clear(); // re-create according to the real selection 01106 for ( std::vector<GpgME::Key>::const_iterator it = keysToDelete.begin() ; it != keysToDelete.end() ; ++it ) 01107 if ( Kleo::KeyListViewItem * item = mKeyListView->itemByFingerprint( it->primaryFingerprint() ) ) 01108 mItemsToDelete.append( item ); 01109 01110 Kleo::MultiDeleteJob * job = new Kleo::MultiDeleteJob( Kleo::CryptoBackendFactory::instance()->smime() ); 01111 assert( job ); 01112 01113 connect( job, TQT_SIGNAL(result(const GpgME::Error&,const GpgME::Key&)), 01114 TQT_SLOT(slotDeleteResult(const GpgME::Error&,const GpgME::Key&)) ); 01115 01116 connectJobToStatusBarProgress( job, i18n("Deleting keys...") ); 01117 01118 const GpgME::Error err = job->start( keys, true ); 01119 if ( err ) 01120 showDeleteError( this, err ); 01121 else 01122 mProgressBar->setProgress( 0, 0 ); 01123 } 01124 01125 void CertManager::slotDeleteResult( const GpgME::Error & err, const GpgME::Key & ) { 01126 if ( err ) 01127 showDeleteError( this, err ); 01128 else { 01129 const int infinity = 100; // infinite loop guard... 01130 mItemsToDelete.setAutoDelete( true ); 01131 for ( int i = 0 ; i < infinity ; ++i ) { 01132 TQPtrListIterator<Kleo::KeyListViewItem> it( mItemsToDelete ); 01133 while ( Kleo::KeyListViewItem * cur = it.current() ) { 01134 ++it; 01135 if ( cur->childCount() == 0 ) { 01136 mItemsToDelete.remove( cur ); 01137 } 01138 } 01139 if ( mItemsToDelete.isEmpty() ) 01140 break; 01141 } 01142 mItemsToDelete.setAutoDelete( false ); 01143 Q_ASSERT( mItemsToDelete.isEmpty() ); 01144 mItemsToDelete.clear(); 01145 } 01146 disconnectJobFromStatusBarProgress( err ); 01147 } 01148 01149 void CertManager::slotViewDetails( Kleo::KeyListViewItem * item ) { 01150 if ( !item || item->key().isNull() ) 01151 return; 01152 01153 // <UGH> 01154 KDialogBase * dialog = new KDialogBase( this, "dialog", false, i18n("Additional Information for Key"), KDialogBase::Close, KDialogBase::Close ); 01155 01156 CertificateInfoWidgetImpl * top = new CertificateInfoWidgetImpl( item->key(), isRemote(), dialog ); 01157 dialog->setMainWidget( top ); 01158 // </UGH> 01159 connect( top, TQT_SIGNAL(requestCertificateDownload(const TQString&, const TQString&)), 01160 TQT_SLOT(slotStartCertificateDownload(const TQString&, const TQString&)) ); 01161 dialog->show(); 01162 } 01163 01164 void CertManager::slotViewDetails() 01165 { 01166 TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems(); 01167 if ( items.isEmpty() ) 01168 return; 01169 01170 // selectedItem() doesn't work in Extended mode. 01171 // But we only want to show the details of one item... 01172 slotViewDetails( items.first() ); 01173 } 01174 01175 void CertManager::slotSelectionChanged() 01176 { 01177 mKeyListView->flushKeys(); 01178 bool b = mKeyListView->hasSelection(); 01179 mExportCertificateAction->setEnabled( b ); 01180 mViewCertDetailsAction->setEnabled( b ); 01181 mDeleteCertificateAction->setEnabled( b ); 01182 #ifdef NOT_IMPLEMENTED_ANYWAY 01183 mRevokeCertificateAction->setEnabled( b ); 01184 mExtendCertificateAction->setEnabled( b ); 01185 #endif 01186 mDownloadCertificateAction->setEnabled( b && mRemote ); 01187 mValidateCertificateAction->setEnabled( !mRemote ); 01188 } 01189 01190 void CertManager::slotExportCertificate() { 01191 TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems(); 01192 if ( items.isEmpty() ) 01193 return; 01194 01195 TQStringList fingerprints; 01196 for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it ) 01197 if ( !it.current()->key().isNull() ) 01198 if ( const char * fpr = it.current()->key().primaryFingerprint() ) 01199 fingerprints.push_back( fpr ); 01200 01201 startCertificateExport( fingerprints ); 01202 } 01203 01204 static void showCertificateExportError( TQWidget * parent, const GpgME::Error & err ) { 01205 assert( err ); 01206 const TQString msg = i18n("<qt><p>An error occurred while trying to export " 01207 "the certificate:</p>" 01208 "<p><b>%1</b></p></qt>") 01209 .arg( TQString::fromLocal8Bit( err.asString() ) ); 01210 KMessageBox::error( parent, msg, i18n("Certificate Export Failed") ); 01211 } 01212 01213 void CertManager::startCertificateExport( const TQStringList & fingerprints ) { 01214 if ( fingerprints.empty() ) 01215 return; 01216 01217 // we need to use PEM (ascii armoured) format, since DER (binary) 01218 // can't transport more than one certificate *sigh* this is madness :/ 01219 Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->publicKeyExportJob( true ); 01220 assert( job ); 01221 01222 connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)), 01223 TQT_SLOT(slotCertificateExportResult(const GpgME::Error&,const TQByteArray&)) ); 01224 01225 connectJobToStatusBarProgress( job, i18n("Exporting certificate...") ); 01226 01227 const GpgME::Error err = job->start( fingerprints ); 01228 if ( err ) 01229 showCertificateExportError( this, err ); 01230 else 01231 mProgressBar->setProgress( 0, 0 ); 01232 } 01233 01234 // return true if we should proceed, false if we should abort 01235 static bool checkOverwrite( const KURL& url, bool& overwrite, TQWidget* w ) 01236 { 01237 if ( TDEIO::NetAccess::exists( url, false /*dest*/, w ) ) { 01238 if ( KMessageBox::Cancel == 01239 KMessageBox::warningContinueCancel( 01240 w, 01241 i18n( "A file named \"%1\" already exists. " 01242 "Are you sure you want to overwrite it?" ).arg( url.prettyURL() ), 01243 i18n( "Overwrite File?" ), 01244 i18n( "&Overwrite" ) ) ) 01245 return false; 01246 overwrite = true; 01247 } 01248 return true; 01249 } 01250 01251 void CertManager::slotCertificateExportResult( const GpgME::Error & err, const TQByteArray & data ) { 01252 disconnectJobFromStatusBarProgress( err ); 01253 if ( err ) { 01254 showCertificateExportError( this, err ); 01255 return; 01256 } 01257 01258 kdDebug() << "CertManager::slotCertificateExportResult(): got " << data.size() << " bytes" << endl; 01259 01260 const TQString filter = TQString("*.pem|") + i18n("ASCII Armored Certificate Bundles (*.pem)"); 01261 const KURL url = KFileDialog::getOpenURL( TQString(), 01262 filter, 01263 this, 01264 i18n( "Save Certificate" ) ); 01265 if ( !url.isValid() ) 01266 return; 01267 01268 bool overwrite = false; 01269 if ( !checkOverwrite( url, overwrite, this ) ) 01270 return; 01271 01272 TDEIO::Job* uploadJob = TDEIOext::put( data, url, -1, overwrite, false /*resume*/ ); 01273 uploadJob->setWindow( this ); 01274 connect( uploadJob, TQT_SIGNAL( result( TDEIO::Job* ) ), 01275 this, TQT_SLOT( slotUploadResult( TDEIO::Job* ) ) ); 01276 } 01277 01278 01279 void CertManager::slotExportSecretKey() { 01280 Kleo::KeySelectionDialog dlg( i18n("Secret Key Export"), 01281 "<qt>" + 01282 i18n("Select the secret key to export " 01283 "(<b>Warning: The PKCS#12 format is insecure; " 01284 "exporting secret keys is discouraged</b>):") + 01285 "</qt>", 01286 std::vector<GpgME::Key>(), 01287 Kleo::KeySelectionDialog::SecretKeys|Kleo::KeySelectionDialog::SMIMEKeys, 01288 false /* no multiple selection */, 01289 false /* no remember choice box */, 01290 this, "secret key export key selection dialog" ); 01291 //dlg.setHideInvalidKeys( false ); 01292 01293 if ( dlg.exec() != TQDialog::Accepted ) 01294 return; 01295 01296 startSecretKeyExport( dlg.fingerprint() ); 01297 } 01298 01299 static void showSecretKeyExportError( TQWidget * parent, const GpgME::Error & err ) { 01300 assert( err ); 01301 const TQString msg = i18n("<qt><p>An error occurred while trying to export " 01302 "the secret key:</p>" 01303 "<p><b>%1</b></p></qt>") 01304 .arg( TQString::fromLocal8Bit( err.asString() ) ); 01305 KMessageBox::error( parent, msg, i18n("Secret-Key Export Failed") ); 01306 } 01307 01308 void CertManager::startSecretKeyExport( const TQString & fingerprint ) { 01309 if ( fingerprint.isEmpty() ) 01310 return; 01311 01312 // PENDING(marc): let user choose between binary and PEM format? 01313 01314 // Check if gpgsm supports --p12-charset 01315 Kleo::CryptoConfig* config = Kleo::CryptoBackendFactory::instance()->config(); 01316 TQString charset; 01317 if ( config && config->entry( "gpgsm", "Configuration", "p12-charset" ) ) { 01318 // This comes from gnupg's sources, agent/minip12.c 01319 // In fact, any charset supported by iconv would work, but we don't link to iconv directly... 01320 static const char *charsets[] = { 01321 "utf8", 01322 "iso-8859-1", 01323 "iso-8859-15", 01324 "iso-8859-2", 01325 "iso-8859-3", 01326 "iso-8859-4", 01327 "iso-8859-5", 01328 "iso-8859-6", 01329 "iso-8859-7", 01330 "iso-8859-8", 01331 "iso-8859-9", 01332 "koi8-r", 01333 "ibm437", 01334 "ibm850", 01335 "euc-jp", 01336 "big5", 01337 NULL 01338 }; 01339 TQStringList charsetList; 01340 for ( const char** c = charsets; *c; ++c ) { 01341 charsetList.append( TQString::fromLatin1( *c ) ); 01342 } 01343 01344 // TODO this selection could be done in a derived KeySelectionDialog which would add a combobox, 01345 // it would be better integrated. 01346 bool ok; 01347 charset = KInputDialog::getItem( i18n("Exporting secret key..."), 01348 i18n("Choose a charset for encoding the pkcs#12 passphrase (utf8 is recommended)"), 01349 charsetList, 01350 0, false /*editable*/, 01351 &ok, this ); 01352 if ( !ok ) 01353 return; 01354 } 01355 01356 Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->secretKeyExportJob( false, charset ); 01357 assert( job ); 01358 01359 connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)), 01360 TQT_SLOT(slotSecretKeyExportResult(const GpgME::Error&,const TQByteArray&)) ); 01361 01362 connectJobToStatusBarProgress( job, i18n("Exporting secret key...") ); 01363 01364 const GpgME::Error err = job->start( fingerprint ); 01365 if ( err ) 01366 showSecretKeyExportError( this, err ); 01367 else 01368 mProgressBar->setProgress( 0, 0 ); 01369 } 01370 01371 void CertManager::slotSecretKeyExportResult( const GpgME::Error & err, const TQByteArray & data ) { 01372 disconnectJobFromStatusBarProgress( err ); 01373 if ( err ) { 01374 showSecretKeyExportError( this, err ); 01375 return; 01376 } 01377 01378 kdDebug() << "CertManager::slotSecretKeyExportResult(): got " << data.size() << " bytes" << endl; 01379 TQString filter = TQString("*.p12|") + i18n("PKCS#12 Key Bundle (*.p12)"); 01380 KURL url = KFileDialog::getOpenURL( TQString(), 01381 filter, 01382 this, 01383 i18n( "Save Certificate" ) ); 01384 if ( !url.isValid() ) 01385 return; 01386 01387 bool overwrite = false; 01388 if ( !checkOverwrite( url, overwrite, this ) ) 01389 return; 01390 01391 TDEIO::Job* uploadJob = TDEIOext::put( data, url, -1, overwrite, false /*resume*/ ); 01392 uploadJob->setWindow( this ); 01393 connect( uploadJob, TQT_SIGNAL( result( TDEIO::Job* ) ), 01394 this, TQT_SLOT( slotUploadResult( TDEIO::Job* ) ) ); 01395 } 01396 01397 void CertManager::slotUploadResult( TDEIO::Job* job ) 01398 { 01399 if ( job->error() ) 01400 job->showErrorDialog(); 01401 } 01402 01403 void CertManager::slotDropped(const KURL::List& lst) 01404 { 01405 mURLsToImport = lst; 01406 if ( !lst.empty() ) 01407 importNextURLOrRedisplay(); 01408 } 01409 01410 void CertManager::importNextURLOrRedisplay() 01411 { 01412 if ( !mURLsToImport.empty() ) { 01413 // We can only import them one by one, otherwise the jobs would run into each other 01414 KURL url = mURLsToImport.front(); 01415 mURLsToImport.pop_front(); 01416 slotImportCertFromFile( url ); 01417 } else { 01418 if ( isRemote() ) 01419 return; 01420 startKeyListing( false, true, mPreviouslySelectedFingerprints ); 01421 } 01422 } 01423 01424 void CertManager::slotStartWatchGnuPG() 01425 { 01426 TDEProcess certManagerProc; 01427 certManagerProc << "kwatchgnupg"; 01428 01429 if( !certManagerProc.start( TDEProcess::DontCare ) ) 01430 KMessageBox::error( this, i18n( "Could not start GnuPG LogViewer (kwatchgnupg). " 01431 "Please check your installation!" ), 01432 i18n( "Kleopatra Error" ) ); 01433 } 01434 01435 #include "certmanager.moc"