/* certmanager.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2001,2002,2004 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the TQt library by Trolltech AS, Norway (or with modified versions of TQt that use the same license as TQt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than TQt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifdef HAVE_CONFIG_H #include #endif #include "certmanager.h" #include "certlistview.h" #include "certificatewizardimpl.h" #include "certificateinfowidgetimpl.h" #include "crlview.h" #include "customactions.h" #include "hierarchyanalyser.h" #include "storedtransferjob.h" #include "conf/configuredialog.h" // libkleopatra #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // GPGME++ #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // TQt #include #include // other #include #include #include #include namespace { class KDE_EXPORT DisplayStrategy : public Kleo::KeyListView::DisplayStrategy{ public: ~DisplayStrategy() {} virtual TQFont keyFont( const GpgME::Key& key, const TQFont& font ) const { const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key ); return filter ? filter->font( font ) : font; } virtual TQColor keyForeground( const GpgME::Key& key, const TQColor& c ) const { const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key ); if ( filter && filter->fgColor().isValid() ) return filter->fgColor(); return c; } virtual TQColor keyBackground( const GpgME::Key& key, const TQColor& c ) const { const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key ); if ( filter && filter->bgColor().isValid() ) return filter->bgColor(); return c; } }; class KDE_EXPORT ColumnStrategy : public Kleo::KeyListView::ColumnStrategy { public: ~ColumnStrategy() {} TQString title( int col ) const; TQString text( const GpgME::Key & key, int col ) const; int width( int col, const TQFontMetrics & fm ) const; }; TQString ColumnStrategy::title( int col ) const { switch ( col ) { case 0: return i18n("Subject"); case 1: return i18n("Issuer"); case 2: return i18n("Serial"); default: return TQString(); } } TQString ColumnStrategy::text( const GpgME::Key & key, int col ) const { switch ( col ) { case 0: return Kleo::DN( key.userID(0).id() ).prettyDN(); case 1: return Kleo::DN( key.issuerName() ).prettyDN(); case 2: return key.issuerSerial() ? TQString::fromUtf8( key.issuerSerial() ) : TQString() ; default: return TQString(); } } int ColumnStrategy::width( int col, const TQFontMetrics & fm ) const { int factor = -1; switch ( col ) { case 0: factor = 6; break; case 1: factor = 4; break; default: return -1; } return fm.width( title( col ) ) * factor; } } // anon namespace CertManager::CertManager( bool remote, const TQString& query, const TQString & import, TQWidget* tqparent, const char* name, WFlags f ) : KMainWindow( tqparent, name, f|WDestructiveClose ), mCrlView( 0 ), mDirmngrProc( 0 ), mHierarchyAnalyser( 0 ), mLineEditAction( 0 ), mComboAction( 0 ), mFindAction( 0 ), mImportCertFromFileAction( 0 ), mImportCRLFromFileAction( 0 ), mNextFindRemote( remote ), mRemote( remote ), mDirMngrFound( false ) { readConfig( query.isEmpty() ); createStatusBar(); createActions(); createGUI(); setAutoSaveSettings(); // Main Window -------------------------------------------------- mKeyListView = new CertKeyListView( new ColumnStrategy(), new DisplayStrategy(), this, "mKeyListView" ); mKeyListView->setSelectionMode( TQListView::Extended ); setCentralWidget( mKeyListView ); connect( mKeyListView, TQT_SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const TQPoint&,int)), TQT_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) ); connect( mKeyListView, TQT_SIGNAL(returnPressed(Kleo::KeyListViewItem*)), TQT_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) ); connect( mKeyListView, TQT_SIGNAL(selectionChanged()), TQT_SLOT(slotSelectionChanged()) ); connect( mKeyListView, TQT_SIGNAL(contextMenu(Kleo::KeyListViewItem*, const TQPoint&)), TQT_SLOT(slotContextMenu(Kleo::KeyListViewItem*, const TQPoint&)) ); connect( mKeyListView, TQT_SIGNAL(dropped(const KURL::List&) ), TQT_SLOT( slotDropped(const KURL::List&) ) ); mLineEditAction->setText(query); if ( !mRemote && !mNextFindRemote || !query.isEmpty() ) slotSearch(); if ( !import.isEmpty() ) slotImportCertFromFile( KURL( import ) ); slotToggleHierarchicalView( mHierarchicalView ); updateStatusBarLabels(); slotSelectionChanged(); // initial state for selection-dependent actions } CertManager::~CertManager() { writeConfig(); delete mDirmngrProc; mDirmngrProc = 0; delete mHierarchyAnalyser; mHierarchyAnalyser = 0; } void CertManager::readConfig( bool noQueryGiven ) { KConfig config( "kleopatrarc" ); config.setGroup( "Display Options" ); mHierarchicalView = config.readBoolEntry( "hierarchicalView", false ); if ( noQueryGiven ) { mNextFindRemote = config.readBoolEntry( "startInRemoteMode", false ); } } void CertManager::writeConfig() { KConfig config( "kleopatrarc" ); config.setGroup( "Display Options" ); config.writeEntry( "hierarchicalView", mKeyListView->hierarchical() ); config.writeEntry( "startInRemoteMode", mNextFindRemote ); } void CertManager::createStatusBar() { KStatusBar * bar = statusBar(); mProgressBar = new Kleo::ProgressBar( bar, "mProgressBar" ); mProgressBar->reset(); mProgressBar->setFixedSize( TQSize( 100, mProgressBar->height() * 3 / 5 ) ); bar->addWidget( mProgressBar, 0, true ); mStatusLabel = new TQLabel( bar, "mStatusLabel" ); bar->addWidget( mStatusLabel, 1, false ); } static inline void connectEnableOperationSignal( TQObject * s, TQObject * d ) { TQObject::connect( s, TQT_SIGNAL(enableOperations(bool)), d, TQT_SLOT(setEnabled(bool)) ); } void CertManager::createActions() { KAction * action = 0; (void)KStdAction::quit( this, TQT_SLOT(close()), actionCollection() ); action = KStdAction::redisplay( this, TQT_SLOT(slotRedisplay()), actionCollection() ); // work around the fact that the stdaction has no shortcut KShortcut reloadShortcut = KStdAccel::shortcut(KStdAccel::Reload); reloadShortcut.append(KKey(CTRL + Key_R)); action->setShortcut( reloadShortcut ); connectEnableOperationSignal( this, action ); action = new KAction( i18n("Stop Operation"), "stop", Key_Escape, this, TQT_SIGNAL(stopOperations()), actionCollection(), "view_stop_operations" ); action->setEnabled( false ); (void) new KAction( i18n("New Key Pair..."), "filenew", 0, this, TQT_SLOT(newCertificate()), actionCollection(), "file_new_certificate" ); connect( new KToggleAction( i18n("Hierarchical Key List"), 0, actionCollection(), "view_hierarchical" ), TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotToggleHierarchicalView(bool)) ); action = new KAction( i18n("Expand All"), 0, CTRL+Key_Period, this, TQT_SLOT(slotExpandAll()), actionCollection(), "view_expandall" ); action = new KAction( i18n("Collapse All"), 0, CTRL+Key_Comma, this, TQT_SLOT(slotCollapseAll()), actionCollection(), "view_collapseall" ); (void) new KAction( i18n("Refresh CRLs"), 0, 0, this, TQT_SLOT(slotRefreshKeys()), actionCollection(), "certificates_refresh_clr" ); #ifdef NOT_IMPLEMENTED_ANYWAY mRevokeCertificateAction = new KAction( i18n("Revoke"), 0, this, TQT_SLOT(revokeCertificate()), actionCollection(), "edit_revoke_certificate" ); connectEnableOperationSignal( this, mRevokeCertificateAction ); mExtendCertificateAction = new KAction( i18n("Extend"), 0, this, TQT_SLOT(extendCertificate()), actionCollection(), "edit_extend_certificate" ); connectEnableOperationSignal( this, mExtendCertificateAction ); #endif mDeleteCertificateAction = new KAction( i18n("Delete"), "editdelete", Key_Delete, this, TQT_SLOT(slotDeleteCertificate()), actionCollection(), "edit_delete_certificate" ); connectEnableOperationSignal( this, mDeleteCertificateAction ); mValidateCertificateAction = new KAction( i18n("Validate"), "reload", SHIFT + Key_F5, this, TQT_SLOT(slotValidate()), actionCollection(), "certificates_validate" ); connectEnableOperationSignal( this, mValidateCertificateAction ); mImportCertFromFileAction = new KAction( i18n("Import Certificates..."), 0, this, TQT_SLOT(slotImportCertFromFile()), actionCollection(), "file_import_certificates" ); connectEnableOperationSignal( this, mImportCertFromFileAction ); mImportCRLFromFileAction = new KAction( i18n("Import CRLs..."), 0, this, TQT_SLOT(importCRLFromFile()), actionCollection(), "file_import_crls" ); connectEnableOperationSignal( this, mImportCRLFromFileAction ); mExportCertificateAction = new KAction( i18n("Export Certificates..."), "export", 0, this, TQT_SLOT(slotExportCertificate()), actionCollection(), "file_export_certificate" ); mExportSecretKeyAction = new KAction( i18n("Export Secret Key..."), "export", 0, this, TQT_SLOT(slotExportSecretKey()), actionCollection(), "file_export_secret_keys" ); connectEnableOperationSignal( this, mExportSecretKeyAction ); mViewCertDetailsAction = new KAction( i18n("Certificate Details..."), 0, 0, this, TQT_SLOT(slotViewDetails()), actionCollection(), "view_certificate_details" ); mDownloadCertificateAction = new KAction( i18n( "Download"), 0, 0, this, TQT_SLOT(slotDownloadCertificate()), actionCollection(), "download_certificate" ); const TQString dirmngr = KStandardDirs::findExe( "gpgsm" ); mDirMngrFound = !dirmngr.isEmpty(); action = new KAction( i18n("Dump CRL Cache..."), 0, this, TQT_SLOT(slotViewCRLs()), actionCollection(), "crl_dump_crl_cache" ); action->setEnabled( mDirMngrFound ); // we also need dirmngr for this action = new KAction( i18n("Clear CRL Cache..."), 0, this, TQT_SLOT(slotClearCRLs()), actionCollection(), "crl_clear_crl_cache" ); action->setEnabled( mDirMngrFound ); // we also need dirmngr for this action = new KAction( i18n("GnuPG Log Viewer..."), "pgp-keys", 0, this, TQT_SLOT(slotStartWatchGnuPG()), actionCollection(), "tools_start_kwatchgnupg"); // disable action if no kwatchgnupg binary is around if (KStandardDirs::findExe("kwatchgnupg").isEmpty()) action->setEnabled(false); (void)new LabelAction( i18n("Search:"), actionCollection(), "label_action" ); mLineEditAction = new LineEditAction( TQString(), actionCollection(), this, TQT_SLOT(slotSearch()), "query_lineedit_action"); TQStringList lst; lst << i18n("In Local Certificates") << i18n("In External Certificates"); mComboAction = new ComboAction( lst, actionCollection(), this, TQT_SLOT( slotToggleRemote(int) ), "location_combo_action", mNextFindRemote? 1 : 0 ); mFindAction = new KAction( i18n("Find"), "tqfind", 0, this, TQT_SLOT(slotSearch()), actionCollection(), "tqfind" ); KStdAction::keyBindings( this, TQT_SLOT(slotEditKeybindings()), actionCollection() ); KStdAction::preferences( this, TQT_SLOT(slotShowConfigurationDialog()), actionCollection() ); new KAction( i18n( "Configure &GpgME Backend" ), 0, 0, this, TQT_SLOT(slotConfigureGpgME()), actionCollection(), "configure_gpgme" ); createStandardStatusBarAction(); updateImportActions( true ); } void CertManager::updateImportActions( bool enable ) { mImportCRLFromFileAction->setEnabled( mDirMngrFound && enable ); mImportCertFromFileAction->setEnabled( enable ); } void CertManager::slotEditKeybindings() { KKeyDialog::configure( actionCollection(), true ); } void CertManager::slotShowConfigurationDialog() { ConfigureDialog dlg( this ); connect( &dlg, TQT_SIGNAL( configCommitted() ), TQT_SLOT( slotRepaint() ) ); dlg.exec(); } void CertManager::slotConfigureGpgME() { Kleo::CryptoConfig* config = Kleo::CryptoBackendFactory::instance()->config(); if ( config ) { Kleo::CryptoConfigDialog dlg( config ); int result = dlg.exec(); // Forget all data parsed from gpgconf, so that we show updated information // when reopening the configuration dialog. config->clear(); if ( result == TQDialog::Accepted ) { // Tell other apps (e.g. kmail) that the gpgconf data might have changed kapp->dcopClient()->emitDCOPSignal( "KPIM::CryptoConfig", "changed()", TQByteArray() ); } } } void CertManager::slotRepaint() { mKeyListView->tqrepaintContents(); } void CertManager::slotToggleRemote( int idx ) { mNextFindRemote = idx != 0; } void CertManager::slotToggleHierarchicalView( bool hier ) { mHierarchicalView = hier; mKeyListView->setHierarchical( hier ); mKeyListView->setRootIsDecorated( hier ); if ( KAction * act = action("view_expandall") ) act->setEnabled( hier ); if ( KAction * act = action("view_collapseall" ) ) act->setEnabled( hier ); if ( KToggleAction * act = static_cast( action("view_hierarchical") ) ) act->setChecked( hier ); if ( hier && !mCurrentQuery.isEmpty() ) startRedisplay( false ); } void CertManager::slotExpandAll() { for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it ) it.current()->setOpen( true ); } void CertManager::slotCollapseAll() { for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it ) it.current()->setOpen( false ); } void CertManager::connectJobToStatusBarProgress( Kleo::Job * job, const TQString & initialText ) { assert( mProgressBar ); if ( !job ) return; if ( !initialText.isEmpty() ) statusBar()->message( initialText ); connect( job, TQT_SIGNAL(progress(const TQString&,int,int)), mProgressBar, TQT_SLOT(slotProgress(const TQString&,int,int)) ); connect( job, TQT_SIGNAL(done()), mProgressBar, TQT_SLOT(reset()) ); connect( this, TQT_SIGNAL(stopOperations()), job, TQT_SLOT(slotCancel()) ); action("view_stop_operations")->setEnabled( true ); emit enableOperations( false ); } void CertManager::disconnectJobFromStatusBarProgress( const GpgME::Error & err ) { updateStatusBarLabels(); const TQString msg = err.isCanceled() ? i18n("Canceled.") : err ? i18n("Failed.") : i18n("Done.") ; statusBar()->message( msg, 4000 ); action("view_stop_operations")->setEnabled( false ); emit enableOperations( true ); slotSelectionChanged(); } void CertManager::updateStatusBarLabels() { mKeyListView->flushKeys(); int total = 0; for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it ) ++total; mStatusLabel->setText( i18n( "%n Key.","%n Keys.", total ) ); } // // // Key Listing: // // static std::set extractKeyFingerprints( const TQPtrList & items ) { std::set result; for ( TQPtrListIterator it( items ) ; it.current() ; ++it ) if ( const char * fpr = it.current()->key().primaryFingerprint() ) result.insert( fpr ); return result; } static TQStringList stringlistFromSet( const std::set & set ) { // ARGH. This is madness. Shitty TQt containers don't support TQStringList( patterns.begin(), patterns.end() ) :/ TQStringList sl; for ( std::set::const_iterator it = set.begin() ; it != set.end() ; ++it ) // let's make extra sure, maybe someone tries to make TQt not support std::string->TQString conversion sl.push_back( TQString::tqfromLatin1( it->c_str() ) ); return sl; } void CertManager::slotRefreshKeys() { const TQStringList keys = stringlistFromSet( extractKeyFingerprints( mKeyListView->selectedItems() ) ); Kleo::RefreshKeysJob * job = Kleo::CryptoBackendFactory::instance()->smime()->refreshKeysJob(); assert( job ); connect( job, TQT_SIGNAL(result(const GpgME::Error&)), this, TQT_SLOT(slotRefreshKeysResult(const GpgME::Error&)) ); connectJobToStatusBarProgress( job, i18n("Refreshing keys...") ); if ( const GpgME::Error err = job->start( keys ) ) slotRefreshKeysResult( err ); } void CertManager::slotRefreshKeysResult( const GpgME::Error & err ) { disconnectJobFromStatusBarProgress( err ); if ( err.isCanceled() ) return; if ( err ) KMessageBox::error( this, i18n("An error occurred while trying to refresh " "keys:\n%1").arg( TQString::fromLocal8Bit( err.asString() ) ), i18n("Refreshing Keys Failed") ); } static void showKeyListError( TQWidget * tqparent, const GpgME::Error & err ) { assert( err ); const TQString msg = i18n( "

An error occurred while fetching " "the certificates from the backend:

" "

%1

" ) .arg( TQString::fromLocal8Bit( err.asString() ) ); KMessageBox::error( tqparent, msg, i18n( "Certificate Listing Failed" ) ); } void CertManager::slotSearch() { mPreviouslySelectedFingerprints.clear(); // Clear display mKeyListView->clear(); mCurrentQuery = mLineEditAction->text(); startKeyListing( false, false, mCurrentQuery ); } void CertManager::startRedisplay( bool validate ) { mPreviouslySelectedFingerprints = extractKeyFingerprints( mKeyListView->selectedItems() ); if ( mPreviouslySelectedFingerprints.empty() ) startKeyListing( validate, true, mCurrentQuery ); else startKeyListing( validate, true, mPreviouslySelectedFingerprints ); } void CertManager::startKeyListing( bool validating, bool refresh, const std::set & patterns ) { startKeyListing( validating, refresh, stringlistFromSet( patterns ) ); } void CertManager::startKeyListing( bool validating, bool refresh, const TQStringList & patterns ) { mRemote = mNextFindRemote; mLineEditAction->setEnabled( false ); mComboAction->setEnabled( false ); mFindAction->setEnabled( false ); Kleo::KeyListJob * job = 0; if ( !validating && !refresh && mKeyListView->hierarchical() && !patterns.empty() ) job = new Kleo::HierarchicalKeyListJob( Kleo::CryptoBackendFactory::instance()->smime(), mRemote, false, validating ); else job = Kleo::CryptoBackendFactory::instance()->smime()->keyListJob( mRemote, false, validating ); assert( job ); connect( job, TQT_SIGNAL(nextKey(const GpgME::Key&)), mKeyListView, refresh ? TQT_SLOT(slotRefreshKey(const GpgME::Key&)) : TQT_SLOT(slotAddKey(const GpgME::Key&)) ); connect( job, TQT_SIGNAL(result(const GpgME::KeyListResult&)), this, TQT_SLOT(slotKeyListResult(const GpgME::KeyListResult&)) ); connectJobToStatusBarProgress( job, i18n("Fetching keys...") ); const GpgME::Error err = job->start( patterns ) ; if ( err ) { showKeyListError( this, err ); return; } mProgressBar->setProgress( 0, 0 ); // enable busy indicator } static void selectKeys( Kleo::KeyListView * lv, const std::set & fprs ) { if ( !lv || fprs.empty() ) return; for ( TQListViewItemIterator it( lv ) ; it.current() ; ++it ) if ( Kleo::KeyListViewItem * item = Kleo::lvi_cast( it.current() ) ) { const char * fpr = item->key().primaryFingerprint(); item->setSelected( fpr && fprs.tqfind( fpr ) != fprs.end() ); } } void CertManager::slotKeyListResult( const GpgME::KeyListResult & res ) { if ( res.error() ) showKeyListError( this, res.error() ); else if ( res.isTruncated() ) KMessageBox::information( this, i18n("The query result has been truncated.\n" "Either the local or a remote limit on " "the maximum number of returned hits has " "been exceeded.\n" "You can try to increase the local limit " "in the configuration dialog, but if one " "of the configured servers is the limiting " "factor, you have to refine your search.") ); mLineEditAction->setEnabled( true ); mComboAction->setEnabled( true ); mFindAction->setEnabled( true ); mLineEditAction->focusAll(); disconnectJobFromStatusBarProgress( res.error() ); selectKeys( mKeyListView, mPreviouslySelectedFingerprints ); } void CertManager::slotContextMenu(Kleo::KeyListViewItem* item, const TQPoint& point) { if ( !item ) return; if ( TQPopupMenu * popup = static_cast(factory()->container("listview_popup",this)) ) popup->exec( point ); } /** This slot is invoked when the user selects "New certificate" */ void CertManager::newCertificate() { CertificateWizardImpl wizard( this ); wizard.exec(); } /** This slot is invoked when the user selects revoke certificate. The slot will revoke the selected certificates */ void CertManager::revokeCertificate() { qDebug("Not Yet Implemented"); } /** This slot is invoked when the user selects extend certificate. It will send an extension request for the selected certificates */ void CertManager::extendCertificate() { qDebug("Not Yet Implemented"); } // // // Downloading / Importing Certificates // // /** This slot is invoked when the user selects Certificates/Import/From File. */ void CertManager::slotImportCertFromFile() { const TQString filter = "application/x-x509-ca-cert application/x-pkcs12 application/pkcs7-mime"; //const TQString filter = TQString("*.pem *.der *.p7c *.p12|") + i18n("Certificates (*.pem *.der *.p7c *.p12)"); slotImportCertFromFile( KFileDialog::getOpenURL( TQString(), filter, this, i18n( "Select Certificate File" ) ) ); } void CertManager::slotImportCertFromFile( const KURL & certURL ) { if ( !certURL.isValid() ) // empty or malformed return; mPreviouslySelectedFingerprints.clear(); // Prevent two simultaneous imports updateImportActions( false ); // Download the cert KIOext::StoredTransferJob* importJob = KIOext::storedGet( certURL ); importJob->setWindow( this ); connect( importJob, TQT_SIGNAL(result(KIO::Job*)), TQT_SLOT(slotImportResult(KIO::Job*)) ); } void CertManager::slotImportResult( KIO::Job* job ) { if ( job->error() ) { job->showErrorDialog(); } else { KIOext::StoredTransferJob* trJob = static_cast( job ); startCertificateImport( trJob->data(), trJob->url().fileName() ); } updateImportActions( true ); } static void showCertificateDownloadError( TQWidget * tqparent, const GpgME::Error & err, const TQString& certDisplayName ) { assert( err ); const TQString msg = i18n( "

