/*
    certmanager.cpp

    This file is part of Kleopatra, the KDE keymanager
    Copyright (c) 2001,2002,2004 Klarälvdalens Datakonsult AB

    Kleopatra is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Kleopatra is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the TQt library by Trolltech AS, Norway (or with modified versions
    of TQt that use the same license as TQt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    TQt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "certmanager.h"

#include "certlistview.h"
#include "certificatewizardimpl.h"
#include "certificateinfowidgetimpl.h"
#include "crlview.h"
#include "customactions.h"
#include "hierarchyanalyser.h"
#include "storedtransferjob.h"
#include "conf/configuredialog.h"

// libkleopatra
#include <kleo/cryptobackendfactory.h>
#include <kleo/downloadjob.h>
#include <kleo/importjob.h>
#include <kleo/exportjob.h>
#include <kleo/multideletejob.h>
#include <kleo/deletejob.h>
#include <kleo/keylistjob.h>
#include <kleo/dn.h>
#include <kleo/keyfilter.h>
#include <kleo/keyfiltermanager.h>
#include <kleo/hierarchicalkeylistjob.h>
#include <kleo/refreshkeysjob.h>
#include <kleo/cryptoconfig.h>

#include <ui/progressdialog.h>
#include <ui/progressbar.h>
#include <ui/keyselectiondialog.h>
#include <ui/cryptoconfigdialog.h>

// GPGME++
#include <gpgmepp/importresult.h>
#include <gpgmepp/keylistresult.h>
#include <gpgmepp/key.h>

// KDE
#include <tdefiledialog.h>
#include <kprocess.h>
#include <tdeaction.h>
#include <tdeapplication.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <dcopclient.h>
#include <tdetoolbar.h>
#include <kstatusbar.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <kdialogbase.h>
#include <kkeydialog.h>
#include <tdetempfile.h>
#include <tdeio/job.h>
#include <tdeio/netaccess.h>
#include <tdestdaccel.h>

// TQt
#include <tqfontmetrics.h>
#include <tqpopupmenu.h>

// other
#include <algorithm>
#include <assert.h>
#include <tdepimmacros.h>
#include <kinputdialog.h>
namespace {

  class KDE_EXPORT DisplayStrategy : public Kleo::KeyListView::DisplayStrategy{
  public:
    ~DisplayStrategy() {}

    virtual TQFont keyFont( const GpgME::Key& key, const TQFont& font ) const {
      const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key );
      return filter ? filter->font( font ) : font;
    }
    virtual TQColor keyForeground( const GpgME::Key& key, const TQColor& c ) const {
      const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key );
      if ( filter && filter->fgColor().isValid() )
        return filter->fgColor();
      return c;
    }
    virtual TQColor keyBackground( const GpgME::Key& key, const TQColor& c  ) const {
      const Kleo::KeyFilter* filter = Kleo::KeyFilterManager::instance()->filterMatching( key );
      if ( filter && filter->bgColor().isValid() )
        return filter->bgColor();
      return c;
    }
  };

  class KDE_EXPORT ColumnStrategy : public Kleo::KeyListView::ColumnStrategy {
  public:
    ~ColumnStrategy() {}

    TQString title( int col ) const;
    TQString text( const GpgME::Key & key, int col ) const;
    int width( int col, const TQFontMetrics & fm ) const;
  };

  TQString ColumnStrategy::title( int col ) const {
    switch ( col ) {
    case 0: return i18n("Subject");
    case 1: return i18n("Issuer");
    case 2: return i18n("Serial");
    default: return TQString();
    }
  }

  TQString ColumnStrategy::text( const GpgME::Key & key, int col ) const {
    switch ( col ) {
    case 0: return Kleo::DN( key.userID(0).id() ).prettyDN();
    case 1: return Kleo::DN( key.issuerName() ).prettyDN();
    case 2: return key.issuerSerial() ? TQString::fromUtf8( key.issuerSerial() ) : TQString() ;
    default: return TQString();
    }
  }

  int ColumnStrategy::width( int col, const TQFontMetrics & fm ) const {
    int factor = -1;
    switch ( col ) {
    case 0: factor = 6; break;
    case 1: factor = 4; break;
    default: return -1;
    }
    return fm.width( title( col ) ) * factor;
  }
} // anon namespace

CertManager::CertManager( bool remote, const TQString& query, const TQString & import,
			  TQWidget* parent, const char* name, WFlags f )
  : TDEMainWindow( parent, name, f|WDestructiveClose ),
    mCrlView( 0 ),
    mDirmngrProc( 0 ),
    mHierarchyAnalyser( 0 ),
    mLineEditAction( 0 ),
    mComboAction( 0 ),
    mFindAction( 0 ),
    mImportCertFromFileAction( 0 ),
    mImportCRLFromFileAction( 0 ),
    mNextFindRemote( remote ),
    mRemote( remote ),
    mDirMngrFound( false )
{
  readConfig( query.isEmpty() );
  createStatusBar();
  createActions();

  createGUI();
  setAutoSaveSettings();

  // Main Window --------------------------------------------------
  mKeyListView = new CertKeyListView( new ColumnStrategy(), new DisplayStrategy(), this, "mKeyListView" );
  mKeyListView->setSelectionMode( TQListView::Extended );
  setCentralWidget( mKeyListView );

  connect( mKeyListView, TQT_SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const TQPoint&,int)),
	   TQT_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) );
  connect( mKeyListView, TQT_SIGNAL(returnPressed(Kleo::KeyListViewItem*)),
	   TQT_SLOT(slotViewDetails(Kleo::KeyListViewItem*)) );
  connect( mKeyListView, TQT_SIGNAL(selectionChanged()),
	   TQT_SLOT(slotSelectionChanged()) );
  connect( mKeyListView, TQT_SIGNAL(contextMenu(Kleo::KeyListViewItem*, const TQPoint&)),
           TQT_SLOT(slotContextMenu(Kleo::KeyListViewItem*, const TQPoint&)) );

  connect( mKeyListView, TQT_SIGNAL(dropped(const KURL::List&) ),
           TQT_SLOT( slotDropped(const KURL::List&) ) );

  mLineEditAction->setText(query);
  if ( !mRemote && !mNextFindRemote || !query.isEmpty() )
    slotSearch();

  if ( !import.isEmpty() )
    slotImportCertFromFile( KURL( import ) );

  slotToggleHierarchicalView( mHierarchicalView );
  updateStatusBarLabels();
  slotSelectionChanged(); // initial state for selection-dependent actions
}

CertManager::~CertManager() {
  writeConfig();
  delete mDirmngrProc; mDirmngrProc = 0;
  delete mHierarchyAnalyser; mHierarchyAnalyser = 0;
}

void CertManager::readConfig( bool noQueryGiven ) {
  TDEConfig config( "kleopatrarc" );
  config.setGroup( "Display Options" );
  mHierarchicalView = config.readBoolEntry( "hierarchicalView", false );
  if ( noQueryGiven ) {
    mNextFindRemote = config.readBoolEntry( "startInRemoteMode", false );
  }
}

void CertManager::writeConfig() {
  TDEConfig config( "kleopatrarc" );
  config.setGroup( "Display Options" );
  config.writeEntry( "hierarchicalView", mKeyListView->hierarchical() );
  config.writeEntry( "startInRemoteMode", mNextFindRemote );
}

void CertManager::createStatusBar() {
  KStatusBar * bar = statusBar();
  mProgressBar = new Kleo::ProgressBar( bar, "mProgressBar" );
  mProgressBar->reset();
  mProgressBar->setFixedSize( TQSize( 100, mProgressBar->height() * 3 / 5 ) );
  bar->addWidget( mProgressBar, 0, true );
  mStatusLabel = new TQLabel( bar, "mStatusLabel" );
  bar->addWidget( mStatusLabel, 1, false );
}

static inline void connectEnableOperationSignal( TQObject * s, TQObject * d ) {
  TQObject::connect( s, TQT_SIGNAL(enableOperations(bool)),
		    d, TQT_SLOT(setEnabled(bool)) );
}


void CertManager::createActions() {
  TDEAction * action = 0;

  (void)KStdAction::quit( TQT_TQOBJECT(this), TQT_SLOT(close()), actionCollection() );

  action = KStdAction::redisplay( TQT_TQOBJECT(this), TQT_SLOT(slotRedisplay()), actionCollection() );
  // work around the fact that the stdaction has no shortcut
  TDEShortcut reloadShortcut = TDEStdAccel::shortcut(TDEStdAccel::Reload);
  reloadShortcut.append(KKey(CTRL + Key_R));
  action->setShortcut( reloadShortcut );

  connectEnableOperationSignal( TQT_TQOBJECT(this), action );

  action = new TDEAction( i18n("Stop Operation"), "process-stop", Key_Escape,
			TQT_TQOBJECT(this), TQT_SIGNAL(stopOperations()),
			actionCollection(), "view_stop_operations" );
  action->setEnabled( false );

  (void)   new TDEAction( i18n("New Key Pair..."), "document-new", 0,
			TQT_TQOBJECT(this), TQT_SLOT(newCertificate()),
			actionCollection(), "file_new_certificate" );

  connect( new TDEToggleAction( i18n("Hierarchical Key List"), 0,
			      actionCollection(), "view_hierarchical" ),
	   TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotToggleHierarchicalView(bool)) );

  action = new TDEAction( i18n("Expand All"), 0, CTRL+Key_Period,
			TQT_TQOBJECT(this), TQT_SLOT(slotExpandAll()),
			actionCollection(), "view_expandall" );
  action = new TDEAction( i18n("Collapse All"), 0, CTRL+Key_Comma,
			TQT_TQOBJECT(this), TQT_SLOT(slotCollapseAll()),
			actionCollection(), "view_collapseall" );

  (void)   new TDEAction( i18n("Refresh CRLs"), 0, 0,
			TQT_TQOBJECT(this), TQT_SLOT(slotRefreshKeys()),
			actionCollection(), "certificates_refresh_clr" );

#ifdef NOT_IMPLEMENTED_ANYWAY
  mRevokeCertificateAction = new TDEAction( i18n("Revoke"), 0,
                                          TQT_TQOBJECT(this), TQT_SLOT(revokeCertificate()),
                                          actionCollection(), "edit_revoke_certificate" );
  connectEnableOperationSignal( this, mRevokeCertificateAction );

  mExtendCertificateAction = new TDEAction( i18n("Extend"), 0,
                                          TQT_TQOBJECT(this), TQT_SLOT(extendCertificate()),
                                          actionCollection(), "edit_extend_certificate" );
  connectEnableOperationSignal( this, mExtendCertificateAction );
#endif

  mDeleteCertificateAction = new TDEAction( i18n("Delete"), "edit-delete", Key_Delete,
                                    TQT_TQOBJECT(this), TQT_SLOT(slotDeleteCertificate()),
                                    actionCollection(), "edit_delete_certificate" );
  connectEnableOperationSignal( TQT_TQOBJECT(this), mDeleteCertificateAction );

  mValidateCertificateAction = new TDEAction( i18n("Validate"), "reload", SHIFT + Key_F5,
					    TQT_TQOBJECT(this), TQT_SLOT(slotValidate()),
					    actionCollection(), "certificates_validate" );
  connectEnableOperationSignal( TQT_TQOBJECT(this), mValidateCertificateAction );

  mImportCertFromFileAction = new TDEAction( i18n("Import Certificates..."), 0,
					   TQT_TQOBJECT(this), TQT_SLOT(slotImportCertFromFile()),
					   actionCollection(), "file_import_certificates" );
  connectEnableOperationSignal( TQT_TQOBJECT(this), mImportCertFromFileAction );

  mImportCRLFromFileAction = new TDEAction( i18n("Import CRLs..."), 0,
					  TQT_TQOBJECT(this), TQT_SLOT(importCRLFromFile()),
					  actionCollection(), "file_import_crls" );
  connectEnableOperationSignal( TQT_TQOBJECT(this), mImportCRLFromFileAction );

  mExportCertificateAction = new TDEAction( i18n("Export Certificates..."), "export", 0,
					  TQT_TQOBJECT(this), TQT_SLOT(slotExportCertificate()),
					  actionCollection(), "file_export_certificate" );

  mExportSecretKeyAction = new TDEAction( i18n("Export Secret Key..."), "export", 0,
                                        TQT_TQOBJECT(this), TQT_SLOT(slotExportSecretKey()),
                                        actionCollection(), "file_export_secret_keys" );
  connectEnableOperationSignal( TQT_TQOBJECT(this), mExportSecretKeyAction );

  mViewCertDetailsAction = new TDEAction( i18n("Certificate Details..."), 0, 0,
                                        TQT_TQOBJECT(this), TQT_SLOT(slotViewDetails()), actionCollection(),
                                        "view_certificate_details" );
  mDownloadCertificateAction = new TDEAction( i18n( "Download"), 0, 0,
                                        TQT_TQOBJECT(this), TQT_SLOT(slotDownloadCertificate()), actionCollection(),
                                        "download_certificate" );

  const TQString dirmngr = TDEStandardDirs::findExe( "gpgsm" );
  mDirMngrFound = !dirmngr.isEmpty();

  action = new TDEAction( i18n("Dump CRL Cache..."), 0,
			TQT_TQOBJECT(this), TQT_SLOT(slotViewCRLs()),
			actionCollection(), "crl_dump_crl_cache" );
  action->setEnabled( mDirMngrFound ); // we also need dirmngr for this

  action = new TDEAction( i18n("Clear CRL Cache..."), 0,
			TQT_TQOBJECT(this), TQT_SLOT(slotClearCRLs()),
			actionCollection(), "crl_clear_crl_cache" );
  action->setEnabled( mDirMngrFound ); // we also need dirmngr for this

  action = new TDEAction( i18n("GnuPG Log Viewer..."), "pgp-keys", 0, TQT_TQOBJECT(this),
                        TQT_SLOT(slotStartWatchGnuPG()), actionCollection(), "tools_start_kwatchgnupg");
  // disable action if no kwatchgnupg binary is around
  if (TDEStandardDirs::findExe("kwatchgnupg").isEmpty()) action->setEnabled(false);

  (void)new LabelAction( i18n("Search:"), actionCollection(), "label_action" );

  mLineEditAction = new LineEditAction( TQString(), actionCollection(), TQT_TQOBJECT(this),
					TQT_SLOT(slotSearch()),
					"query_lineedit_action");

  TQStringList lst;
  lst << i18n("In Local Certificates") << i18n("In External Certificates");
  mComboAction = new ComboAction( lst, actionCollection(), TQT_TQOBJECT(this), TQT_SLOT( slotToggleRemote(int) ),
                                  "location_combo_action", mNextFindRemote? 1 : 0 );

  mFindAction = new TDEAction( i18n("Find"), "edit-find", 0, TQT_TQOBJECT(this), TQT_SLOT(slotSearch()),
			     actionCollection(), "find" );

  KStdAction::keyBindings( TQT_TQOBJECT(this), TQT_SLOT(slotEditKeybindings()), actionCollection() );
  KStdAction::preferences( TQT_TQOBJECT(this), TQT_SLOT(slotShowConfigurationDialog()), actionCollection() );

  new TDEAction( i18n( "Configure &GpgME Backend" ), 0, 0, TQT_TQOBJECT(this), TQT_SLOT(slotConfigureGpgME()),
               actionCollection(), "configure_gpgme" );

  createStandardStatusBarAction();
  updateImportActions( true );
}

void CertManager::updateImportActions( bool enable ) {
  mImportCRLFromFileAction->setEnabled( mDirMngrFound && enable );
  mImportCertFromFileAction->setEnabled( enable );
}

void CertManager::slotEditKeybindings() {
  KKeyDialog::configure( actionCollection(), true );
}

void CertManager::slotShowConfigurationDialog() {
  ConfigureDialog dlg( this );
  connect( &dlg, TQT_SIGNAL( configCommitted() ), TQT_SLOT( slotRepaint() ) );
  dlg.exec();
}

void CertManager::slotConfigureGpgME() {
  Kleo::CryptoConfig* config = Kleo::CryptoBackendFactory::instance()->config();
  if ( config ) {
    Kleo::CryptoConfigDialog dlg( config );

    int result = dlg.exec();

    // Forget all data parsed from gpgconf, so that we show updated information
    // when reopening the configuration dialog.
    config->clear();

    if ( result == TQDialog::Accepted )
    {
      // Tell other apps (e.g. kmail) that the gpgconf data might have changed
      kapp->dcopClient()->emitDCOPSignal( "KPIM::CryptoConfig", "changed()", TQByteArray() );
    }
  }
}

void CertManager::slotRepaint()
{
  mKeyListView->repaintContents();
}

void CertManager::slotToggleRemote( int idx ) {
  mNextFindRemote = idx != 0;
}

void CertManager::slotToggleHierarchicalView( bool hier ) {
  mHierarchicalView = hier;
  mKeyListView->setHierarchical( hier );
  mKeyListView->setRootIsDecorated( hier );
  if ( TDEAction * act = action("view_expandall") )
    act->setEnabled( hier );
  if ( TDEAction * act = action("view_collapseall" ) )
    act->setEnabled( hier );
  if ( TDEToggleAction * act =
      static_cast<TDEToggleAction*>( action("view_hierarchical") ) )
    act->setChecked( hier );

  if ( hier && !mCurrentQuery.isEmpty() )
    startRedisplay( false );
}

void CertManager::slotExpandAll() {
  for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
    it.current()->setOpen( true );
}

void CertManager::slotCollapseAll() {
  for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
    it.current()->setOpen( false );
}

void CertManager::connectJobToStatusBarProgress( Kleo::Job * job, const TQString & initialText ) {
  assert( mProgressBar );
  if ( !job )
    return;
  if ( !initialText.isEmpty() )
    statusBar()->message( initialText );
  connect( job, TQT_SIGNAL(progress(const TQString&,int,int)),
	   mProgressBar, TQT_SLOT(slotProgress(const TQString&,int,int)) );
  connect( job, TQT_SIGNAL(done()), mProgressBar, TQT_SLOT(reset()) );
  connect( this, TQT_SIGNAL(stopOperations()), job, TQT_SLOT(slotCancel()) );

  action("view_stop_operations")->setEnabled( true );
  emit enableOperations( false );
}

void CertManager::disconnectJobFromStatusBarProgress( const GpgME::Error & err ) {
  updateStatusBarLabels();
  const TQString msg = err.isCanceled() ? i18n("Canceled.")
    : err ? i18n("Failed.")
    : i18n("Done.") ;
  statusBar()->message( msg, 4000 );

  action("view_stop_operations")->setEnabled( false );
  emit enableOperations( true );
  slotSelectionChanged();
}

void CertManager::updateStatusBarLabels() {
  mKeyListView->flushKeys();
  int total = 0;
  for ( TQListViewItemIterator it( mKeyListView ) ; it.current() ; ++it )
    ++total;
  mStatusLabel->setText( i18n( "%n Key.","%n Keys.", total ) );
}

//
//
// Key Listing:
//
//


static std::set<std::string> extractKeyFingerprints( const TQPtrList<Kleo::KeyListViewItem> & items ) {
  std::set<std::string> result;
  for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it )
    if ( const char * fpr = it.current()->key().primaryFingerprint() )
      result.insert( fpr );
  return result;
}

static TQStringList stringlistFromSet( const std::set<std::string> & set ) {
  // ARGH. This is madness. Shitty TQt containers don't support TQStringList( patterns.begin(), patterns.end() ) :/
  TQStringList sl;
  for ( std::set<std::string>::const_iterator it = set.begin() ; it != set.end() ; ++it )
    // let's make extra sure, maybe someone tries to make TQt not support std::string->TQString conversion
    sl.push_back( TQString::fromLatin1( it->c_str() ) );
  return sl;
}

void CertManager::slotRefreshKeys() {
  const TQStringList keys = stringlistFromSet( extractKeyFingerprints( mKeyListView->selectedItems() ) );
  Kleo::RefreshKeysJob * job = Kleo::CryptoBackendFactory::instance()->smime()->refreshKeysJob();
  assert( job );

  connect( job, TQT_SIGNAL(result(const GpgME::Error&)),
	   this, TQT_SLOT(slotRefreshKeysResult(const GpgME::Error&)) );

  connectJobToStatusBarProgress( job, i18n("Refreshing keys...") );
  if ( const GpgME::Error err = job->start( keys ) )
    slotRefreshKeysResult( err );
}

void CertManager::slotRefreshKeysResult( const GpgME::Error & err ) {
  disconnectJobFromStatusBarProgress( err );
  if ( err.isCanceled() )
    return;
  if ( err )
    KMessageBox::error( this, i18n("An error occurred while trying to refresh "
				   "keys:\n%1").arg( TQString::fromLocal8Bit( err.asString() ) ),
			i18n("Refreshing Keys Failed") );
}

static void showKeyListError( TQWidget * parent, const GpgME::Error & err ) {
  assert( err );
  const TQString msg = i18n( "<qt><p>An error occurred while fetching "
			    "the certificates from the backend:</p>"
			    "<p><b>%1</b></p></qt>" )
    .arg( TQString::fromLocal8Bit( err.asString() ) );

  KMessageBox::error( parent, msg, i18n( "Certificate Listing Failed" ) );
}

void CertManager::slotSearch() {
  mPreviouslySelectedFingerprints.clear();
  // Clear display
  mKeyListView->clear();
  mCurrentQuery = mLineEditAction->text();
  startKeyListing( false, false, mCurrentQuery );
}

void CertManager::startRedisplay( bool validate ) {
  mPreviouslySelectedFingerprints = extractKeyFingerprints( mKeyListView->selectedItems() );
  if ( mPreviouslySelectedFingerprints.empty() )
    startKeyListing( validate, true, mCurrentQuery );
  else
    startKeyListing( validate, true, mPreviouslySelectedFingerprints );
}

void CertManager::startKeyListing( bool validating, bool refresh, const std::set<std::string> & patterns ) {
  startKeyListing( validating, refresh, stringlistFromSet( patterns ) );
}

void CertManager::startKeyListing( bool validating, bool refresh, const TQStringList & patterns ) {
  mRemote = mNextFindRemote;
  mLineEditAction->setEnabled( false );
  mComboAction->setEnabled( false );
  mFindAction->setEnabled( false );

  Kleo::KeyListJob * job = 0;
  if ( !validating && !refresh && mKeyListView->hierarchical() && !patterns.empty() )
    job = new Kleo::HierarchicalKeyListJob( Kleo::CryptoBackendFactory::instance()->smime(),
					    mRemote, false, validating );
  else
    job = Kleo::CryptoBackendFactory::instance()->smime()->keyListJob( mRemote, false, validating );
  assert( job );

  connect( job, TQT_SIGNAL(nextKey(const GpgME::Key&)),
	   mKeyListView, refresh ? TQT_SLOT(slotRefreshKey(const GpgME::Key&)) : TQT_SLOT(slotAddKey(const GpgME::Key&)) );
  connect( job, TQT_SIGNAL(result(const GpgME::KeyListResult&)),
	   this, TQT_SLOT(slotKeyListResult(const GpgME::KeyListResult&)) );

  connectJobToStatusBarProgress( job, i18n("Fetching keys...") );

  const GpgME::Error err = job->start( patterns ) ;
  if ( err ) {
    showKeyListError( this, err );
    return;
  }
  mProgressBar->setProgress( 0, 0 ); // enable busy indicator
}

static void selectKeys( Kleo::KeyListView * lv, const std::set<std::string> & fprs ) {
  if ( !lv || fprs.empty() )
    return;
  for  ( TQListViewItemIterator it( lv ) ; it.current() ; ++it )
    if ( Kleo::KeyListViewItem * item = Kleo::lvi_cast<Kleo::KeyListViewItem>( it.current() ) ) {
      const char * fpr = item->key().primaryFingerprint();
      item->setSelected( fpr && fprs.find( fpr ) != fprs.end() );
    }
}

void CertManager::slotKeyListResult( const GpgME::KeyListResult & res ) {
  if ( res.error() )
    showKeyListError( this, res.error() );
  else if ( res.isTruncated() )
    KMessageBox::information( this,
			      i18n("The query result has been truncated.\n"
				   "Either the local or a remote limit on "
				   "the maximum number of returned hits has "
				   "been exceeded.\n"
				   "You can try to increase the local limit "
				   "in the configuration dialog, but if one "
				   "of the configured servers is the limiting "
				   "factor, you have to refine your search.") );

  mLineEditAction->setEnabled( true );
  mComboAction->setEnabled( true );
  mFindAction->setEnabled( true );

  mLineEditAction->focusAll();
  disconnectJobFromStatusBarProgress( res.error() );
  selectKeys( mKeyListView, mPreviouslySelectedFingerprints );
}

void CertManager::slotContextMenu(Kleo::KeyListViewItem* item, const TQPoint& point) {
  if ( !item )
    return;
  if ( TQPopupMenu * popup = static_cast<TQPopupMenu*>(factory()->container("listview_popup",this)) )
    popup->exec( point );
}

/**
  This slot is invoked when the user selects "New certificate"
*/
void CertManager::newCertificate()
{
  CertificateWizardImpl wizard( this );
  wizard.exec();
}

