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 TQt library by Trolltech AS, Norway (or with modified versions
00024     of TQt that use the same license as TQt), 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     TQt.  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 // TQt
00093 #include <tqfontmetrics.h>
00094 #include <tqpopupmenu.h>
00095 
00096 // other
00097 #include <algorithm>
00098 #include <assert.h>
00099 #include <kdepimmacros.h>
00100 #include <kinputdialog.h>
00101 namespace {
00102 
00103   class KDE_EXPORT DisplayStrategy : public Kleo::KeyListView::DisplayStrategy{
00104   public:
00105     ~DisplayStrategy() {}
00106 
00107     virtual TQFont keyFont( const GpgME::Key& key, const TQFont& font ) const {
00108       const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key );
00109       return filter ? filter->font( font ) : font;
00110     }
00111     virtual TQColor keyForeground( const GpgME::Key& key, const TQColor& 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 TQColor keyBackground( const GpgME::Key& key, const TQColor& 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 KDE_EXPORT ColumnStrategy : public Kleo::KeyListView::ColumnStrategy {
00126   public:
00127     ~ColumnStrategy() {}
00128 
00129     TQString title( int col ) const;
00130     TQString text( const GpgME::Key & key, int col ) const;
00131     int width( int col, const TQFontMetrics & fm ) const;
00132   };
00133 
00134   TQString 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 TQString();
00140     }
00141   }
00142 
00143   TQString 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() ? TQString::fromUtf8( key.issuerSerial() ) : TQString() ;
00148     default: return TQString();
00149     }
00150   }
00151 
00152   int ColumnStrategy::width( int col, const TQFontMetrics & 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 TQString& query, const TQString & import,
00164               TQWidget* 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( TQListView::Extended );
00188   setCentralWidget( mKeyListView );
00189 
00190   connect( mKeyListView, TQT_SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const TQPoint&,int)),
00191        TQT_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) );
00192   connect( mKeyListView, TQT_SIGNAL(returnPressed(Kleo::KeyListViewItem*)),
00193        TQT_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) );
00194   connect( mKeyListView, TQT_SIGNAL(selectionChanged()),
00195        TQT_SLOT(slotSelectionChanged()) );
00196   connect( mKeyListView, TQT_SIGNAL(contextMenu(Kleo::KeyListViewItem*, const TQPoint&)),
00197            TQT_SLOT(slotContextMenu(Kleo::KeyListViewItem*, const TQPoint&)) );
00198 
00199   connect( mKeyListView, TQT_SIGNAL(dropped(const KURL::List&) ),
00200            TQT_SLOT( slotDropped(const KURL::List&) ) );
00201 
00202   mLineEditAction->setText(query);
00203   if ( !mRemote && !mNextFindRemote || !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( TQSize( 100, mProgressBar->height() * 3 / 5 ) );
00241   bar->addWidget( mProgressBar, 0, true );
00242   mStatusLabel = new TQLabel( bar, "mStatusLabel" );
00243   bar->addWidget( mStatusLabel, 1, false );
00244 }
00245 
00246 static inline void connectEnableOperationSignal( TQObject * s, TQObject * d ) {
00247   TQObject::connect( s, TQT_SIGNAL(enableOperations(bool)),
00248             d, TQT_SLOT(setEnabled(bool)) );
00249 }
00250 
00251 
00252 void CertManager::createActions() {
00253   KAction * action = 0;
00254 
00255   (void)KStdAction::quit( TQT_TQOBJECT(this), TQT_SLOT(close()), actionCollection() );
00256 
00257   action = KStdAction::redisplay( TQT_TQOBJECT(this), TQT_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( TQT_TQOBJECT(this), action );
00264 
00265   action = new KAction( i18n("Stop Operation"), "stop", Key_Escape,
00266             TQT_TQOBJECT(this), TQT_SIGNAL(stopOperations()),
00267             actionCollection(), "view_stop_operations" );
00268   action->setEnabled( false );
00269 
00270   (void)   new KAction( i18n("New Key Pair..."), "filenew", 0,
00271             TQT_TQOBJECT(this), TQT_SLOT(newCertificate()),
00272             actionCollection(), "file_new_certificate" );
00273 
00274   connect( new KToggleAction( i18n("Hierarchical Key List"), 0,
00275                   actionCollection(), "view_hierarchical" ),
00276        TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotToggleHierarchicalView(bool)) );
00277 
00278   action = new KAction( i18n("Expand All"), 0, CTRL+Key_Period,
00279             TQT_TQOBJECT(this), TQT_SLOT(slotExpandAll()),
00280             actionCollection(), "view_expandall" );
00281   action = new KAction( i18n("Collapse All"), 0, CTRL+Key_Comma,
00282             TQT_TQOBJECT(this), TQT_SLOT(slotCollapseAll()),
00283             actionCollection(), "view_collapseall" );
00284 
00285   (void)   new KAction( i18n("Refresh CRLs"), 0, 0,
00286             TQT_TQOBJECT(this), TQT_SLOT(slotRefreshKeys()),
00287             actionCollection(), "certificates_refresh_clr" );
00288 
00289 #ifdef NOT_IMPLEMENTED_ANYWAY
00290   mRevokeCertificateAction = new KAction( i18n("Revoke"), 0,
00291                                           TQT_TQOBJECT(this), TQT_SLOT(revokeCertificate()),
00292                                           actionCollection(), "edit_revoke_certificate" );
00293   connectEnableOperationSignal( this, mRevokeCertificateAction );
00294 
00295   mExtendCertificateAction = new KAction( i18n("Extend"), 0,
00296                                           TQT_TQOBJECT(this), TQT_SLOT(extendCertificate()),
00297                                           actionCollection(), "edit_extend_certificate" );
00298   connectEnableOperationSignal( this, mExtendCertificateAction );
00299 #endif
00300 
00301   mDeleteCertificateAction = new KAction( i18n("Delete"), "editdelete", Key_Delete,
00302                                     TQT_TQOBJECT(this), TQT_SLOT(slotDeleteCertificate()),
00303                                     actionCollection(), "edit_delete_certificate" );
00304   connectEnableOperationSignal( TQT_TQOBJECT(this), mDeleteCertificateAction );
00305 
00306   mValidateCertificateAction = new KAction( i18n("Validate"), "reload", SHIFT + Key_F5,
00307                         TQT_TQOBJECT(this), TQT_SLOT(slotValidate()),
00308                         actionCollection(), "certificates_validate" );
00309   connectEnableOperationSignal( TQT_TQOBJECT(this), mValidateCertificateAction );
00310 
00311   mImportCertFromFileAction = new KAction( i18n("Import Certificates..."), 0,
00312                        TQT_TQOBJECT(this), TQT_SLOT(slotImportCertFromFile()),
00313                        actionCollection(), "file_import_certificates" );
00314   connectEnableOperationSignal( TQT_TQOBJECT(this), mImportCertFromFileAction );
00315 
00316   mImportCRLFromFileAction = new KAction( i18n("Import CRLs..."), 0,
00317                       TQT_TQOBJECT(this), TQT_SLOT(importCRLFromFile()),
00318                       actionCollection(), "file_import_crls" );
00319   connectEnableOperationSignal( TQT_TQOBJECT(this), mImportCRLFromFileAction );
00320 
00321   mExportCertificateAction = new KAction( i18n("Export Certificates..."), "export", 0,
00322                       TQT_TQOBJECT(this), TQT_SLOT(slotExportCertificate()),
00323                       actionCollection(), "file_export_certificate" );
00324 
00325   mExportSecretKeyAction = new KAction( i18n("Export Secret Key..."), "export", 0,
00326                                         TQT_TQOBJECT(this), TQT_SLOT(slotExportSecretKey()),
00327                                         actionCollection(), "file_export_secret_keys" );
00328   connectEnableOperationSignal( TQT_TQOBJECT(this), mExportSecretKeyAction );
00329 
00330   mViewCertDetailsAction = new KAction( i18n("Certificate Details..."), 0, 0,
00331                                         TQT_TQOBJECT(this), TQT_SLOT(slotViewDetails()), actionCollection(),
00332                                         "view_certificate_details" );
00333   mDownloadCertificateAction = new KAction( i18n( "Download"), 0, 0,
00334                                         TQT_TQOBJECT(this), TQT_SLOT(slotDownloadCertificate()), actionCollection(),
00335                                         "download_certificate" );
00336 
00337   const TQString dirmngr = KStandardDirs::findExe( "gpgsm" );
00338   mDirMngrFound = !dirmngr.isEmpty();
00339 
00340   action = new KAction( i18n("Dump CRL Cache..."), 0,
00341             TQT_TQOBJECT(this), TQT_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             TQT_TQOBJECT(this), TQT_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, TQT_TQOBJECT(this),
00351                         TQT_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( TQString(), actionCollection(), TQT_TQOBJECT(this),
00358                     TQT_SLOT(slotSearch()),
00359                     "query_lineedit_action");
00360 
00361   TQStringList lst;
00362   lst << i18n("In Local Certificates") << i18n("In External Certificates");
00363   mComboAction = new ComboAction( lst, actionCollection(), TQT_TQOBJECT(this), TQT_SLOT( slotToggleRemote(int) ),
00364                                   "location_combo_action", mNextFindRemote? 1 : 0 );
00365 
00366   mFindAction = new KAction( i18n("Find"), "find", 0, TQT_TQOBJECT(this), TQT_SLOT(slotSearch()),
00367                  actionCollection(), "find" );
00368 
00369   KStdAction::keyBindings( TQT_TQOBJECT(this), TQT_SLOT(slotEditKeybindings()), actionCollection() );
00370   KStdAction::preferences( TQT_TQOBJECT(this), TQT_SLOT(slotShowConfigurationDialog()), actionCollection() );
00371 
00372   new KAction( i18n( "Configure &GpgME Backend" ), 0, 0, TQT_TQOBJECT(this), TQT_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, TQT_SIGNAL( configCommitted() ), TQT_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 == TQDialog::Accepted )
00406     {
00407       // Tell other apps (e.g. kmail) that the gpgconf data might have changed
00408       kapp->dcopClient()->emitDCOPSignal( "KPIM::CryptoConfig", "changed()", TQByteArray() );
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 ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
00440     it.current()->setOpen( true );
00441 }
00442 
00443 void CertManager::slotCollapseAll() {
00444   for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
00445     it.current()->setOpen( false );
00446 }
00447 
00448 void CertManager::connectJobToStatusBarProgress( Kleo::Job * job, const TQString & initialText ) {
00449   assert( mProgressBar );
00450   if ( !job )
00451     return;
00452   if ( !initialText.isEmpty() )
00453     statusBar()->message( initialText );
00454   connect( job, TQT_SIGNAL(progress(const TQString&,int,int)),
00455        mProgressBar, TQT_SLOT(slotProgress(const TQString&,int,int)) );
00456   connect( job, TQT_SIGNAL(done()), mProgressBar, TQT_SLOT(reset()) );
00457   connect( this, TQT_SIGNAL(stopOperations()), job, TQT_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 TQString 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 ( TQListViewItemIterator 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 TQPtrList<Kleo::KeyListViewItem> & items ) {
00491   std::set<std::string> result;
00492   for ( TQPtrListIterator<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 TQStringList stringlistFromSet( const std::set<std::string> & set ) {
00499   // ARGH. This is madness. Shitty TQt containers don't support TQStringList( patterns.begin(), patterns.end() ) :/
00500   TQStringList 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 TQt not support std::string->TQString conversion
00503     sl.push_back( TQString::fromLatin1( it->c_str() ) );
00504   return sl;
00505 }
00506 
00507 void CertManager::slotRefreshKeys() {
00508   const TQStringList keys = stringlistFromSet( extractKeyFingerprints( mKeyListView->selectedItems() ) );
00509   Kleo::RefreshKeysJob * job = Kleo::CryptoBackendFactory::instance()->smime()->refreshKeysJob();
00510   assert( job );
00511 
00512   connect( job, TQT_SIGNAL(result(const GpgME::Error&)),
00513        this, TQT_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( TQString::fromLocal8Bit( err.asString() ) ),
00527             i18n("Refreshing Keys Failed") );
00528 }
00529 
00530 static void showKeyListError( TQWidget * parent, const GpgME::Error & err ) {
00531   assert( err );
00532   const TQString 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( TQString::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 TQStringList & 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, TQT_SIGNAL(nextKey(const GpgME::Key&)),
00575        mKeyListView, refresh ? TQT_SLOT(slotRefreshKey(const GpgME::Key&)) : TQT_SLOT(slotAddKey(const GpgME::Key&)) );
00576   connect( job, TQT_SIGNAL(result(const GpgME::KeyListResult&)),
00577        this, TQT_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  ( TQListViewItemIterator 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 TQPoint& point) {
00623   if ( !item )
00624     return;
00625   if ( TQPopupMenu * popup = static_cast<TQPopupMenu*>(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 TQString filter = "application/x-x509-ca-cert application/x-pkcs12 application/pkcs7-mime";
00670   //const TQString filter = TQString("*.pem *.der *.p7c *.p12|") + i18n("Certificates (*.pem *.der *.p7c *.p12)");
00671   slotImportCertFromFile( KFileDialog::getOpenURL( TQString(), 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, TQT_SIGNAL(result(KIO::Job*)), TQT_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( TQWidget * parent, const GpgME::Error & err, const TQString& certDisplayName ) {
00704   assert( err );
00705   const TQString 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( TQString::fromLocal8Bit( err.asString() ) );
00710 
00711   KMessageBox::error( parent, msg, i18n( "Certificate Download Failed" ) );
00712 }
00713 
00714 void CertManager::slotDownloadCertificate() {
00715   mPreviouslySelectedFingerprints.clear();
00716   TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems();
00717   for ( TQPtrListIterator<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 TQString& fingerprint, const TQString& 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, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
00733        TQT_SLOT(slotCertificateDownloadResult(const GpgME::Error&,const TQByteArray&)) );
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 TQString CertManager::displayNameForJob( const Kleo::Job *job )
00747 {
00748   JobsDisplayNameMap::iterator it = mJobsDisplayNameMap.find( job );
00749   TQString 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 TQByteArray & keyData ) {
00761 
00762   TQString 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( TQWidget * parent, const GpgME::Error & err, const TQString& certDisplayName ) {
00772   assert( err );
00773   const TQString 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( TQString::fromLocal8Bit( err.asString() ) );
00778   KMessageBox::error( parent, msg, i18n( "Certificate Import Failed" ) );
00779 }
00780 
00781 void CertManager::startCertificateImport( const TQByteArray & keyData, const TQString& certDisplayName ) {
00782   Kleo::ImportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->importJob();
00783   assert( job );
00784 
00785   connect( job, TQT_SIGNAL(result(const GpgME::ImportResult&)),
00786        TQT_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   TQString 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 TQString normalLine = i18n("<tr><td align=\"right\">%1</td><td>%2</td></tr>");
00810     const TQString boldLine = i18n("<tr><td align=\"right\"><b>%1</b></td><td>%2</td></tr>");
00811 
00812     TQStringList lines;
00813     lines.push_back( normalLine.arg( i18n("Total number processed:"),
00814                      TQString::number( res.numConsidered() ) ) );
00815     lines.push_back( normalLine.arg( i18n("Imported:"),
00816                      TQString::number( res.numImported() ) ) );
00817     if ( res.newSignatures() )
00818       lines.push_back( normalLine.arg( i18n("New signatures:"),
00819                        TQString::number( res.newSignatures() ) ) );
00820     if ( res.newUserIDs() )
00821       lines.push_back( normalLine.arg( i18n("New user IDs:"),
00822                        TQString::number( res.newUserIDs() ) ) );
00823     if ( res.numKeysWithoutUserID() )
00824       lines.push_back( normalLine.arg( i18n("Keys without user IDs:"),
00825                        TQString::number( res.numKeysWithoutUserID() ) ) );
00826     if ( res.newSubkeys() )
00827       lines.push_back( normalLine.arg( i18n("New subkeys:"),
00828                        TQString::number( res.newSubkeys() ) ) );
00829     if ( res.newRevocations() )
00830       lines.push_back( boldLine.arg( i18n("Newly revoked:"),
00831                      TQString::number( res.newRevocations() ) ) );
00832     if ( res.notImported() )
00833       lines.push_back( boldLine.arg( i18n("Not imported:"),
00834                      TQString::number( res.notImported() ) ) );
00835     if ( res.numUnchanged() )
00836       lines.push_back( normalLine.arg( i18n("Unchanged:"),
00837                        TQString::number( res.numUnchanged() ) ) );
00838     if ( res.numSecretKeysConsidered() )
00839       lines.push_back( normalLine.arg( i18n("Secret keys processed:"),
00840                        TQString::number( res.numSecretKeysConsidered() ) ) );
00841     if ( res.numSecretKeysImported() )
00842       lines.push_back( normalLine.arg( i18n("Secret keys imported:"),
00843                        TQString::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                      TQString::number( res.numSecretKeysConsidered()
00847                               - res.numSecretKeysImported()
00848                               - res.numSecretKeysUnchanged() ) ) );
00849     if ( res.numSecretKeysUnchanged() )
00850       lines.push_back( normalLine.arg( i18n("Secret keys unchanged:"),
00851                        TQString::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( TQString() ) ),
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       TQFile::remove( mImportCRLTempFile );
00885     updateImportActions( true );
00886 }
00887 
00891 void CertManager::importCRLFromFile() {
00892   // loadcrl can only work with DER encoded files (verified with dirmngr 1.0.3)
00893   TQString filter = TQString("*.crl *.arl *-crl.der *-arl.der|") + i18n("Certificate Revocation List, DER encoded (*.crl *.arl *-crl.der *-arl.der)");
00894   KURL url = KFileDialog::getOpenURL( TQString(),
00895                                       filter,
00896                                       this,
00897                                       i18n( "Select CRL File" ) );
00898   if ( url.isValid() ) {
00899     updateImportActions( false );
00900     if ( url.isLocalFile() ) {
00901       startImportCRL( url.path(), false );
00902       updateImportActions( true );
00903     } else {
00904       KTempFile tempFile;
00905       KURL destURL;
00906       destURL.setPath( tempFile.name() );
00907       KIO::Job* copyJob = KIO::file_copy( url, destURL, 0600, true, false );
00908       copyJob->setWindow( this );
00909       connect( copyJob, TQT_SIGNAL( result( KIO::Job * ) ),
00910                TQT_SLOT( slotImportCRLJobFinished( KIO::Job * ) ) );
00911     }
00912   }
00913 }
00914 
00915 void CertManager::slotImportCRLJobFinished( KIO::Job *job )
00916 {
00917   KIO::FileCopyJob* fcjob = static_cast<KIO::FileCopyJob*>( job );
00918   TQString tempFilePath = fcjob->destURL().path();
00919   if ( job->error() ) {
00920     job->showErrorDialog();
00921     TQFile::remove( tempFilePath ); // unlink tempfile
00922     updateImportActions( true );
00923     return;
00924   }
00925   startImportCRL( tempFilePath, true );
00926 }
00927 
00928 bool CertManager::connectAndStartDirmngr( const char * slot, const char * processname ) {
00929   assert( slot );
00930   assert( processname );
00931   assert( mDirmngrProc );
00932   mErrorbuffer = TQString();
00933   connect( mDirmngrProc, TQT_SIGNAL(processExited(KProcess*)), slot );
00934   connect( mDirmngrProc, TQT_SIGNAL(receivedStderr(KProcess*,char*,int) ),
00935            this, TQT_SLOT(slotStderr(KProcess*,char*,int)) );
00936   if( !mDirmngrProc->start( KProcess::NotifyOnExit, KProcess::Stderr ) ) {
00937     delete mDirmngrProc; mDirmngrProc = 0;
00938     KMessageBox::error( this, i18n( "Unable to start %1 process. Please check your installation." ).arg( processname ), i18n( "Certificate Manager Error" ) );
00939     return false;
00940   }
00941   return true;
00942 }
00943 
00944 void CertManager::startImportCRL( const TQString& filename, bool isTempFile )
00945 {
00946   assert( !mDirmngrProc );
00947   mImportCRLTempFile = isTempFile ? filename : TQString();
00948   mDirmngrProc = new KProcess();
00949   *mDirmngrProc << "gpgsm" << "--call-dirmngr" << "loadcrl" << filename;
00950   if ( !connectAndStartDirmngr( TQT_SLOT(slotDirmngrExited()), "gpgsm" ) ) {
00951     updateImportActions( true );
00952     if ( isTempFile )
00953       TQFile::remove( mImportCRLTempFile ); // unlink tempfile
00954   }
00955 }
00956 
00957 void CertManager::startClearCRLs() {
00958   assert( !mDirmngrProc );
00959   mDirmngrProc = new KProcess();
00960   *mDirmngrProc << "dirmngr" << "--flush";
00961   //*mDirmngrProc << "gpgsm" << "--call-dimngr" << "flush"; // use this once it's implemented!
00962   connectAndStartDirmngr( TQT_SLOT(slotClearCRLsResult()), "dirmngr" );
00963 }
00964 
00965 void CertManager::slotStderr( KProcess*, char* buf, int len ) {
00966   mErrorbuffer += TQString::fromLocal8Bit( buf, len );
00967 }
00968 
00972 void CertManager::importCRLFromLDAP()
00973 {
00974   qDebug("Not Yet Implemented");
00975 }
00976 
00977 void CertManager::slotViewCRLs() {
00978   if ( !mCrlView )
00979     mCrlView = new CRLView( this );
00980 
00981   mCrlView->show();
00982   mCrlView->slotUpdateView();
00983 }
00984 
00985 
00986 void CertManager::slotClearCRLs() {
00987   startClearCRLs();
00988 }
00989 
00990 void CertManager::slotClearCRLsResult() {
00991   assert( mDirmngrProc );
00992   if ( !mDirmngrProc->normalExit() )
00993     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" ) );
00994   else if ( mDirmngrProc->exitStatus() )
00995     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" ) );
00996   else
00997     KMessageBox::information( this, i18n( "CRL cache cleared successfully." ), i18n( "Certificate Manager Information" ) );
00998   delete mDirmngrProc; mDirmngrProc = 0;
00999 }
01000 
01001 static void showDeleteError( TQWidget * parent, const GpgME::Error & err ) {
01002   assert( err );
01003   const TQString msg = i18n("<qt><p>An error occurred while trying to delete "
01004                "the certificates:</p>"
01005                "<p><b>%1</b></p></qt>")
01006     .arg( TQString::fromLocal8Bit( err.asString() ) );
01007   KMessageBox::error( parent, msg, i18n("Certificate Deletion Failed") );
01008 }
01009 
01010 static bool ByFingerprint( const GpgME::Key & left, const GpgME::Key & right ) {
01011   return qstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) < 0 ;
01012 }
01013 
01014 static bool WithRespectToFingerprints( const GpgME::Key & left, const GpgME::Key & right ) {
01015   return qstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) == 0;
01016 }
01017 
01018 void CertManager::slotDeleteCertificate() {
01019   mItemsToDelete = mKeyListView->selectedItems();
01020   if ( mItemsToDelete.isEmpty() )
01021     return;
01022   std::vector<GpgME::Key> keys;
01023   keys.reserve( mItemsToDelete.count() );
01024   TQStringList keyDisplayNames;
01025   for ( TQPtrListIterator<Kleo::KeyListViewItem> it( mItemsToDelete ) ; it.current() ; ++it )
01026     if ( !it.current()->key().isNull() ) {
01027       keys.push_back( it.current()->key() );
01028       keyDisplayNames.push_back( it.current()->text( 0 ) );
01029     }
01030   if ( keys.empty() )
01031     return;
01032 
01033   if ( !mHierarchyAnalyser ) {
01034     mHierarchyAnalyser = new HierarchyAnalyser( TQT_TQOBJECT(this), "mHierarchyAnalyser" );
01035     Kleo::KeyListJob * job = Kleo::CryptoBackendFactory::instance()->smime()->keyListJob();
01036     assert( job );
01037     connect( job, TQT_SIGNAL(nextKey(const GpgME::Key&)),
01038          mHierarchyAnalyser, TQT_SLOT(slotNextKey(const GpgME::Key&)) );
01039     connect( job, TQT_SIGNAL(result(const GpgME::KeyListResult&)),
01040          this, TQT_SLOT(slotDeleteCertificate()) );
01041     connectJobToStatusBarProgress( job, i18n("Checking key dependencies...") );
01042     if ( const GpgME::Error error = job->start( TQStringList() ) ) {
01043       showKeyListError( this, error );
01044       delete mHierarchyAnalyser; mHierarchyAnalyser = 0;
01045     }
01046     return;
01047   } else
01048     disconnectJobFromStatusBarProgress( 0 );
01049 
01050   std::vector<GpgME::Key> keysToDelete = keys;
01051   for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it )
01052     if ( !it->isNull() ) {
01053       const std::vector<GpgME::Key> subjects
01054     = mHierarchyAnalyser->subjectsForIssuerRecursive( it->primaryFingerprint() );
01055       keysToDelete.insert( keysToDelete.end(), subjects.begin(), subjects.end() );
01056     }
01057 
01058   std::sort( keysToDelete.begin(), keysToDelete.end(), ByFingerprint );
01059   keysToDelete.erase( std::unique( keysToDelete.begin(), keysToDelete.end(),
01060                    WithRespectToFingerprints ),
01061               keysToDelete.end() );
01062 
01063   delete mHierarchyAnalyser; mHierarchyAnalyser = 0;
01064 
01065   if ( keysToDelete.size() > keys.size() )
01066     if ( KMessageBox::warningContinueCancel( this,
01067                          i18n("Some or all of the selected "
01068                           "certificates are issuers (CA certificates) "
01069                           "for other, non-selected certificates.\n"
01070                           "Deleting a CA certificate will also delete "
01071                           "all certificates issued by it."),
01072                          i18n("Deleting CA Certificates") )
01073      != KMessageBox::Continue )
01074       return;
01075 
01076   const TQString msg = keysToDelete.size() > keys.size()
01077     ? i18n("Do you really want to delete this certificate and the %1 certificates it certified?",
01078        "Do you really want to delete these %n certificates and the %1 certificates they certified?",
01079        keys.size() ).arg( keysToDelete.size() - keys.size() )
01080     : i18n("Do you really want to delete this certificate?",
01081        "Do you really want to delete these %n certificates?", keys.size() ) ;
01082 
01083   if ( KMessageBox::warningContinueCancelList( this, msg, keyDisplayNames,
01084                            i18n( "Delete Certificates" ),
01085                            KGuiItem( i18n( "Delete" ), "editdelete" ),
01086                            "ConfirmDeleteCert", KMessageBox::Dangerous )
01087        != KMessageBox::Continue )
01088     return;
01089 
01090   if ( Kleo::DeleteJob * job = Kleo::CryptoBackendFactory::instance()->smime()->deleteJob() )
01091     job->slotCancel();
01092   else {
01093     TQString str = keys.size() == 1
01094                   ? i18n("<qt><p>An error occurred while trying to delete "
01095                          "the certificate:</p>"
01096                          "<p><b>%1</b><p></qt>" )
01097                   : i18n( "<qt><p>An error occurred while trying to delete "
01098                           "the certificates:</p>"
01099                           "<p><b>%1</b><p></qt>" );
01100     KMessageBox::error( this,
01101             str.arg( i18n("Operation not supported by the backend.") ),
01102             i18n("Certificate Deletion Failed") );
01103   }
01104 
01105   mItemsToDelete.clear(); // re-create according to the real selection
01106   for ( std::vector<GpgME::Key>::const_iterator it = keysToDelete.begin() ; it != keysToDelete.end() ; ++it )
01107     if ( Kleo::KeyListViewItem * item = mKeyListView->itemByFingerprint( it->primaryFingerprint() ) )
01108       mItemsToDelete.append( item );
01109 
01110   Kleo::MultiDeleteJob * job = new Kleo::MultiDeleteJob( Kleo::CryptoBackendFactory::instance()->smime() );
01111   assert( job );
01112 
01113   connect( job, TQT_SIGNAL(result(const GpgME::Error&,const GpgME::Key&)),
01114        TQT_SLOT(slotDeleteResult(const GpgME::Error&,const GpgME::Key&)) );
01115 
01116   connectJobToStatusBarProgress( job, i18n("Deleting keys...") );
01117 
01118   const GpgME::Error err = job->start( keys, true );
01119   if ( err )
01120     showDeleteError( this, err );
01121   else
01122     mProgressBar->setProgress( 0, 0 );
01123 }
01124 
01125 void CertManager::slotDeleteResult( const GpgME::Error & err, const GpgME::Key & ) {
01126   if ( err )
01127     showDeleteError( this, err );
01128   else {
01129     const int infinity = 100; // infinite loop guard...
01130     mItemsToDelete.setAutoDelete( true );
01131     for ( int i = 0 ; i < infinity ; ++i ) {
01132       TQPtrListIterator<Kleo::KeyListViewItem> it( mItemsToDelete );
01133       while ( Kleo::KeyListViewItem * cur = it.current() ) {
01134     ++it;
01135     if ( cur->childCount() == 0 ) {
01136       mItemsToDelete.remove( cur );
01137     }
01138       }
01139       if ( mItemsToDelete.isEmpty() )
01140     break;
01141     }
01142     mItemsToDelete.setAutoDelete( false );
01143     Q_ASSERT( mItemsToDelete.isEmpty() );
01144     mItemsToDelete.clear();
01145   }
01146   disconnectJobFromStatusBarProgress( err );
01147 }
01148 
01149 void CertManager::slotViewDetails( Kleo::KeyListViewItem * item ) {
01150   if ( !item || item->key().isNull() )
01151     return;
01152 
01153   // <UGH>
01154   KDialogBase * dialog = new KDialogBase( this, "dialog", false, i18n("Additional Information for Key"), KDialogBase::Close, KDialogBase::Close );
01155 
01156   CertificateInfoWidgetImpl * top = new CertificateInfoWidgetImpl( item->key(), isRemote(), dialog );
01157   dialog->setMainWidget( top );
01158   // </UGH>
01159   connect( top, TQT_SIGNAL(requestCertificateDownload(const TQString&, const TQString&)),
01160        TQT_SLOT(slotStartCertificateDownload(const TQString&, const TQString&)) );
01161   dialog->show();
01162 }
01163 
01164 void CertManager::slotViewDetails()
01165 {
01166   TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems();
01167   if ( items.isEmpty() )
01168     return;
01169 
01170   // selectedItem() doesn't work in Extended mode.
01171   // But we only want to show the details of one item...
01172   slotViewDetails( items.first() );
01173 }
01174 
01175 void CertManager::slotSelectionChanged()
01176 {
01177   mKeyListView->flushKeys();
01178   bool b = mKeyListView->hasSelection();
01179   mExportCertificateAction->setEnabled( b );
01180   mViewCertDetailsAction->setEnabled( b );
01181   mDeleteCertificateAction->setEnabled( b );
01182 #ifdef NOT_IMPLEMENTED_ANYWAY
01183   mRevokeCertificateAction->setEnabled( b );
01184   mExtendCertificateAction->setEnabled( b );
01185 #endif
01186   mDownloadCertificateAction->setEnabled( b && mRemote );
01187   mValidateCertificateAction->setEnabled( !mRemote );
01188 }
01189 
01190 void CertManager::slotExportCertificate() {
01191   TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems();
01192   if ( items.isEmpty() )
01193     return;
01194 
01195   TQStringList fingerprints;
01196   for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it )
01197     if ( !it.current()->key().isNull() )
01198       if ( const char * fpr = it.current()->key().primaryFingerprint() )
01199     fingerprints.push_back( fpr );
01200 
01201   startCertificateExport( fingerprints );
01202 }
01203 
01204 static void showCertificateExportError( TQWidget * parent, const GpgME::Error & err ) {
01205   assert( err );
01206   const TQString msg = i18n("<qt><p>An error occurred while trying to export "
01207                "the certificate:</p>"
01208                "<p><b>%1</b></p></qt>")
01209     .arg( TQString::fromLocal8Bit( err.asString() ) );
01210   KMessageBox::error( parent, msg, i18n("Certificate Export Failed") );
01211 }
01212 
01213 void CertManager::startCertificateExport( const TQStringList & fingerprints ) {
01214   if ( fingerprints.empty() )
01215     return;
01216 
01217   // we need to use PEM (ascii armoured) format, since DER (binary)
01218   // can't transport more than one certificate *sigh* this is madness :/
01219   Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->publicKeyExportJob( true );
01220   assert( job );
01221 
01222   connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
01223        TQT_SLOT(slotCertificateExportResult(const GpgME::Error&,const TQByteArray&)) );
01224 
01225   connectJobToStatusBarProgress( job, i18n("Exporting certificate...") );
01226 
01227   const GpgME::Error err = job->start( fingerprints );
01228   if ( err )
01229     showCertificateExportError( this, err );
01230   else
01231     mProgressBar->setProgress( 0, 0 );
01232 }
01233 
01234 // return true if we should proceed, false if we should abort
01235 static bool checkOverwrite( const KURL& url, bool& overwrite, TQWidget* w )
01236 {
01237   if ( KIO::NetAccess::exists( url, false /*dest*/, w ) ) {
01238     if ( KMessageBox::Cancel ==
01239          KMessageBox::warningContinueCancel(
01240                                             w,
01241                                             i18n( "A file named \"%1\" already exists. "
01242                                                   "Are you sure you want to overwrite it?" ).arg( url.prettyURL() ),
01243                                             i18n( "Overwrite File?" ),
01244                                             i18n( "&Overwrite" ) ) )
01245       return false;
01246     overwrite = true;
01247   }
01248   return true;
01249 }
01250 
01251 void CertManager::slotCertificateExportResult( const GpgME::Error & err, const TQByteArray & data ) {
01252   disconnectJobFromStatusBarProgress( err );
01253   if ( err ) {
01254     showCertificateExportError( this, err );
01255     return;
01256   }
01257 
01258   kdDebug() << "CertManager::slotCertificateExportResult(): got " << data.size() << " bytes" << endl;
01259 
01260   const TQString filter = TQString("*.pem|") + i18n("ASCII Armored Certificate Bundles (*.pem)");
01261   const KURL url = KFileDialog::getOpenURL( TQString(),
01262                                       filter,
01263                                       this,
01264                                       i18n( "Save Certificate" ) );
01265   if ( !url.isValid() )
01266     return;
01267 
01268   bool overwrite = false;
01269   if ( !checkOverwrite( url, overwrite, this ) )
01270     return;
01271 
01272   KIO::Job* uploadJob = KIOext::put( data, url, -1, overwrite, false /*resume*/ );
01273   uploadJob->setWindow( this );
01274   connect( uploadJob, TQT_SIGNAL( result( KIO::Job* ) ),
01275            this, TQT_SLOT( slotUploadResult( KIO::Job* ) ) );
01276 }
01277 
01278 
01279 void CertManager::slotExportSecretKey() {
01280   Kleo::KeySelectionDialog dlg( i18n("Secret Key Export"),
01281                 "<qt>" +
01282                                 i18n("Select the secret key to export "
01283                      "(<b>Warning: The PKCS#12 format is insecure; "
01284                      "exporting secret keys is discouraged</b>):") +
01285                                 "</qt>",
01286                 std::vector<GpgME::Key>(),
01287                 Kleo::KeySelectionDialog::SecretKeys|Kleo::KeySelectionDialog::SMIMEKeys,
01288                 false /* no multiple selection */,
01289                 false /* no remember choice box */,
01290                 this, "secret key export key selection dialog" );
01291   //dlg.setHideInvalidKeys( false );
01292 
01293   if ( dlg.exec() != TQDialog::Accepted )
01294     return;
01295 
01296   startSecretKeyExport( dlg.fingerprint() );
01297 }
01298 
01299 static void showSecretKeyExportError( TQWidget * parent, const GpgME::Error & err ) {
01300   assert( err );
01301   const TQString msg = i18n("<qt><p>An error occurred while trying to export "
01302                "the secret key:</p>"
01303                "<p><b>%1</b></p></qt>")
01304     .arg( TQString::fromLocal8Bit( err.asString() ) );
01305   KMessageBox::error( parent, msg, i18n("Secret-Key Export Failed") );
01306 }
01307 
01308 void CertManager::startSecretKeyExport( const TQString & fingerprint ) {
01309   if ( fingerprint.isEmpty() )
01310     return;
01311 
01312   // PENDING(marc): let user choose between binary and PEM format?
01313 
01314   // Check if gpgsm supports --p12-charset
01315   Kleo::CryptoConfig* config = Kleo::CryptoBackendFactory::instance()->config();
01316   TQString charset;
01317   if ( config && config->entry( "gpgsm", "Configuration", "p12-charset" ) ) {
01318     // This comes from gnupg's sources, agent/minip12.c
01319     // In fact, any charset supported by iconv would work, but we don't link to iconv directly...
01320     static const char *charsets[] = {
01321       "utf8",
01322       "iso-8859-1",
01323       "iso-8859-15",
01324       "iso-8859-2",
01325       "iso-8859-3",
01326       "iso-8859-4",
01327       "iso-8859-5",
01328       "iso-8859-6",
01329       "iso-8859-7",
01330       "iso-8859-8",
01331       "iso-8859-9",
01332       "koi8-r",
01333       "ibm437",
01334       "ibm850",
01335       "euc-jp",
01336       "big5",
01337       NULL
01338     };
01339     TQStringList charsetList;
01340     for ( const char** c = charsets; *c; ++c ) {
01341       charsetList.append( TQString::fromLatin1( *c ) );
01342     }
01343 
01344     // TODO this selection could be done in a derived KeySelectionDialog which would add a combobox,
01345     // it would be better integrated.
01346     bool ok;
01347     charset = KInputDialog::getItem( i18n("Exporting secret key..."),
01348                                      i18n("Choose a charset for encoding the pkcs#12 passphrase (utf8 is recommended)"),
01349                                      charsetList,
01350                                      0, false /*editable*/,
01351                                      &ok, this );
01352     if ( !ok )
01353       return;
01354   }
01355 
01356   Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->secretKeyExportJob( false, charset );
01357   assert( job );
01358 
01359   connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
01360        TQT_SLOT(slotSecretKeyExportResult(const GpgME::Error&,const TQByteArray&)) );
01361 
01362   connectJobToStatusBarProgress( job, i18n("Exporting secret key...") );
01363 
01364   const GpgME::Error err = job->start( fingerprint );
01365   if ( err )
01366     showSecretKeyExportError( this, err );
01367   else
01368     mProgressBar->setProgress( 0, 0 );
01369 }
01370 
01371 void CertManager::slotSecretKeyExportResult( const GpgME::Error & err, const TQByteArray & data ) {
01372   disconnectJobFromStatusBarProgress( err );
01373   if ( err ) {
01374     showSecretKeyExportError( this, err );
01375     return;
01376   }
01377 
01378   kdDebug() << "CertManager::slotSecretKeyExportResult(): got " << data.size() << " bytes" << endl;
01379   TQString filter = TQString("*.p12|") + i18n("PKCS#12 Key Bundle (*.p12)");
01380   KURL url = KFileDialog::getOpenURL( TQString(),
01381                                       filter,
01382                                       this,
01383                                       i18n( "Save Certificate" ) );
01384   if ( !url.isValid() )
01385     return;
01386 
01387   bool overwrite = false;
01388   if ( !checkOverwrite( url, overwrite, this ) )
01389     return;
01390 
01391   KIO::Job* uploadJob = KIOext::put( data, url, -1, overwrite, false /*resume*/ );
01392   uploadJob->setWindow( this );
01393   connect( uploadJob, TQT_SIGNAL( result( KIO::Job* ) ),
01394            this, TQT_SLOT( slotUploadResult( KIO::Job* ) ) );
01395 }
01396 
01397 void CertManager::slotUploadResult( KIO::Job* job )
01398 {
01399   if ( job->error() )
01400     job->showErrorDialog();
01401 }
01402 
01403 void CertManager::slotDropped(const KURL::List& lst)
01404 {
01405   mURLsToImport = lst;
01406   if ( !lst.empty() )
01407     importNextURLOrRedisplay();
01408 }
01409 
01410 void CertManager::importNextURLOrRedisplay()
01411 {
01412   if ( !mURLsToImport.empty() ) {
01413     // We can only import them one by one, otherwise the jobs would run into each other
01414     KURL url = mURLsToImport.front();
01415     mURLsToImport.pop_front();
01416     slotImportCertFromFile( url );
01417   } else {
01418     if ( isRemote() )
01419       return;
01420     startKeyListing( false, true, mPreviouslySelectedFingerprints );
01421   }
01422 }
01423 
01424 void CertManager::slotStartWatchGnuPG()
01425 {
01426   KProcess certManagerProc;
01427   certManagerProc << "kwatchgnupg";
01428 
01429   if( !certManagerProc.start( KProcess::DontCare ) )
01430     KMessageBox::error( this, i18n( "Could not start GnuPG LogViewer (kwatchgnupg). "
01431                                     "Please check your installation!" ),
01432                                     i18n( "Kleopatra Error" ) );
01433 }
01434 
01435 #include "certmanager.moc"