diff --git a/src/view/openpgpkeycardwidget.cpp b/src/view/openpgpkeycardwidget.cpp index cda2186c6..40df9a599 100644 --- a/src/view/openpgpkeycardwidget.cpp +++ b/src/view/openpgpkeycardwidget.cpp @@ -1,246 +1,250 @@ /* view/openpgpkeycardwidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "openpgpkeycardwidget.h" #include "commands/detailscommand.h" #include "smartcard/card.h" #include "smartcard/keypairinfo.h" #include "smartcard/openpgpcard.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace SmartCard; namespace { struct KeyWidgets { std::string cardKeyRef; std::string keyGrip; std::string keyFingerprint; QLabel *keyTitleLabel = nullptr; QLabel *keyInfoLabel = nullptr; QPushButton *showCertificateDetailsButton = nullptr; QPushButton *createCSRButton = nullptr; }; KeyWidgets createKeyWidgets(const KeyPairInfo &keyInfo, QWidget *parent) { KeyWidgets keyWidgets; keyWidgets.keyTitleLabel = new QLabel{OpenPGPCard::keyDisplayName(keyInfo.keyRef), parent}; keyWidgets.keyInfoLabel = new QLabel{parent}; keyWidgets.keyInfoLabel->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard); keyWidgets.showCertificateDetailsButton = new QPushButton{i18nc("@action:button", "Show Details"), parent}; keyWidgets.showCertificateDetailsButton->setToolTip(i18nc("@action:tooltip", "Show detailed information about this key")); keyWidgets.showCertificateDetailsButton->setEnabled(false); if (keyInfo.canCertify() || keyInfo.canSign() || keyInfo.canAuthenticate()) { keyWidgets.createCSRButton = new QPushButton{i18nc("@action:button", "Create CSR"), parent}; keyWidgets.createCSRButton->setToolTip(i18nc("@info:tooltip", "Create a certificate signing request for this key")); keyWidgets.createCSRButton->setEnabled(false); } return keyWidgets; } } class OpenPGPKeyCardWidget::Private { public: explicit Private(OpenPGPKeyCardWidget *q); ~Private() = default; void setAllowedActions(Actions actions); void update(const Card *card = nullptr); private: void updateCachedValues(const std::string &openPGPKeyRef, const std::string &cardKeyRef, const Card *card); void updateKeyWidgets(const std::string &openPGPKeyRef); void showCertificateDetails(const std::string &openPGPKeyRef); private: OpenPGPKeyCardWidget *const q; Actions mAllowedActions = AllActions; std::map mKeyWidgets; }; OpenPGPKeyCardWidget::Private::Private(OpenPGPKeyCardWidget *q) : q{q} { auto grid = new QGridLayout{q}; grid->setContentsMargins(0, 0, 0, 0); for (const auto &keyInfo : OpenPGPCard::supportedKeys()) { const KeyWidgets keyWidgets = createKeyWidgets(keyInfo, q); const std::string keyRef = keyInfo.keyRef; connect(keyWidgets.showCertificateDetailsButton, &QPushButton::clicked, q, [this, keyRef] () { showCertificateDetails(keyRef); }); if (keyWidgets.createCSRButton) { connect(keyWidgets.createCSRButton, &QPushButton::clicked, q, [q, keyRef] () { Q_EMIT q->createCSRRequested(keyRef); }); } const int row = grid->rowCount(); grid->addWidget(keyWidgets.keyTitleLabel, row, 0, Qt::AlignTop); grid->addWidget(keyWidgets.keyInfoLabel, row, 1, Qt::AlignTop); auto buttons = new QHBoxLayout; buttons->addWidget(keyWidgets.showCertificateDetailsButton); if (keyWidgets.createCSRButton) { buttons->addWidget(keyWidgets.createCSRButton); } buttons->addStretch(1); grid->addLayout(buttons, row, 2, Qt::AlignTop); mKeyWidgets.insert({keyInfo.keyRef, keyWidgets}); } grid->setColumnStretch(grid->columnCount(), 1); } void OpenPGPKeyCardWidget::Private::setAllowedActions(Actions actions) { mAllowedActions = actions; update(); } void OpenPGPKeyCardWidget::Private::update(const Card *card) { if (card) { updateCachedValues(OpenPGPCard::pgpSigKeyRef(), card->signingKeyRef(), card); updateCachedValues(OpenPGPCard::pgpEncKeyRef(), card->encryptionKeyRef(), card); updateCachedValues(OpenPGPCard::pgpAuthKeyRef(), card->authenticationKeyRef(), card); } updateKeyWidgets(OpenPGPCard::pgpSigKeyRef()); updateKeyWidgets(OpenPGPCard::pgpEncKeyRef()); updateKeyWidgets(OpenPGPCard::pgpAuthKeyRef()); } void OpenPGPKeyCardWidget::Private::updateCachedValues(const std::string &openPGPKeyRef, const std::string &cardKeyRef, const Card *card) { KeyWidgets &widgets = mKeyWidgets.at(openPGPKeyRef); widgets.cardKeyRef = cardKeyRef; widgets.keyGrip = card->keyInfo(cardKeyRef).grip; widgets.keyFingerprint = card->keyFingerprint(openPGPKeyRef); } void OpenPGPKeyCardWidget::Private::updateKeyWidgets(const std::string &openPGPKeyRef) { const KeyWidgets &widgets = mKeyWidgets.at(openPGPKeyRef); const auto cardSupportsKey = !widgets.cardKeyRef.empty(); widgets.keyTitleLabel->setVisible(cardSupportsKey); widgets.keyInfoLabel->setVisible(cardSupportsKey); widgets.showCertificateDetailsButton->setVisible(cardSupportsKey); if (widgets.createCSRButton) { widgets.createCSRButton->setVisible(cardSupportsKey && (mAllowedActions & Action::CreateCSR)); } if (!cardSupportsKey) { return; } widgets.showCertificateDetailsButton->setEnabled(false); if (widgets.keyFingerprint.empty()) { widgets.keyInfoLabel->setTextFormat(Qt::RichText); widgets.keyInfoLabel->setText(i18nc("@info", "No key")); if (widgets.createCSRButton) { widgets.createCSRButton->setEnabled(false); } } else { - QStringList lines = {Formatting::prettyID(widgets.keyFingerprint.c_str())}; + QStringList lines; if (widgets.keyFingerprint.size() >= 16) { const std::string keyid = widgets.keyFingerprint.substr(widgets.keyFingerprint.size() - 16); const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid}); if (subkeys.empty() || subkeys[0].isNull()) { widgets.keyInfoLabel->setTextFormat(Qt::RichText); lines.push_back(i18nc("@info", "Public key not found locally")); widgets.keyInfoLabel->setToolTip({}); } else { // force interpretation of text as plain text to avoid problems with HTML in user IDs widgets.keyInfoLabel->setTextFormat(Qt::PlainText); QStringList toolTips; toolTips.reserve(subkeys.size()); for (const auto &sub: subkeys) { // Yep you can have one subkey associated with multiple primary keys. const GpgME::Key key = sub.parent(); toolTips << Formatting::toolTip(key, Formatting::Validity | Formatting::ExpiryDates | Formatting::UserIDs | Formatting::Fingerprint); const auto uids = key.userIDs(); for (const auto &uid: uids) { lines.push_back(Formatting::prettyUserID(uid)); } } widgets.keyInfoLabel->setToolTip(toolTips.join(QLatin1String("
"))); widgets.showCertificateDetailsButton->setEnabled(true); } } else { widgets.keyInfoLabel->setTextFormat(Qt::RichText); lines.push_back(i18nc("@info", "Invalid fingerprint")); } + const QString fingerprint = widgets.keyInfoLabel->textFormat() == Qt::RichText ? + Formatting::prettyID(widgets.keyFingerprint.c_str()).replace(QLatin1Char(' '), QLatin1String(" ")) : + Formatting::prettyID(widgets.keyFingerprint.c_str()); + lines.insert(0, fingerprint); const auto lineSeparator = widgets.keyInfoLabel->textFormat() == Qt::PlainText ? QLatin1String("\n") : QLatin1String("
"); widgets.keyInfoLabel->setText(lines.join(lineSeparator)); if (widgets.createCSRButton) { widgets.createCSRButton->setEnabled(true); } } } void OpenPGPKeyCardWidget::Private::showCertificateDetails(const std::string &openPGPKeyRef) { const KeyWidgets &widgets = mKeyWidgets.at(openPGPKeyRef); if (widgets.keyFingerprint.size() >= 16) { const std::string keyid = widgets.keyFingerprint.substr(widgets.keyFingerprint.size() - 16); const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid}); if (!subkeys.empty() && !subkeys[0].isNull()) { auto cmd = new Commands::DetailsCommand(subkeys[0].parent(), nullptr); cmd->setParentWidget(q); cmd->start(); return; } } KMessageBox::sorry(q, i18nc("@info", "Sorry, I cannot find the key with fingerprint %1.", Formatting::prettyID(widgets.keyFingerprint.c_str()))); } OpenPGPKeyCardWidget::OpenPGPKeyCardWidget(QWidget *parent) : QWidget{parent} , d{std::make_unique(this)} { } OpenPGPKeyCardWidget::~OpenPGPKeyCardWidget() = default; void OpenPGPKeyCardWidget::setAllowedActions(Actions actions) { d->setAllowedActions(actions); } void OpenPGPKeyCardWidget::update(const Card *card) { d->update(card); }