/**
   This slot is invoked when the user selects revoke certificate.
   The slot will revoke the selected certificates
*/
void CertManager::revokeCertificate()
{
  tqDebug("Not Yet Implemented");
}

/**
   This slot is invoked when the user selects extend certificate.
   It will send an extension request for the selected certificates
*/
void CertManager::extendCertificate()
{
  tqDebug("Not Yet Implemented");
}


//
//
// Downloading / Importing Certificates
//
//


/**
   This slot is invoked when the user selects Certificates/Import/From File.
*/
void CertManager::slotImportCertFromFile()
{
  const TQString filter = "application/x-x509-ca-cert application/x-pkcs12 application/pkcs7-mime";
  //const TQString filter = TQString("*.pem *.der *.p7c *.p12|") + i18n("Certificates (*.pem *.der *.p7c *.p12)");
  slotImportCertFromFile( KFileDialog::getOpenURL( TQString(), filter, this,
                                                   i18n( "Select Certificate File" ) ) );
}

void CertManager::slotImportCertFromFile( const KURL & certURL )
{
  if ( !certURL.isValid() ) // empty or malformed
    return;

  mPreviouslySelectedFingerprints.clear();

  // Prevent two simultaneous imports
  updateImportActions( false );

  // Download the cert
  TDEIOext::StoredTransferJob* importJob = TDEIOext::storedGet( certURL );
  importJob->setWindow( this );
  connect( importJob, TQT_SIGNAL(result(TDEIO::Job*)), TQT_SLOT(slotImportResult(TDEIO::Job*)) );
}