An error occurred while trying " "to download the certificate %1:

" "

%2

" ) .arg( certDisplayName ) .arg( TQString::fromLocal8Bit( err.asString() ) ); KMessageBox::error( tqparent, msg, i18n( "Certificate Download Failed" ) ); } void CertManager::slotDownloadCertificate() { mPreviouslySelectedFingerprints.clear(); TQPtrList items = mKeyListView->selectedItems(); for ( TQPtrListIterator it( items ) ; it.current() ; ++it ) if ( !it.current()->key().isNull() ) if ( const char * fpr = it.current()->key().primaryFingerprint() ) slotStartCertificateDownload( fpr, it.current()->text(0) ); } // Called from slotDownloadCertificate and from the certificate-details widget void CertManager::slotStartCertificateDownload( const TQString& fingerprint, const TQString& displayName ) { if ( fingerprint.isEmpty() ) return; Kleo::DownloadJob * job = Kleo::CryptoBackendFactory::instance()->smime()->downloadJob( false /* no armor */ ); assert( job ); connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)), TQT_SLOT(slotCertificateDownloadResult(const GpgME::Error&,const TQByteArray&)) ); connectJobToStatusBarProgress( job, i18n("Fetching certificate from server...") ); const GpgME::Error err = job->start( fingerprint ); if ( err ) showCertificateDownloadError( this, err, displayName ); else { mProgressBar->setProgress( 0, 0 ); mJobsDisplayNameMap.insert( job, displayName ); } } TQString CertManager::displayNameForJob( const Kleo::Job *job ) { JobsDisplayNameMap::iterator it = mJobsDisplayNameMap.tqfind( job ); TQString displayName; if ( it != mJobsDisplayNameMap.end() ) { displayName = *it; mJobsDisplayNameMap.remove( it ); } else { kdWarning() << "Job not found in map: " << job << endl; } return displayName; } // Don't call directly! void CertManager::slotCertificateDownloadResult( const GpgME::Error & err, const TQByteArray & keyData ) { TQString displayName = displayNameForJob( static_cast( sender() ) ); if ( err ) showCertificateDownloadError( this, err, displayName ); else startCertificateImport( keyData, displayName ); disconnectJobFromStatusBarProgress( err ); } static void showCertificateImportError( TQWidget * tqparent, const GpgME::Error & err, const TQString& certDisplayName ) { assert( err ); const TQString msg = i18n( "

