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