certmanager

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