void CertManager::slotImportResult( TDEIO::Job* job )
{
  if ( job->error() ) {
    job->showErrorDialog();
  } else {
    TDEIOext::StoredTransferJob* trJob = static_cast<TDEIOext::StoredTransferJob *>( job );
    startCertificateImport( trJob->data(), trJob->url().fileName() );
  }

  updateImportActions( true );
}

static void showCertificateDownloadError( TQWidget * parent, const GpgME::Error & err, const TQString& certDisplayName ) {
  assert( err );
  const TQString msg = i18n( "<qt><p>An error occurred while trying "
			    "to download the certificate %1:</p>"
			    "<p><b>%2</b></p></qt>" )
                      .arg( certDisplayName )
                      .arg( TQString::fromLocal8Bit( err.asString() ) );

  KMessageBox::error( parent, msg, i18n( "Certificate Download Failed" ) );
}

void CertManager::slotDownloadCertificate() {
  mPreviouslySelectedFingerprints.clear();
  TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems();
  for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it )
    if ( !it.current()->key().isNull() )
      if ( const char * fpr = it.current()->key().primaryFingerprint() )
        slotStartCertificateDownload( fpr, it.current()->text(0) );
}

// Called from slotDownloadCertificate and from the certificate-details widget
void CertManager::slotStartCertificateDownload( const TQString& fingerprint, const TQString& displayName ) {
  if ( fingerprint.isEmpty() )
    return;

  Kleo::DownloadJob * job =
    Kleo::CryptoBackendFactory::instance()->smime()->downloadJob( false /* no armor */ );
  assert( job );

  connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
	   TQT_SLOT(slotCertificateDownloadResult(const GpgME::Error&,const TQByteArray&)) );

  connectJobToStatusBarProgress( job, i18n("Fetching certificate from server...") );

  const GpgME::Error err = job->start( fingerprint );
  if ( err )
    showCertificateDownloadError( this, err, displayName );
  else {
    mProgressBar->setProgress( 0, 0 );
    mJobsDisplayNameMap.insert( job, displayName );
  }
}

