48 #include <gpgme++/key.h>
49 #include <gpgme++/keylistresult.h>
54 #include <kiconloader.h>
56 #include <kwindowsystem.h>
58 #include <kmessagebox.h>
59 #include <kpushbutton.h>
60 #include <kconfiggroup.h>
62 #include <klineedit.h>
67 #include <QToolButton>
77 #include <QPushButton>
79 #include <QApplication>
80 #include <QHBoxLayout>
81 #include <QVBoxLayout>
88 #include <qscrollbar.h>
90 static bool checkKeyUsage(
const GpgME::Key & key,
unsigned int keyUsage ) {
93 if ( key.isInvalid() ) {
94 if ( key.keyListMode() & GpgME::Validate ) {
95 kDebug(5150) <<
"key is invalid";
98 kDebug(5150) <<
"key is invalid - ignoring";
101 if ( key.isExpired() ) {
102 kDebug(5150) <<
"key is expired";
104 }
else if ( key.isRevoked() ) {
105 kDebug(5150) <<
"key is revoked";
107 }
else if ( key.isDisabled() ) {
108 kDebug(5150) <<
"key is disabled";
114 !key.canEncrypt() ) {
115 kDebug(5150) <<
"key can't encrypt";
120 kDebug(5150) <<
"key can't sign";
124 !key.canCertify() ) {
125 kDebug(5150) <<
"key can't certify";
129 !key.canAuthenticate() ) {
130 kDebug(5150) <<
"key can't authenticate";
137 kDebug(5150) <<
"key isn't secret";
142 key.protocol() == GpgME::OpenPGP &&
146 std::vector<GpgME::UserID> uids = key.userIDs();
147 for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
148 if ( !it->isRevoked() && it->validity() >= GpgME::UserID::Marginal )
150 kDebug(5150) <<
"key has no UIDs with validity >= Marginal";
159 static bool checkKeyUsage(
const std::vector<GpgME::Key> & keys,
unsigned int keyUsage ) {
160 for ( std::vector<GpgME::Key>::const_iterator it = keys.begin() ; it != keys.end() ; ++it )
176 ColumnStrategy(
unsigned int keyUsage );
178 QString title(
int col )
const;
181 QString text(
const GpgME::Key & key,
int col )
const;
182 QString toolTip(
const GpgME::Key & key,
int col )
const;
183 KIcon icon(
const GpgME::Key & key,
int col )
const;
186 const KIcon mKeyGoodPix, mKeyBadPix, mKeyUnknownPix, mKeyValidPix;
187 const unsigned int mKeyUsage;
190 ColumnStrategy::ColumnStrategy(
unsigned int keyUsage )
191 : Kleo::KeyListView::ColumnStrategy(),
196 mKeyUsage( keyUsage )
198 kWarning( keyUsage == 0, 5150 )
199 <<
"KeySelectionDialog: keyUsage == 0. You want to use AllKeys instead.";
202 QString ColumnStrategy::title(
int col )
const {
204 case 0:
return i18n(
"Key ID");
205 case 1:
return i18n(
"User ID");
210 int ColumnStrategy::width(
int col,
const QFontMetrics & fm )
const {
212 static const char hexchars[] =
"0123456789ABCDEF";
214 for (
unsigned int i = 0 ; i < 16 ; ++i )
216 return 8 * maxWidth + 2 * KIconLoader::SizeSmall;
221 QString ColumnStrategy::text(
const GpgME::Key & key,
int col )
const {
225 if ( key.shortKeyID() )
228 return i18n(
"<placeholder>unknown</placeholder>");
233 const char * uid = key.userID(0).id();
234 if ( key.protocol() == GpgME::OpenPGP )
244 QString ColumnStrategy::toolTip(
const GpgME::Key & key,
int )
const {
245 const char * uid = key.userID(0).id();
246 const char * fpr = key.primaryFingerprint();
247 const char * issuer = key.issuerName();
248 const GpgME::Subkey subkey = key.subkey(0);
249 const QString expiry = subkey.neverExpires() ? i18n(
"never") :
time_t2string( subkey.expirationTime() ) ;
251 if ( key.protocol() == GpgME::OpenPGP )
252 return i18n(
"OpenPGP key for %1\n"
260 return i18n(
"S/MIME key for %1\n"
265 uid ?
Kleo::DN( uid ).prettyDN() : i18n(
"unknown"),
267 fpr ? QString::fromLatin1( fpr ) : i18n(
"unknown"),
268 issuer ?
Kleo::DN( issuer ).prettyDN() : i18n(
"unknown") );
271 KIcon ColumnStrategy::icon(
const GpgME::Key & key,
int col )
const {
275 if ( !( key.keyListMode() & GpgME::Validate ) )
276 return mKeyUnknownPix;
281 if ( key.protocol() == GpgME::CMS )
284 switch ( key.userID(0).validity() ) {
286 case GpgME::UserID::Unknown:
287 case GpgME::UserID::Undefined:
288 return mKeyUnknownPix;
289 case GpgME::UserID::Never:
291 case GpgME::UserID::Marginal:
292 case GpgME::UserID::Full:
293 case GpgME::UserID::Ultimate:
305 const std::vector<GpgME::Key> & selectedKeys,
306 unsigned int keyUsage,
307 bool extendedSelection,
312 mOpenPGPBackend( 0 ),
315 mSelectedKeys( selectedKeys ),
316 mKeyUsage( keyUsage ),
317 mCurrentContextMenuItem( 0 )
320 setButtons( User1|User2|Ok|Cancel );
321 setDefaultButton( Ok );
323 init( rememberChoice, extendedSelection, text,
QString() );
329 const std::vector<GpgME::Key> & selectedKeys,
330 unsigned int keyUsage,
331 bool extendedSelection,
336 mOpenPGPBackend( 0 ),
339 mSelectedKeys( selectedKeys ),
340 mKeyUsage( keyUsage ),
341 mSearchText( initialQuery ),
342 mInitialQuery( initialQuery ),
343 mCurrentContextMenuItem( 0 )
346 setButtons( User1|User2|Ok|Cancel );
347 setDefaultButton( Ok );
349 init( rememberChoice, extendedSelection, text, initialQuery );
355 unsigned int keyUsage,
356 bool extendedSelection,
361 mOpenPGPBackend( 0 ),
364 mKeyUsage( keyUsage ),
365 mSearchText( initialQuery ),
366 mInitialQuery( initialQuery ),
367 mCurrentContextMenuItem( 0 )
370 setButtons( User1|User2|Ok|Cancel );
371 setDefaultButton( Ok );
373 init( rememberChoice, extendedSelection, text, initialQuery );
376 void Kleo::KeySelectionDialog::init(
bool rememberChoice,
bool extendedSelection,
378 if ( mKeyUsage & OpenPGPKeys )
380 if ( mKeyUsage & SMIMEKeys )
383 mCheckSelectionTimer =
new QTimer(
this );
384 mStartSearchTimer =
new QTimer(
this );
387 setMainWidget( page );
389 mTopLayout->setMargin( 0 );
390 mTopLayout->setSpacing( spacingHint() );
393 #ifndef KDEPIM_MOBILE_UI
399 textLabel->
setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );
400 connect( textLabel, SIGNAL(linkActivated(
QString)), SLOT(slotStartCertificateManager(
QString)) );
401 mTopLayout->addWidget( textLabel );
405 #ifndef KDEPIM_MOBILE_UI
407 new QPushButton( i18n(
"Search for &External Certificates" ), page );
408 mTopLayout->addWidget( searchExternalPB, 0, Qt::AlignLeft );
409 connect( searchExternalPB, SIGNAL(clicked()),
410 this, SLOT(slotStartSearchForExternalCertificates()) );
411 if ( initialQuery.
isEmpty() ) {
412 searchExternalPB->
hide();
417 mTopLayout->addLayout( hlay );
419 KLineEdit * le =
new KLineEdit( page );
420 le->setClearButtonShown(
true);
421 le->setText( initialQuery );
423 QLabel* lbSearchFor =
new QLabel( i18n(
"&Search for:"), page ) ;
430 connect( le, SIGNAL(textChanged(
QString)),
431 this, SLOT(slotSearch(
QString)) );
432 connect( mStartSearchTimer, SIGNAL(timeout()), SLOT(slotFilter()) );
434 mKeyListView =
new KeyListView(
new ColumnStrategy( mKeyUsage ), 0, page );
435 mKeyListView->setObjectName(
QLatin1String(
"mKeyListView") );
436 mKeyListView->header()->stretchLastSection();
437 mKeyListView->setRootIsDecorated(
true );
438 mKeyListView->setSortingEnabled(
true );
439 mKeyListView->header()->setSortIndicatorShown(
true );
440 mKeyListView->header()->setSortIndicator( 1, Qt::AscendingOrder );
441 if ( extendedSelection )
442 mKeyListView->setSelectionMode( QAbstractItemView::ExtendedSelection );
443 mTopLayout->addWidget( mKeyListView, 10 );
445 if ( rememberChoice ) {
446 #ifndef KDEPIM_MOBILE_UI
447 mRememberCB =
new QCheckBox( i18n(
"&Remember choice"), page );
448 mTopLayout->addWidget( mRememberCB );
449 mRememberCB->setWhatsThis(
450 i18n(
"<qt><p>If you check this box your choice will "
451 "be stored and you will not be asked again."
456 connect( mCheckSelectionTimer, SIGNAL(timeout()),
457 SLOT(slotCheckSelection()) );
460 connect( mKeyListView,
463 connect( mKeyListView,
467 setButtonText( KDialog::User1, i18n(
"&Reread Keys") );
468 setButtonText( KDialog::User2, i18n(
"&Start Certificate Manager") );
469 connect(
this, SIGNAL(user1Clicked()),
this, SLOT(slotRereadKeys()) );
470 connect(
this, SIGNAL(user2Clicked()),
this, SLOT(slotStartCertificateManager()) );
471 connect(
this, SIGNAL(okClicked()),
this, SLOT(slotOk()));
472 connect(
this, SIGNAL(cancelClicked()),
this,SLOT(slotCancel()));
474 mTopLayout->activate();
477 QSize dialogSize( sizeHint() );
478 int iconSize = IconSize(KIconLoader::Desktop);
479 int miniSize = IconSize(KIconLoader::Small);
480 KWindowSystem::setIcons( winId(), qApp->windowIcon().pixmap(iconSize, iconSize),
481 qApp->windowIcon().pixmap(miniSize, miniSize) );
483 KConfigGroup dialogConfig( KGlobal::config(),
"Key Selection Dialog" );
484 dialogSize = dialogConfig.readEntry(
"Dialog size", dialogSize );
487 mKeyListView->header()->restoreState(headerState);
488 resize( dialogSize );
493 KConfigGroup dialogConfig( KGlobal::config(),
"Key Selection Dialog" );
494 dialogConfig.writeEntry(
"Dialog size", size() );
495 dialogConfig.writeEntry(
"header", mKeyListView->header()->saveState());
500 void Kleo::KeySelectionDialog::connectSignals() {
501 if ( mKeyListView->isMultiSelection() )
502 connect( mKeyListView, SIGNAL(itemSelectionChanged()),
503 SLOT(slotSelectionChanged()) );
509 void Kleo::KeySelectionDialog::disconnectSignals() {
510 if ( mKeyListView->isMultiSelection() )
511 disconnect( mKeyListView, SIGNAL(itemSelectionChanged()),
512 this, SLOT(slotSelectionChanged()) );
519 static const GpgME::Key null = GpgME::Key::null;
520 if ( mKeyListView->isMultiSelection() || !mKeyListView->selectedItem() )
522 return mKeyListView->selectedItem()->key();
531 for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
532 if (
const char * fpr = it->primaryFingerprint() )
539 for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
540 if ( it->protocol() == GpgME::OpenPGP )
541 if (
const char * fpr = it->primaryFingerprint() )
548 for ( std::vector<GpgME::Key>::const_iterator it = mSelectedKeys.begin() ; it != mSelectedKeys.end() ; ++it )
549 if ( it->protocol() == GpgME::CMS )
550 if (
const char * fpr = it->primaryFingerprint() )
555 void Kleo::KeySelectionDialog::slotRereadKeys() {
556 mKeyListView->clear();
559 mSavedOffsetY = mKeyListView->verticalScrollBar()->value();
562 mKeyListView->setEnabled(
false );
565 if ( mOpenPGPBackend )
566 startKeyListJobForBackend( mOpenPGPBackend, std::vector<GpgME::Key>(),
false );
568 startKeyListJobForBackend( mSMIMEBackend, std::vector<GpgME::Key>(),
false );
570 if ( mListJobCount == 0 ) {
571 mKeyListView->setEnabled(
true );
572 KMessageBox::information(
this,
573 i18n(
"No backends found for listing keys. "
574 "Check your installation."),
575 i18n(
"Key Listing Failed") );
580 void Kleo::KeySelectionDialog::slotStartCertificateManager(
const QString &query )
591 KMessageBox::error(
this,
592 i18n(
"Could not start certificate manager; "
593 "please check your installation." ),
594 i18n(
"Certificate Manager Error" ) );
596 kDebug(5150) <<
"\nslotStartCertManager(): certificate manager started.";
599 #ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
600 #define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
603 const QString msg = i18n(
"<qt><p>An error occurred while fetching "
604 "the keys from the backend:</p>"
605 "<p><b>%1</b></p></qt>" ,
608 KMessageBox::error( parent, msg, i18n(
"Key Listing Failed" ) );
610 #endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
613 struct ExtractFingerprint {
614 QString operator()(
const GpgME::Key & key ) {
620 void Kleo::KeySelectionDialog::startKeyListJobForBackend(
const CryptoBackend::Protocol * backend,
const std::vector<GpgME::Key> & keys,
bool validate ) {
622 KeyListJob * job = backend->keyListJob(
false,
false, validate );
626 connect( job, SIGNAL(result(GpgME::KeyListResult)),
627 SLOT(slotKeyListResult(GpgME::KeyListResult)) );
629 connect( job, SIGNAL(nextKey(GpgME::Key)),
630 mKeyListView, SLOT(slotRefreshKey(GpgME::Key)) );
632 connect( job, SIGNAL(nextKey(GpgME::Key)),
633 mKeyListView, SLOT(slotAddKey(GpgME::Key)) );
636 std::transform( keys.begin(), keys.end(), std::back_inserter( fprs ), ExtractFingerprint() );
637 const GpgME::Error err = job->start( fprs, mKeyUsage & SecretKeys && !( mKeyUsage & PublicKeys ) );
642 #ifndef LIBKLEO_NO_PROGRESSDIALOG
644 (void)
new ProgressDialog( job, validate ? i18n(
"Checking selected keys..." ) : i18n(
"Fetching keys..." ), this );
651 if ( selectedKeys.empty() )
653 for ( std::vector<GpgME::Key>::const_iterator it = selectedKeys.begin() ; it != selectedKeys.end() ; ++it )
655 item->setSelected(
true );
658 void Kleo::KeySelectionDialog::slotKeyListResult(
const GpgME::KeyListResult & res ) {
661 else if ( res.isTruncated() )
664 if ( --mListJobCount > 0 )
667 if ( mTruncated > 0 )
668 KMessageBox::information(
this,
669 i18np(
"<qt>One backend returned truncated output.<p>"
670 "Not all available keys are shown</p></qt>",
671 "<qt>%1 backends returned truncated output.<p>"
672 "Not all available keys are shown</p></qt>",
674 i18n(
"Key List Result") );
676 mKeyListView->flushKeys();
678 mKeyListView->setEnabled(
true );
679 mListJobCount = mTruncated = 0;
680 mKeysToCheck.clear();
688 slotSelectionChanged();
691 mKeyListView->verticalScrollBar()->setValue( mSavedOffsetY ); mSavedOffsetY = 0;
694 void Kleo::KeySelectionDialog::slotSelectionChanged() {
695 kDebug(5150) <<
"KeySelectionDialog::slotSelectionChanged()";
700 mCheckSelectionTimer->start( sCheckSelectionDelay );
704 struct AlreadyChecked {
705 bool operator()(
const GpgME::Key & key )
const {
706 return key.keyListMode() & GpgME::Validate ;
711 void Kleo::KeySelectionDialog::slotCheckSelection( KeyListViewItem * item ) {
712 kDebug(5150) <<
"KeySelectionDialog::slotCheckSelection()";
714 mCheckSelectionTimer->stop();
716 mSelectedKeys.clear();
718 if ( !mKeyListView->isMultiSelection() ) {
720 mSelectedKeys.push_back( item->key() );
723 for ( KeyListViewItem * it = mKeyListView->firstChild() ; it ; it = it->nextSibling() )
724 if ( it->isSelected() )
725 mSelectedKeys.push_back( it->key() );
727 mKeysToCheck.clear();
728 std::remove_copy_if( mSelectedKeys.begin(), mSelectedKeys.end(),
729 std::back_inserter( mKeysToCheck ),
731 if ( mKeysToCheck.empty() ) {
732 enableButton( Ok, !mSelectedKeys.empty() &&
738 startValidatingKeyListing();
741 void Kleo::KeySelectionDialog::startValidatingKeyListing() {
742 if ( mKeysToCheck.empty() )
747 mSavedOffsetY = mKeyListView->verticalScrollBar()->value();
750 mKeyListView->setEnabled(
false );
752 std::vector<GpgME::Key> smime, openpgp;
753 for ( std::vector<GpgME::Key>::const_iterator it = mKeysToCheck.begin() ; it != mKeysToCheck.end() ; ++it )
754 if ( it->protocol() == GpgME::OpenPGP )
755 openpgp.push_back( *it );
757 smime.push_back( *it );
759 if ( !openpgp.empty() ) {
760 assert( mOpenPGPBackend );
761 startKeyListJobForBackend( mOpenPGPBackend, openpgp,
true );
763 if ( !smime.empty() ) {
764 assert( mSMIMEBackend );
765 startKeyListJobForBackend( mSMIMEBackend, smime,
true );
768 assert( mListJobCount > 0 );
772 return mRememberCB && mRememberCB->isChecked() ;
778 mCurrentContextMenuItem = item;
781 menu.addAction( i18n(
"Recheck Key" ),
this, SLOT(slotRecheckKey()) );
785 void Kleo::KeySelectionDialog::slotRecheckKey() {
786 if ( !mCurrentContextMenuItem || mCurrentContextMenuItem->key().isNull() )
789 mKeysToCheck.clear();
790 mKeysToCheck.push_back( mCurrentContextMenuItem->key() );
793 void Kleo::KeySelectionDialog::slotTryOk() {
794 if ( !mSelectedKeys.empty() &&
checkKeyUsage( mSelectedKeys, mKeyUsage ) )
798 void Kleo::KeySelectionDialog::slotOk() {
799 if ( mCheckSelectionTimer->isActive() )
800 slotCheckSelection();
803 if ( !mSelectedKeys.empty() &&
checkKeyUsage( mSelectedKeys, mKeyUsage ) )
806 mStartSearchTimer->stop();
811 void Kleo::KeySelectionDialog::slotCancel() {
812 mCheckSelectionTimer->stop();
813 mStartSearchTimer->stop();
817 void Kleo::KeySelectionDialog::slotSearch(
const QString & text ) {
822 void Kleo::KeySelectionDialog::slotSearch() {
823 mStartSearchTimer->setSingleShot(
true );
824 mStartSearchTimer->start( sCheckSelectionDelay );
827 void Kleo::KeySelectionDialog::slotFilter() {
828 if ( mSearchText.isEmpty() ) {
835 if ( keyIdRegExp.exactMatch( mSearchText ) ) {
838 filterByKeyID( mSearchText.mid( 2 ) );
841 filterByKeyIDOrUID( mSearchText );
844 filterByUID( mSearchText );
848 void Kleo::KeySelectionDialog::filterByKeyID(
const QString & keyID ) {
849 assert( keyID.
length() <= 8 );
854 for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->
nextSibling() )
862 const std::vector<GpgME::UserID> uids = item->
key().userIDs();
863 for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() ; it != uids.end() ; ++it )
869 void Kleo::KeySelectionDialog::filterByKeyIDOrUID(
const QString & str ) {
875 for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->
nextSibling() )
880 void Kleo::KeySelectionDialog::filterByUID(
const QString & str ) {
886 for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->
nextSibling() )
891 void Kleo::KeySelectionDialog::showAllItems() {
892 for ( KeyListViewItem * item = mKeyListView->firstChild() ; item ; item = item->
nextSibling() )
static QString time_t2string(time_t t)
QString toString(Qt::DateFormat format) const
QStringList smimeFingerprints() const
Return the selected smime fingerprints.
static void selectKeys(Kleo::KeyListView *klv, const std::vector< GpgME::Key > &selectedKeys)
virtual int width(int column, const QFontMetrics &fm) const
void push_back(const T &value)
const CryptoBackend::Protocol * smime() const
QString escape(const QString &str)
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
QString fingerprint() const
static CryptoBackendFactory * instance()
static void showKeyListError(QWidget *parent, const GpgME::Error &err)
void setBuddy(QWidget *buddy)
const GpgME::Key & selectedKey() const
Returns the key ID of the selected key in single selection mode.
int indexIn(const QString &str, int offset, CaretMode caretMode) const
static const int sCheckSelectionDelay
KeyListViewItem * nextSibling() const
QString fromLocal8Bit(const char *str, int size)
QString fromUtf8(const char *str, int size)
bool rememberSelection() const
QStringList fingerprints() const
Return all the selected fingerprints.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
KeySelectionDialog(const QString &title, const QString &text, const std::vector< GpgME::Key > &selectedKeys=std::vector< GpgME::Key >(), unsigned int keyUsage=AllKeys, bool extendedSelection=false, bool rememberChoice=false, QWidget *parent=0, bool modal=true)
KeyListViewItem * itemByFingerprint(const QByteArray &) const
int width(const QString &text, int len) const
const GpgME::Key & key() const
QString fromLatin1(const char *str, int size)
static bool checkKeyUsage(const GpgME::Key &key, unsigned int keyUsage)
QString text(int column) const
QStringList pgpKeyFingerprints() const
Return the selected openpgp fingerprints.
void setWordWrap(bool on)
static bool anyUIDMatches(const Kleo::KeyListViewItem *item, QRegExp &rx)
const CryptoBackend::Protocol * openpgp() const
void setTime_t(uint seconds)