An error occurred while trying " "to import the certificate %1:

" "

%2

" ) .arg( certDisplayName ) .arg( TQString::fromLocal8Bit( err.asString() ) ); KMessageBox::error( tqparent, msg, i18n( "Certificate Import Failed" ) ); } void CertManager::startCertificateImport( const TQByteArray & keyData, const TQString& certDisplayName ) { Kleo::ImportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->importJob(); assert( job ); connect( job, TQT_SIGNAL(result(const GpgME::ImportResult&)), TQT_SLOT(slotCertificateImportResult(const GpgME::ImportResult&)) ); connectJobToStatusBarProgress( job, i18n("Importing certificates...") ); kdDebug() << "Importing certificate. keyData size:" << keyData.size() << endl; const GpgME::Error err = job->start( keyData ); if ( err ) showCertificateImportError( this, err, certDisplayName ); else { mProgressBar->setProgress( 0, 0 ); mJobsDisplayNameMap.insert( job, certDisplayName ); } } void CertManager::slotCertificateImportResult( const GpgME::ImportResult & res ) { TQString displayName = displayNameForJob( static_cast( sender() ) ); if ( res.error().isCanceled() ) { // do nothing } else if ( res.error() ) { showCertificateImportError( this, res.error(), displayName ); } else { const TQString normalLine = i18n("%1%2"); const TQString boldLine = i18n("%1%2"); TQStringList lines; lines.push_back( normalLine.arg( i18n("Total number processed:"), TQString::number( res.numConsidered() ) ) ); lines.push_back( normalLine.arg( i18n("Imported:"), TQString::number( res.numImported() ) ) ); if ( res.newSignatures() ) lines.push_back( normalLine.arg( i18n("New signatures:"), TQString::number( res.newSignatures() ) ) ); if ( res.newUserIDs() ) lines.push_back( normalLine.arg( i18n("New user IDs:"), TQString::number( res.newUserIDs() ) ) ); if ( res.numKeysWithoutUserID() ) lines.push_back( normalLine.arg( i18n("Keys without user IDs:"), TQString::number( res.numKeysWithoutUserID() ) ) ); if ( res.newSubkeys() ) lines.push_back( normalLine.arg( i18n("New subkeys:"), TQString::number( res.newSubkeys() ) ) ); if ( res.newRevocations() ) lines.push_back( boldLine.arg( i18n("Newly revoked:"), TQString::number( res.newRevocations() ) ) ); if ( res.notImported() ) lines.push_back( boldLine.arg( i18n("Not imported:"), TQString::number( res.notImported() ) ) ); if ( res.numUnchanged() ) lines.push_back( normalLine.arg( i18n("Unchanged:"), TQString::number( res.numUnchanged() ) ) ); if ( res.numSecretKeysConsidered() ) lines.push_back( normalLine.arg( i18n("Secret keys processed:"), TQString::number( res.numSecretKeysConsidered() ) ) ); if ( res.numSecretKeysImported() ) lines.push_back( normalLine.arg( i18n("Secret keys imported:"), TQString::number( res.numSecretKeysImported() ) ) ); if ( res.numSecretKeysConsidered() - res.numSecretKeysImported() - res.numSecretKeysUnchanged() > 0 ) lines.push_back( boldLine.arg( i18n("Secret keys not imported:"), TQString::number( res.numSecretKeysConsidered() - res.numSecretKeysImported() - res.numSecretKeysUnchanged() ) ) ); if ( res.numSecretKeysUnchanged() ) lines.push_back( normalLine.arg( i18n("Secret keys unchanged:"), TQString::number( res.numSecretKeysUnchanged() ) ) ); KMessageBox::information( this, i18n( "