TQString CertManager::displayNameForJob( const Kleo::Job *job )
{
  JobsDisplayNameMap::iterator it = mJobsDisplayNameMap.find( job );
  TQString displayName;
  if ( it != mJobsDisplayNameMap.end() ) {
    displayName = *it;
    mJobsDisplayNameMap.remove( it );
  } else {
    kdWarning() << "Job not found in map: " << job << endl;
  }
  return displayName;
}

// Don't call directly!
void CertManager::slotCertificateDownloadResult( const GpgME::Error & err, const TQByteArray & keyData ) {

  TQString displayName = displayNameForJob( static_cast<const Kleo::Job *>( sender() ) );

  if ( err )
    showCertificateDownloadError( this, err, displayName );
  else
    startCertificateImport( keyData, displayName );
  disconnectJobFromStatusBarProgress( err );
}

static void showCertificateImportError( TQWidget * parent, const GpgME::Error & err, const TQString& certDisplayName ) {
  assert( err );
  const TQString msg = i18n( "<qt><p>An error occurred while trying "
			    "to import the certificate %1:</p>"
			    "<p><b>%2</b></p></qt>" )
                      .arg( certDisplayName )
                      .arg( TQString::fromLocal8Bit( err.asString() ) );
  KMessageBox::error( parent, msg, i18n( "Certificate Import Failed" ) );
}

