You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1436 lines
52 KiB
1436 lines
52 KiB
/*
|
|
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 <kdemacros.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"
|