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