void CertManager::startCertificateImport( const TQByteArray & keyData, const TQString& certDisplayName ) {
  Kleo::ImportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->importJob();
  assert( job );

  connect( job, TQT_SIGNAL(result(const GpgME::ImportResult&)),
	   TQT_SLOT(slotCertificateImportResult(const GpgME::ImportResult&)) );

  connectJobToStatusBarProgress( job, i18n("Importing certificates...") );

  kdDebug() << "Importing certificate. keyData size:" << keyData.size() << endl;
  const GpgME::Error err = job->start( keyData );
  if ( err )
    showCertificateImportError( this, err, certDisplayName );
  else {
    mProgressBar->setProgress( 0, 0 );
    mJobsDisplayNameMap.insert( job, certDisplayName );
  }
}

void CertManager::slotCertificateImportResult( const GpgME::ImportResult & res ) {
  TQString displayName = displayNameForJob( static_cast<const Kleo::Job *>( sender() ) );

  if ( res.error().isCanceled() ) {
    // do nothing
  } else if ( res.error() ) {
    showCertificateImportError( this, res.error(), displayName );
  } else {

    const TQString normalLine = i18n("<tr><td align=\"right\">%1</td><td>%2</td></tr>");
    const TQString boldLine = i18n("<tr><td align=\"right\"><b>%1</b></td><td>%2</td></tr>");

    TQStringList lines;
    lines.push_back( normalLine.arg( i18n("Total number processed:"),
				     TQString::number( res.numConsidered() ) ) );
    lines.push_back( normalLine.arg( i18n("Imported:"),
				     TQString::number( res.numImported() ) ) );
    if ( res.newSignatures() )
      lines.push_back( normalLine.arg( i18n("New signatures:"),
				       TQString::number( res.newSignatures() ) ) );
    if ( res.newUserIDs() )
      lines.push_back( normalLine.arg( i18n("New user IDs:"),
				       TQString::number( res.newUserIDs() ) ) );
    if ( res.numKeysWithoutUserID() )
      lines.push_back( normalLine.arg( i18n("Keys without user IDs:"),
				       TQString::number( res.numKeysWithoutUserID() ) ) );
    if ( res.newSubkeys() )
      lines.push_back( normalLine.arg( i18n("New subkeys:"),
				       TQString::number( res.newSubkeys() ) ) );
    if ( res.newRevocations() )
      lines.push_back( boldLine.arg( i18n("Newly revoked:"),
				     TQString::number( res.newRevocations() ) ) );
    if ( res.notImported() )
      lines.push_back( boldLine.arg( i18n("Not imported:"),
				     TQString::number( res.notImported() ) ) );
    if ( res.numUnchanged() )
      lines.push_back( normalLine.arg( i18n("Unchanged:"),
				       TQString::number( res.numUnchanged() ) ) );
    if ( res.numSecretKeysConsidered() )
      lines.push_back( normalLine.arg( i18n("Secret keys processed:"),
				       TQString::number( res.numSecretKeysConsidered() ) ) );
    if ( res.numSecretKeysImported() )
      lines.push_back( normalLine.arg( i18n("Secret keys imported:"),
				       TQString::number( res.numSecretKeysImported() ) ) );
    if ( res.numSecretKeysConsidered() - res.numSecretKeysImported() - res.numSecretKeysUnchanged() > 0 )
      lines.push_back( boldLine.arg( i18n("Secret keys <em>not</em> imported:"),
				     TQString::number( res.numSecretKeysConsidered()
						      - res.numSecretKeysImported()
						      - res.numSecretKeysUnchanged() ) ) );
    if ( res.numSecretKeysUnchanged() )
      lines.push_back( normalLine.arg( i18n("Secret keys unchanged:"),
				       TQString::number( res.numSecretKeysUnchanged() ) ) );

    KMessageBox::information( this,
			      i18n( "<qt><p>Detailed results of importing %1:</p>"
				    "<table>%2</table></qt>" )
			      .arg( displayName ).arg( lines.join( TQString() ) ),
			      i18n( "Certificate Import Result" ) );

    disconnectJobFromStatusBarProgress( res.error() );
    // save the fingerprints of imported certs for later selection:
    const std::vector<GpgME::Import> imports = res.imports();
    for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it )
      mPreviouslySelectedFingerprints.insert( it->fingerprint() );
  }
  importNextURLOrRedisplay();
}



/**
   This slot is called when the dirmngr process that imports a
   certificate file exists.
*/
void CertManager::slotDirmngrExited() {
    if ( !mDirmngrProc->normalExit() )
        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" ) );
    else if ( mDirmngrProc->exitStatus() )
      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" ) );
    else
      KMessageBox::information( this, i18n( "CRL file imported successfully." ), i18n( "Certificate Manager Information" ) );

    delete mDirmngrProc; mDirmngrProc = 0;
    if ( !mImportCRLTempFile.isEmpty() )
      TQFile::remove( mImportCRLTempFile );
    updateImportActions( true );
}