Detailed results of importing %1:

" "%2
" ) .arg( displayName ).arg( lines.join( TQString() ) ), i18n( "Certificate Import Result" ) ); disconnectJobFromStatusBarProgress( res.error() ); // save the fingerprints of imported certs for later selection: const std::vector imports = res.imports(); for ( std::vector::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) mPreviouslySelectedFingerprints.insert( it->fingerprint() ); } importNextURLOrRedisplay(); } /** This slot is called when the dirmngr process that imports a certificate file exists. */ void CertManager::slotDirmngrExited() { if ( !mDirmngrProc->normalExit() ) 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" ) ); else if ( mDirmngrProc->exitStatus() ) 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" ) ); else KMessageBox::information( this, i18n( "CRL file imported successfully." ), i18n( "Certificate Manager Information" ) ); delete mDirmngrProc; mDirmngrProc = 0; if ( !mImportCRLTempFile.isEmpty() ) TQFile::remove( mImportCRLTempFile ); updateImportActions( true ); } /** This slot will import CRLs from a file. */ void CertManager::importCRLFromFile() { // loadcrl can only work with DER encoded files (verified with dirmngr 1.0.3) TQString filter = TQString("*.crl *.arl *-crl.der *-arl.der|") + i18n("Certificate Revocation List, DER encoded (*.crl *.arl *-crl.der *-arl.der)"); KURL url = KFileDialog::getOpenURL( TQString(), filter, this, i18n( "Select CRL File" ) ); if ( url.isValid() ) { updateImportActions( false ); if ( url.isLocalFile() ) { startImportCRL( url.path(), false ); updateImportActions( true ); } else { KTempFile tempFile; KURL destURL; destURL.setPath( tempFile.name() ); KIO::Job* copyJob = KIO::file_copy( url, destURL, 0600, true, false ); copyJob->setWindow( this ); connect( copyJob, TQT_SIGNAL( result( KIO::Job * ) ), TQT_SLOT( slotImportCRLJobFinished( KIO::Job * ) ) ); } } } void CertManager::slotImportCRLJobFinished( KIO::Job *job ) { KIO::FileCopyJob* fcjob = static_cast( job ); TQString tempFilePath = fcjob->destURL().path(); if ( job->error() ) { job->showErrorDialog(); TQFile::remove( tempFilePath ); // unlink tempfile updateImportActions( true ); return; } startImportCRL( tempFilePath, true ); } bool CertManager::connectAndStartDirmngr( const char * slot, const char * processname ) { assert( slot ); assert( processname ); assert( mDirmngrProc ); mErrorbuffer = TQString(); connect( mDirmngrProc, TQT_SIGNAL(processExited(KProcess*)), slot ); connect( mDirmngrProc, TQT_SIGNAL(receivedStderr(KProcess*,char*,int) ), this, TQT_SLOT(slotStderr(KProcess*,char*,int)) ); if( !mDirmngrProc->start( KProcess::NotifyOnExit, KProcess::Stderr ) ) { delete mDirmngrProc; mDirmngrProc = 0; KMessageBox::error( this, i18n( "Unable to start %1 process. Please check your installation." ).arg( processname ), i18n( "Certificate Manager Error" ) ); return false; } return true; } void CertManager::startImportCRL( const TQString& filename, bool isTempFile ) { assert( !mDirmngrProc ); mImportCRLTempFile = isTempFile ? filename : TQString(); mDirmngrProc = new KProcess(); *mDirmngrProc << "gpgsm" << "--call-dirmngr" << "loadcrl" << filename; if ( !connectAndStartDirmngr( TQT_SLOT(slotDirmngrExited()), "gpgsm" ) ) { updateImportActions( true ); if ( isTempFile ) TQFile::remove( mImportCRLTempFile ); // unlink tempfile } } void CertManager::startClearCRLs() { assert( !mDirmngrProc ); mDirmngrProc = new KProcess(); *mDirmngrProc << "dirmngr" << "--flush"; //*mDirmngrProc << "gpgsm" << "--call-dimngr" << "flush"; // use this once it's implemented! connectAndStartDirmngr( TQT_SLOT(slotClearCRLsResult()), "dirmngr" ); } void CertManager::slotStderr( KProcess*, char* buf, int len ) { mErrorbuffer += TQString::fromLocal8Bit( buf, len ); } /** This slot will import CRLs from an LDAP server. */ void CertManager::importCRLFromLDAP() { qDebug("Not Yet Implemented"); } void CertManager::slotViewCRLs() { if ( !mCrlView ) mCrlView = new CRLView( this ); mCrlView->show(); mCrlView->slotUpdateView(); } void CertManager::slotClearCRLs() { startClearCRLs(); } void CertManager::slotClearCRLsResult() { assert( mDirmngrProc ); if ( !mDirmngrProc->normalExit() ) 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" ) ); else if ( mDirmngrProc->exitStatus() ) 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" ) ); else KMessageBox::information( this, i18n( "CRL cache cleared successfully." ), i18n( "Certificate Manager Information" ) ); delete mDirmngrProc; mDirmngrProc = 0; } static void showDeleteError( TQWidget * tqparent, const GpgME::Error & err ) { assert( err ); const TQString msg = i18n("

