summaryrefslogtreecommitdiffstats
path: root/certmanager/lib/ui/keyselectiondialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'certmanager/lib/ui/keyselectiondialog.cpp')
-rw-r--r--certmanager/lib/ui/keyselectiondialog.cpp806
1 files changed, 806 insertions, 0 deletions
diff --git a/certmanager/lib/ui/keyselectiondialog.cpp b/certmanager/lib/ui/keyselectiondialog.cpp
new file mode 100644
index 000000000..e21cb39d4
--- /dev/null
+++ b/certmanager/lib/ui/keyselectiondialog.cpp
@@ -0,0 +1,806 @@
+/* -*- c++ -*-
+ keyselectiondialog.cpp
+
+ This file is part of libkleopatra, the KDE keymanagement library
+ Copyright (c) 2004 Klarävdalens Datakonsult AB
+
+ Based on kpgpui.cpp
+ Copyright (C) 2001,2002 the KPGP authors
+ See file libkdenetwork/AUTHORS.kpgp for details
+
+ Libkleopatra 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.
+
+ Libkleopatra 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 "keyselectiondialog.h"
+
+#include "keylistview.h"
+#include "progressdialog.h"
+
+#include <kleo/dn.h>
+#include <kleo/keylistjob.h>
+#include <kleo/cryptobackendfactory.h>
+
+// gpgme++
+#include <gpgmepp/key.h>
+#include <gpgmepp/keylistresult.h>
+
+// KDE
+#include <klocale.h>
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kwin.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+
+// Qt
+#include <qcheckbox.h>
+#include <qtoolbutton.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qwhatsthis.h>
+#include <qpopupmenu.h>
+#include <qregexp.h>
+#include <qpushbutton.h>
+
+#include <algorithm>
+#include <iterator>
+
+#include <string.h>
+#include <assert.h>
+
+static bool checkKeyUsage( const GpgME::Key & key, unsigned int keyUsage ) {
+
+ if ( keyUsage & Kleo::KeySelectionDialog::ValidKeys ) {
+ if ( key.isInvalid() )
+ kdDebug() << "key is invalid - ignoring" << endl;
+ if ( key.isExpired() ) {
+ kdDebug() << "key is expired" << endl;
+ return false;
+ } else if ( key.isRevoked() ) {
+ kdDebug() << "key is revoked" << endl;
+ return false;
+ } else if ( key.isDisabled() ) {
+ kdDebug() << "key is disabled" << endl;
+ return false;
+ }
+ }
+
+ if ( keyUsage & Kleo::KeySelectionDialog::EncryptionKeys &&
+ !key.canEncrypt() ) {
+ kdDebug() << "key can't encrypt" << endl;
+ return false;
+ }
+ if ( keyUsage & Kleo::KeySelectionDialog::SigningKeys &&
+ !key.canSign() ) {
+ kdDebug() << "key can't sign" << endl;
+ return false;
+ }
+ if ( keyUsage & Kleo::KeySelectionDialog::CertificationKeys &&
+ !key.canCertify() ) {
+ kdDebug() << "key can't certify" << endl;
+ return false;
+ }
+ if ( keyUsage & Kleo::KeySelectionDialog::AuthenticationKeys &&
+ !key.canAuthenticate() ) {
+ kdDebug() << "key can't authenticate" << endl;
+ return false;
+ }
+
+ if ( keyUsage & Kleo::KeySelectionDialog::SecretKeys &&
+ !( keyUsage & Kleo::KeySelectionDialog::PublicKeys ) &&
+ !key.isSecret() ) {
+ kdDebug() << "key isn't secret" << endl;
+ return false;
+ }
+
+ if ( keyUsage & Kleo::KeySelectionDialog::TrustedKeys &&
+ key.protocol() == GpgME::Context::OpenPGP &&
+ // only check this for secret keys for now.
+ // Seems validity isn't checked for secret keylistings...
+ !key.isSecret() ) {
+ std::vector<GpgME::UserID> uids = key.userIDs();
+ for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
+ if ( !it->isRevoked() && it->validity() >= GpgME::UserID::Marginal )
+ return true;
+ kdDebug() << "key has no UIDs with validity >= Marginal" << endl;
+ return false;
+ }
+ // X.509 keys are always trusted, else they won't be the keybox.
+ // PENDING(marc) check that this ^ is correct
+
+ return true;
+}
+
+static bool checkKeyUsage( const std::vector<GpgME::Key> & keys, unsigned int keyUsage ) {
+ for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it )
+ if ( !checkKeyUsage( *it, keyUsage ) )
+ return false;
+ return true;
+}
+
+static inline QString time_t2string( time_t t ) {
+ QDateTime dt;
+ dt.setTime_t( t );
+ return dt.toString();
+}
+
+namespace {
+
+ class ColumnStrategy : public Kleo::KeyListView::ColumnStrategy {
+ public:
+ ColumnStrategy( unsigned int keyUsage );
+
+ QString title( int col ) const;
+ int width( int col, const QFontMetrics & fm ) const;
+
+ QString text( const GpgME::Key & key, int col ) const;
+ QString toolTip( const GpgME::Key & key, int col ) const;
+ const QPixmap * pixmap( const GpgME::Key & key, int col ) const;
+
+ private:
+ const QPixmap mKeyGoodPix, mKeyBadPix, mKeyUnknownPix, mKeyValidPix;
+ const unsigned int mKeyUsage;
+ };
+
+ ColumnStrategy::ColumnStrategy( unsigned int keyUsage )
+ : Kleo::KeyListView::ColumnStrategy(),
+ mKeyGoodPix( UserIcon( "key_ok" ) ),
+ mKeyBadPix( UserIcon( "key_bad" ) ),
+ mKeyUnknownPix( UserIcon( "key_unknown" ) ),
+ mKeyValidPix( UserIcon( "key" ) ),
+ mKeyUsage( keyUsage )
+ {
+ kdWarning( keyUsage == 0, 5150 )
+ << "KeySelectionDialog: keyUsage == 0. You want to use AllKeys instead." << endl;
+ }
+
+ QString ColumnStrategy::title( int col ) const {
+ switch ( col ) {
+ case 0: return i18n("Key ID");
+ case 1: return i18n("User ID");
+ default: return QString::null;
+ }
+ }
+
+ int ColumnStrategy::width( int col, const QFontMetrics & fm ) const {
+ if ( col == 0 ) {
+ static const char hexchars[] = "0123456789ABCDEF";
+ int maxWidth = 0;
+ for ( unsigned int i = 0 ; i < 16 ; ++i )
+ maxWidth = kMax( fm.width( QChar( hexchars[i] ) ), maxWidth );
+ return 8 * maxWidth + 2 * mKeyGoodPix.width();
+ }
+ return Kleo::KeyListView::ColumnStrategy::width( col, fm );
+ }
+
+ QString ColumnStrategy::text( const GpgME::Key & key, int col ) const {
+ switch ( col ) {
+ case 0:
+ {
+ if ( key.shortKeyID() )
+ return QString::fromUtf8( key.shortKeyID() );
+ else
+ return i18n("<unknown>");
+ }
+ break;
+ case 1:
+ {
+ const char * uid = key.userID(0).id();
+ if ( key.protocol() == GpgME::Context::OpenPGP )
+ return uid && *uid ? QString::fromUtf8( uid ) : QString::null ;
+ else // CMS
+ return Kleo::DN( uid ).prettyDN();
+ }
+ break;
+ default: return QString::null;
+ }
+ }
+
+ QString ColumnStrategy::toolTip( const GpgME::Key & key, int ) const {
+ const char * uid = key.userID(0).id();
+ const char * fpr = key.primaryFingerprint();
+ const char * issuer = key.issuerName();
+ const GpgME::Subkey subkey = key.subkey(0);
+ const QString expiry = subkey.neverExpires() ? i18n("never") : time_t2string( subkey.expirationTime() ) ;
+ const QString creation = time_t2string( subkey.creationTime() );
+ if ( key.protocol() == GpgME::Context::OpenPGP )
+ return i18n( "OpenPGP key for %1\n"
+ "Created: %2\n"
+ "Expiry: %3\n"
+ "Fingerprint: %4" )
+ .arg( uid ? QString::fromUtf8( uid ) : i18n("unknown"),
+ creation, expiry,
+ fpr ? QString::fromLatin1( fpr ) : i18n("unknown") );
+ else
+ return i18n( "S/MIME key for %1\n"
+ "Created: %2\n"
+ "Expiry: %3\n"
+ "Fingerprint: %4\n"
+ "Issuer: %5" )
+ .arg( uid ? Kleo::DN( uid ).prettyDN() : i18n("unknown"),
+ creation, expiry,
+ fpr ? QString::fromLatin1( fpr ) : i18n("unknown") )
+ .arg( issuer ? Kleo::DN( issuer ).prettyDN() : i18n("unknown") );
+ }
+
+ const QPixmap * ColumnStrategy::pixmap( const GpgME::Key & key, int col ) const {
+ if ( col != 0 )
+ return 0;
+ // this key did not undergo a validating keylisting yet:
+ if ( !( key.keyListMode() & GpgME::Context::Validate ) )
+ return &mKeyUnknownPix;
+
+ if ( !checkKeyUsage( key, mKeyUsage ) )
+ return &mKeyBadPix;
+
+ if ( key.protocol() == GpgME::Context::CMS )
+ return &mKeyGoodPix;
+
+ switch ( key.userID(0).validity() ) {
+ default:
+ case GpgME::UserID::Unknown:
+ case GpgME::UserID::Undefined:
+ return &mKeyUnknownPix;
+ case GpgME::UserID::Never:
+ return &mKeyValidPix;
+ case GpgME::UserID::Marginal:
+ case GpgME::UserID::Full:
+ case GpgME::UserID::Ultimate:
+ return &mKeyGoodPix;
+ }
+ }
+
+}
+
+
+static const int sCheckSelectionDelay = 250;
+
+Kleo::KeySelectionDialog::KeySelectionDialog( const QString & title,
+ const QString & text,
+ const std::vector<GpgME::Key> & selectedKeys,
+ unsigned int keyUsage,
+ bool extendedSelection,
+ bool rememberChoice,
+ QWidget * parent, const char * name,
+ bool modal )
+ : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ),
+ mOpenPGPBackend( 0 ),
+ mSMIMEBackend( 0 ),
+ mRememberCB( 0 ),
+ mSelectedKeys( selectedKeys ),
+ mKeyUsage( keyUsage ),
+ mCurrentContextMenuItem( 0 )
+{
+ init( rememberChoice, extendedSelection, text, QString::null );
+}
+
+Kleo::KeySelectionDialog::KeySelectionDialog( const QString & title,
+ const QString & text,
+ const QString & initialQuery,
+ unsigned int keyUsage,
+ bool extendedSelection,
+ bool rememberChoice,
+ QWidget * parent, const char * name,
+ bool modal )
+ : KDialogBase( parent, name, modal, title, Default|Ok|Cancel|Help, Ok ),
+ mOpenPGPBackend( 0 ),
+ mSMIMEBackend( 0 ),
+ mRememberCB( 0 ),
+ mKeyUsage( keyUsage ),
+ mSearchText( initialQuery ),
+ mCurrentContextMenuItem( 0 )
+{
+ init( rememberChoice, extendedSelection, text, initialQuery );
+}
+
+void Kleo::KeySelectionDialog::init( bool rememberChoice, bool extendedSelection,
+ const QString & text, const QString & initialQuery ) {
+ if ( mKeyUsage & OpenPGPKeys )
+ mOpenPGPBackend = Kleo::CryptoBackendFactory::instance()->openpgp();
+ if ( mKeyUsage & SMIMEKeys )
+ mSMIMEBackend = Kleo::CryptoBackendFactory::instance()->smime();
+
+ mCheckSelectionTimer = new QTimer( this );
+ mStartSearchTimer = new QTimer( this );
+
+ QFrame *page = makeMainWidget();
+ mTopLayout = new QVBoxLayout( page, 0, spacingHint() );
+
+ if ( !text.isEmpty() ) {
+ QLabel* textLabel = new QLabel( text, page );
+ textLabel->setAlignment( textLabel->alignment() | Qt::WordBreak );
+ mTopLayout->addWidget( textLabel );
+ }
+
+ QHBoxLayout * hlay = new QHBoxLayout( mTopLayout ); // inherits spacing
+ QLineEdit * le = new QLineEdit( page );
+ le->setText( initialQuery );
+ QToolButton *clearButton = new QToolButton( page );
+ clearButton->setIconSet( KGlobal::iconLoader()->loadIconSet(
+ KApplication::reverseLayout() ? "clear_left":"locationbar_erase", KIcon::Small, 0 ) );
+ hlay->addWidget( clearButton );
+ hlay->addWidget( new QLabel( le, i18n("&Search for:"), page ) );
+ hlay->addWidget( le, 1 );
+ le->setFocus();
+
+ connect( clearButton, SIGNAL( clicked() ), le, SLOT( clear() ) );
+ connect( le, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotSearch(const QString&)) );
+ connect( mStartSearchTimer, SIGNAL(timeout()), SLOT(slotFilter()) );
+
+ mKeyListView = new KeyListView( new ColumnStrategy( mKeyUsage ), 0, page, "mKeyListView" );
+ mKeyListView->setResizeMode( QListView::LastColumn );
+ mKeyListView->setRootIsDecorated( true );
+ mKeyListView->setShowSortIndicator( true );
+ mKeyListView->setSorting( 1, true ); // sort by User ID
+ mKeyListView->setShowToolTips( true );
+ if ( extendedSelection )
+ mKeyListView->setSelectionMode( QListView::Extended );
+ mTopLayout->addWidget( mKeyListView, 10 );
+
+ if ( rememberChoice ) {
+ mRememberCB = new QCheckBox( i18n("&Remember choice"), page );
+ mTopLayout->addWidget( mRememberCB );
+ QWhatsThis::add( mRememberCB,
+ i18n("<qt><p>If you check this box your choice will "
+ "be stored and you will not be asked again."
+ "</p></qt>") );
+ }
+
+ connect( mCheckSelectionTimer, SIGNAL(timeout()),
+ SLOT(slotCheckSelection()) );
+ connectSignals();
+
+ connect( mKeyListView,
+ SIGNAL(doubleClicked(Kleo::KeyListViewItem*,const QPoint&,int)),
+ SLOT(slotTryOk()) );
+ connect( mKeyListView,
+ SIGNAL(contextMenu(Kleo::KeyListViewItem*,const QPoint&)),
+ SLOT(slotRMB(Kleo::KeyListViewItem*,const QPoint&)) );
+
+ setButtonText( KDialogBase::Default, i18n("&Reread Keys") );
+ setButtonGuiItem( KDialogBase::Help, i18n("&Start Certificate Manager") );
+ connect( this, SIGNAL(defaultClicked()), this, SLOT(slotRereadKeys()) );
+ connect( this, SIGNAL(helpClicked()), this, SLOT(slotStartCertificateManager()) );
+
+ slotRereadKeys();
+ mTopLayout->activate();
+
+ if ( kapp ) {
+ KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
+ QSize dialogSize( 500, 400 );
+
+ KConfigGroup dialogConfig( KGlobal::config(), "Key Selection Dialog" );
+ dialogSize = dialogConfig.readSizeEntry( "Dialog size", &dialogSize );
+ resize( dialogSize );
+ }
+}
+
+Kleo::KeySelectionDialog::~KeySelectionDialog() {
+ KConfigGroup dialogConfig( KGlobal::config(), "Key Selection Dialog" );
+ dialogConfig.writeEntry( "Dialog size", size() );
+ dialogConfig.sync();
+}
+
+
+void Kleo::KeySelectionDialog::connectSignals() {
+ if ( mKeyListView->isMultiSelection() )
+ connect( mKeyListView, SIGNAL(selectionChanged()),
+ SLOT(slotSelectionChanged()) );
+ else
+ connect( mKeyListView, SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
+ SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
+}
+
+void Kleo::KeySelectionDialog::disconnectSignals() {
+ if ( mKeyListView->isMultiSelection() )
+ disconnect( mKeyListView, SIGNAL(selectionChanged()),
+ this, SLOT(slotSelectionChanged()) );
+ else
+ disconnect( mKeyListView, SIGNAL(selectionChanged(Kleo::KeyListViewItem*)),
+ this, SLOT(slotCheckSelection(Kleo::KeyListViewItem*)) );
+}
+
+const GpgME::Key & Kleo::KeySelectionDialog::selectedKey() const {
+ if ( mKeyListView->isMultiSelection() || !mKeyListView->selectedItem() )
+ return GpgME::Key::null;
+ return mKeyListView->selectedItem()->key();
+}
+
+QString Kleo::KeySelectionDialog::fingerprint() const {
+ return selectedKey().primaryFingerprint();
+}
+
+QStringList Kleo::KeySelectionDialog::fingerprints() const {
+ QStringList result;
+ for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
+ if ( const char * fpr = it->primaryFingerprint() )
+ result.push_back( fpr );
+ return result;
+}
+
+QStringList Kleo::KeySelectionDialog::pgpKeyFingerprints() const {
+ QStringList result;
+ for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
+ if ( it->protocol() == GpgME::Context::OpenPGP )
+ if ( const char * fpr = it->primaryFingerprint() )
+ result.push_back( fpr );
+ return result;
+}
+
+QStringList Kleo::KeySelectionDialog::smimeFingerprints() const {
+ QStringList result;
+ for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
+ if ( it->protocol() == GpgME::Context::CMS )
+ if ( const char * fpr = it->primaryFingerprint() )
+ result.push_back( fpr );
+ return result;
+}
+
+void Kleo::KeySelectionDialog::slotRereadKeys() {
+ mKeyListView->clear();
+ mListJobCount = 0;
+ mTruncated = 0;
+ mSavedOffsetY = mKeyListView->contentsY();
+
+ disconnectSignals();
+ mKeyListView->setEnabled( false );
+
+ // FIXME: save current selection
+ if ( mOpenPGPBackend )
+ startKeyListJobForBackend( mOpenPGPBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
+ if ( mSMIMEBackend )
+ startKeyListJobForBackend( mSMIMEBackend, std::vector<GpgME::Key>(), false /*non-validating*/ );
+
+ if ( mListJobCount == 0 ) {
+ mKeyListView->setEnabled( true );
+ KMessageBox::information( this,
+ i18n("No backends found for listing keys. "
+ "Check your installation."),
+ i18n("Key Listing Failed") );
+ connectSignals();
+ }
+}
+
+void Kleo::KeySelectionDialog::slotHelp()
+{
+ emit helpClicked();
+}
+
+void Kleo::KeySelectionDialog::slotStartCertificateManager()
+{
+ KProcess certManagerProc;
+ certManagerProc << "kleopatra";
+
+ if( !certManagerProc.start( KProcess::DontCare ) )
+ KMessageBox::error( this, i18n( "Could not start certificate manager; "
+ "please check your installation." ),
+ i18n( "Certificate Manager Error" ) );
+ else
+ kdDebug(5006) << "\nslotStartCertManager(): certificate manager started.\n" << endl;
+}
+
+#ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
+#define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
+static void showKeyListError( QWidget * parent, const GpgME::Error & err ) {
+ assert( err );
+ const QString msg = i18n( "<qt><p>An error occurred while fetching "
+ "the keys from the backend:</p>"
+ "<p><b>%1</b></p></qt>" )
+ .arg( QString::fromLocal8Bit( err.asString() ) );
+
+ KMessageBox::error( parent, msg, i18n( "Key Listing Failed" ) );
+}
+#endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
+
+namespace {
+ struct ExtractFingerprint {
+ QString operator()( const GpgME::Key & key ) {
+ return key.primaryFingerprint();
+ }
+ };
+}
+
+void Kleo::KeySelectionDialog::startKeyListJobForBackend( const CryptoBackend::Protocol * backend, const std::vector<GpgME::Key> & keys, bool validate ) {
+ assert( backend );
+ KeyListJob * job = backend->keyListJob( false, false, validate ); // local, w/o sigs, validation as givem
+ if ( !job )
+ return;
+
+ connect( job, SIGNAL(result(const GpgME::KeyListResult&)),
+ SLOT(slotKeyListResult(const GpgME::KeyListResult&)) );
+ connect( job, SIGNAL(nextKey(const GpgME::Key&)),
+ mKeyListView, validate ?
+ SLOT(slotRefreshKey(const GpgME::Key&)) :
+ SLOT(slotAddKey(const GpgME::Key&)) );
+
+ QStringList fprs;
+ std::transform( keys.begin(), keys.end(), std::back_inserter( fprs ), ExtractFingerprint() );
+ const GpgME::Error err = job->start( fprs, mKeyUsage & SecretKeys && !( mKeyUsage & PublicKeys ) );
+
+ if ( err )
+ return showKeyListError( this, err );
+
+ // FIXME: create a MultiProgressDialog:
+ (void)new ProgressDialog( job, validate ? i18n( "Checking selected keys..." ) : i18n( "Fetching keys..." ), this );
+ ++mListJobCount;
+}
+
+static void selectKeys( Kleo::KeyListView * klv, const std::vector<GpgME::Key> & selectedKeys ) {
+ klv->clearSelection();
+ if ( selectedKeys.empty() )
+ return;
+ for ( std::vector<GpgME::Key>::const_iterator it = selectedKeys.begin() ; it != selectedKeys.end() ; ++it )
+ if ( Kleo::KeyListViewItem * item = klv->itemByFingerprint( it->primaryFingerprint() ) )
+ item->setSelected( true );
+}
+
+void Kleo::KeySelectionDialog::slotKeyListResult( const GpgME::KeyListResult & res ) {
+ if ( res.error() )
+ showKeyListError( this, res.error() );
+ else if ( res.isTruncated() )
+ ++mTruncated;
+
+ if ( --mListJobCount > 0 )
+ return; // not yet finished...
+
+ if ( mTruncated > 0 )
+ KMessageBox::information( this,
+ i18n("<qt>One backend returned truncated output.<br>"
+ "Not all available keys are shown</qt>",
+ "<qt>%n backends returned truncated output.<br>"
+ "Not all available keys are shown</qt>",
+ mTruncated),
+ i18n("Key List Result") );
+
+ mKeyListView->flushKeys();
+
+ mKeyListView->setEnabled( true );
+ mListJobCount = mTruncated = 0;
+ mKeysToCheck.clear();
+
+ selectKeys( mKeyListView, mSelectedKeys );
+
+ slotFilter();
+
+ connectSignals();
+
+ slotSelectionChanged();
+
+ // restore the saved position of the contents
+ mKeyListView->setContentsPos( 0, mSavedOffsetY ); mSavedOffsetY = 0;
+}
+
+void Kleo::KeySelectionDialog::slotSelectionChanged() {
+ kdDebug(5150) << "KeySelectionDialog::slotSelectionChanged()" << endl;
+
+ // (re)start the check selection timer. Checking the selection is delayed
+ // because else drag-selection doesn't work very good (checking key trust
+ // is slow).
+ mCheckSelectionTimer->start( sCheckSelectionDelay );
+}
+
+namespace {
+ struct AlreadyChecked {
+ bool operator()( const GpgME::Key & key ) const {
+ return key.keyListMode() & GpgME::Context::Validate ;
+ }
+ };
+}
+
+void Kleo::KeySelectionDialog::slotCheckSelection( KeyListViewItem * item ) {
+ kdDebug(5150) << "KeySelectionDialog::slotCheckSelection()\n";
+
+ mCheckSelectionTimer->stop();
+
+ mSelectedKeys.clear();
+
+ if ( !mKeyListView->isMultiSelection() ) {
+ if ( item )
+ mSelectedKeys.push_back( item->key() );
+ }
+
+ for ( KeyListViewItem * it = mKeyListView->firstChild() ; it ; it = it->nextSibling() )
+ if ( it->isSelected() )
+ mSelectedKeys.push_back( it->key() );
+
+ mKeysToCheck.clear();
+ std::remove_copy_if( mSelectedKeys.begin(), mSelectedKeys.end(),
+ std::back_inserter( mKeysToCheck ),
+ AlreadyChecked() );
+ if ( mKeysToCheck.empty() ) {
+ enableButtonOK( !mSelectedKeys.empty() &&
+ checkKeyUsage( mSelectedKeys, mKeyUsage ) );
+ return;
+ }
+
+ // performed all fast checks - now for validating key listing:
+ startValidatingKeyListing();
+}
+
+void Kleo::KeySelectionDialog::startValidatingKeyListing() {
+ if ( mKeysToCheck.empty() )
+ return;
+
+ mListJobCount = 0;
+ mTruncated = 0;
+ mSavedOffsetY = mKeyListView->contentsY();
+
+ disconnectSignals();
+ mKeyListView->setEnabled( false );
+
+ std::vector<GpgME::Key> smime, openpgp;
+ for ( std::vector<GpgME::Key>::const_iterator it = mKeysToCheck.begin() ; it != mKeysToCheck.end() ; ++it )
+ if ( it->protocol() == GpgME::Context::OpenPGP )
+ openpgp.push_back( *it );
+ else
+ smime.push_back( *it );
+
+ if ( !openpgp.empty() ) {
+ assert( mOpenPGPBackend );
+ startKeyListJobForBackend( mOpenPGPBackend, openpgp, true /*validate*/ );
+ }
+ if ( !smime.empty() ) {
+ assert( mSMIMEBackend );
+ startKeyListJobForBackend( mSMIMEBackend, smime, true /*validate*/ );
+ }
+
+ assert( mListJobCount > 0 );
+}
+
+bool Kleo::KeySelectionDialog::rememberSelection() const {
+ return mRememberCB && mRememberCB->isChecked() ;
+}
+
+void Kleo::KeySelectionDialog::slotRMB( Kleo::KeyListViewItem * item, const QPoint & p ) {
+ if ( !item ) return;
+
+ mCurrentContextMenuItem = item;
+
+ QPopupMenu menu;
+ menu.insertItem( i18n( "Recheck Key" ), this, SLOT(slotRecheckKey()) );
+ menu.exec( p );
+}
+
+void Kleo::KeySelectionDialog::slotRecheckKey() {
+ if ( !mCurrentContextMenuItem || mCurrentContextMenuItem->key().isNull() )
+ return;
+
+ mKeysToCheck.clear();
+ mKeysToCheck.push_back( mCurrentContextMenuItem->key() );
+}
+
+void Kleo::KeySelectionDialog::slotTryOk() {
+ if ( actionButton( Ok )->isEnabled() )
+ slotOk();
+}
+
+void Kleo::KeySelectionDialog::slotOk() {
+ if ( mCheckSelectionTimer->isActive() )
+ slotCheckSelection();
+ // button could be disabled again after checking the selected key
+ if ( !actionButton( Ok )->isEnabled() )
+ return;
+ mStartSearchTimer->stop();
+ accept();
+}
+
+
+void Kleo::KeySelectionDialog::slotCancel() {
+ mCheckSelectionTimer->stop();
+ mStartSearchTimer->stop();
+ reject();
+}
+
+void Kleo::KeySelectionDialog::slotSearch( const QString & text ) {
+ mSearchText = text.stripWhiteSpace().upper();
+ slotSearch();
+}
+
+void Kleo::KeySelectionDialog::slotSearch() {
+ mStartSearchTimer->start( sCheckSelectionDelay, true /*single-shot*/ );
+}
+
+void Kleo::KeySelectionDialog::slotFilter() {
+ if ( mSearchText.isEmpty() ) {
+ showAllItems();
+ return;
+ }
+
+ // OK, so we need to filter:
+ QRegExp keyIdRegExp( "(?:0x)?[A-F0-9]{1,8}", false /*case-insens.*/ );
+ if ( keyIdRegExp.exactMatch( mSearchText ) ) {
+ if ( mSearchText.startsWith( "0X" ) )
+ // search for keyID only:
+ filterByKeyID( mSearchText.mid( 2 ) );
+ else
+ // search for UID and keyID:
+ filterByKeyIDOrUID( mSearchText );
+ } else {
+ // search in UID:
+ filterByUID( mSearchText );
+ }
+}
+
+void Kleo::KeySelectionDialog::filterByKeyID( const QString & keyID ) {
+ assert( keyID.length() <= 8 );
+ assert( !keyID.isEmpty() ); // regexp in slotFilter should prevent these
+ if ( keyID.isEmpty() )
+ showAllItems();
+ else
+ for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
+ item->setVisible( item->text( 0 ).upper().startsWith( keyID ) );
+}
+
+static bool anyUIDMatches( const Kleo::KeyListViewItem * item, QRegExp & rx ) {
+ if ( !item )
+ return false;
+
+ const std::vector<GpgME::UserID> uids = item->key().userIDs();
+ for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
+ if ( it->id() && rx.search( QString::fromUtf8( it->id() ) ) >= 0 )
+ return true;
+ return false;
+}
+
+void Kleo::KeySelectionDialog::filterByKeyIDOrUID( const QString & str ) {
+ assert( !str.isEmpty() );
+
+ // match beginnings of words:
+ QRegExp rx( "\\b" + QRegExp::escape( str ), false );
+
+ for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
+ item->setVisible( item->text( 0 ).upper().startsWith( str ) || anyUIDMatches( item, rx ) );
+
+}
+
+void Kleo::KeySelectionDialog::filterByUID( const QString & str ) {
+ assert( !str.isEmpty() );
+
+ // match beginnings of words:
+ QRegExp rx( "\\b" + QRegExp::escape( str ), false );
+
+ for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
+ item->setVisible( anyUIDMatches( item, rx ) );
+}
+
+
+void Kleo::KeySelectionDialog::showAllItems() {
+ for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->nextSibling() )
+ item->setVisible( true );
+}
+
+#include "keyselectiondialog.moc"