/**
   This slot will import CRLs from a file.
*/
void CertManager::importCRLFromFile() {
  // loadcrl can only work with DER encoded files (verified with dirmngr 1.0.3)
  TQString filter = TQString("*.crl *.arl *-crl.der *-arl.der|") + i18n("Certificate Revocation List, DER encoded (*.crl *.arl *-crl.der *-arl.der)");
  KURL url = KFileDialog::getOpenURL( TQString(),
                                      filter,
                                      this,
                                      i18n( "Select CRL File" ) );
  if ( url.isValid() ) {
    updateImportActions( false );
    if ( url.isLocalFile() ) {
      startImportCRL( url.path(), false );
      updateImportActions( true );
    } else {
      KTempFile tempFile;
      KURL destURL;
      destURL.setPath( tempFile.name() );
      TDEIO::Job* copyJob = TDEIO::file_copy( url, destURL, 0600, true, false );
      copyJob->setWindow( this );
      connect( copyJob, TQT_SIGNAL( result( TDEIO::Job * ) ),
               TQT_SLOT( slotImportCRLJobFinished( TDEIO::Job * ) ) );
    }
  }
}

void CertManager::slotImportCRLJobFinished( TDEIO::Job *job )
{
  TDEIO::FileCopyJob* fcjob = static_cast<TDEIO::FileCopyJob*>( job );
  TQString tempFilePath = fcjob->destURL().path();
  if ( job->error() ) {
    job->showErrorDialog();
    TQFile::remove( tempFilePath ); // unlink tempfile
    updateImportActions( true );
    return;
  }
  startImportCRL( tempFilePath, true );
}

bool CertManager::connectAndStartDirmngr( const char * slot, const char * processname ) {
  assert( slot );
  assert( processname );
  assert( mDirmngrProc );
  mErrorbuffer = TQString();
  connect( mDirmngrProc, TQT_SIGNAL(processExited(TDEProcess*)), slot );
  connect( mDirmngrProc, TQT_SIGNAL(receivedStderr(TDEProcess*,char*,int) ),
           this, TQT_SLOT(slotStderr(TDEProcess*,char*,int)) );
  if( !mDirmngrProc->start( TDEProcess::NotifyOnExit, TDEProcess::Stderr ) ) {
    delete mDirmngrProc; mDirmngrProc = 0;
    KMessageBox::error( this, i18n( "Unable to start %1 process. Please check your installation." ).arg( processname ), i18n( "Certificate Manager Error" ) );
    return false;
  }
  return true;
}

void CertManager::startImportCRL( const TQString& filename, bool isTempFile )
{
  assert( !mDirmngrProc );
  mImportCRLTempFile = isTempFile ? filename : TQString();
  mDirmngrProc = new TDEProcess();
  *mDirmngrProc << "gpgsm" << "--call-dirmngr" << "loadcrl" << filename;
  if ( !connectAndStartDirmngr( TQT_SLOT(slotDirmngrExited()), "gpgsm" ) ) {
    updateImportActions( true );
    if ( isTempFile )
      TQFile::remove( mImportCRLTempFile ); // unlink tempfile
  }
}

void CertManager::startClearCRLs() {
  assert( !mDirmngrProc );
  mDirmngrProc = new TDEProcess();
  *mDirmngrProc << "dirmngr" << "--flush";
  //*mDirmngrProc << "gpgsm" << "--call-dimngr" << "flush"; // use this once it's implemented!
  connectAndStartDirmngr( TQT_SLOT(slotClearCRLsResult()), "dirmngr" );
}

void CertManager::slotStderr( TDEProcess*, char* buf, int len ) {
  mErrorbuffer += TQString::fromLocal8Bit( buf, len );
}

/**
   This slot will import CRLs from an LDAP server.
*/
void CertManager::importCRLFromLDAP()
{
  tqDebug("Not Yet Implemented");
}

void CertManager::slotViewCRLs() {
  if ( !mCrlView )
    mCrlView = new CRLView( this );

  mCrlView->show();
  mCrlView->slotUpdateView();
}


void CertManager::slotClearCRLs() {
  startClearCRLs();
}

void CertManager::slotClearCRLsResult() {
  assert( mDirmngrProc );
  if ( !mDirmngrProc->normalExit() )
    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" ) );
  else if ( mDirmngrProc->exitStatus() )
    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" ) );
  else
    KMessageBox::information( this, i18n( "CRL cache cleared successfully." ), i18n( "Certificate Manager Information" ) );
  delete mDirmngrProc; mDirmngrProc = 0;
}

static void showDeleteError( TQWidget * parent, const GpgME::Error & err ) {
  assert( err );
  const TQString msg = i18n("<qt><p>An error occurred while trying to delete "
			   "the certificates:</p>"
			   "<p><b>%1</b></p></qt>")
    .arg( TQString::fromLocal8Bit( err.asString() ) );
  KMessageBox::error( parent, msg, i18n("Certificate Deletion Failed") );
}

static bool ByFingerprint( const GpgME::Key & left, const GpgME::Key & right ) {
  return tqstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) < 0 ;
}

static bool WithRespectToFingerprints( const GpgME::Key & left, const GpgME::Key & right ) {
  return tqstricmp( left.primaryFingerprint(), right.primaryFingerprint() ) == 0;
}