An error occurred while trying to delete " "the certificates:

" "

%1

") .arg( TQString::fromLocal8Bit( err.asString() ) ); KMessageBox::error( tqparent, msg, i18n("Certificate Deletion Failed") ); } static bool ByFingerprint( const GpgME::Key & left, const GpgME::Key & right ) { return qstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) < 0 ; } static bool WithRespectToFingerprints( const GpgME::Key & left, const GpgME::Key & right ) { return qstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) == 0; } void CertManager::slotDeleteCertificate() { mItemsToDelete = mKeyListView->selectedItems(); if ( mItemsToDelete.isEmpty() ) return; std::vector keys; keys.reserve( mItemsToDelete.count() ); TQStringList keyDisplayNames; for ( TQPtrListIterator it( mItemsToDelete ) ; it.current() ; ++it ) if ( !it.current()->key().isNull() ) { keys.push_back( it.current()->key() ); keyDisplayNames.push_back( it.current()->text( 0 ) ); } if ( keys.empty() ) return; if ( !mHierarchyAnalyser ) { mHierarchyAnalyser = new HierarchyAnalyser( this, "mHierarchyAnalyser" ); Kleo::KeyListJob * job = Kleo::CryptoBackendFactory::instance()->smime()->keyListJob(); assert( job ); connect( job, TQT_SIGNAL(nextKey(const GpgME::Key&)), mHierarchyAnalyser, TQT_SLOT(slotNextKey(const GpgME::Key&)) ); connect( job, TQT_SIGNAL(result(const GpgME::KeyListResult&)), this, TQT_SLOT(slotDeleteCertificate()) ); connectJobToStatusBarProgress( job, i18n("Checking key dependencies...") ); if ( const GpgME::Error error = job->start( TQStringList() ) ) { showKeyListError( this, error ); delete mHierarchyAnalyser; mHierarchyAnalyser = 0; } return; } else disconnectJobFromStatusBarProgress( 0 ); std::vector keysToDelete = keys; for ( std::vector::const_iterator it = keys.begin() ; it != keys.end() ; ++it ) if ( !it->isNull() ) { const std::vector subjects = mHierarchyAnalyser->subjectsForIssuerRecursive( it->primaryFingerprint() ); keysToDelete.insert( keysToDelete.end(), subjects.begin(), subjects.end() ); } std::sort( keysToDelete.begin(), keysToDelete.end(), ByFingerprint ); keysToDelete.erase( std::unique( keysToDelete.begin(), keysToDelete.end(), WithRespectToFingerprints ), keysToDelete.end() ); delete mHierarchyAnalyser; mHierarchyAnalyser = 0; if ( keysToDelete.size() > keys.size() ) if ( KMessageBox::warningContinueCancel( this, i18n("Some or all of the selected " "certificates are issuers (CA certificates) " "for other, non-selected certificates.\n" "Deleting a CA certificate will also delete " "all certificates issued by it."), i18n("Deleting CA Certificates") ) != KMessageBox::Continue ) return; const TQString msg = keysToDelete.size() > keys.size() ? i18n("Do you really want to delete this certificate and the %1 certificates it certified?", "Do you really want to delete these %n certificates and the %1 certificates they certified?", keys.size() ).arg( keysToDelete.size() - keys.size() ) : i18n("Do you really want to delete this certificate?", "Do you really want to delete these %n certificates?", keys.size() ) ; if ( KMessageBox::warningContinueCancelList( this, msg, keyDisplayNames, i18n( "Delete Certificates" ), KGuiItem( i18n( "Delete" ), "editdelete" ), "ConfirmDeleteCert", KMessageBox::Dangerous ) != KMessageBox::Continue ) return; if ( Kleo::DeleteJob * job = Kleo::CryptoBackendFactory::instance()->smime()->deleteJob() ) job->slotCancel(); else { TQString str = keys.size() == 1 ? i18n("

