certmanager Library API Documentation

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