void CertManager::slotDeleteCertificate() {
  mItemsToDelete = mKeyListView->selectedItems();
  if ( mItemsToDelete.isEmpty() )
    return;
  std::vector<GpgME::Key> keys;
  keys.reserve( mItemsToDelete.count() );
  TQStringList keyDisplayNames;
  for ( TQPtrListIterator<Kleo::KeyListViewItem> it( mItemsToDelete ) ; it.current() ; ++it )
    if ( !it.current()->key().isNull() ) {
      keys.push_back( it.current()->key() );
      keyDisplayNames.push_back( it.current()->text( 0 ) );
    }
  if ( keys.empty() )
    return;

  if ( !mHierarchyAnalyser ) {
    mHierarchyAnalyser = new HierarchyAnalyser( TQT_TQOBJECT(this), "mHierarchyAnalyser" );
    Kleo::KeyListJob * job = Kleo::CryptoBackendFactory::instance()->smime()->keyListJob();
    assert( job );
    connect( job, TQT_SIGNAL(nextKey(const GpgME::Key&)),
	     mHierarchyAnalyser, TQT_SLOT(slotNextKey(const GpgME::Key&)) );
    connect( job, TQT_SIGNAL(result(const GpgME::KeyListResult&)),
	     this, TQT_SLOT(slotDeleteCertificate()) );
    connectJobToStatusBarProgress( job, i18n("Checking key dependencies...") );
    if ( const GpgME::Error error = job->start( TQStringList() ) ) {
      showKeyListError( this, error );
      delete mHierarchyAnalyser; mHierarchyAnalyser = 0;
    }
    return;
  } else
    disconnectJobFromStatusBarProgress( 0 );

  std::vector<GpgME::Key> keysToDelete = keys;
  for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it )
    if ( !it->isNull() ) {
      const std::vector<GpgME::Key> subjects
	= mHierarchyAnalyser->subjectsForIssuerRecursive( it->primaryFingerprint() );
      keysToDelete.insert( keysToDelete.end(), subjects.begin(), subjects.end() );
    }

  std::sort( keysToDelete.begin(), keysToDelete.end(), ByFingerprint );
  keysToDelete.erase( std::unique( keysToDelete.begin(), keysToDelete.end(),
				   WithRespectToFingerprints ),
		      keysToDelete.end() );

  delete mHierarchyAnalyser; mHierarchyAnalyser = 0;

  if ( keysToDelete.size() > keys.size() )
    if ( KMessageBox::warningContinueCancel( this,
					     i18n("Some or all of the selected "
						  "certificates are issuers (CA certificates) "
						  "for other, non-selected certificates.\n"
						  "Deleting a CA certificate will also delete "
						  "all certificates issued by it."),
					     i18n("Deleting CA Certificates") )
	 != KMessageBox::Continue )
      return;

  const TQString msg = keysToDelete.size() > keys.size()
    ? i18n("Do you really want to delete this certificate and the %1 certificates it certified?",
	   "Do you really want to delete these %n certificates and the %1 certificates they certified?",
	   keys.size() ).arg( keysToDelete.size() - keys.size() )
    : i18n("Do you really want to delete this certificate?",
	   "Do you really want to delete these %n certificates?", keys.size() ) ;

  if ( KMessageBox::warningContinueCancelList( this, msg, keyDisplayNames,
					       i18n( "Delete Certificates" ),
					       KGuiItem( i18n( "Delete" ), "edit-delete" ),
					       "ConfirmDeleteCert", KMessageBox::Dangerous )
       != KMessageBox::Continue )
    return;

  if ( Kleo::DeleteJob * job = Kleo::CryptoBackendFactory::instance()->smime()->deleteJob() )
    job->slotCancel();
  else {
    TQString str = keys.size() == 1
                  ? i18n("<qt><p>An error occurred while trying to delete "
                         "the certificate:</p>"
                         "<p><b>%1</b><p></qt>" )
                  : i18n( "<qt><p>An error occurred while trying to delete "
                          "the certificates:</p>"
                          "<p><b>%1</b><p></qt>" );
    KMessageBox::error( this,
			str.arg( i18n("Operation not supported by the backend.") ),
			i18n("Certificate Deletion Failed") );
  }

  mItemsToDelete.clear(); // re-create according to the real selection
  for ( std::vector<GpgME::Key>::const_iterator it = keysToDelete.begin() ; it != keysToDelete.end() ; ++it )
    if ( Kleo::KeyListViewItem * item = mKeyListView->itemByFingerprint( it->primaryFingerprint() ) )
      mItemsToDelete.append( item );

  Kleo::MultiDeleteJob * job = new Kleo::MultiDeleteJob( Kleo::CryptoBackendFactory::instance()->smime() );
  assert( job );

  connect( job, TQT_SIGNAL(result(const GpgME::Error&,const GpgME::Key&)),
	   TQT_SLOT(slotDeleteResult(const GpgME::Error&,const GpgME::Key&)) );

  connectJobToStatusBarProgress( job, i18n("Deleting keys...") );

  const GpgME::Error err = job->start( keys, true );
  if ( err )
    showDeleteError( this, err );
  else
    mProgressBar->setProgress( 0, 0 );
}

void CertManager::slotDeleteResult( const GpgME::Error & err, const GpgME::Key & ) {
  if ( err )
    showDeleteError( this, err );
  else {
    const int infinity = 100; // infinite loop guard...
    mItemsToDelete.setAutoDelete( true );
    for ( int i = 0 ; i < infinity ; ++i ) {
      TQPtrListIterator<Kleo::KeyListViewItem> it( mItemsToDelete );
      while ( Kleo::KeyListViewItem * cur = it.current() ) {
	++it;
	if ( cur->childCount() == 0 ) {
	  mItemsToDelete.remove( cur );
	}
      }
      if ( mItemsToDelete.isEmpty() )
	break;
    }
    mItemsToDelete.setAutoDelete( false );
    Q_ASSERT( mItemsToDelete.isEmpty() );
    mItemsToDelete.clear();
  }
  disconnectJobFromStatusBarProgress( err );
}

void CertManager::slotViewDetails( Kleo::KeyListViewItem * item ) {
  if ( !item || item->key().isNull() )
    return;

  // <UGH>
  KDialogBase * dialog = new KDialogBase( this, "dialog", false, i18n("Additional Information for Key"), KDialogBase::Close, KDialogBase::Close );

  CertificateInfoWidgetImpl * top = new CertificateInfoWidgetImpl( item->key(), isRemote(), dialog );
  dialog->setMainWidget( top );
  // </UGH>
  connect( top, TQT_SIGNAL(requestCertificateDownload(const TQString&, const TQString&)),
	   TQT_SLOT(slotStartCertificateDownload(const TQString&, const TQString&)) );
  dialog->show();
}

void CertManager::slotViewDetails()
{
  TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems();
  if ( items.isEmpty() )
    return;

  // selectedItem() doesn't work in Extended mode.
  // But we only want to show the details of one item...
  slotViewDetails( items.first() );
}

void CertManager::slotSelectionChanged()
{
  mKeyListView->flushKeys();
  bool b = mKeyListView->hasSelection();
  mExportCertificateAction->setEnabled( b );
  mViewCertDetailsAction->setEnabled( b );
  mDeleteCertificateAction->setEnabled( b );
#ifdef NOT_IMPLEMENTED_ANYWAY
  mRevokeCertificateAction->setEnabled( b );
  mExtendCertificateAction->setEnabled( b );
#endif
  mDownloadCertificateAction->setEnabled( b && mRemote );
  mValidateCertificateAction->setEnabled( !mRemote );
}

void CertManager::slotExportCertificate() {
  TQPtrList<Kleo::KeyListViewItem> items = mKeyListView->selectedItems();
  if ( items.isEmpty() )
    return;

  TQStringList fingerprints;
  for ( TQPtrListIterator<Kleo::KeyListViewItem> it( items ) ; it.current() ; ++it )
    if ( !it.current()->key().isNull() )
      if ( const char * fpr = it.current()->key().primaryFingerprint() )
	fingerprints.push_back( fpr );

  startCertificateExport( fingerprints );
}

static void showCertificateExportError( TQWidget * parent, const GpgME::Error & err ) {
  assert( err );
  const TQString msg = i18n("<qt><p>An error occurred while trying to export "
			   "the certificate:</p>"
			   "<p><b>%1</b></p></qt>")
    .arg( TQString::fromLocal8Bit( err.asString() ) );
  KMessageBox::error( parent, msg, i18n("Certificate Export Failed") );
}

void CertManager::startCertificateExport( const TQStringList & fingerprints ) {
  if ( fingerprints.empty() )
    return;

  // we need to use PEM (ascii armoured) format, since DER (binary)
  // can't transport more than one certificate *sigh* this is madness :/
  Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->publicKeyExportJob( true );
  assert( job );

  connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
	   TQT_SLOT(slotCertificateExportResult(const GpgME::Error&,const TQByteArray&)) );

  connectJobToStatusBarProgress( job, i18n("Exporting certificate...") );

  const GpgME::Error err = job->start( fingerprints );
  if ( err )
    showCertificateExportError( this, err );
  else
    mProgressBar->setProgress( 0, 0 );
}