An error occurred while trying to delete " "the certificate:

" "

%1

" ) : i18n( "

An error occurred while trying to delete " "the certificates:

" "

%1

" ); KMessageBox::error( this, str.arg( i18n("Operation not supported by the backend.") ), i18n("Certificate Deletion Failed") ); } mItemsToDelete.clear(); // re-create according to the real selection for ( std::vector::const_iterator it = keysToDelete.begin() ; it != keysToDelete.end() ; ++it ) if ( Kleo::KeyListViewItem * item = mKeyListView->itemByFingerprint( it->primaryFingerprint() ) ) mItemsToDelete.append( item ); Kleo::MultiDeleteJob * job = new Kleo::MultiDeleteJob( Kleo::CryptoBackendFactory::instance()->smime() ); assert( job ); connect( job, TQT_SIGNAL(result(const GpgME::Error&,const GpgME::Key&)), TQT_SLOT(slotDeleteResult(const GpgME::Error&,const GpgME::Key&)) ); connectJobToStatusBarProgress( job, i18n("Deleting keys...") ); const GpgME::Error err = job->start( keys, true ); if ( err ) showDeleteError( this, err ); else mProgressBar->setProgress( 0, 0 ); } void CertManager::slotDeleteResult( const GpgME::Error & err, const GpgME::Key & ) { if ( err ) showDeleteError( this, err ); else { const int infinity = 100; // infinite loop guard... mItemsToDelete.setAutoDelete( true ); for ( int i = 0 ; i < infinity ; ++i ) { TQPtrListIterator it( mItemsToDelete ); while ( Kleo::KeyListViewItem * cur = it.current() ) { ++it; if ( cur->childCount() == 0 ) { mItemsToDelete.remove( cur ); } } if ( mItemsToDelete.isEmpty() ) break; } mItemsToDelete.setAutoDelete( false ); Q_ASSERT( mItemsToDelete.isEmpty() ); mItemsToDelete.clear(); } disconnectJobFromStatusBarProgress( err ); } void CertManager::slotViewDetails( Kleo::KeyListViewItem * item ) { if ( !item || item->key().isNull() ) return; // KDialogBase * dialog = new KDialogBase( this, "dialog", false, i18n("Additional Information for Key"), KDialogBase::Close, KDialogBase::Close ); CertificateInfoWidgetImpl * top = new CertificateInfoWidgetImpl( item->key(), isRemote(), dialog ); dialog->setMainWidget( top ); // connect( top, TQT_SIGNAL(requestCertificateDownload(const TQString&, const TQString&)), TQT_SLOT(slotStartCertificateDownload(const TQString&, const TQString&)) ); dialog->show(); } void CertManager::slotViewDetails() { TQPtrList items = mKeyListView->selectedItems(); if ( items.isEmpty() ) return; // selectedItem() doesn't work in Extended mode. // But we only want to show the details of one item... slotViewDetails( items.first() ); } void CertManager::slotSelectionChanged() { mKeyListView->flushKeys(); bool b = mKeyListView->hasSelection(); mExportCertificateAction->setEnabled( b ); mViewCertDetailsAction->setEnabled( b ); mDeleteCertificateAction->setEnabled( b ); #ifdef NOT_IMPLEMENTED_ANYWAY mRevokeCertificateAction->setEnabled( b ); mExtendCertificateAction->setEnabled( b ); #endif mDownloadCertificateAction->setEnabled( b && mRemote ); mValidateCertificateAction->setEnabled( !mRemote ); } void CertManager::slotExportCertificate() { TQPtrList items = mKeyListView->selectedItems(); if ( items.isEmpty() ) return; TQStringList fingerprints; for ( TQPtrListIterator it( items ) ; it.current() ; ++it ) if ( !it.current()->key().isNull() ) if ( const char * fpr = it.current()->key().primaryFingerprint() ) fingerprints.push_back( fpr ); startCertificateExport( fingerprints ); } static void showCertificateExportError( TQWidget * tqparent, const GpgME::Error & err ) { assert( err ); const TQString msg = i18n("

