diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -107,10 +107,12 @@ dialogs/certifycertificatedialog.cpp dialogs/exportsecretkeydialog.cpp dialogs/adduseriddialog.cpp - dialogs/certificatedetailsdialog.cpp dialogs/exportcertificatesdialog.cpp dialogs/deletecertificatesdialog.cpp dialogs/setinitialpindialog.cpp + dialogs/certificatedetailswidget.cpp + dialogs/trustchainwidget.cpp + dialogs/subkeyswidget.cpp crypto/controller.cpp crypto/certificateresolver.cpp @@ -239,8 +241,10 @@ dialogs/selftestdialog.ui dialogs/exportsecretkeydialog.ui dialogs/adduseriddialog.ui - dialogs/certificatedetailsdialog.ui dialogs/setinitialpindialog.ui + dialogs/certificatedetailswidget.ui + dialogs/trustchainwidget.ui + dialogs/subkeyswidget.ui newcertificatewizard/listwidget.ui newcertificatewizard/chooseprotocolpage.ui newcertificatewizard/enterdetailspage.ui diff --git a/src/commands/detailscommand.cpp b/src/commands/detailscommand.cpp --- a/src/commands/detailscommand.cpp +++ b/src/commands/detailscommand.cpp @@ -35,7 +35,7 @@ #include "detailscommand.h" #include "command_p.h" -#include +#include #include "kleopatra_debug.h" @@ -43,7 +43,6 @@ using namespace Kleo; using namespace Kleo::Commands; -using namespace Kleo::Dialogs; using namespace GpgME; class DetailsCommand::Private : public Command::Private diff --git a/src/dialogs/adduseriddialog.cpp b/src/dialogs/adduseriddialog.cpp --- a/src/dialogs/adduseriddialog.cpp +++ b/src/dialogs/adduseriddialog.cpp @@ -69,11 +69,9 @@ { if (attr == QLatin1String("NAME")) { return i18n("Name"); - } - if (attr == QLatin1String("COMMENT")) { + } else if (attr == QLatin1String("COMMENT")) { return i18n("Comment"); - } - if (attr == QLatin1String("EMAIL")) { + } else if (attr == QLatin1String("EMAIL")) { return i18n("EMail"); } return QString(); diff --git a/src/dialogs/certificatedetailsdialog.h b/src/dialogs/certificatedetailsdialog.h deleted file mode 100644 --- a/src/dialogs/certificatedetailsdialog.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -*- mode: c++; c-basic-offset:4 -*- - dialogs/certificatedetailsdialog.h - - This file is part of Kleopatra, the KDE keymanager - Copyright (c) 2008 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 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. -*/ - -#ifndef __KLEOPATRA_DIALOGS_CERTIFICATEDETAILSDIALOG_H__ -#define __KLEOPATRA_DIALOGS_CERTIFICATEDETAILSDIALOG_H__ - -#include - -#include - -namespace GpgME -{ -class Key; -} - -namespace Kleo -{ -namespace Dialogs -{ - -class CertificateDetailsDialog : public QDialog -{ - Q_OBJECT -public: - explicit CertificateDetailsDialog(QWidget *parent = Q_NULLPTR); - ~CertificateDetailsDialog(); - - void setKey(const GpgME::Key &key); - GpgME::Key key() const; - -private: - class Private; - kdtools::pimpl_ptr d; - Q_PRIVATE_SLOT(d, void slotChangePassphraseClicked()) - Q_PRIVATE_SLOT(d, void slotChangePassphraseCommandFinished()) - Q_PRIVATE_SLOT(d, void slotChangeTrustLevelClicked()) - Q_PRIVATE_SLOT(d, void slotChangeOwnerTrustCommandFinished()) - Q_PRIVATE_SLOT(d, void slotChangeExpiryDateClicked()) - Q_PRIVATE_SLOT(d, void slotChangeExpiryDateCommandFinished()) - Q_PRIVATE_SLOT(d, void slotRevokeCertificateClicked()) - Q_PRIVATE_SLOT(d, void slotAddUserIDClicked()) - Q_PRIVATE_SLOT(d, void slotAddUserIDCommandFinished()) - Q_PRIVATE_SLOT(d, void slotRevokeUserIDClicked()) - Q_PRIVATE_SLOT(d, void slotCertifyUserIDClicked()) - Q_PRIVATE_SLOT(d, void slotSignCertificateCommandFinished()) - Q_PRIVATE_SLOT(d, void slotRevokeCertificationClicked()) - Q_PRIVATE_SLOT(d, void slotSignatureListingNextKey(GpgME::Key)) - Q_PRIVATE_SLOT(d, void slotSignatureListingDone(GpgME::KeyListResult)) - Q_PRIVATE_SLOT(d, void slotCertificationSelectionChanged()) - Q_PRIVATE_SLOT(d, void slotKeysMayHaveChanged()) - Q_PRIVATE_SLOT(d, void slotDumpCertificate()) - Q_PRIVATE_SLOT(d, void slotDumpCertificateCommandFinished()) -}; - -} -} - -#endif /* __KLEOPATRA_DIALOGS_CERTIFICATEDETAILSDIALOG_H */ - diff --git a/src/dialogs/certificatedetailsdialog.cpp b/src/dialogs/certificatedetailsdialog.cpp deleted file mode 100644 --- a/src/dialogs/certificatedetailsdialog.cpp +++ /dev/null @@ -1,521 +0,0 @@ -/* -*- mode: c++; c-basic-offset:4 -*- - dialogs/certificatedetailsdialog.cpp - - This file is part of Kleopatra, the KDE keymanager - Copyright (c) 2008 Klarälvdalens Datakonsult AB - 2016 Andre Heinecke - - 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 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. -*/ - -#include - -#include "certificatedetailsdialog.h" - -#include "ui_certificatedetailsdialog.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -using namespace Kleo; -using namespace Kleo::Dialogs; -using namespace Kleo::Commands; -using namespace GpgME; - -static bool own(const QVector &sigs) -{ - const std::shared_ptr kc = KeyCache::instance(); - Q_FOREACH (const UserID::Signature &sig, sigs) { - const Key signer = kc->findByKeyIDOrFingerprint(sig.signerKeyID()); - if (signer.isNull() || !signer.hasSecret()) { - return false; - } - } - return !sigs.empty(); -} - -class CertificateDetailsDialog::Private -{ - friend class ::Kleo::Dialogs::CertificateDetailsDialog; - CertificateDetailsDialog *const q; -public: - explicit Private(CertificateDetailsDialog *qq) - : q(qq), - key(), - certificationsModel(), - subkeysModel(), - ui(q) - { - ui.certificationsTV->setModel(&certificationsModel); - connect(ui.certificationsTV->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - q, SLOT(slotCertificationSelectionChanged())); - connect(ui.changePassphrasePB, SIGNAL(clicked()), - q, SLOT(slotChangePassphraseClicked())); - connect(ui.changeTrustLevelPB, SIGNAL(clicked()), - q, SLOT(slotChangeTrustLevelClicked())); - connect(ui.changeExpiryDatePB, SIGNAL(clicked()), - q, SLOT(slotChangeExpiryDateClicked())); - connect(ui.revokeCertificationPB, SIGNAL(clicked()), - q, SLOT(slotRevokeCertificationClicked())); - connect(ui.addUserIDPB, SIGNAL(clicked()), - q, SLOT(slotAddUserIDClicked())); - connect(ui.revokeUserIDPB, SIGNAL(clicked()), - q, SLOT(slotRevokeUserIDClicked())); - connect(ui.certifyUserIDPB, SIGNAL(clicked()), - q, SLOT(slotCertifyUserIDClicked())); - connect(ui.revokeCertificationPB, SIGNAL(clicked()), - q, SLOT(slotRevokeCertificationClicked())); - - ui.subkeyTV->setModel(&subkeysModel); - // no selection (yet) - - connect(KeyCache::instance().get(), SIGNAL(keysMayHaveChanged()), - q, SLOT(slotKeysMayHaveChanged())); - } - - void readConfig() - { - KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog"); - const QSize size = dialog.readEntry("Size", QSize(600, 400)); - if (size.isValid()) { - q->resize(size); - } - } - - void writeConfig() - { - KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog"); - dialog.writeEntry("Size", q->size()); - dialog.sync(); - } - -private: - void startCommandImplementation(const QPointer &ptr, const char *slot) - { - connect(ptr, SIGNAL(finished()), q, slot); - ptr->start(); - enableDisableWidgets(); - } - template - void startCommand(QPointer &ptr, const A &arg, const char *slot) - { - if (ptr) { - return; - } - ptr = new T(arg); - startCommandImplementation(ptr, slot); - } - template - void startCommand(QPointer &ptr, const char *slot) - { - startCommand(ptr, this->key, slot); - } - void commandFinished(QPointer &ptr) - { - ptr = 0; - enableDisableWidgets(); - } - - void slotChangePassphraseClicked() - { - startCommand(changePassphraseCommand, SLOT(slotChangePassphraseCommandFinished())); - } - void slotChangePassphraseCommandFinished() - { - commandFinished(changePassphraseCommand); - } - - void slotChangeTrustLevelClicked() - { - startCommand(changeOwnerTrustCommand, SLOT(slotChangeOwnerTrustCommandFinished())); - } - void slotChangeOwnerTrustCommandFinished() - { - commandFinished(changeOwnerTrustCommand); - } - - void slotChangeExpiryDateClicked() - { - startCommand(changeExpiryDateCommand, SLOT(slotChangeExpiryDateCommandFinished())); - } - void slotChangeExpiryDateCommandFinished() - { - commandFinished(changeExpiryDateCommand); - } - - void slotAddUserIDClicked() - { - startCommand(addUserIDCommand, SLOT(slotAddUserIDCommandFinished())); - } - void slotAddUserIDCommandFinished() - { - commandFinished(addUserIDCommand); - } - - void slotCertifyUserIDClicked() - { - const QVector uids = selectedUserIDs(); - if (uids.isEmpty()) { - return; - } - startCommand(signCertificateCommand, - uids.toStdVector(), - SLOT(slotSignCertificateCommandFinished())); - } - void slotSignCertificateCommandFinished() - { - commandFinished(signCertificateCommand); - } - - void slotRevokeCertificateClicked() - { - - } - - void slotRevokeUserIDClicked() - { - - } - - void slotRevokeCertificationClicked() - { - - } - - void startSignatureListing() - { - if (keyListJob) { - return; - } - const QGpgME::Protocol *const protocol = (key.protocol() == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); - if (!protocol) { - return; - } - QGpgME::KeyListJob *const job = protocol->keyListJob(/*remote*/false, /*includeSigs*/true, /*validate*/true); - if (!job) { - return; - } - connect(job, SIGNAL(result(GpgME::KeyListResult)), - q, SLOT(slotSignatureListingDone(GpgME::KeyListResult))); - connect(job, SIGNAL(nextKey(GpgME::Key)), - q, SLOT(slotSignatureListingNextKey(GpgME::Key))); - if (const Error err = job->start(QStringList(QString::fromLatin1(key.primaryFingerprint())))) { - showSignatureListingErrorDialog(err); - } else { - keyListJob = job; - } - } - void slotSignatureListingNextKey(const Key &key) - { - // don't lose the secret flags ... - Key merged = key; - merged.mergeWith(this->key); - q->setKey(merged); - - // fixup the tree view - ui.certificationsTV->expandAll(); - ui.certificationsTV->header()->resizeSections(QHeaderView::ResizeToContents); - } - void slotSignatureListingDone(const KeyListResult &result) - { - if (result.error().isCanceled()) - ; - else if (result.error()) { - showSignatureListingErrorDialog(result.error()); - } else { - ; // nothing to do - } - keyListJob = 0; - enableDisableWidgets(); - } - void showSignatureListingErrorDialog(const Error &err) - { - KMessageBox::information(q, xi18nc("@info", - "An error occurred while loading the certifications: " - "%1", - QString::fromLocal8Bit(err.asString())), - i18nc("@title", "Certifications Loading Failed")); - } - - void slotCertificationSelectionChanged() - { - enableDisableWidgets(); - } - - void slotKeysMayHaveChanged() - { - if (const char *const fpr = key.primaryFingerprint()) - if (!(key.keyListMode() & Extern)) { - q->setKey(KeyCache::instance()->findByFingerprint(fpr)); - } - } - - void slotDumpCertificate() - { - - if (dumpCertificateCommand) { - return; - } - - if (key.protocol() != CMS) { - ui.dumpLTW->clear(); - return; - } - - ui.dumpLTW->setLines(QStringList(i18n("Please wait while generating the dump..."))); - - dumpCertificateCommand = new DumpCertificateCommand(key); - dumpCertificateCommand->setUseDialog(false); - QPointer cmd = dumpCertificateCommand.data(); - startCommandImplementation(cmd, SLOT(slotDumpCertificateCommandFinished())); - } - - void slotDumpCertificateCommandFinished() - { - ui.dumpLTW->setLines(dumpCertificateCommand->output()); - } - -private: - void updateWidgetVisibility() - { - const bool x509 = key.protocol() == CMS; - const bool pgp = key.protocol() == OpenPGP; - const bool secret = key.hasSecret(); - const bool sigs = (key.keyListMode() & Signatures); - const bool ultimateTrust = key.ownerTrust() == Key::Ultimate; - const bool external = (key.keyListMode() & Extern); - - // Overview Tab - ui.overviewActionsGB->setVisible(!external); - ui.changePassphrasePB->setVisible(secret); - ui.changeTrustLevelPB->setVisible(pgp && (!secret || !ultimateTrust)); - ui.changeExpiryDatePB->setVisible(pgp && secret); - - // Certifications Tab - ui.userIDsActionsGB->setVisible(!external && pgp); - ui.certificationsActionGB->setVisible(!external && pgp); - ui.addUserIDPB->setVisible(secret); - ui.expandAllCertificationsPB->setVisible(pgp && sigs); - ui.collapseAllCertificationsPB->setVisible(pgp && sigs); - - // Technical Details Tab - ui.tabWidget->setTabEnabled(ui.tabWidget->indexOf(ui.detailsTab), pgp); - - // Chain tab - ui.tabWidget->setTabEnabled(ui.tabWidget->indexOf(ui.chainTab), x509); - - // Dump tab - ui.tabWidget->setTabEnabled(ui.tabWidget->indexOf(ui.dumpTab), x509); - - // not implemented: - ui.revokeCertificatePB->hide(); - ui.revokeUserIDPB->hide(); - ui.certificationsActionGB->hide(); - } - - QModelIndexList selectedCertificationsIndexes() const - { - return ui.certificationsTV->selectionModel()->selectedRows(); - } - - QVector selectedUserIDs() const - { - const QModelIndexList mil = selectedCertificationsIndexes(); - return certificationsModel.userIDs(mil); - } - - QVector selectedSignatures() const - { - const QModelIndexList mil = selectedCertificationsIndexes(); - return certificationsModel.signatures(mil); - } - - void enableDisableWidgets() - { - // Overview Tab - ui.changePassphrasePB->setEnabled(!changePassphraseCommand); - ui.changeTrustLevelPB->setEnabled(!changeOwnerTrustCommand); - ui.changeExpiryDatePB->setEnabled(!changeExpiryDateCommand); - - // Certifications Tab - ui.addUserIDPB->setEnabled(!addUserIDCommand); - - const QVector uids = selectedUserIDs(); - const QVector sigs = selectedSignatures(); - - ui.certifyUserIDPB->setEnabled(!uids.empty() && sigs.empty() && !signCertificateCommand); - ui.revokeUserIDPB->setEnabled(!uids.empty() && sigs.empty()); - ui.revokeCertificationPB->setEnabled(uids.empty() && !sigs.empty() && own(sigs)); - } - - void updateLabel() - { - ui.overviewLB->setText(Formatting::formatOverview(key)); - } - - void updateChainTab() - { - ui.chainTW->clear(); - - if (key.protocol() != CMS) { - return; - } - - QTreeWidgetItem *last = 0; - const std::vector chain = KeyCache::instance()->findIssuers(key, KeyCache::RecursiveSearch | KeyCache::IncludeSubject); - if (chain.empty()) { - return; - } - if (!chain.back().isRoot()) { - last = new QTreeWidgetItem(ui.chainTW); - last->setText(0, i18n("Issuer Certificate Not Found (%1)", - DN(chain.back().issuerName()).prettyDN())); - //last->setSelectable( false ); - const QBrush &fg = ui.chainTW->palette().brush(QPalette::Disabled, QPalette::WindowText); - last->setForeground(0, fg); - } - for (std::vector::const_reverse_iterator it = chain.rbegin(), end = chain.rend(); it != end; ++it) { - last = last ? new QTreeWidgetItem(last) : new QTreeWidgetItem(ui.chainTW); - last->setText(0, DN(it->userID(0).id()).prettyDN()); - //last->setSelectable( true ); - } - ui.chainTW->expandAll(); - } - - void propagateKey() - { - certificationsModel.setKey(key); - subkeysModel.setKey(key); - ui.subkeyTV->header()->resizeSections(QHeaderView::ResizeToContents); - - updateChainTab(); - slotDumpCertificate(); - } - -private: - Key key; - UserIDListModel certificationsModel; - SubkeyListModel subkeysModel; - - QPointer changePassphraseCommand; - QPointer changeOwnerTrustCommand; - QPointer changeExpiryDateCommand; - - QPointer addUserIDCommand; - QPointer signCertificateCommand; - - QPointer dumpCertificateCommand; - - QPointer keyListJob; - - struct UI : public Ui_CertificateDetailsDialog { - explicit UI(Dialogs::CertificateDetailsDialog *qq) - : Ui_CertificateDetailsDialog() - { - QWidget *mainWidget = new QWidget; - setupUi(mainWidget); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Help | - QDialogButtonBox::Close); - chainTW->header()->setSectionResizeMode(0, QHeaderView::Stretch); - - dumpLTW->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - dumpLTW->setMinimumVisibleLines(15); - dumpLTW->setMinimumVisibleColumns(40); - - subkeyHLine->setTitle(i18nc("@title", "Subkeys")); - - QVBoxLayout *layout = new QVBoxLayout; - layout->addWidget(mainWidget); - layout->addWidget(buttonBox); - qq->setLayout(layout); - - QObject::connect(buttonBox, &QDialogButtonBox::rejected, qq, &QDialog::reject); - QObject::connect(buttonBox, &QDialogButtonBox::helpRequested, qq, [] { - KHelpClient::invokeHelp(QStringLiteral("kleopatra")); - }); - } - } ui; -}; - -CertificateDetailsDialog::CertificateDetailsDialog(QWidget *p) - : QDialog(p), d(new Private(this)) -{ - d->readConfig(); -} - -CertificateDetailsDialog::~CertificateDetailsDialog() -{ - d->writeConfig(); -} - -void CertificateDetailsDialog::setKey(const Key &key) -{ - d->key = key; - d->updateWidgetVisibility(); - d->updateLabel(); - d->propagateKey(); - d->enableDisableWidgets(); - d->startSignatureListing(); -} - -Key CertificateDetailsDialog::key() const -{ - return d->key; -} - -#include "moc_certificatedetailsdialog.cpp" diff --git a/src/dialogs/certificatedetailsdialog.ui b/src/dialogs/certificatedetailsdialog.ui deleted file mode 100644 --- a/src/dialogs/certificatedetailsdialog.ui +++ /dev/null @@ -1,396 +0,0 @@ - - - CertificateDetailsDialog - - - - 0 - 0 - 666 - 449 - - - - Certificate Details - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - Overview - - - - - - Actions - - - - - - Change Passphrase... - - - false - - - - - - - Trust Certifications Made by This Certificate... - - - false - - - - - - - Change Expiry Date... - - - false - - - - - - - Revoke This Certificate... - - - false - - - - - - - Qt::Vertical - - - - 0 - 1 - - - - - - - - - - - - 0 - 1 - - - - QFrame::NoFrame - - - false - - - - - - - - 1 - 0 - - - - Photo - - - - - - <p>At the moment, Kleopatra does not support photos in certificates. It has no support for adding, nor for displaying them. This is for the following reasons:</p> -<ul> -<li>Photos give a false sense of security.</li> -<li>Photos increase the size of certificates.</li> -</ul> - - - false - - - true - - - - - - - - - - - User-IDs && Certifications - - - - - - - - QAbstractItemView::ExtendedSelection - - - true - - - - - - - - - User-IDs - - - - - - Add... - - - false - - - - - - - Revoke... - - - false - - - - - - - Certify... - - - false - - - - - - - - - - Certifications - - - - - - Revoke... - - - false - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Expand All - - - false - - - - - - - Collapse All - - - false - - - - - - - - - - - - Technical Details - - - - - - - - - - - - - - - - - - - QAbstractItemView::NoSelection - - - false - - - true - - - - - - - - Chain - - - - - - true - - - true - - - - 1 - - - - - - - - - Dump - - - - - - This is a dump of all information the backend has about this certificate: - - - - - - - - 0 - 0 - - - - - - - - - - - - - KDLogTextWidget - QWidget -
utils/kdlogtextwidget.h
- 1 -
- - KDHorizontalLine - QFrame -
libkleo/kdhorizontalline.h
- 1 -
-
- - - - expandAllCertificationsPB - clicked() - certificationsTV - expandAll() - - - 665 - 424 - - - 525 - 320 - - - - - collapseAllCertificationsPB - clicked() - certificationsTV - collapseAll() - - - 701 - 464 - - - 585 - 352 - - - - -
diff --git a/src/dialogs/certificatedetailswidget.h b/src/dialogs/certificatedetailswidget.h new file mode 100644 --- /dev/null +++ b/src/dialogs/certificatedetailswidget.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2016 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 +*/ + +#ifndef KLEO_CERTIFICATEDETAILS_WIDGET_H +#define KLEO_CERTIFICATEDETAILS_WIDGET_H + +#include +#include + +namespace GpgME { +class Key; +} + +class CertificateDetailsWidget : public QWidget +{ + Q_OBJECT + +public: + explicit CertificateDetailsWidget(QWidget *parent = Q_NULLPTR); + ~CertificateDetailsWidget(); + + void setKey(const GpgME::Key &key); + GpgME::Key key() const; + +private: + class Private; + const QScopedPointer d; +}; + + +class CertificateDetailsDialog : public QDialog +{ + Q_OBJECT +public: + explicit CertificateDetailsDialog(QWidget *parent = Q_NULLPTR); + ~CertificateDetailsDialog(); + + void setKey(const GpgME::Key &key); + GpgME::Key key() const; + +private: + void readConfig(); + void writeConfig(); +}; + +#endif diff --git a/src/dialogs/certificatedetailswidget.cpp b/src/dialogs/certificatedetailswidget.cpp new file mode 100644 --- /dev/null +++ b/src/dialogs/certificatedetailswidget.cpp @@ -0,0 +1,481 @@ +/* Copyright (c) 2016 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 +*/ + +#include "certificatedetailswidget.h" +#include "ui_certificatedetailswidget.h" +#include "kleopatra_debug.h" +#include "trustchainwidget.h" +#include "subkeyswidget.h" + +#include "commands/changepassphrasecommand.h" +#include "commands/changeexpirycommand.h" +#include "commands/certifycertificatecommand.h" +#include "commands/adduseridcommand.h" +#include "commands/dumpcertificatecommand.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define HIDE_ROW(row) \ + ui.row->setVisible(false); \ + ui.row##Lbl->setVisible(false); + +Q_DECLARE_METATYPE(GpgME::UserID); + +class CertificateDetailsWidget::Private +{ +public: + Private(CertificateDetailsWidget *parent) + : q(parent) + {} + + void setupCommonProperties(); + void setupPGPProperties(); + void setupSMIMEProperties(); + + void revokeUID(const GpgME::UserID &uid); + void addUserID(); + void changePassphrase(); + void changeExpiration(); + void keysMayHaveChanged(); + void showTrustChainDialog(); + void showMoreDetails(); + void publishCertificate(); + void userIDTableContextMenuRequested(const QPoint &p); + + QString tofuTooltipString(const GpgME::UserID &uid) const; + + void smimeLinkActivated(const QString &link); + + Ui::CertificateDetailsWidget ui; + GpgME::Key key; +private: + CertificateDetailsWidget *q; +}; + +void CertificateDetailsWidget::Private::setupCommonProperties() +{ + // TODO: Enable once implemented + HIDE_ROW(publishing) + + const bool hasSecret = key.hasSecret(); + const bool isOpenPGP = key.protocol() == GpgME::OpenPGP; + // TODO: Enable once implemented + const bool canRevokeUID = false; // isOpenPGP && hasSecret + + ui.changePassphraseBtn->setVisible(hasSecret); + ui.changeExpirationBtn->setVisible(isOpenPGP && hasSecret); + ui.addUserIDBtn->setVisible(hasSecret); + + ui.validFrom->setText(Kleo::Formatting::creationDateString(key)); + const QString expiry = Kleo::Formatting::expirationDateString(key); + ui.expires->setText(expiry.isEmpty() ? i18nc("Expires", "never") : expiry); + ui.type->setText(Kleo::Formatting::type(key)); + ui.fingerprint->setText(QString::fromLatin1(key.primaryFingerprint())); + + ui.userIDTable->clear(); + + QStringList headers = { i18n("Email"), i18n("Name") }; + if (isOpenPGP) { + headers << i18n("Trust Level"); + if (canRevokeUID) { + headers << QString(); + } + } + ui.userIDTable->setColumnCount(headers.count()); + ui.userIDTable->setColumnWidth(0, 200); + ui.userIDTable->setColumnWidth(1, 200); + ui.userIDTable->setHeaderLabels(headers); + + const auto uids = key.userIDs(); + for (const auto &uid : uids) { + auto item = new QTreeWidgetItem; + const QString toolTip = tofuTooltipString(uid); + item->setData(0, Qt::UserRole, QVariant::fromValue(uid)); + item->setData(0, Qt::DisplayRole, Kleo::Formatting::prettyEMail(uid)); + item->setData(0, Qt::ToolTipRole, toolTip); + item->setData(1, Qt::DisplayRole, Kleo::Formatting::prettyName(uid)); + item->setData(1, Qt::ToolTipRole, toolTip); + if (isOpenPGP) { + QIcon trustIcon; + switch (uid.validity()) { + case GpgME::UserID::Unknown: + case GpgME::UserID::Undefined: + trustIcon = QIcon::fromTheme(QStringLiteral("emblem-question")); + break; + case GpgME::UserID::Never: + trustIcon = QIcon::fromTheme(QStringLiteral("emblem-error")); + break; + case GpgME::UserID::Marginal: + trustIcon = QIcon::fromTheme(QStringLiteral("emblem-warning")); + break; + case GpgME::UserID::Full: + case GpgME::UserID::Ultimate: + trustIcon = QIcon::fromTheme(QStringLiteral("emblem-success")); + break; + } + item->setData(2, Qt::DecorationRole, trustIcon); + item->setData(2, Qt::DisplayRole, Kleo::Formatting::validityShort(uid)); + item->setData(2, Qt::ToolTipRole, toolTip); + + ui.userIDTable->addTopLevelItem(item); + + if (canRevokeUID) { + auto button = new QPushButton; + button->setIcon(QIcon::fromTheme(QStringLiteral("entry-delete"))); + button->setToolTip(i18n("Revoke this User ID")); + button->setMaximumWidth(32); + QObject::connect(button, &QPushButton::clicked, + q, [this, uid]() { revokeUID(uid); }); + ui.userIDTable->setItemWidget(item, 4, button); + } + } else { + ui.userIDTable->addTopLevelItem(item); + } + } +} + +void CertificateDetailsWidget::Private::revokeUID(const GpgME::UserID &uid) +{ + Q_UNUSED(uid); + qCWarning(KLEOPATRA_LOG) << "Revoking UserID is not implemented. How did you even get here?!?!"; +} + +void CertificateDetailsWidget::Private::changeExpiration() +{ + auto cmd = new Kleo::Commands::ChangeExpiryCommand(key); + QObject::connect(cmd, &Kleo::Commands::ChangeExpiryCommand::finished, + q, [this]() { + ui.changeExpirationBtn->setEnabled(true); + }); + ui.changeExpirationBtn->setEnabled(false); + cmd->start(); +} + +void CertificateDetailsWidget::Private::changePassphrase() +{ + auto cmd = new Kleo::Commands::ChangePassphraseCommand(key); + QObject::connect(cmd, &Kleo::Commands::ChangePassphraseCommand::finished, + q, [this, cmd]() { + ui.changePassphraseBtn->setEnabled(true); + }); + ui.changePassphraseBtn->setEnabled(false); + cmd->start(); +} + +void CertificateDetailsWidget::Private::addUserID() +{ + auto cmd = new Kleo::Commands::AddUserIDCommand(key); + QObject::connect(cmd, &Kleo::Commands::AddUserIDCommand::finished, + q, [this, cmd]() { + ui.addUserIDBtn->setEnabled(true); + }); + ui.addUserIDBtn->setEnabled(false); + cmd->start(); +} + +void CertificateDetailsWidget::Private::keysMayHaveChanged() +{ + auto newKey = Kleo::KeyCache::instance()->findByFingerprint(key.primaryFingerprint()); + if (!newKey.isNull()) { + q->setKey(newKey); + } +} + +void CertificateDetailsWidget::Private::showTrustChainDialog() +{ + QScopedPointer dlg(new TrustChainDialog(q)); + dlg->setKey(key); + dlg->exec(); +} + +void CertificateDetailsWidget::Private::publishCertificate() +{ + qCWarning(KLEOPATRA_LOG) << "publishCertificateis not implemented."; + //TODO +} + +void CertificateDetailsWidget::Private::userIDTableContextMenuRequested(const QPoint &p) +{ + auto item = ui.userIDTable->itemAt(p); + if (!item) { + return; + } + + const auto userID = item->data(0, Qt::UserRole).value(); + + QMenu *menu = new QMenu(q); + menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-sign")), + i18n("Certify ..."), + q, [this, userID]() { + auto cmd = new Kleo::Commands::CertifyCertificateCommand(userID); + ui.userIDTable->setEnabled(false); + connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished, + q, [this]() { ui.userIDTable->setEnabled(true); }); + cmd->start(); + }); + connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater); + menu->popup(ui.userIDTable->viewport()->mapToGlobal(p)); +} + +void CertificateDetailsWidget::Private::showMoreDetails() +{ + ui.moreDetailsBtn->setEnabled(false); + if (key.protocol() == GpgME::CMS) { + auto cmd = new Kleo::Commands::DumpCertificateCommand(key); + connect(cmd, &Kleo::Commands::DumpCertificateCommand::finished, + q, [this]() { + ui.moreDetailsBtn->setEnabled(true); + }); + cmd->setUseDialog(true); + cmd->start(); + } else { + QScopedPointer dlg(new SubKeysDialog(q)); + dlg->setKey(key); + dlg->exec(); + ui.moreDetailsBtn->setEnabled(true); + } +} + +QString CertificateDetailsWidget::Private::tofuTooltipString(const GpgME::UserID &uid) const +{ + const auto tofu = uid.tofuInfo(); + if (tofu.isNull()) { + return QString(); + } + + QString html = QStringLiteral(""); + const auto appendRow = [&html](const QString &lbl, const QString &val) { + html += QStringLiteral("" + "" + "" + "") + .arg(lbl, val); + }; + const auto appendHeader = [this, &html](const QString &hdr) { + html += QStringLiteral("") + .arg(q->palette().highlight().color().name(), + q->palette().highlightedText().color().name(), + hdr); + }; + const auto dateTime = [](long ts) { + return ts == 0 ? i18n("never") : QDateTime::fromTime_t(ts).toString(Qt::SystemLocaleShortDate); + }; + appendHeader(i18n("Signing")); + appendRow(i18n("First message"), dateTime(tofu.signFirst())); + appendRow(i18n("Last message"), dateTime(tofu.signLast())); + appendRow(i18n("Message count"), QString::number(tofu.signCount())); + appendHeader(i18n("Encryption")); + appendRow(i18n("First message"), dateTime(tofu.encrFirst())); + appendRow(i18n("Last message"), dateTime(tofu.encrLast())); + appendRow(i18n("Message count"), QString::number(tofu.encrCount())); + + html += QStringLiteral("
%1:%2
%3
"); + // Make sure the tooltip string is different for each UserID, even if the + // data are the same, otherwise the tooltip is not updated and moved when + // user moves mouse from one row to another. + html += QStringLiteral("").arg(QString::fromUtf8(uid.id())); + return html; +} + + +void CertificateDetailsWidget::Private::setupPGPProperties() +{ + HIDE_ROW(smimeOwner) + HIDE_ROW(smimeIssuer) + ui.smimeRelatedAddresses->setVisible(false); + ui.trustChainDetailsBtn->setVisible(false); + + ui.userIDTable->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui.userIDTable, &QAbstractItemView::customContextMenuRequested, + q, [this](const QPoint &p) { userIDTableContextMenuRequested(p); }); +} + +void CertificateDetailsWidget::Private::setupSMIMEProperties() +{ + HIDE_ROW(publishing) + + const auto ownerId = key.userID(0); + const Kleo::DN dn(ownerId.id()); + const QString cn = dn[QStringLiteral("CN")]; + const QString o = dn[QStringLiteral("O")]; + const QString dnEmail = dn[QStringLiteral("EMAIL")]; + const QString name = cn.isEmpty() ? dnEmail : cn; + + QString owner; + if (name.isEmpty()) { + owner = dn.dn(); + } else if (o.isEmpty()) { + owner = name; + } else { + owner = i18nc(" of ", "%1 of %2", name, o); + } + ui.smimeOwner->setText(QStringLiteral("%1").arg(owner)); + + const Kleo::DN issuerDN(key.issuerName()); + const QString issuerCN = issuerDN[QStringLiteral("CN")]; + const QString issuer = issuerCN.isEmpty() ? key.issuerName() : issuerCN; + ui.smimeIssuer->setText(QStringLiteral("%1").arg(issuer)); +} + +void CertificateDetailsWidget::Private::smimeLinkActivated(const QString &link) +{ + Kleo::DN dn; + QWidget *w; + if (link == QLatin1String("#ownerDetails")) { + dn = Kleo::DN(key.userID(0).id()); + w = ui.smimeOwner; + } else if (link == QLatin1String("#issuerDetails")) { + dn = Kleo::DN(key.issuerName()); + w = ui.smimeIssuer; + } else { + Q_UNREACHABLE(); + return; + } + + QString html = QStringLiteral(""); + const auto appendRow = [&html, dn](const QString &lbl, const QString &attr) { + const QString val = dn[attr]; + if (!val.isEmpty()) { + html += QStringLiteral( + "" + "" + "").arg(lbl, val); + } + }; + appendRow(i18n("Common Name"), QStringLiteral("CN")); + appendRow(i18n("Organization"), QStringLiteral("O")); + appendRow(i18n("Street"), QStringLiteral("STREET")); + appendRow(i18n("City"), QStringLiteral("L")); + appendRow(i18n("State"), QStringLiteral("ST")); + appendRow(i18n("Country"), QStringLiteral("C")); + html += QStringLiteral("
%1:%2
"); + + QToolTip::showText(QCursor::pos(), html, w); +} + + + +CertificateDetailsWidget::CertificateDetailsWidget(QWidget *parent) + : QWidget(parent) + , d(new Private(this)) +{ + d->ui.setupUi(this); + + connect(d->ui.addUserIDBtn, &QPushButton::clicked, + this, [this]() { d->addUserID(); }); + connect(d->ui.changePassphraseBtn, &QPushButton::clicked, + this, [this]() { d->changePassphrase(); }); + connect(d->ui.changeExpirationBtn, &QPushButton::clicked, + this, [this]() { d->changeExpiration(); }); + connect(d->ui.smimeOwner, &QLabel::linkActivated, + this, [this](const QString &link) { d->smimeLinkActivated(link); }); + connect(d->ui.smimeIssuer, &QLabel::linkActivated, + this, [this](const QString &link) { d->smimeLinkActivated(link); }); + connect(d->ui.trustChainDetailsBtn, &QPushButton::pressed, + this, [this]() { d->showTrustChainDialog(); }); + connect(d->ui.moreDetailsBtn, &QPushButton::pressed, + this, [this]() { d->showMoreDetails(); }); + connect(d->ui.publishing, &QPushButton::pressed, + this, [this]() { d->publishCertificate(); }); + + connect(Kleo::KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, + this, [this]() { d->keysMayHaveChanged(); }); +} + +CertificateDetailsWidget::~CertificateDetailsWidget() +{ +} + +void CertificateDetailsWidget::setKey(const GpgME::Key &key) +{ + d->key = key; + d->key.update(); // Fetch TOFU info (TODO: could be blocking, use async?) + + d->setupCommonProperties(); + if (key.protocol() == GpgME::OpenPGP) { + d->setupPGPProperties(); + } else { + d->setupSMIMEProperties(); + } +} + +GpgME::Key CertificateDetailsWidget::key() const +{ + return d->key; +} + +CertificateDetailsDialog::CertificateDetailsDialog(QWidget *parent) + : QDialog(parent) +{ + setWindowTitle(i18n("Certificate Details")); + auto l = new QVBoxLayout(this); + l->addWidget(new CertificateDetailsWidget(this)); + + auto bbox = new QDialogButtonBox(this); + auto btn = bbox->addButton(QDialogButtonBox::Close); + connect(btn, &QPushButton::pressed, this, &QDialog::accept); + l->addWidget(bbox); + readConfig(); +} + +CertificateDetailsDialog::~CertificateDetailsDialog() +{ + writeConfig(); +} + +void CertificateDetailsDialog::readConfig() +{ + KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog"); + const QSize size = dialog.readEntry("Size", QSize(730, 280)); + if (size.isValid()) { + resize(size); + } +} + +void CertificateDetailsDialog::writeConfig() +{ + KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog"); + dialog.writeEntry("Size", size()); + dialog.sync(); +} + +void CertificateDetailsDialog::setKey(const GpgME::Key &key) +{ + auto w = findChild(); + Q_ASSERT(w); + w->setKey(key); +} + +GpgME::Key CertificateDetailsDialog::key() const +{ + auto w = findChild(); + Q_ASSERT(w); + return w->key(); +} diff --git a/src/dialogs/certificatedetailswidget.ui b/src/dialogs/certificatedetailswidget.ui new file mode 100644 --- /dev/null +++ b/src/dialogs/certificatedetailswidget.ui @@ -0,0 +1,285 @@ + + + CertificateDetailsWidget + + + + 0 + 0 + 631 + 571 + + + + Certificate Details + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + &Add email address + + + + + + + Trust chain &details... + + + + + + + Certificate Details + + + false + + + + 60 + + + 6 + + + + + Expires: + + + + + + + unknown + + + true + + + Qt::TextBrowserInteraction + + + + + + + &More details... + + + + + + + + + 00/00/00 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Change + + + + .. + + + + 16 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + + + Valid from: + + + + + + + unknown + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Fingerprint: + + + + + + + 00/00/00 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Issuer: + + + + + + + Publishing: + + + + + + + Type: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + unknown + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Publish Certificate + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + false + + + true + + + true + + + + 1 + + + + + + + + Change &passphrase + + + + + + + You can use this certificate to secure communication with the following email addresses: + + + true + + + + + + + Owner: + + + + + + + + 75 + true + + + + Related addresses: + + + + + + + unknown + + + true + + + Qt::TextBrowserInteraction + + + + + + + + diff --git a/src/dialogs/expirydialog.cpp b/src/dialogs/expirydialog.cpp --- a/src/dialogs/expirydialog.cpp +++ b/src/dialogs/expirydialog.cpp @@ -145,7 +145,7 @@ ExpiryDialog::ExpiryDialog(QWidget *p) : QDialog(p), d(new Private(this)) { - + setWindowTitle(i18n("Change Expiry")); } ExpiryDialog::~ExpiryDialog() {} diff --git a/src/dialogs/expirydialog.ui b/src/dialogs/expirydialog.ui --- a/src/dialogs/expirydialog.ui +++ b/src/dialogs/expirydialog.ui @@ -7,13 +7,25 @@ 0 0 436 - 326 + 386 Change Certificate Date of Expiry + + 0 + + + 0 + + + 0 + + + 0 + @@ -24,7 +36,7 @@ - Never + Ne&ver false @@ -98,7 +110,7 @@ - On this day: + On this da&y: true diff --git a/src/dialogs/subkeyswidget.h b/src/dialogs/subkeyswidget.h new file mode 100644 --- /dev/null +++ b/src/dialogs/subkeyswidget.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2016 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 +*/ + +#ifndef KLEO_SUBKEYSWIDGET_H +#define KLEO_SUBKEYSWIDGET_H + +#include +#include + +namespace GpgME { +class Key; +} + +class SubKeysWidget : public QWidget +{ + Q_OBJECT +public: + explicit SubKeysWidget(QWidget *parent = Q_NULLPTR); + ~SubKeysWidget(); + + void setKey(const GpgME::Key &key); + GpgME::Key key() const; + +private: + class Private; + const QScopedPointer d; +}; + + +class SubKeysDialog : public QDialog +{ + Q_OBJECT +public: + explicit SubKeysDialog(QWidget *parent = Q_NULLPTR); + ~SubKeysDialog(); + + void setKey(const GpgME::Key &key); + GpgME::Key key() const; + +private: + void readConfig(); + void writeConfig(); +}; + +#endif diff --git a/src/dialogs/subkeyswidget.cpp b/src/dialogs/subkeyswidget.cpp new file mode 100644 --- /dev/null +++ b/src/dialogs/subkeyswidget.cpp @@ -0,0 +1,137 @@ +/* Copyright (c) 2016 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 +*/ + +#include "subkeyswidget.h" +#include "ui_subkeyswidget.h" + +#include + +#include +#include +#include +#include +#include + +#include + + +class SubKeysWidget::Private +{ +public: + Private(SubKeysWidget *q) + : q(q) + {} + + GpgME::Key key; + Ui::SubKeysWidget ui; + +private: + SubKeysWidget *q; +}; + +SubKeysWidget::SubKeysWidget(QWidget *parent) + : QWidget(parent) + , d(new Private(this)) +{ + d->ui.setupUi(this); +} + +SubKeysWidget::~SubKeysWidget() +{ +} + +void SubKeysWidget::setKey(const GpgME::Key &key) +{ + d->key = key; + + for (const auto &subkey : key.subkeys()) { + auto item = new QTreeWidgetItem(); + item->setData(0, Qt::DisplayRole, QString::fromLatin1(subkey.keyID())); + item->setData(1, Qt::DisplayRole, Kleo::Formatting::type(subkey)); + item->setData(2, Qt::DisplayRole, Kleo::Formatting::creationDateString(subkey)); + item->setData(3, Qt::DisplayRole, Kleo::Formatting::expirationDateString(subkey)); + item->setData(4, Qt::DisplayRole, Kleo::Formatting::validityShort(subkey)); + item->setData(5, Qt::DisplayRole, QString::number(subkey.length())); + item->setData(6, Qt::DisplayRole, Kleo::Formatting::usageString(subkey)); + d->ui.subkeysTree->addTopLevelItem(item); + } + + const auto subkey = key.subkey(0); + if (const char *card = subkey.cardSerialNumber()) { + d->ui.stored->setText(i18nc("stored...", "on SmartCard with serial no. %1", QString::fromUtf8(card))); + } else { + d->ui.stored->setText(i18nc("stored...", "on this computer")); + } +} + + +GpgME::Key SubKeysWidget::key() const +{ + return d->key; +} + + + +SubKeysDialog::SubKeysDialog(QWidget *parent) + : QDialog(parent) +{ + setWindowTitle(i18n("Subkeys details")); + auto l = new QVBoxLayout(this); + setLayout(l); + l->addWidget(new SubKeysWidget(this)); + + auto bbox = new QDialogButtonBox(this); + auto btn = bbox->addButton(QDialogButtonBox::Close); + connect(btn, &QPushButton::clicked, this, &QDialog::accept); + l->addWidget(bbox); + readConfig(); +} + +SubKeysDialog::~SubKeysDialog() +{ + writeConfig(); +} + +void SubKeysDialog::readConfig() +{ + KConfigGroup dialog(KSharedConfig::openConfig(), "SubKeysDialog"); + const QSize size = dialog.readEntry("Size", QSize(730, 280)); + if (size.isValid()) { + resize(size); + } +} + +void SubKeysDialog::writeConfig() +{ + KConfigGroup dialog(KSharedConfig::openConfig(), "SubKeysDialog"); + dialog.writeEntry("Size", size()); + dialog.sync(); +} + +void SubKeysDialog::setKey(const GpgME::Key &key) +{ + auto w = findChild(); + Q_ASSERT(w); + w->setKey(key); +} + +GpgME::Key SubKeysDialog::key() const +{ + auto w = findChild(); + Q_ASSERT(w); + return w->key(); +} diff --git a/src/dialogs/subkeyswidget.ui b/src/dialogs/subkeyswidget.ui new file mode 100644 --- /dev/null +++ b/src/dialogs/subkeyswidget.ui @@ -0,0 +1,113 @@ + + + SubKeysWidget + + + + 0 + 0 + 720 + 200 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Subkeys: + + + + + + + false + + + + ID + + + + + Type + + + + + Valid From + + + + + Valid Until + + + + + Status + + + + + Strength + + + + + Usage + + + + + + + + + + unknown + + + + + + + Stored: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/src/dialogs/trustchainwidget.h b/src/dialogs/trustchainwidget.h new file mode 100644 --- /dev/null +++ b/src/dialogs/trustchainwidget.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2016 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 +*/ + +#ifndef KLEO_TRUSTCHAINWIDGET_H +#define KLEO_TRUSTCHAINWIDGET_H + +#include +#include + +namespace GpgME { +class Key; +} + +class TrustChainWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TrustChainWidget(QWidget *parent = Q_NULLPTR); + ~TrustChainWidget(); + + void setKey(const GpgME::Key &key); + GpgME::Key key() const; + +private: + class Private; + const QScopedPointer d; +}; + +class TrustChainDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TrustChainDialog(QWidget *parent = Q_NULLPTR); + ~TrustChainDialog(); + + void setKey(const GpgME::Key &key); + GpgME::Key key() const; +}; + + +#endif diff --git a/src/dialogs/trustchainwidget.cpp b/src/dialogs/trustchainwidget.cpp new file mode 100644 --- /dev/null +++ b/src/dialogs/trustchainwidget.cpp @@ -0,0 +1,126 @@ +/* Copyright (c) 2016 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 +*/ + +#include "trustchainwidget.h" +#include "ui_trustchainwidget.h" + +#include "kleopatra_debug.h" + +#include +#include +#include +#include + +#include + +#include +#include + +class TrustChainWidget::Private +{ +public: + Private(TrustChainWidget *qq) + : q(qq) + {} + + GpgME::Key key; + Ui::TrustChainWidget ui; + +private: + TrustChainWidget *q; +}; + +TrustChainWidget::TrustChainWidget(QWidget *parent) + : QWidget(parent) + , d(new Private(this)) +{ + d->ui.setupUi(this); +} + +TrustChainWidget::~TrustChainWidget() +{ +} + +void TrustChainWidget::setKey(const GpgME::Key &key) +{ + if (key.protocol() != GpgME::CMS) { + qCDebug(KLEOPATRA_LOG) << "Trust chain is only supported for CMS keys"; + return; + } + + d->key = key; + d->ui.treeWidget->clear(); + const auto chain = Kleo::KeyCache::instance()->findIssuers(key, + Kleo::KeyCache::RecursiveSearch | Kleo::KeyCache::IncludeSubject); + if (chain.empty()) { + return; + } + QTreeWidgetItem *last = 0; + if (!chain.back().isRoot()) { + last = new QTreeWidgetItem(d->ui.treeWidget); + last->setText(0, i18n("Issuer Certificate Not Found (%1)", + Kleo::DN(chain.back().issuerName()).prettyDN())); + const QBrush &fg = d->ui.treeWidget->palette().brush(QPalette::Disabled, QPalette::WindowText); + last->setForeground(0, fg); + } + for (auto it = chain.rbegin(), end = chain.rend(); it != end; ++it) { + last = last ? new QTreeWidgetItem(last) : new QTreeWidgetItem(d->ui.treeWidget); + last->setText(0, Kleo::DN(it->userID(0).id()).prettyDN()); + } + d->ui.treeWidget->expandAll(); +} + +GpgME::Key TrustChainWidget::key() const +{ + return d->key; +} + + + +TrustChainDialog::TrustChainDialog(QWidget *parent) + : QDialog(parent) +{ + resize(650, 330); + setWindowTitle(i18n("Trust Chain")); + + auto l = new QVBoxLayout(this); + l->addWidget(new TrustChainWidget(this)); + + auto bbox = new QDialogButtonBox(this); + auto btn = bbox->addButton(QDialogButtonBox::Close); + connect(btn, &QPushButton::pressed, this, &QDialog::accept); + l->addWidget(bbox); +} + +TrustChainDialog::~TrustChainDialog() +{ +} + +void TrustChainDialog::setKey(const GpgME::Key &key) +{ + auto w = findChild(); + Q_ASSERT(w); + w->setKey(key); +} + +GpgME::Key TrustChainDialog::key() const +{ + auto w = findChild(); + Q_ASSERT(w); + return w->key(); +} + diff --git a/src/dialogs/trustchainwidget.ui b/src/dialogs/trustchainwidget.ui new file mode 100644 --- /dev/null +++ b/src/dialogs/trustchainwidget.ui @@ -0,0 +1,45 @@ + + + TrustChainWidget + + + + 0 + 0 + 650 + 330 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false + + + + 1 + + + + + + + + +