1 /* -*- mode: c++; c-basic-offset:4 -*-
2 dialogs/certificatedetailsdialog.cpp
4 This file is part of Kleopatra, the KDE keymanager
5 Copyright (c) 2008 Klarälvdalens Datakonsult AB
7 Kleopatra is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Kleopatra is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
33 #include <config-kleopatra.h>
35 #include "certificatedetailsdialog.h"
37 #include "ui_certificatedetailsdialog.h"
39 #include <models/useridlistmodel.h>
40 #include <models/subkeylistmodel.h>
41 #include <models/keycache.h>
43 #include <commands/changepassphrasecommand.h>
44 #include <commands/changeownertrustcommand.h>
45 #include <commands/changeexpirycommand.h>
46 #include <commands/adduseridcommand.h>
47 #include <commands/certifycertificatecommand.h>
48 #include <commands/dumpcertificatecommand.h>
50 #include <utils/formatting.h>
51 #include <utils/gnupg-helper.h>
53 #include <kleo/cryptobackendfactory.h>
54 #include <kleo/cryptobackend.h>
55 #include <kleo/keylistjob.h>
58 #include <gpgme++/key.h>
59 #include <gpgme++/keylistresult.h>
62 #include <KMessageBox>
63 #include <KLocalizedString>
64 #include <KGlobalSettings>
67 #include <QHeaderView>
69 #include <boost/mem_fn.hpp>
75 using namespace Kleo::Dialogs
;
76 using namespace Kleo::Commands
;
77 using namespace GpgME
;
78 using namespace boost
;
80 static bool own( const std::vector
<UserID::Signature
> & sigs
) {
81 const shared_ptr
<const KeyCache
> kc
= KeyCache::instance();
82 Q_FOREACH( const UserID::Signature
& sig
, sigs
) {
83 const Key signer
= kc
->findByKeyIDOrFingerprint( sig
.signerKeyID() );
84 if ( signer
.isNull() || !signer
.hasSecret() )
90 class CertificateDetailsDialog::Private
{
91 friend class ::Kleo::Dialogs::CertificateDetailsDialog
;
92 CertificateDetailsDialog
* const q
;
94 explicit Private( CertificateDetailsDialog
* qq
)
97 certificationsModel(),
101 ui
.certificationsTV
->setModel( &certificationsModel
);
102 connect( ui
.certificationsTV
->selectionModel(), SIGNAL(selectionChanged(QItemSelection
,QItemSelection
)),
103 q
, SLOT(slotCertificationSelectionChanged()) );
105 ui
.subkeyTV
->setModel( &subkeysModel
);
106 // no selection (yet)
108 connect( KeyCache::instance().get(), SIGNAL(keysMayHaveChanged()),
109 q
, SLOT(slotKeysMayHaveChanged()) );
113 void startCommandImplementation( const QPointer
<Command
> & ptr
, const char * slot
) {
114 connect( ptr
, SIGNAL(finished()), q
, slot
);
116 enableDisableWidgets();
118 template <typename T
, typename A
>
119 void startCommand( QPointer
<Command
> & ptr
, const A
& arg
, const char * slot
) {
123 startCommandImplementation( ptr
, slot
);
125 template <typename T
>
126 void startCommand( QPointer
<Command
> & ptr
, const char * slot
) {
127 startCommand
<T
>( ptr
, this->key
, slot
);
129 void commandFinished( QPointer
<Command
> & ptr
) {
131 enableDisableWidgets();
134 void slotChangePassphraseClicked() {
135 startCommand
<ChangePassphraseCommand
>( changePassphraseCommand
, SLOT(slotChangePassphraseCommandFinished()) );
137 void slotChangePassphraseCommandFinished() {
138 commandFinished( changePassphraseCommand
);
141 void slotChangeTrustLevelClicked() {
142 startCommand
<ChangeOwnerTrustCommand
>( changeOwnerTrustCommand
, SLOT(slotChangeOwnerTrustCommandFinished()) );
144 void slotChangeOwnerTrustCommandFinished() {
145 commandFinished( changeOwnerTrustCommand
);
148 void slotChangeExpiryDateClicked() {
149 startCommand
<ChangeExpiryCommand
>( changeExpiryDateCommand
, SLOT(slotChangeExpiryDateCommandFinished()) );
151 void slotChangeExpiryDateCommandFinished() {
152 commandFinished( changeExpiryDateCommand
);
155 void slotAddUserIDClicked() {
156 startCommand
<AddUserIDCommand
>( addUserIDCommand
, SLOT(slotAddUserIDCommandFinished()) );
158 void slotAddUserIDCommandFinished() {
159 commandFinished( addUserIDCommand
);
162 void slotCertifyUserIDClicked() {
163 const std::vector
<UserID
> uids
= selectedUserIDs();
166 startCommand
<CertifyCertificateCommand
>( signCertificateCommand
, uids
, SLOT(slotSignCertificateCommandFinished()) );
168 void slotSignCertificateCommandFinished() {
169 commandFinished( signCertificateCommand
);
172 void slotRevokeCertificateClicked() {
176 void slotRevokeUserIDClicked() {
180 void slotRevokeCertificationClicked() {
184 void slotShowCertificationsClicked() {
185 startSignatureListing();
186 enableDisableWidgets();
189 void startSignatureListing() {
192 const CryptoBackend::Protocol
* const protocol
= CryptoBackendFactory::instance()->protocol( key
.protocol() );
195 KeyListJob
* const job
= protocol
->keyListJob( /*remote*/false, /*includeSigs*/true, /*validate*/true );
198 connect( job
, SIGNAL(result(GpgME::KeyListResult
)),
199 q
, SLOT(slotSignatureListingDone(GpgME::KeyListResult
)) );
200 connect( job
, SIGNAL(nextKey(GpgME::Key
)),
201 q
, SLOT(slotSignatureListingNextKey(GpgME::Key
)) );
202 if ( const Error err
= job
->start( QStringList( QString::fromLatin1( key
.primaryFingerprint() ) ) ) )
203 showSignatureListingErrorDialog( err
);
207 void slotSignatureListingNextKey( const Key
& key
) {
208 // don't lose the secret flags ...
210 merged
.mergeWith( this->key
);
213 // fixup the tree view
214 ui
.certificationsTV
->expandAll();
215 ui
.certificationsTV
->header()->resizeSections( QHeaderView::ResizeToContents
);
217 void slotSignatureListingDone( const KeyListResult
& result
) {
218 if ( result
.error().isCanceled() )
220 else if ( result
.error() )
221 showSignatureListingErrorDialog( result
.error() );
226 enableDisableWidgets();
228 void showSignatureListingErrorDialog( const Error
& err
) {
229 KMessageBox::information( q
, i18nc("@info",
230 "<para>An error occurred while loading the certifications: "
231 "<message>%1</message></para>",
232 QString::fromLocal8Bit( err
.asString() ) ),
233 i18nc("@title","Certifications Loading Failed") );
236 void slotCertificationSelectionChanged() {
237 enableDisableWidgets();
240 void slotKeysMayHaveChanged() {
241 if ( const char * const fpr
= key
.primaryFingerprint() )
242 if ( !(key
.keyListMode() & Extern
) )
243 q
->setKey( KeyCache::instance()->findByFingerprint( fpr
) );
246 void slotDumpCertificate() {
248 if ( dumpCertificateCommand
)
251 if ( key
.protocol() != CMS
) {
256 ui
.dumpLTW
->setLines( QStringList( i18n("Please wait while generating the dump...") ) );
258 dumpCertificateCommand
= new DumpCertificateCommand( key
);
259 dumpCertificateCommand
->setUseDialog( false );
260 QPointer
<Command
> cmd
= dumpCertificateCommand
.data();
261 startCommandImplementation( cmd
, SLOT(slotDumpCertificateCommandFinished()) );
264 void slotDumpCertificateCommandFinished() {
265 ui
.dumpLTW
->setLines( dumpCertificateCommand
->output() );
269 void updateWidgetVisibility() {
270 const bool x509
= key
.protocol() == CMS
;
271 const bool pgp
= key
.protocol() == OpenPGP
;
272 const bool secret
= key
.hasSecret();
273 const bool sigs
= (key
.keyListMode() & Signatures
);
274 const bool ultimateTrust
= key
.ownerTrust() == Key::Ultimate
;
275 const bool external
= (key
.keyListMode() & Extern
);
278 ui
.overviewActionsGB
->setVisible( !external
);
279 ui
.changePassphrasePB
->setVisible( secret
);
280 ui
.changeTrustLevelPB
->setVisible( pgp
&& ( !secret
|| !ultimateTrust
) );
281 ui
.changeExpiryDatePB
->setVisible( pgp
&& secret
);
283 // Certifications Tab
284 ui
.userIDsActionsGB
->setVisible( !external
&& pgp
);
285 ui
.certificationsActionGB
->setVisible( !external
&& pgp
);
286 ui
.addUserIDPB
->setVisible( secret
);
287 ui
.expandAllCertificationsPB
->setVisible( pgp
&& sigs
);
288 ui
.collapseAllCertificationsPB
->setVisible( pgp
&& sigs
);
289 ui
.showCertificationsPB
->setVisible( !external
&& pgp
&& !sigs
);
291 // Technical Details Tab
292 ui
.tabWidget
->setTabEnabled( ui
.tabWidget
->indexOf( ui
.detailsTab
), pgp
);
295 ui
.tabWidget
->setTabEnabled( ui
.tabWidget
->indexOf( ui
.chainTab
), x509
);
298 ui
.tabWidget
->setTabEnabled( ui
.tabWidget
->indexOf( ui
.dumpTab
), x509
);
301 ui
.revokeCertificatePB
->hide();
302 ui
.revokeUserIDPB
->hide();
303 ui
.certificationsActionGB
->hide();
306 QModelIndexList
selectedCertificationsIndexes() const {
307 return ui
.certificationsTV
->selectionModel()->selectedRows();
310 std::vector
<UserID
> selectedUserIDs() const {
311 const QModelIndexList mil
= selectedCertificationsIndexes();
312 std::vector
<UserID
> uids
= certificationsModel
.userIDs( mil
, true );
313 uids
.erase( std::remove_if( uids
.begin(), uids
.end(), mem_fn( &UserID::isNull
) ), uids
.end() );
317 std::vector
<UserID::Signature
> selectedSignatures() const {
318 const QModelIndexList mil
= selectedCertificationsIndexes();
319 std::vector
<UserID::Signature
> sigs
= certificationsModel
.signatures( mil
);
320 sigs
.erase( std::remove_if( sigs
.begin(), sigs
.end(), mem_fn( &UserID::Signature::isNull
) ), sigs
.end() );
324 void enableDisableWidgets() {
326 ui
.changePassphrasePB
->setEnabled( !changePassphraseCommand
);
327 ui
.changeTrustLevelPB
->setEnabled( !changeOwnerTrustCommand
);
328 ui
.changeExpiryDatePB
->setEnabled( !changeExpiryDateCommand
);
330 // Certifications Tab
331 ui
.addUserIDPB
->setEnabled( !addUserIDCommand
);
332 ui
.showCertificationsPB
->setEnabled( !keyListJob
);
333 ui
.showCertificationsPB
->setText( keyListJob
334 ? i18n("(please wait while certifications are being loaded)")
335 : i18n("Load Certifications (may take a while)") );
337 const std::vector
<UserID
> uids
= selectedUserIDs();
338 const std::vector
<UserID::Signature
> sigs
= selectedSignatures();
340 ui
.certifyUserIDPB
->setEnabled( !uids
.empty() && sigs
.empty() && !signCertificateCommand
);
341 ui
.revokeUserIDPB
->setEnabled( !uids
.empty() && sigs
.empty() );
342 ui
.revokeCertificationPB
->setEnabled( uids
.empty() && !sigs
.empty() && own( sigs
) );
346 ui
.overviewLB
->setText( Formatting::formatOverview( key
) );
349 void updateChainTab() {
352 if ( key
.protocol() != CMS
)
355 QTreeWidgetItem
* last
= 0;
356 const std::vector
<Key
> chain
= KeyCache::instance()->findIssuers( key
, KeyCache::RecursiveSearch
|KeyCache::IncludeSubject
);
359 if ( !chain
.back().isRoot() ) {
360 last
= new QTreeWidgetItem( ui
.chainTW
);
361 last
->setText( 0, i18n("Issuer Certificate Not Found (%1)",
362 DN( chain
.back().issuerName() ).prettyDN() ) );
363 //last->setSelectable( false );
364 const QBrush
& fg
= ui
.chainTW
->palette().brush( QPalette::Disabled
, QPalette::WindowText
);
365 last
->setForeground( 0, fg
);
367 for ( std::vector
<Key
>::const_reverse_iterator it
= chain
.rbegin(), end
= chain
.rend() ; it
!= end
; ++it
) {
368 last
= last
? new QTreeWidgetItem( last
) : new QTreeWidgetItem( ui
.chainTW
) ;
369 last
->setText( 0, DN( it
->userID(0).id() ).prettyDN() );
370 //last->setSelectable( true );
372 ui
.chainTW
->expandAll();
375 void propagateKey() {
376 certificationsModel
.setKey( key
);
377 const QModelIndexList uidIndexes
= certificationsModel
.indexes( key
.userIDs() );
378 Q_FOREACH( const QModelIndex
& idx
, uidIndexes
)
379 ui
.certificationsTV
->setFirstColumnSpanned( idx
.row(), idx
.parent(), true );
381 subkeysModel
.setKey( key
);
382 ui
.subkeyTV
->header()->resizeSections( QHeaderView::ResizeToContents
);
385 slotDumpCertificate();
391 UserIDListModel certificationsModel
;
392 SubkeyListModel subkeysModel
;
394 QPointer
<Command
> changePassphraseCommand
;
395 QPointer
<Command
> changeOwnerTrustCommand
;
396 QPointer
<Command
> changeExpiryDateCommand
;
398 QPointer
<Command
> addUserIDCommand
;
399 QPointer
<Command
> signCertificateCommand
;
401 QPointer
<DumpCertificateCommand
> dumpCertificateCommand
;
403 QPointer
<KeyListJob
> keyListJob
;
405 struct UI
: public Ui_CertificateDetailsDialog
{
406 explicit UI( Dialogs::CertificateDetailsDialog
* qq
)
407 : Ui_CertificateDetailsDialog()
409 setupUi( qq
->mainWidget() );
410 qq
->setButtons( KDialog::Help
| KDialog::Close
);
411 qq
->setHelp(QString(), "kleopatra");
412 chainTW
->header()->setResizeMode( 0, QHeaderView::Stretch
);
414 dumpLTW
->setFont( KGlobalSettings::fixedFont() );
415 dumpLTW
->setMinimumVisibleLines( 15 );
416 dumpLTW
->setMinimumVisibleColumns( 40 );
418 subkeyHLine
->setTitle( i18nc("@title","Subkeys") );
423 CertificateDetailsDialog::CertificateDetailsDialog( QWidget
* p
, Qt::WindowFlags f
)
424 : KDialog( p
, f
), d( new Private( this ) )
429 CertificateDetailsDialog::~CertificateDetailsDialog() {}
432 void CertificateDetailsDialog::setKey( const Key
& key
) {
434 d
->updateWidgetVisibility();
437 d
->enableDisableWidgets();
440 Key
CertificateDetailsDialog::key() const {
445 #include "moc_certificatedetailsdialog.cpp"