diff --git a/src/view/p15cardwidget.cpp b/src/view/p15cardwidget.cpp index af6613dc8..ffdaaf376 100644 --- a/src/view/p15cardwidget.cpp +++ b/src/view/p15cardwidget.cpp @@ -1,171 +1,219 @@ /* view/p15cardwiget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Andre Heinecke SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "p15cardwidget.h" #include "openpgpkeycardwidget.h" #include "settings.h" #include "smartcard/p15card.h" #include "smartcard/openpgpcard.h" #include "smartcard/readerstatus.h" +#include "commands/learncardkeyscommand.h" + #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::SmartCard; +using namespace Kleo::Commands; P15CardWidget::P15CardWidget(QWidget *parent) : QWidget{parent} , mVersionLabel{new QLabel{this}} , mSerialNumber{new QLabel{this}} , mStatusLabel{new QLabel{this}} , mOpenPGPKeysSection{new QWidget{this}} + , mCMSKeysSection{new QWidget{this}} , mOpenPGPKeysWidget{new OpenPGPKeyCardWidget{this}} { // Set up the scroll area auto myLayout = new QVBoxLayout(this); myLayout->setContentsMargins(0, 0, 0, 0); auto area = new QScrollArea; area->setFrameShape(QFrame::NoFrame); area->setWidgetResizable(true); myLayout->addWidget(area); auto areaWidget = new QWidget; area->setWidget(areaWidget); auto areaVLay = new QVBoxLayout(areaWidget); auto cardInfoGrid = new QGridLayout; { int row = 0; // Version and Serialnumber cardInfoGrid->addWidget(mVersionLabel, row++, 0, 1, 2); mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard); cardInfoGrid->addWidget(new QLabel(i18n("Serial number:")), row, 0); cardInfoGrid->addWidget(mSerialNumber, row++, 1); mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard); cardInfoGrid->setColumnStretch(cardInfoGrid->columnCount(), 1); } areaVLay->addLayout(cardInfoGrid); areaVLay->addWidget(mStatusLabel); mStatusLabel->setVisible(false); areaVLay->addWidget(new KSeparator(Qt::Horizontal)); { auto l = new QVBoxLayout{mOpenPGPKeysSection}; l->setContentsMargins(0, 0, 0, 0); l->addWidget(new QLabel(QStringLiteral("%1").arg(i18n("OpenPGP keys:")))); mOpenPGPKeysWidget->setAllowedActions(OpenPGPKeyCardWidget::NoAction); l->addWidget(mOpenPGPKeysWidget); l->addWidget(new KSeparator(Qt::Horizontal)); } + { + auto l = new QVBoxLayout{mCMSKeysSection}; + /* For an improvement we need to make a hybrid of + * the NetKeyWidget with the treeview and all the + * CMS Related actions here, too. For now it + * is just a button that is shown when we can + * learn additional CMS keys from the smartcard. */ + auto hl = new QHBoxLayout; + auto learnBtn = new QPushButton(i18n("Load additional certificates")); + learnBtn->setToolTip(i18n("Search for additional certificates on the card and add them to the list. " + "The certificates might be usable for S/MIME.")); + auto learnLbl = new QLabel(); + learnLbl->setVisible(false); + hl->addWidget(learnBtn); + hl->addStretch(1); + l->addLayout(hl); + l->addWidget(learnLbl); + connect(learnBtn, &QPushButton::clicked, this, [this, learnBtn, learnLbl] () { + learnBtn->setEnabled(false); + auto cmd = new LearnCardKeysCommand(GpgME::CMS); + cmd->setParentWidget(this); + cmd->start(); + learnLbl->setVisible(true); + learnLbl->setText(i18n("Loading certificates...")); + + connect(cmd, &Command::finished, this, [learnBtn, learnLbl] () { + learnLbl->setText(i18n("Certificates added to the certificate list.")); + }); + }); + l->addWidget(new KSeparator(Qt::Horizontal)); + } mOpenPGPKeysSection->setVisible(false); + mCMSKeysSection->setVisible(false); areaVLay->addWidget(mOpenPGPKeysSection); + areaVLay->addWidget(mCMSKeysSection); areaVLay->addStretch(1); } P15CardWidget::~P15CardWidget() = default; void P15CardWidget::searchPGPFpr(const std::string &fpr) { /* Only do auto import from LDAP */ auto conf = QGpgME::cryptoConfig(); Q_ASSERT (conf); if (!Settings().alwaysSearchCardOnKeyserver() && !Kleo::keyserver().startsWith(QLatin1String{"ldap"})) { return; } mStatusLabel->setText(i18n("Searching in directory service...")); mStatusLabel->setVisible(true); qCDebug(KLEOPATRA_LOG) << "Looking for:" << fpr.c_str() << "on ldap server"; QGpgME::KeyListJob *job = QGpgME::openpgp()->keyListJob(true); connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, [this, fpr] () { qCDebug(KLEOPATRA_LOG) << "Updating key info after changes"; ReaderStatus::mutableInstance()->updateStatus(); mOpenPGPKeysWidget->update(nullptr); }); connect(job, &QGpgME::KeyListJob::result, job, [this](GpgME::KeyListResult, std::vector keys, QString, GpgME::Error) { if (keys.size() == 1) { auto importJob = QGpgME::openpgp()->importFromKeyserverJob(); qCDebug(KLEOPATRA_LOG) << "Importing: " << keys[0].primaryFingerprint(); connect(importJob, &QGpgME::ImportFromKeyserverJob::result, importJob, [this](GpgME::ImportResult, QString, GpgME::Error) { qCDebug(KLEOPATRA_LOG) << "import job done"; mStatusLabel->setText(i18n("Automatic import finished.")); }); importJob->start(keys); } else if (keys.size() > 1) { qCDebug(KLEOPATRA_LOG) << "Multiple keys found on server"; mStatusLabel->setText(i18n("Error multiple keys found on server.")); } else { qCDebug(KLEOPATRA_LOG) << "No key found"; mStatusLabel->setText(i18n("Key not found in directory service.")); } }); job->start(QStringList() << QString::fromStdString(fpr)); } void P15CardWidget::setCard(const P15Card *card) { mCardSerialNumber = card->serialNumber(); mVersionLabel->setText(i18nc("%1 is a smartcard manufacturer", "%1 PKCS#15 card", QString::fromStdString(card->manufacturer()))); mSerialNumber->setText(card->displaySerialNumber()); mSerialNumber->setToolTip(QString::fromStdString(card->serialNumber())); const auto sigInfo = card->keyInfo(card->signingKeyRef()); if (!sigInfo.grip.empty()) { const auto key = KeyCache::instance()->findSubkeyByKeyGrip(sigInfo.grip, GpgME::OpenPGP).parent(); if (key.isNull()) { qCDebug(KLEOPATRA_LOG) << "Failed to find key for grip:" << sigInfo.grip.c_str(); const auto pgpSigFpr = card->keyFingerprint(OpenPGPCard::pgpSigKeyRef()); if (!pgpSigFpr.empty()) { qCDebug(KLEOPATRA_LOG) << "Should be pgp key:" << pgpSigFpr.c_str(); searchPGPFpr(pgpSigFpr); } } else { mStatusLabel->setVisible(false); } } const bool cardHasOpenPGPKeys = !card->keyFingerprint(OpenPGPCard::pgpSigKeyRef()).empty() || !card->keyFingerprint(OpenPGPCard::pgpEncKeyRef()).empty(); mOpenPGPKeysSection->setVisible(cardHasOpenPGPKeys); if (cardHasOpenPGPKeys) { mOpenPGPKeysWidget->update(card); } + + /* Check if additional keys could be available */ + for (const auto &info: card->keyInfos()) { + const auto key = KeyCache::instance()->findSubkeyByKeyGrip(info.grip); + if (key.isNull()) { + mCMSKeysSection->setVisible(true); + break; + } + } + } diff --git a/src/view/p15cardwidget.h b/src/view/p15cardwidget.h index 9f99026ef..0848a88f4 100644 --- a/src/view/p15cardwidget.h +++ b/src/view/p15cardwidget.h @@ -1,47 +1,48 @@ /* view/p15cardwiget.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Andre Heinecke SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include class QLabel; namespace Kleo { class OpenPGPKeyCardWidget; namespace SmartCard { struct KeyPairInfo; class P15Card; } class P15CardWidget: public QWidget { Q_OBJECT public: explicit P15CardWidget(QWidget *parent = nullptr); ~P15CardWidget() override; void setCard(const SmartCard::P15Card* card); private: void searchPGPFpr(const std::string &fpr); private: std::string mCardSerialNumber; QLabel *mVersionLabel = nullptr; QLabel *mSerialNumber = nullptr; QLabel *mStatusLabel = nullptr; QWidget *mOpenPGPKeysSection = nullptr; + QWidget *mCMSKeysSection = nullptr; OpenPGPKeyCardWidget *mOpenPGPKeysWidget = nullptr; }; }