00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include <config-kleopatra.h>
00034
00035 #include "certificatedetailsdialog.h"
00036
00037 #include "ui_certificatedetailsdialog.h"
00038
00039 #include <models/useridlistmodel.h>
00040 #include <models/subkeylistmodel.h>
00041 #include <models/keycache.h>
00042
00043 #include <commands/changepassphrasecommand.h>
00044 #include <commands/changeownertrustcommand.h>
00045 #include <commands/changeexpirycommand.h>
00046 #include <commands/adduseridcommand.h>
00047 #include <commands/certifycertificatecommand.h>
00048 #include <commands/dumpcertificatecommand.h>
00049
00050 #include <utils/formatting.h>
00051 #include <utils/gnupg-helper.h>
00052
00053 #include <kleo/cryptobackendfactory.h>
00054 #include <kleo/cryptobackend.h>
00055 #include <kleo/keylistjob.h>
00056 #include <kleo/dn.h>
00057
00058 #include <gpgme++/key.h>
00059 #include <gpgme++/keylistresult.h>
00060
00061 #include <KDebug>
00062 #include <KMessageBox>
00063 #include <KLocalizedString>
00064 #include <KGlobalSettings>
00065
00066 #include <QPointer>
00067 #include <QHeaderView>
00068
00069 #include <boost/mem_fn.hpp>
00070
00071 #include <algorithm>
00072 #include <cassert>
00073
00074 using namespace Kleo;
00075 using namespace Kleo::Dialogs;
00076 using namespace Kleo::Commands;
00077 using namespace GpgME;
00078 using namespace boost;
00079
00080 static bool own( const std::vector<UserID::Signature> & sigs ) {
00081 const shared_ptr<const KeyCache> kc = KeyCache::instance();
00082 Q_FOREACH( const UserID::Signature & sig, sigs ) {
00083 const Key signer = kc->findByKeyIDOrFingerprint( sig.signerKeyID() );
00084 if ( signer.isNull() || !signer.hasSecret() )
00085 return false;
00086 }
00087 return !sigs.empty();
00088 }
00089
00090 class CertificateDetailsDialog::Private {
00091 friend class ::Kleo::Dialogs::CertificateDetailsDialog;
00092 CertificateDetailsDialog * const q;
00093 public:
00094 explicit Private( CertificateDetailsDialog * qq )
00095 : q( qq ),
00096 key(),
00097 certificationsModel(),
00098 subkeysModel(),
00099 ui( q )
00100 {
00101 ui.certificationsTV->setModel( &certificationsModel );
00102 connect( ui.certificationsTV->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
00103 q, SLOT(slotCertificationSelectionChanged()) );
00104
00105 ui.subkeyTV->setModel( &subkeysModel );
00106
00107
00108 connect( KeyCache::instance().get(), SIGNAL(keysMayHaveChanged()),
00109 q, SLOT(slotKeysMayHaveChanged()) );
00110 }
00111
00112 private:
00113 void startCommandImplementation( const QPointer<Command> & ptr, const char * slot ) {
00114 connect( ptr, SIGNAL(finished()), q, slot );
00115 ptr->start();
00116 enableDisableWidgets();
00117 }
00118 template <typename T, typename A>
00119 void startCommand( QPointer<Command> & ptr, const A & arg, const char * slot ) {
00120 if ( ptr )
00121 return;
00122 ptr = new T( arg );
00123 startCommandImplementation( ptr, slot );
00124 }
00125 template <typename T>
00126 void startCommand( QPointer<Command> & ptr, const char * slot ) {
00127 startCommand<T>( ptr, this->key, slot );
00128 }
00129 void commandFinished( QPointer<Command> & ptr ) {
00130 ptr = 0;
00131 enableDisableWidgets();
00132 }
00133
00134 void slotChangePassphraseClicked() {
00135 startCommand<ChangePassphraseCommand>( changePassphraseCommand, SLOT(slotChangePassphraseCommandFinished()) );
00136 }
00137 void slotChangePassphraseCommandFinished() {
00138 commandFinished( changePassphraseCommand );
00139 }
00140
00141 void slotChangeTrustLevelClicked() {
00142 startCommand<ChangeOwnerTrustCommand>( changeOwnerTrustCommand, SLOT(slotChangeOwnerTrustCommandFinished()) );
00143 }
00144 void slotChangeOwnerTrustCommandFinished() {
00145 commandFinished( changeOwnerTrustCommand );
00146 }
00147
00148 void slotChangeExpiryDateClicked() {
00149 startCommand<ChangeExpiryCommand>( changeExpiryDateCommand, SLOT(slotChangeExpiryDateCommandFinished()) );
00150 }
00151 void slotChangeExpiryDateCommandFinished() {
00152 commandFinished( changeExpiryDateCommand );
00153 }
00154
00155 void slotAddUserIDClicked() {
00156 startCommand<AddUserIDCommand>( addUserIDCommand, SLOT(slotAddUserIDCommandFinished()) );
00157 }
00158 void slotAddUserIDCommandFinished() {
00159 commandFinished( addUserIDCommand );
00160 }
00161
00162 void slotCertifyUserIDClicked() {
00163 const std::vector<UserID> uids = selectedUserIDs();
00164 if ( uids.empty() )
00165 return;
00166 startCommand<CertifyCertificateCommand>( signCertificateCommand, uids, SLOT(slotSignCertificateCommandFinished()) );
00167 }
00168 void slotSignCertificateCommandFinished() {
00169 commandFinished( signCertificateCommand );
00170 }
00171
00172 void slotRevokeCertificateClicked() {
00173
00174 }
00175
00176 void slotRevokeUserIDClicked() {
00177
00178 }
00179
00180 void slotRevokeCertificationClicked() {
00181
00182 }
00183
00184 void slotShowCertificationsClicked() {
00185 startSignatureListing();
00186 enableDisableWidgets();
00187 }
00188
00189 void startSignatureListing() {
00190 if ( keyListJob )
00191 return;
00192 const CryptoBackend::Protocol * const protocol = CryptoBackendFactory::instance()->protocol( key.protocol() );
00193 if ( !protocol )
00194 return;
00195 KeyListJob * const job = protocol->keyListJob( false, true, true );
00196 if ( !job )
00197 return;
00198 connect( job, SIGNAL(result(GpgME::KeyListResult)),
00199 q, SLOT(slotSignatureListingDone(GpgME::KeyListResult)) );
00200 connect( job, SIGNAL(nextKey(GpgME::Key)),
00201 q, SLOT(slotSignatureListingNextKey(GpgME::Key)) );
00202 if ( const Error err = job->start( QStringList( QString::fromLatin1( key.primaryFingerprint() ) ) ) )
00203 showSignatureListingErrorDialog( err );
00204 else
00205 keyListJob = job;
00206 }
00207 void slotSignatureListingNextKey( const Key & key ) {
00208
00209 Key merged = key;
00210 merged.mergeWith( this->key );
00211 q->setKey( merged );
00212
00213
00214 ui.certificationsTV->expandAll();
00215 ui.certificationsTV->header()->resizeSections( QHeaderView::ResizeToContents );
00216 }
00217 void slotSignatureListingDone( const KeyListResult & result ) {
00218 if ( result.error().isCanceled() )
00219 ;
00220 else if ( result.error() )
00221 showSignatureListingErrorDialog( result.error() );
00222 else
00223 ;
00224 keyListJob = 0;
00225 enableDisableWidgets();
00226 }
00227 void showSignatureListingErrorDialog( const Error & err ) {
00228 KMessageBox::information( q, i18nc("@info",
00229 "<para>An error occurred while loading the certifications: "
00230 "<message>%1</message></para>",
00231 QString::fromLocal8Bit( err.asString() ) ),
00232 i18nc("@title","Certifications Loading Failed") );
00233 }
00234
00235 void slotCertificationSelectionChanged() {
00236 enableDisableWidgets();
00237 }
00238
00239 void slotKeysMayHaveChanged() {
00240 if ( const char * const fpr = key.primaryFingerprint() )
00241 if ( !(key.keyListMode() & Extern) )
00242 q->setKey( KeyCache::instance()->findByFingerprint( fpr ) );
00243 }
00244
00245 void slotDumpCertificate() {
00246
00247 if ( dumpCertificateCommand )
00248 return;
00249
00250 if ( key.protocol() != CMS ) {
00251 ui.dumpLTW->clear();
00252 return;
00253 }
00254
00255 ui.dumpLTW->setLines( QStringList( i18n("Please wait while generating the dump...") ) );
00256
00257 dumpCertificateCommand = new DumpCertificateCommand( key );
00258 dumpCertificateCommand->setUseDialog( false );
00259 QPointer<Command> cmd = dumpCertificateCommand.data();
00260 startCommandImplementation( cmd, SLOT(slotDumpCertificateCommandFinished()) );
00261 }
00262
00263 void slotDumpCertificateCommandFinished() {
00264 ui.dumpLTW->setLines( dumpCertificateCommand->output() );
00265 }
00266
00267 private:
00268 void updateWidgetVisibility() {
00269 const bool x509 = key.protocol() == CMS;
00270 const bool pgp = key.protocol() == OpenPGP;
00271 const bool secret = key.hasSecret();
00272 const bool sigs = (key.keyListMode() & Signatures);
00273 const bool ultimateTrust = key.ownerTrust() == Key::Ultimate;
00274 const bool external = (key.keyListMode() & Extern);
00275
00276
00277 ui.overviewActionsGB->setVisible( !external );
00278 ui.changePassphrasePB->setVisible( secret );
00279 ui.changeTrustLevelPB->setVisible( pgp && ( !secret || !ultimateTrust ) );
00280 ui.changeExpiryDatePB->setVisible( pgp && secret );
00281
00282
00283 ui.userIDsActionsGB->setVisible( !external && pgp );
00284 ui.certificationsActionGB->setVisible( !external && pgp );
00285 ui.addUserIDPB->setVisible( secret );
00286 ui.expandAllCertificationsPB->setVisible( pgp && sigs );
00287 ui.collapseAllCertificationsPB->setVisible( pgp && sigs );
00288 ui.showCertificationsPB->setVisible( pgp && !sigs );
00289
00290
00291 ui.tabWidget->setTabEnabled( ui.tabWidget->indexOf( ui.detailsTab ), pgp );
00292
00293
00294 ui.tabWidget->setTabEnabled( ui.tabWidget->indexOf( ui.chainTab ), x509 );
00295
00296
00297 ui.tabWidget->setTabEnabled( ui.tabWidget->indexOf( ui.dumpTab ), x509 );
00298
00299
00300 ui.revokeCertificatePB->hide();
00301 ui.revokeUserIDPB->hide();
00302 ui.certificationsActionGB->hide();
00303 }
00304
00305 QModelIndexList selectedCertificationsIndexes() const {
00306 return ui.certificationsTV->selectionModel()->selectedRows();
00307 }
00308
00309 std::vector<UserID> selectedUserIDs() const {
00310 const QModelIndexList mil = selectedCertificationsIndexes();
00311 std::vector<UserID> uids = certificationsModel.userIDs( mil, true );
00312 uids.erase( std::remove_if( uids.begin(), uids.end(), mem_fn( &UserID::isNull ) ), uids.end() );
00313 return uids;
00314 }
00315
00316 std::vector<UserID::Signature> selectedSignatures() const {
00317 const QModelIndexList mil = selectedCertificationsIndexes();
00318 std::vector<UserID::Signature> sigs = certificationsModel.signatures( mil );
00319 sigs.erase( std::remove_if( sigs.begin(), sigs.end(), mem_fn( &UserID::Signature::isNull ) ), sigs.end() );
00320 return sigs;
00321 }
00322
00323 void enableDisableWidgets() {
00324
00325 ui.changePassphrasePB->setEnabled( !changePassphraseCommand );
00326 ui.changeTrustLevelPB->setEnabled( !changeOwnerTrustCommand );
00327 ui.changeExpiryDatePB->setEnabled( !changeExpiryDateCommand );
00328
00329
00330 ui.addUserIDPB->setEnabled( !addUserIDCommand );
00331 ui.showCertificationsPB->setEnabled( !keyListJob );
00332 ui.showCertificationsPB->setText( keyListJob
00333 ? i18n("(please wait while certifications are being loaded)")
00334 : i18n("Load Certifications (may take a while)") );
00335
00336 const std::vector<UserID> uids = selectedUserIDs();
00337 const std::vector<UserID::Signature> sigs = selectedSignatures();
00338
00339 ui.certifyUserIDPB->setEnabled( !uids.empty() && sigs.empty() && !signCertificateCommand );
00340 ui.revokeUserIDPB->setEnabled( !uids.empty() && sigs.empty() );
00341 ui.revokeCertificationPB->setEnabled( uids.empty() && !sigs.empty() && own( sigs ) );
00342 }
00343
00344 void updateLabel() {
00345 ui.overviewLB->setText( Formatting::formatOverview( key ) );
00346 }
00347
00348 void updateChainTab() {
00349 ui.chainTW->clear();
00350
00351 if ( key.protocol() != CMS )
00352 return;
00353
00354 QTreeWidgetItem * last = 0;
00355 const std::vector<Key> chain = KeyCache::instance()->findIssuers( key, KeyCache::RecursiveSearch|KeyCache::IncludeSubject );
00356 if ( chain.empty() )
00357 return;
00358 if ( !chain.back().isRoot() ) {
00359 last = new QTreeWidgetItem( ui.chainTW );
00360 last->setText( 0, i18n("Issuer Certificate Not Found (%1)",
00361 DN( chain.back().issuerName() ).prettyDN() ) );
00362
00363 const QBrush & fg = ui.chainTW->palette().brush( QPalette::Disabled, QPalette::WindowText );
00364 last->setForeground( 0, fg );
00365 }
00366 for ( std::vector<Key>::const_reverse_iterator it = chain.rbegin(), end = chain.rend() ; it != end ; ++it ) {
00367 last = last ? new QTreeWidgetItem( last ) : new QTreeWidgetItem( ui.chainTW ) ;
00368 last->setText( 0, DN( it->userID(0).id() ).prettyDN() );
00369
00370 }
00371 ui.chainTW->expandAll();
00372 }
00373
00374 void propagateKey() {
00375 certificationsModel.setKey( key );
00376 const QModelIndexList uidIndexes = certificationsModel.indexes( key.userIDs() );
00377 Q_FOREACH( const QModelIndex & idx, uidIndexes )
00378 ui.certificationsTV->setFirstColumnSpanned( idx.row(), idx.parent(), true );
00379
00380 subkeysModel.setKey( key );
00381 ui.subkeyTV->header()->resizeSections( QHeaderView::ResizeToContents );
00382
00383 updateChainTab();
00384 slotDumpCertificate();
00385 }
00386
00387
00388 private:
00389 Key key;
00390 UserIDListModel certificationsModel;
00391 SubkeyListModel subkeysModel;
00392
00393 QPointer<Command> changePassphraseCommand;
00394 QPointer<Command> changeOwnerTrustCommand;
00395 QPointer<Command> changeExpiryDateCommand;
00396
00397 QPointer<Command> addUserIDCommand;
00398 QPointer<Command> signCertificateCommand;
00399
00400 QPointer<DumpCertificateCommand> dumpCertificateCommand;
00401
00402 QPointer<KeyListJob> keyListJob;
00403
00404 struct UI : public Ui_CertificateDetailsDialog {
00405 explicit UI( Dialogs::CertificateDetailsDialog * qq )
00406 : Ui_CertificateDetailsDialog()
00407 {
00408 setupUi( qq );
00409
00410 chainTW->header()->setResizeMode( 0, QHeaderView::Stretch );
00411
00412 dumpLTW->setFont( KGlobalSettings::fixedFont() );
00413 dumpLTW->setMinimumVisibleLines( 15 );
00414 dumpLTW->setMinimumVisibleColumns( 40 );
00415
00416 subkeyHLine->setTitle( i18nc("@title","Subkeys") );
00417 }
00418 } ui;
00419 };
00420
00421 CertificateDetailsDialog::CertificateDetailsDialog( QWidget * p, Qt::WindowFlags f )
00422 : QDialog( p, f ), d( new Private( this ) )
00423 {
00424
00425 }
00426
00427 CertificateDetailsDialog::~CertificateDetailsDialog() {}
00428
00429
00430 void CertificateDetailsDialog::setKey( const Key & key ) {
00431 d->key = key;
00432 d->updateWidgetVisibility();
00433 d->updateLabel();
00434 d->propagateKey();
00435 d->enableDisableWidgets();
00436 }
00437
00438 Key CertificateDetailsDialog::key() const {
00439 return d->key;
00440 }
00441
00442
00443 #include "moc_certificatedetailsdialog.cpp"