// return true if we should proceed, false if we should abort
static bool checkOverwrite( const KURL& url, bool& overwrite, TQWidget* w )
{
  if ( TDEIO::NetAccess::exists( url, false /*dest*/, w ) ) {
    if ( KMessageBox::Cancel ==
         KMessageBox::warningContinueCancel(
                                            w,
                                            i18n( "A file named \"%1\" already exists. "
                                                  "Are you sure you want to overwrite it?" ).arg( url.prettyURL() ),
                                            i18n( "Overwrite File?" ),
                                            i18n( "&Overwrite" ) ) )
      return false;
    overwrite = true;
  }
  return true;
}

void CertManager::slotCertificateExportResult( const GpgME::Error & err, const TQByteArray & data ) {
  disconnectJobFromStatusBarProgress( err );
  if ( err ) {
    showCertificateExportError( this, err );
    return;
  }

  kdDebug() << "CertManager::slotCertificateExportResult(): got " << data.size() << " bytes" << endl;

  const TQString filter = TQString("*.pem|") + i18n("ASCII Armored Certificate Bundles (*.pem)");
  const KURL url = KFileDialog::getOpenURL( TQString(),
                                      filter,
                                      this,
                                      i18n( "Save Certificate" ) );
  if ( !url.isValid() )
    return;

  bool overwrite = false;
  if ( !checkOverwrite( url, overwrite, this ) )
    return;

  TDEIO::Job* uploadJob = TDEIOext::put( data, url, -1, overwrite, false /*resume*/ );
  uploadJob->setWindow( this );
  connect( uploadJob, TQT_SIGNAL( result( TDEIO::Job* ) ),
           this, TQT_SLOT( slotUploadResult( TDEIO::Job* ) ) );
}


void CertManager::slotExportSecretKey() {
  Kleo::KeySelectionDialog dlg( i18n("Secret Key Export"),
				"<qt>" +
                                i18n("Select the secret key to export "
				     "(<b>Warning: The PKCS#12 format is insecure; "
				     "exporting secret keys is discouraged</b>):") +
                                "</qt>",
				std::vector<GpgME::Key>(),
				Kleo::KeySelectionDialog::SecretKeys|Kleo::KeySelectionDialog::SMIMEKeys,
				false /* no multiple selection */,
				false /* no remember choice box */,
				this, "secret key export key selection dialog" );
  //dlg.setHideInvalidKeys( false );

  if ( dlg.exec() != TQDialog::Accepted )
    return;

  startSecretKeyExport( dlg.fingerprint() );
}

static void showSecretKeyExportError( TQWidget * parent, const GpgME::Error & err ) {
  assert( err );
  const TQString msg = i18n("<qt><p>An error occurred while trying to export "
			   "the secret key:</p>"
			   "<p><b>%1</b></p></qt>")
    .arg( TQString::fromLocal8Bit( err.asString() ) );
  KMessageBox::error( parent, msg, i18n("Secret-Key Export Failed") );
}

void CertManager::startSecretKeyExport( const TQString & fingerprint ) {
  if ( fingerprint.isEmpty() )
    return;

  // PENDING(marc): let user choose between binary and PEM format?

  // Check if gpgsm supports --p12-charset
  Kleo::CryptoConfig* config = Kleo::CryptoBackendFactory::instance()->config();
  TQString charset;
  if ( config && config->entry( "gpgsm", "Configuration", "p12-charset" ) ) {
    // This comes from gnupg's sources, agent/minip12.c
    // In fact, any charset supported by iconv would work, but we don't link to iconv directly...
    static const char *charsets[] = {
      "utf8",
      "iso-8859-1",
      "iso-8859-15",
      "iso-8859-2",
      "iso-8859-3",
      "iso-8859-4",
      "iso-8859-5",
      "iso-8859-6",
      "iso-8859-7",
      "iso-8859-8",
      "iso-8859-9",
      "koi8-r",
      "ibm437",
      "ibm850",
      "euc-jp",
      "big5",
      NULL
    };
    TQStringList charsetList;
    for ( const char** c = charsets; *c; ++c ) {
      charsetList.append( TQString::fromLatin1( *c ) );
    }

    // TODO this selection could be done in a derived KeySelectionDialog which would add a combobox,
    // it would be better integrated.
    bool ok;
    charset = KInputDialog::getItem( i18n("Exporting secret key..."),
                                     i18n("Choose a charset for encoding the pkcs#12 passphrase (utf8 is recommended)"),
                                     charsetList,
                                     0, false /*editable*/,
                                     &ok, this );
    if ( !ok )
      return;
  }

  Kleo::ExportJob * job = Kleo::CryptoBackendFactory::instance()->smime()->secretKeyExportJob( false, charset );
  assert( job );

  connect( job, TQT_SIGNAL(result(const GpgME::Error&,const TQByteArray&)),
	   TQT_SLOT(slotSecretKeyExportResult(const GpgME::Error&,const TQByteArray&)) );

  connectJobToStatusBarProgress( job, i18n("Exporting secret key...") );

  const GpgME::Error err = job->start( fingerprint );
  if ( err )
    showSecretKeyExportError( this, err );
  else
    mProgressBar->setProgress( 0, 0 );
}

void CertManager::slotSecretKeyExportResult( const GpgME::Error & err, const TQByteArray & data ) {
  disconnectJobFromStatusBarProgress( err );
  if ( err ) {
    showSecretKeyExportError( this, err );
    return;
  }

  kdDebug() << "CertManager::slotSecretKeyExportResult(): got " << data.size() << " bytes" << endl;
  TQString filter = TQString("*.p12|") + i18n("PKCS#12 Key Bundle (*.p12)");
  KURL url = KFileDialog::getOpenURL( TQString(),
                                      filter,
                                      this,
                                      i18n( "Save Certificate" ) );
  if ( !url.isValid() )
    return;

  bool overwrite = false;
  if ( !checkOverwrite( url, overwrite, this ) )
    return;

  TDEIO::Job* uploadJob = TDEIOext::put( data, url, -1, overwrite, false /*resume*/ );
  uploadJob->setWindow( this );
  connect( uploadJob, TQT_SIGNAL( result( TDEIO::Job* ) ),
           this, TQT_SLOT( slotUploadResult( TDEIO::Job* ) ) );
}

void CertManager::slotUploadResult( TDEIO::Job* job )
{
  if ( job->error() )
    job->showErrorDialog();
}

void CertManager::slotDropped(const KURL::List& lst)
{
  mURLsToImport = lst;
  if ( !lst.empty() )
    importNextURLOrRedisplay();
}

void CertManager::importNextURLOrRedisplay()
{
  if ( !mURLsToImport.empty() ) {
    // We can only import them one by one, otherwise the jobs would run into each other
    KURL url = mURLsToImport.front();
    mURLsToImport.pop_front();
    slotImportCertFromFile( url );
  } else {
    if ( isRemote() )
      return;
    startKeyListing( false, true, mPreviouslySelectedFingerprints );
  }
}

void CertManager::slotStartWatchGnuPG()
{
  TDEProcess certManagerProc;
  certManagerProc << "kwatchgnupg";

  if( !certManagerProc.start( TDEProcess::DontCare ) )
    KMessageBox::error( this, i18n( "Could not start GnuPG LogViewer (kwatchgnupg). "
                                    "Please check your installation!" ),
                                    i18n( "Kleopatra Error" ) );
}

#include "certmanager.moc"