An error occurred while trying to export " "the certificate:

" "

%1

") .arg( TQString::fromLocal8Bit( err.asString() ) ); KMessageBox::error( tqparent, msg, i18n("Certificate Export Failed") ); } void CertManager::startCertificateExport( const TQStringList & fingerprints ) { if ( fingerprints.empty() ) return; // we need to use PEM (ascii armoured) format, since DER (binary) // can't transport more than one certificate *sigh* this is madness :/ Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->publicKeyExportJob( true ); assert( job ); connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)), TQT_SLOT(slotCertificateExportResult(const GpgME::Error&,const TQByteArray&)) ); connectJobToStatusBarProgress( job, i18n("Exporting certificate...") ); const GpgME::Error err = job->start( fingerprints ); if ( err ) showCertificateExportError( this, err ); else mProgressBar->setProgress( 0, 0 ); } // return true if we should proceed, false if we should abort static bool checkOverwrite( const KURL& url, bool& overwrite, TQWidget* w ) { if ( KIO::NetAccess::exists( url, false /*dest*/, w ) ) { if ( KMessageBox::Cancel == KMessageBox::warningContinueCancel( w, i18n( "A file named \"%1\" already exists. " "Are you sure you want to overwrite it?" ).arg( url.prettyURL() ), i18n( "Overwrite File?" ), i18n( "&Overwrite" ) ) ) return false; overwrite = true; } return true; } void CertManager::slotCertificateExportResult( const GpgME::Error & err, const TQByteArray & data ) { disconnectJobFromStatusBarProgress( err ); if ( err ) { showCertificateExportError( this, err ); return; } kdDebug() << "CertManager::slotCertificateExportResult(): got " << data.size() << " bytes" << endl; const TQString filter = TQString("*.pem|") + i18n("ASCII Armored Certificate Bundles (*.pem)"); const KURL url = KFileDialog::getOpenURL( TQString(), filter, this, i18n( "Save Certificate" ) ); if ( !url.isValid() ) return; bool overwrite = false; if ( !checkOverwrite( url, overwrite, this ) ) return; KIO::Job* uploadJob = KIOext::put( data, url, -1, overwrite, false /*resume*/ ); uploadJob->setWindow( this ); connect( uploadJob, TQT_SIGNAL( result( KIO::Job* ) ), this, TQT_SLOT( slotUploadResult( KIO::Job* ) ) ); } void CertManager::slotExportSecretKey() { Kleo::KeySelectionDialog dlg( i18n("Secret Key Export"), "" + i18n("Select the secret key to export " "(Warning: The PKCS#12 format is insecure; " "exporting secret keys is discouraged):") + "", std::vector(), Kleo::KeySelectionDialog::SecretKeys|Kleo::KeySelectionDialog::SMIMEKeys, false /* no multiple selection */, false /* no remember choice box */, this, "secret key export key selection dialog" ); //dlg.setHideInvalidKeys( false ); if ( dlg.exec() != TQDialog::Accepted ) return; startSecretKeyExport( dlg.fingerprint() ); } static void showSecretKeyExportError( TQWidget * tqparent, const GpgME::Error & err ) { assert( err ); const TQString msg = i18n("

An error occurred while trying to export " "the secret key:

" "

%1

") .arg( TQString::fromLocal8Bit( err.asString() ) ); KMessageBox::error( tqparent, msg, i18n("Secret-Key Export Failed") ); } void CertManager::startSecretKeyExport( const TQString & fingerprint ) { if ( fingerprint.isEmpty() ) return; // PENDING(marc): let user choose between binary and PEM format? // Check if gpgsm supports --p12-charset Kleo::CryptoConfig* config = Kleo::CryptoBackendFactory::instance()->config(); TQString charset; if ( config && config->entry( "gpgsm", "Configuration", "p12-charset" ) ) { // This comes from gnupg's sources, agent/minip12.c // In fact, any charset supported by iconv would work, but we don't link to iconv directly... static const char *charsets[] = { "utf8", "iso-8859-1", "iso-8859-15", "iso-8859-2", "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", "iso-8859-7", "iso-8859-8", "iso-8859-9", "koi8-r", "ibm437", "ibm850", "euc-jp", "big5", NULL }; TQStringList charsetList; for ( const char** c = charsets; *c; ++c ) { charsetList.append( TQString::tqfromLatin1( *c ) ); } // TODO this selection could be done in a derived KeySelectionDialog which would add a combobox, // it would be better integrated. bool ok; charset = KInputDialog::getItem( i18n("Exporting secret key..."), i18n("Choose a charset for encoding the pkcs#12 passphrase (utf8 is recommended)"), charsetList, 0, false /*editable*/, &ok, this ); if ( !ok ) return; } Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->secretKeyExportJob( false, charset ); assert( job ); connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)), TQT_SLOT(slotSecretKeyExportResult(const GpgME::Error&,const TQByteArray&)) ); connectJobToStatusBarProgress( job, i18n("Exporting secret key...") ); const GpgME::Error err = job->start( fingerprint ); if ( err ) showSecretKeyExportError( this, err ); else mProgressBar->setProgress( 0, 0 ); } void CertManager::slotSecretKeyExportResult( const GpgME::Error & err, const TQByteArray & data ) { disconnectJobFromStatusBarProgress( err ); if ( err ) { showSecretKeyExportError( this, err ); return; } kdDebug() << "CertManager::slotSecretKeyExportResult(): got " << data.size() << " bytes" << endl; TQString filter = TQString("*.p12|") + i18n("PKCS#12 Key Bundle (*.p12)"); KURL url = KFileDialog::getOpenURL( TQString(), filter, this, i18n( "Save Certificate" ) ); if ( !url.isValid() ) return; bool overwrite = false; if ( !checkOverwrite( url, overwrite, this ) ) return; KIO::Job* uploadJob = KIOext::put( data, url, -1, overwrite, false /*resume*/ ); uploadJob->setWindow( this ); connect( uploadJob, TQT_SIGNAL( result( KIO::Job* ) ), this, TQT_SLOT( slotUploadResult( KIO::Job* ) ) ); } void CertManager::slotUploadResult( KIO::Job* job ) { if ( job->error() ) job->showErrorDialog(); } void CertManager::slotDropped(const KURL::List& lst) { mURLsToImport = lst; if ( !lst.empty() ) importNextURLOrRedisplay(); } void CertManager::importNextURLOrRedisplay() { if ( !mURLsToImport.empty() ) { // We can only import them one by one, otherwise the jobs would run into each other KURL url = mURLsToImport.front(); mURLsToImport.pop_front(); slotImportCertFromFile( url ); } else { if ( isRemote() ) return; startKeyListing( false, true, mPreviouslySelectedFingerprints ); } } void CertManager::slotStartWatchGnuPG() { KProcess certManagerProc; certManagerProc << "kwatchgnupg"; if( !certManagerProc.start( KProcess::DontCare ) ) KMessageBox::error( this, i18n( "Could not start GnuPG LogViewer (kwatchgnupg). " "Please check your installation!" ), i18n( "Kleopatra Error" ) ); } #include "certmanager.moc"