Page MenuHome GnuPG

No OneTemporary

diff --git a/src/dialogs/smartcardwindow.cpp b/src/dialogs/smartcardwindow.cpp
index efb3ad6d6..b5af4c63b 100644
--- a/src/dialogs/smartcardwindow.cpp
+++ b/src/dialogs/smartcardwindow.cpp
@@ -1,156 +1,159 @@
/* dialogs/smartcardwindow.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2024 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "smartcardwindow.h"
#include <kleopatraapplication.h>
#include <mainwindow.h>
#include <smartcard/readerstatus.h>
#include <utils/statusmessage.h>
#include <view/smartcardactions.h>
#include <view/smartcardswidget.h>
#include <KActionCollection>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <KStandardAction>
#include <QLabel>
#include <QStatusBar>
#include <QVBoxLayout>
using namespace Kleo;
using namespace Kleo::SmartCard;
using namespace Qt::Literals::StringLiterals;
class SmartCardWindow::Private
{
friend class ::SmartCardWindow;
SmartCardWindow *const q;
public:
Private(SmartCardWindow *qq);
private:
void saveLayout();
void restoreLayout(const QSize &defaultSize = {});
void connectActions();
void setUpStatusBar();
private:
std::shared_ptr<const SmartCardActions> smartCardActions;
SmartCardsWidget *smartCardWidget = nullptr;
StatusMessage *statusMessage = nullptr;
};
SmartCardWindow::Private::Private(SmartCardWindow *qq)
: q(qq)
, smartCardActions{SmartCardActions::instance()}
{
}
void SmartCardWindow::Private::saveLayout()
{
KConfigGroup configGroup(KSharedConfig::openStateConfig(), QLatin1StringView("SmartCardWindow"));
configGroup.writeEntry("Size", q->size());
configGroup.sync();
}
void SmartCardWindow::Private::restoreLayout(const QSize &defaultSize)
{
const KConfigGroup configGroup(KSharedConfig::openStateConfig(), QLatin1StringView("SmartCardWindow"));
const QSize size = configGroup.readEntry("Size", defaultSize);
if (size.isValid()) {
q->resize(size);
}
}
void SmartCardWindow::Private::connectActions()
{
q->addAction(smartCardActions->action(u"window_close"_s));
smartCardActions->connectAction(u"window_close"_s, q, &SmartCardWindow::close);
}
void SmartCardWindow::Private::setUpStatusBar()
{
auto statusBar = q->statusBar();
statusBar->setSizeGripEnabled(false);
auto statusMessageLabel = new QLabel{statusBar};
statusBar->addWidget(statusMessageLabel, 1);
q->setStatusBar(statusBar);
statusMessage = new StatusMessage{q};
connect(statusMessage, &StatusMessage::messageChanged, statusMessageLabel, &QLabel::setText);
+ connect(smartCardWidget, &SmartCardsWidget::statusMessage, statusMessage, &StatusMessage::showMessage);
+ connect(smartCardWidget, &SmartCardsWidget::cardChanged, statusMessage, &StatusMessage::setContext);
+
connect(ReaderStatus::instance(), &ReaderStatus::updateCardsStarted, q, [this]() {
statusMessage->showMessage(i18nc("@info:status", "Loading smart cards..."));
});
connect(ReaderStatus::instance(), &ReaderStatus::updateCardStarted, q, [this](const std::string &serialNumber, const std::string &appName) {
const auto card = ReaderStatus::instance()->getCard(serialNumber, appName);
if (card) {
statusMessage->showMessage(i18nc("@info:status", "Updating smart card %1...", card->displaySerialNumber()));
} else {
statusMessage->showMessage(i18nc("@info:status", "Updating smart card..."));
}
});
connect(ReaderStatus::instance(), &ReaderStatus::updateFinished, q, [this]() {
statusMessage->clearMessage();
});
connect(ReaderStatus::instance(), &ReaderStatus::startingLearnCards, q, [this]() {
statusMessage->showMessage(i18nc("@info:status", "Importing certificates from smart cards..."));
});
connect(ReaderStatus::instance(), &ReaderStatus::cardsLearned, q, [this]() {
statusMessage->clearMessage();
});
switch (ReaderStatus::instance()->currentAction()) {
case ReaderStatus::UpdateCards: {
statusMessage->showMessage(i18nc("@info:status", "Loading smart cards..."));
break;
}
case ReaderStatus::LearnCards: {
statusMessage->showMessage(i18nc("@info:status", "Importing certificates from smart cards..."));
break;
}
case ReaderStatus::NoAction:
break;
}
}
SmartCardWindow::SmartCardWindow(QWidget *parent)
: QMainWindow(parent)
, d(new Private(this))
{
setWindowTitle(i18nc("@title:window", "Manage Smart Cards"));
d->smartCardWidget = new SmartCardsWidget{this};
d->smartCardWidget->setContentsMargins({});
setCentralWidget(d->smartCardWidget);
d->connectActions();
d->setUpStatusBar();
// use size of main window as default size
const auto mainWindow = KleopatraApplication::instance()->mainWindow();
d->restoreLayout(mainWindow ? mainWindow->size() : QSize{1024, 500});
// load the currently known cards and trigger an update
d->smartCardWidget->showCards(ReaderStatus::instance()->getCards());
d->smartCardWidget->reload();
}
SmartCardWindow::~SmartCardWindow()
{
d->saveLayout();
}
#include "moc_smartcardwindow.cpp"
diff --git a/src/view/smartcardswidget.cpp b/src/view/smartcardswidget.cpp
index 20f03a1d8..cb6c97d11 100644
--- a/src/view/smartcardswidget.cpp
+++ b/src/view/smartcardswidget.cpp
@@ -1,641 +1,669 @@
/* view/smartcardswidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "smartcardswidget.h"
#include "netkeywidget.h"
#include "p15cardwidget.h"
#include "pgpcardwidget.h"
#include "pivcardwidget.h"
#include "smartcardactions.h"
#include "smartcardwidget.h"
#include <commands/certificatetopivcardcommand.h>
#include <commands/changepincommand.h>
#include <commands/createcsrforcardkeycommand.h>
#include <commands/createopenpgpkeyfromcardkeyscommand.h>
#include <commands/detailscommand.h>
#include <commands/generateopenpgpcardkeysandcertificatecommand.h>
#include <commands/importcertificatefrompivcardcommand.h>
#include <commands/keytocardcommand.h>
#include <commands/openpgpgeneratecardkeycommand.h>
#include <commands/pivgeneratecardkeycommand.h>
#include <commands/setpivcardapplicationadministrationkeycommand.h>
#include "smartcard/netkeycard.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/p15card.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "smartcard/utils.h"
#include "kleopatra_debug.h"
#include <Libkleo/Formatting>
#include <KActionCollection>
#include <KLocalizedString>
#include <KMessageBox>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPointer>
#include <QStackedWidget>
#include <QTabWidget>
#include <QToolButton>
#include <QVBoxLayout>
#include <gpgme++/key.h>
using namespace GpgME;
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace Qt::Literals::StringLiterals;
namespace
{
class PlaceHolderWidget : public QWidget
{
Q_OBJECT
public:
explicit PlaceHolderWidget(QWidget *parent = nullptr)
: QWidget{parent}
{
auto lay = new QVBoxLayout;
lay->addStretch(-1);
const QStringList supported{
i18nc("OpenPGP refers to a smartcard protocol", "OpenPGP v2.0 or later"),
i18nc("Gnuk is a cryptographic token for GnuPG", "Gnuk"),
i18nc("NetKey refers to a smartcard protocol", "NetKey v3 or later"),
i18nc("PIV refers to a smartcard protocol", "PIV (requires GnuPG 2.3 or later)"),
i18nc("CardOS is a smartcard operating system", "CardOS 5 (various apps)"),
};
lay->addWidget(new QLabel(QStringLiteral("\t\t<h3>") + i18n("Please insert a compatible smartcard.") + QStringLiteral("</h3>"), this));
lay->addSpacing(10);
lay->addWidget(new QLabel(QStringLiteral("\t\t") + i18n("Kleopatra currently supports the following card types:") + QStringLiteral("<ul><li>")
+ supported.join(QLatin1StringView("</li><li>")) + QStringLiteral("</li></ul>"),
this));
lay->addSpacing(10);
{
auto hbox = new QHBoxLayout;
hbox->addStretch(1);
mReloadButton = new QToolButton{this};
mReloadButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
mReloadButton->setDefaultAction(SmartCardActions::instance()->action(u"reload"_s));
hbox->addWidget(mReloadButton);
hbox->addStretch(1);
lay->addLayout(hbox);
}
lay->addStretch(-1);
auto hLay = new QHBoxLayout(this);
hLay->addStretch(-1);
hLay->addLayout(lay);
hLay->addStretch(-1);
lay->addStretch(-1);
connect(ReaderStatus::instance(), &ReaderStatus::currentActionChanged, this, &PlaceHolderWidget::updateReloadButton);
updateReloadButton();
}
void updateReloadButton()
{
mReloadButton->setEnabled(ReaderStatus::instance()->currentAction() != ReaderStatus::UpdateCards);
}
private:
QToolButton *mReloadButton = nullptr;
};
+
+static QByteArray getCardId(const Card *card)
+{
+ return QByteArray::fromStdString(card->appName() + ':' + card->serialNumber());
+}
} // namespace
class SmartCardsWidget::Private
{
friend class ::Kleo::SmartCardsWidget;
public:
Private(SmartCardsWidget *qq);
const SmartCardWidget *currentCardWidget() const;
AppType currentCardType() const;
std::string currentSerialNumber() const;
std::string currentCardSlot() const;
GpgME::Key currentCertificate() const;
void cardAddedOrChanged(const std::string &serialNumber, const std::string &appName);
void cardRemoved(const std::string &serialNumber, const std::string &appName);
void enableCurrentWidget();
void disableCurrentWidget();
void startCommand(Command *cmd);
// card actions
void generateCardKeysAndOpenPGPCertificate();
void createOpenPGPCertificate();
void changePin(const std::string &keyRef, ChangePinCommand::ChangePinMode mode = ChangePinCommand::NormalMode);
void unblockOpenPGPCard();
void changeCardholder();
void changePublicKeyUrl();
void setPIVAdminKey();
// card slot actions
void showCertificateDetails();
void generateKey();
void createCSR();
void writeCertificateToCard();
void readCertificateFromCard();
void writeKeyToCard();
private:
template<typename C, typename W>
void cardAddedOrChanged(const std::string &serialNumber);
private:
SmartCardsWidget *const q;
QMap<std::pair<std::string, std::string>, QPointer<SmartCardWidget>> mCardWidgets;
PlaceHolderWidget *mPlaceHolderWidget;
QStackedWidget *mStack;
QTabWidget *mTabWidget;
QToolButton *mReloadButton;
};
SmartCardsWidget::Private::Private(SmartCardsWidget *qq)
: q{qq}
{
auto vLay = new QVBoxLayout(q);
mStack = new QStackedWidget{q};
vLay->addWidget(mStack);
mPlaceHolderWidget = new PlaceHolderWidget{q};
mStack->addWidget(mPlaceHolderWidget);
mTabWidget = new QTabWidget{q};
// create "Reload" button after tab widget to ensure correct tab order
mReloadButton = new QToolButton{q};
mTabWidget->setCornerWidget(mReloadButton, Qt::TopRightCorner);
mStack->addWidget(mTabWidget);
mStack->setCurrentWidget(mPlaceHolderWidget);
+ connect(mStack, &QStackedWidget::currentChanged, q, [this]() {
+ if (mStack->currentWidget() == mPlaceHolderWidget) {
+ Q_EMIT q->cardChanged({});
+ } else if (auto cardWidget = currentCardWidget()) {
+ Q_EMIT q->cardChanged(getCardId(cardWidget->card()));
+ }
+ });
+ connect(mTabWidget, &QTabWidget::currentChanged, q, [this]() {
+ if (auto cardWidget = currentCardWidget()) {
+ // check if card is already set because it is set after the card widget is added to the tab widget
+ if (cardWidget->card()) {
+ Q_EMIT q->cardChanged(getCardId(cardWidget->card()));
+ }
+ } else {
+ Q_EMIT q->cardChanged({});
+ }
+ });
connect(ReaderStatus::instance(), &ReaderStatus::cardAdded, q, [this](const std::string &serialNumber, const std::string &appName) {
cardAddedOrChanged(serialNumber, appName);
});
connect(ReaderStatus::instance(), &ReaderStatus::cardChanged, q, [this](const std::string &serialNumber, const std::string &appName) {
cardAddedOrChanged(serialNumber, appName);
});
connect(ReaderStatus::instance(), &ReaderStatus::cardRemoved, q, [this](const std::string &serialNumber, const std::string &appName) {
cardRemoved(serialNumber, appName);
});
const auto actions = SmartCardActions::instance();
actions->connectAction(u"reload"_s, q, &SmartCardsWidget::reload);
mReloadButton->setDefaultAction(actions->action(u"reload"_s));
// connect card actions
actions->connectAction(u"card_all_create_openpgp_certificate"_s, q, [this]() {
createOpenPGPCertificate();
});
actions->connectAction(u"card_netkey_set_nks_pin"_s, q, [this]() {
changePin(NetKeyCard::nksPinKeyRef());
});
actions->connectAction(u"card_netkey_set_sigg_pin"_s, q, [this]() {
changePin(NetKeyCard::sigGPinKeyRef());
});
actions->connectAction(u"card_pgp_generate_keys_and_certificate"_s, q, [this]() {
generateCardKeysAndOpenPGPCertificate();
});
actions->connectAction(u"card_pgp_change_pin"_s, q, [this]() {
changePin(OpenPGPCard::pinKeyRef());
});
actions->connectAction(u"card_pgp_unblock_card"_s, q, [this]() {
unblockOpenPGPCard();
});
actions->connectAction(u"card_pgp_change_admin_pin"_s, q, [this]() {
changePin(OpenPGPCard::adminPinKeyRef());
});
actions->connectAction(u"card_pgp_change_puk"_s, q, [this]() {
changePin(OpenPGPCard::resetCodeKeyRef(), ChangePinCommand::ResetMode);
});
actions->connectAction(u"card_pgp_change_cardholder"_s, q, [this]() {
changeCardholder();
});
actions->connectAction(u"card_pgp_change_publickeyurl"_s, q, [this]() {
changePublicKeyUrl();
});
actions->connectAction(u"card_piv_change_pin"_s, q, [this]() {
changePin(PIVCard::pinKeyRef());
});
actions->connectAction(u"card_piv_change_puk"_s, q, [this]() {
changePin(PIVCard::pukKeyRef());
});
actions->connectAction(u"card_piv_change_admin_key"_s, q, [this]() {
setPIVAdminKey();
});
// connect card slot actions
actions->connectAction(u"card_slot_show_certificate_details"_s, q, [this]() {
showCertificateDetails();
});
actions->connectAction(u"card_slot_generate_key"_s, q, [this]() {
generateKey();
});
actions->connectAction(u"card_slot_write_key"_s, q, [this]() {
writeKeyToCard();
});
actions->connectAction(u"card_slot_write_certificate"_s, q, [this]() {
writeCertificateToCard();
});
actions->connectAction(u"card_slot_read_certificate"_s, q, [this]() {
readCertificateFromCard();
});
actions->connectAction(u"card_slot_create_csr"_s, q, [this]() {
createCSR();
});
}
const SmartCardWidget *SmartCardsWidget::Private::currentCardWidget() const
{
return qobject_cast<const SmartCardWidget *>(mTabWidget->currentWidget());
}
AppType SmartCardsWidget::Private::currentCardType() const
{
if (const SmartCardWidget *widget = currentCardWidget()) {
return widget->cardType();
}
return AppType::NoApp;
}
std::string SmartCardsWidget::Private::currentSerialNumber() const
{
if (const SmartCardWidget *widget = currentCardWidget()) {
return widget->serialNumber();
}
return {};
}
std::string SmartCardsWidget::Private::currentCardSlot() const
{
if (const SmartCardWidget *widget = currentCardWidget()) {
return widget->currentCardSlot();
}
return {};
}
GpgME::Key SmartCardsWidget::Private::currentCertificate() const
{
if (const SmartCardWidget *widget = currentCardWidget()) {
return widget->currentCertificate();
}
return {};
}
void SmartCardsWidget::Private::cardAddedOrChanged(const std::string &serialNumber, const std::string &appName)
{
if (appName == SmartCard::NetKeyCard::AppName) {
cardAddedOrChanged<NetKeyCard, NetKeyWidget>(serialNumber);
} else if (appName == SmartCard::OpenPGPCard::AppName) {
cardAddedOrChanged<OpenPGPCard, PGPCardWidget>(serialNumber);
} else if (appName == SmartCard::PIVCard::AppName) {
cardAddedOrChanged<PIVCard, PIVCardWidget>(serialNumber);
} else if (appName == SmartCard::P15Card::AppName) {
cardAddedOrChanged<P15Card, P15CardWidget>(serialNumber);
} else {
qCWarning(KLEOPATRA_LOG) << "SmartCardsWidget::Private::cardAddedOrChanged:"
<< "App" << appName.c_str() << "is not supported";
}
}
namespace
{
static QString getCardLabel(const std::shared_ptr<Card> &card)
{
if (!card->cardHolder().isEmpty()) {
return i18nc("@title:tab smartcard application - name of card holder - serial number of smartcard",
"%1 - %2 - %3",
displayAppName(card->appName()),
card->cardHolder(),
card->displaySerialNumber());
} else {
return i18nc("@title:tab smartcard application - serial number of smartcard", "%1 - %2", displayAppName(card->appName()), card->displaySerialNumber());
}
}
}
template<typename C, typename W>
void SmartCardsWidget::Private::cardAddedOrChanged(const std::string &serialNumber)
{
const auto card = ReaderStatus::instance()->getCard<C>(serialNumber);
if (!card) {
qCWarning(KLEOPATRA_LOG) << "SmartCardsWidget::Private::cardAddedOrChanged:"
<< "New or changed card" << serialNumber.c_str() << "with app" << C::AppName.c_str() << "not found";
return;
}
W *cardWidget = dynamic_cast<W *>(mCardWidgets.value({serialNumber, C::AppName}).data());
if (!cardWidget) {
cardWidget = new W;
mCardWidgets.insert({serialNumber, C::AppName}, cardWidget);
mTabWidget->addTab(cardWidget, getCardLabel(card));
+ cardWidget->setCard(card.get());
if (mCardWidgets.size() == 1) {
mStack->setCurrentWidget(mTabWidget);
}
+ const QByteArray context = getCardId(card.get());
+ connect(cardWidget, &SmartCardWidget::statusMessage, q, [this, context](const QString &message) {
+ Q_EMIT q->statusMessage(message, context);
+ });
+ } else {
+ cardWidget->setCard(card.get());
}
- cardWidget->setCard(card.get());
}
void SmartCardsWidget::Private::cardRemoved(const std::string &serialNumber, const std::string &appName)
{
QWidget *cardWidget = mCardWidgets.take({serialNumber, appName});
if (cardWidget) {
const int index = mTabWidget->indexOf(cardWidget);
if (index != -1) {
mTabWidget->removeTab(index);
}
delete cardWidget;
}
if (mCardWidgets.empty()) {
mStack->setCurrentWidget(mPlaceHolderWidget);
}
}
void SmartCardsWidget::Private::enableCurrentWidget()
{
mTabWidget->currentWidget()->setEnabled(true);
}
void SmartCardsWidget::Private::disableCurrentWidget()
{
mTabWidget->currentWidget()->setEnabled(false);
}
void SmartCardsWidget::Private::startCommand(Command *cmd)
{
Q_ASSERT(cmd);
disableCurrentWidget();
connect(cmd, &Command::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
void SmartCardsWidget::Private::generateCardKeysAndOpenPGPCertificate()
{
Q_ASSERT(currentCardType() == AppType::OpenPGPApp);
startCommand(new GenerateOpenPGPCardKeysAndCertificateCommand(currentSerialNumber(), q->window()));
}
void SmartCardsWidget::Private::createOpenPGPCertificate()
{
const auto app = currentCardType();
Q_ASSERT(app == AppType::NetKeyApp || app == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
startCommand(new CreateOpenPGPKeyFromCardKeysCommand(serialNumber, appName(app), q->window()));
}
void SmartCardsWidget::Private::changePin(const std::string &keyRef, ChangePinCommand::ChangePinMode mode)
{
const auto app = currentCardType();
Q_ASSERT(app == AppType::NetKeyApp || app == AppType::OpenPGPApp || app == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
auto cmd = new ChangePinCommand(serialNumber, appName(app), q->window());
cmd->setKeyRef(keyRef);
cmd->setMode(mode);
if (app == AppType::NetKeyApp) {
const Card *card = currentCardWidget()->card();
if ((keyRef == NetKeyCard::nksPinKeyRef() && card->hasNKSNullPin()) //
|| (keyRef == NetKeyCard::sigGPinKeyRef() && card->hasSigGNullPin())) {
cmd->setMode(ChangePinCommand::NullPinMode);
}
}
startCommand(cmd);
}
void SmartCardsWidget::Private::unblockOpenPGPCard()
{
Q_ASSERT(currentCardType() == AppType::OpenPGPApp);
const auto pinCounters = currentCardWidget()->card()->pinCounters();
const bool pukIsAvailable = (pinCounters.size() == 3) && (pinCounters[1] > 0);
if (pukIsAvailable) {
// unblock card with the PUK
changePin(OpenPGPCard::resetCodeKeyRef());
} else {
// unblock card with the Admin PIN
changePin(OpenPGPCard::pinKeyRef(), ChangePinCommand::ResetMode);
}
}
void SmartCardsWidget::Private::changeCardholder()
{
const auto app = currentCardType();
Q_ASSERT(app == AppType::OpenPGPApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
QString text = currentCardWidget()->card()->cardHolder();
while (true) {
bool ok = false;
text = QInputDialog::getText(q,
i18nc("@title:window", "Change Cardholder"),
i18nc("@label ", "Enter the new cardholder name:"),
QLineEdit::Normal,
text,
&ok,
Qt::WindowFlags(),
Qt::ImhLatinOnly);
if (!ok) {
return;
}
// Some additional restrictions imposed by gnupg
if (text.contains(u'<')) {
KMessageBox::error(q, i18nc("@info", "The \"<\" character may not be used."));
continue;
}
if (text.contains(" "_L1)) {
KMessageBox::error(q, i18nc("@info", "Double spaces are not allowed"));
continue;
}
if (text.size() > 38) {
KMessageBox::error(q, i18nc("@info", "The size of the name may not exceed 38 characters."));
continue;
}
break;
}
auto parts = text.split(u' ');
const auto lastName = parts.takeLast();
const QString formatted = lastName + "<<"_L1 + parts.join(u'<');
const auto pgpCard = ReaderStatus::instance()->getCard<OpenPGPCard>(serialNumber);
if (!pgpCard) {
KMessageBox::error(q, i18nc("@info", "Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(serialNumber)));
return;
}
const QByteArray command = QByteArrayLiteral("SCD SETATTR DISP-NAME ") + formatted.toUtf8();
ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, command, q, [this, serialNumber, app](const GpgME::Error &err) {
if (err) {
KMessageBox::error(q, i18nc("@info", "Name change failed: %1", Formatting::errorAsString(err)));
} else if (!err.isCanceled()) {
ReaderStatus::mutableInstance()->updateCard(serialNumber, appName(app));
}
});
}
void SmartCardsWidget::Private::changePublicKeyUrl()
{
const auto app = currentCardType();
Q_ASSERT(app == AppType::OpenPGPApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
QString text = currentCardWidget()->card()->publicKeyUrl();
while (true) {
bool ok = false;
text = QInputDialog::getText(q,
i18nc("@title:window", "Change Public Key URL"),
i18nc("@label", "Enter the new public key URL:"),
QLineEdit::Normal,
text,
&ok,
Qt::WindowFlags(),
Qt::ImhLatinOnly);
if (!ok) {
return;
}
// Some additional restrictions imposed by gnupg
if (text.size() > 254) {
KMessageBox::error(q, i18nc("@info", "The size of the URL may not exceed 254 characters."));
continue;
}
break;
}
const auto pgpCard = ReaderStatus::instance()->getCard<OpenPGPCard>(serialNumber);
if (!pgpCard) {
KMessageBox::error(q, i18nc("@info", "Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(serialNumber)));
return;
}
const QByteArray command = QByteArrayLiteral("SCD SETATTR PUBKEY-URL ") + text.toUtf8();
ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, command, q, [this, serialNumber, app](const GpgME::Error &err) {
if (err) {
KMessageBox::error(q, i18nc("@info", "URL change failed: %1", Formatting::errorAsString(err)));
} else if (!err.isCanceled()) {
ReaderStatus::mutableInstance()->updateCard(serialNumber, appName(app));
}
});
}
void SmartCardsWidget::Private::setPIVAdminKey()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
startCommand(new SetPIVCardApplicationAdministrationKeyCommand(currentSerialNumber(), q->window()));
}
void SmartCardsWidget::Private::showCertificateDetails()
{
const Key certificate = currentCertificate();
if (!certificate.isNull()) {
auto cmd = new DetailsCommand(certificate);
cmd->setParentWidget(q->window());
cmd->start();
}
}
static Command *createGenerateKeyCommand(AppType app, const std::string &serialNumber, const std::string &keyRef, QWidget *parent)
{
Q_ASSERT(app == AppType::OpenPGPApp || app == AppType::PIVApp);
Q_ASSERT(!serialNumber.empty());
if (app == AppType::OpenPGPApp) {
return new OpenPGPGenerateCardKeyCommand(keyRef, serialNumber, parent);
}
auto cmd = new PIVGenerateCardKeyCommand(serialNumber, parent);
cmd->setKeyRef(keyRef);
return cmd;
}
void SmartCardsWidget::Private::generateKey()
{
startCommand(createGenerateKeyCommand(currentCardType(), currentSerialNumber(), currentCardSlot(), q->window()));
}
void SmartCardsWidget::Private::createCSR()
{
const auto app = currentCardType();
Q_ASSERT(app == AppType::NetKeyApp || app == AppType::OpenPGPApp || app == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
startCommand(new CreateCSRForCardKeyCommand(keyRef, serialNumber, appName(app), q->window()));
}
void SmartCardsWidget::Private::writeCertificateToCard()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
startCommand(new CertificateToPIVCardCommand(keyRef, serialNumber, q->window()));
}
void SmartCardsWidget::Private::readCertificateFromCard()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
startCommand(new ImportCertificateFromPIVCardCommand(keyRef, serialNumber, q->window()));
}
void SmartCardsWidget::Private::writeKeyToCard()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
startCommand(new KeyToCardCommand(keyRef, serialNumber, PIVCard::AppName, q->window()));
}
SmartCardsWidget::SmartCardsWidget(QWidget *parent)
: QWidget{parent}
, d{std::make_unique<Private>(this)}
{
connect(ReaderStatus::instance(), &ReaderStatus::currentActionChanged, this, &SmartCardsWidget::updateReloadButton);
updateReloadButton();
}
SmartCardsWidget::~SmartCardsWidget() = default;
void SmartCardsWidget::showCards(const std::vector<std::shared_ptr<Kleo::SmartCard::Card>> &cards)
{
for (const auto &card : cards) {
d->cardAddedOrChanged(card->serialNumber(), card->appName());
}
}
void SmartCardsWidget::reload()
{
ReaderStatus::mutableInstance()->updateStatus();
}
void SmartCardsWidget::updateReloadButton()
{
d->mReloadButton->setEnabled(ReaderStatus::instance()->currentAction() != ReaderStatus::UpdateCards);
}
#include "smartcardswidget.moc"
#include "moc_smartcardswidget.cpp"
diff --git a/src/view/smartcardswidget.h b/src/view/smartcardswidget.h
index e2881e087..b1be635ae 100644
--- a/src/view/smartcardswidget.h
+++ b/src/view/smartcardswidget.h
@@ -1,46 +1,50 @@
/* view/smartcardswidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QWidget>
#include <memory>
#include <vector>
class KActionCollection;
namespace Kleo
{
namespace SmartCard
{
class Card;
}
/* SmartCardsWidget a generic widget to interact with smartcards */
class SmartCardsWidget : public QWidget
{
Q_OBJECT
public:
explicit SmartCardsWidget(QWidget *parent = nullptr);
~SmartCardsWidget() override;
void showCards(const std::vector<std::shared_ptr<Kleo::SmartCard::Card>> &cards);
public Q_SLOTS:
void reload();
+Q_SIGNALS:
+ void statusMessage(const QString &message, const QByteArray &cardId);
+ void cardChanged(const QByteArray &cardId);
+
private:
void updateReloadButton();
private:
class Private;
std::unique_ptr<Private> d;
};
} // namespace Kleo
diff --git a/src/view/smartcardwidget.cpp b/src/view/smartcardwidget.cpp
index 90583f787..98db9c4bb 100644
--- a/src/view/smartcardwidget.cpp
+++ b/src/view/smartcardwidget.cpp
@@ -1,472 +1,467 @@
/* view/smartcardwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2024 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "smartcardwidget.h"
#include "infofield.h"
#include "smartcardactions.h"
#include <smartcard/card.h>
#include <smartcard/netkeycard.h>
#include <smartcard/pivcard.h>
#include <utils/accessibility.h>
#include <view/cardkeysview.h>
#include <kleopatra_debug.h>
#include <settings.h>
#include <Libkleo/Compliance>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyCache>
#include <KLocalizedString>
#include <KMessageWidget>
#include <QGpgME/ImportFromKeyserverJob>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <QGridLayout>
#include <QLabel>
#include <QMenu>
#include <QScrollArea>
#include <QToolButton>
#include <QVBoxLayout>
#include <gpgme++/importresult.h>
#include <gpgme++/keylistresult.h>
using namespace Kleo;
using namespace Kleo::SmartCard;
using namespace Qt::Literals::StringLiterals;
static QString cardTypeForDisplay(const Card *card)
{
switch (card->appType()) {
case AppType::NetKeyApp:
return i18nc("1 is a Version number", "NetKey v%1 Card", card->appVersion());
case AppType::OpenPGPApp: {
const std::string manufacturer = card->manufacturer();
const bool manufacturerIsUnknown = manufacturer.empty() || manufacturer == "unknown";
return (manufacturerIsUnknown //
? i18nc("Placeholder is a version number", "Unknown OpenPGP v%1 card", card->displayAppVersion())
: i18nc("First placeholder is manufacturer, second placeholder is a version number",
"%1 OpenPGP v%2 card",
QString::fromStdString(manufacturer),
card->displayAppVersion()));
}
case AppType::P15App:
return i18nc("%1 is a smartcard manufacturer", "%1 PKCS#15 card", QString::fromStdString(card->manufacturer()));
case AppType::PIVApp:
return i18nc("%1 version number", "PIV v%1 card", card->displayAppVersion());
default:
return {};
};
}
static std::vector<QAction *> actionsForCard(SmartCard::AppType appType)
{
std::vector<QString> actions;
switch (appType) {
case AppType::NetKeyApp:
actions = {
u"card_all_create_openpgp_certificate"_s,
u"card_netkey_set_nks_pin"_s,
u"card_netkey_set_sigg_pin"_s,
};
break;
case AppType::OpenPGPApp:
actions = {
u"card_pgp_generate_keys_and_certificate"_s,
u"card_pgp_change_pin"_s,
u"card_pgp_unblock_card"_s,
u"card_pgp_change_admin_pin"_s,
u"card_pgp_change_puk"_s,
u"card_pgp_change_cardholder"_s,
u"card_pgp_change_publickeyurl"_s,
};
break;
case AppType::P15App:
// there are no card actions for generic PKCS#15 cards
break;
case AppType::PIVApp:
actions = {
u"card_all_create_openpgp_certificate"_s,
u"card_piv_change_pin"_s,
u"card_piv_change_puk"_s,
u"card_piv_change_admin_key"_s,
};
break;
case AppType::NoApp:
break;
};
return SmartCardActions::instance()->actions(actions);
}
static void updateCardAction(QAction *action, const Card *card)
{
switch (card->appType()) {
case AppType::NetKeyApp: {
if (card->pinStates().empty()) {
action->setEnabled(false);
return;
}
if (action->objectName() == "card_all_create_openpgp_certificate"_L1) {
action->setEnabled(!card->hasNKSNullPin() && card->hasSigningKey() && card->hasEncryptionKey()
&& DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->signingKeyRef()).algorithm)
&& DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->encryptionKeyRef()).algorithm));
} else if (action->objectName() == "card_netkey_set_nks_pin"_L1) {
if (!card->hasNKSNullPin()) {
action->setText(i18nc("@action NKS is an identifier for a type of keys on a NetKey card", "Change NKS PIN"));
}
} else if (action->objectName() == "card_netkey_set_sigg_pin"_L1) {
if (!card->hasSigGNullPin()) {
action->setText(i18nc("@action SigG is an identifier for a type of keys on a NetKey card", "Change SigG PIN"));
}
}
break;
}
case AppType::OpenPGPApp:
if (action->objectName() == "card_pgp_change_puk"_L1) {
const auto pinCounters = card->pinCounters();
const bool pukIsAvailable = (pinCounters.size() == 3) && (pinCounters[1] > 0);
action->setText(pukIsAvailable ? i18nc("@action", "Change PUK") : i18nc("@action", "Set PUK"));
}
break;
case AppType::P15App:
break;
case AppType::PIVApp: {
if (action->objectName() == "card_all_create_openpgp_certificate"_L1) {
action->setEnabled(card->hasSigningKey() && card->hasEncryptionKey()
&& DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->signingKeyRef()).algorithm)
&& DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->encryptionKeyRef()).algorithm));
}
break;
}
case AppType::NoApp:
break;
};
}
static void updateCardActions(QToolButton *actionsButton, const Card *card)
{
if (!actionsButton->menu()) {
const auto actions = actionsForCard(card->appType());
if (actions.empty()) {
// there are no card actions for this card app
return;
} else {
actionsButton->setVisible(true);
}
auto menu = new QMenu{actionsButton};
for (auto action : actions) {
menu->addAction(SmartCardActions::createProxyAction(action, menu));
}
actionsButton->setMenu(menu);
}
for (auto action : actionsButton->menu()->actions()) {
updateCardAction(action, card);
}
}
static void updateNullPinWidget(KMessageWidget *nullPinWidget, const Card *card)
{
Q_ASSERT(card);
if (card->appType() != AppType::NetKeyApp) {
return;
}
/* Only check for the standard NKS NullPIN.
* According to users of NetKey cards it is fairly uncommon to use SigG certificates at all.
* So it should be optional to set the SigG pins. */
if (card->hasNKSNullPin()) {
nullPinWidget->setMessageType(KMessageWidget::Information);
nullPinWidget->setIcon(QIcon::fromTheme(u"data-information"_s));
const auto nullTitle = i18nc(
"NullPIN is a word that is used all over in the netkey "
"documentation and should be understandable by Netkey cardholders",
"The NullPIN is still active on this card.");
const auto nullDescription = i18n("You need to set a PIN before you can use the certificates.");
nullPinWidget->setText(QStringLiteral("<b>%1</b><br/>%2").arg(nullTitle, nullDescription));
nullPinWidget->setCloseButtonVisible(false);
if (nullPinWidget->actions().isEmpty()) {
nullPinWidget->addAction(SmartCardActions::createProxyAction(SmartCardActions::instance()->action(u"card_netkey_set_nks_pin"_s), nullPinWidget));
}
nullPinWidget->setVisible(true);
} else {
nullPinWidget->setVisible(false);
}
}
SmartCardWidget::SmartCardWidget(Kleo::SmartCard::AppType appType, QWidget *parent)
: QWidget{parent}
, mAppType{appType}
{
auto mainLayout = new QVBoxLayout{this};
mainLayout->setContentsMargins({});
auto area = new QScrollArea{this};
area->setFocusPolicy(Qt::NoFocus);
area->setFrameShape(QFrame::NoFrame);
area->setWidgetResizable(true);
mainLayout->addWidget(area);
auto areaWidget = new QWidget{this};
area->setWidget(areaWidget);
auto contentLayout = new QVBoxLayout{areaWidget};
auto upperLayout = new QHBoxLayout;
{
auto gridLayout = new QGridLayout;
gridLayout->setColumnStretch(1, 1);
int row = -1;
row++;
mCardTypeField = std::make_unique<InfoField>(i18nc("@label", "Card type:"), parent);
gridLayout->addWidget(mCardTypeField->label(), row, 0);
gridLayout->addLayout(mCardTypeField->layout(), row, 1);
row++;
mSerialNumberField = std::make_unique<InfoField>(i18nc("@label", "Serial number:"), parent);
gridLayout->addWidget(mSerialNumberField->label(), row, 0);
gridLayout->addLayout(mSerialNumberField->layout(), row, 1);
if (mAppType == AppType::OpenPGPApp) {
row++;
mCardholderField =
std::make_unique<InfoField>(i18nc("@label The owner of a smartcard. GnuPG refers to this as cardholder.", "Cardholder:"), parent);
const auto action = SmartCardActions::createProxyAction(SmartCardActions::instance()->action(u"card_pgp_change_cardholder"_s), parent);
action->setIcon(QIcon::fromTheme(u"document-edit"_s));
Kleo::setAccessibleName(action, action->text());
action->setText({});
mCardholderField->setAction(action);
gridLayout->addWidget(mCardholderField->label(), row, 0);
gridLayout->addLayout(mCardholderField->layout(), row, 1);
}
if (mAppType == AppType::OpenPGPApp) {
row++;
mPublicKeyUrlField = std::make_unique<InfoField>(i18nc("@label", "Public key URL:"), parent);
// make the public key URL clickable
mPublicKeyUrlField->valueLabel()->setTextInteractionFlags(Qt::TextBrowserInteraction);
mPublicKeyUrlField->valueLabel()->setOpenExternalLinks(true);
const auto action = SmartCardActions::createProxyAction(SmartCardActions::instance()->action(u"card_pgp_change_publickeyurl"_s), parent);
action->setIcon(QIcon::fromTheme(u"document-edit"_s));
Kleo::setAccessibleName(action, action->text());
action->setText({});
mPublicKeyUrlField->setAction(action);
gridLayout->addWidget(mPublicKeyUrlField->label(), row, 0);
gridLayout->addLayout(mPublicKeyUrlField->layout(), row, 1);
}
if (mAppType == AppType::OpenPGPApp) {
row++;
mPinCountersField = std::make_unique<InfoField>(i18nc("@label The number of remaining attempts to enter a PIN or PUK, as in "
"Remaining attempts: PIN: 2, PUK: 3, Admin PIN: 3",
"Remaining attempts:"),
parent);
mPinCountersField->setToolTip(xi18nc("@info:tooltip", "Shows the number of remaining attempts for entering the correct PIN or PUK."));
gridLayout->addWidget(mPinCountersField->label(), row, 0);
gridLayout->addLayout(mPinCountersField->layout(), row, 1);
}
upperLayout->addLayout(gridLayout, 1);
}
{
auto layout = new QVBoxLayout;
mCardActionsButton = new QToolButton{this};
mCardActionsButton->setPopupMode(QToolButton::InstantPopup);
mCardActionsButton->setText(i18nc("@action:button", "Card Actions"));
mCardActionsButton->setToolTip(i18nc("@info", "Show actions available for this smart card"));
mCardActionsButton->setVisible(false);
layout->addWidget(mCardActionsButton);
layout->addStretch(1);
upperLayout->addLayout(layout);
}
contentLayout->addLayout(upperLayout);
if (mAppType == AppType::NetKeyApp) {
mNullPinWidget = new KMessageWidget{this};
mNullPinWidget->setVisible(false);
contentLayout->addWidget(mNullPinWidget);
}
- // used for OpenPGP and PKCS#15 cards when looking up missing OpenPGP certificates
- mStatusLabel = new QLabel{this};
- mStatusLabel->setVisible(false);
- contentLayout->addWidget(mStatusLabel);
-
mErrorWidget = new KMessageWidget{this};
mErrorWidget->setVisible(false);
contentLayout->addWidget(mErrorWidget);
Q_ASSERT(!mCardKeysView);
switch (mAppType) {
case AppType::NetKeyApp:
// do not show Created column by default; creation time is not reported by scdaemon for NetKey cards
mCardKeysView = new CardKeysView{this, CardKeysView::NoOptions};
break;
case AppType::OpenPGPApp:
case AppType::P15App:
mCardKeysView = new CardKeysView{this, CardKeysView::ShowCreated};
break;
case AppType::PIVApp:
// do not show Created column by default; creation time is not reported by scdaemon for PIV cards
mCardKeysView = new CardKeysView{this, CardKeysView::NoOptions};
break;
case AppType::NoApp:
return;
};
contentLayout->addWidget(mCardKeysView, 1);
}
SmartCardWidget::~SmartCardWidget()
{
if (mJob) {
mJob->slotCancel();
}
}
void SmartCardWidget::setCard(const Card *card)
{
Q_ASSERT(mAppType == card->appType());
const bool firstSetup = !mCard;
mCard.reset(card->clone());
mCardTypeField->setValue(cardTypeForDisplay(card));
mSerialNumberField->setValue(card->displaySerialNumber());
if (mAppType == AppType::OpenPGPApp) {
const auto holder = card->cardHolder();
mCardholderField->setValue(holder.isEmpty() ? ("<em>"_L1 + i18n("not set") + "</em>"_L1) : holder);
const auto url = card->publicKeyUrl();
mPublicKeyUrlField->setValue(url.isEmpty() //
? ("<em>"_L1 + i18n("not set") + "</em>"_L1)
: u"<a href=\"%1\">%1</a>"_s.arg(url.toHtmlEscaped()));
const auto pinLabels = card->pinLabels();
const auto pinCounters = card->pinCounters();
QStringList countersWithLabels;
countersWithLabels.reserve(pinCounters.size());
for (const auto &pinCounter : pinCounters) {
// sanity check
if (countersWithLabels.size() == pinLabels.size()) {
break;
}
countersWithLabels.push_back(i18nc("label: value", "%1: %2", pinLabels[countersWithLabels.size()], pinCounter));
}
mPinCountersField->setValue(countersWithLabels.join(", "_L1));
}
updateCardActions(mCardActionsButton, card);
updateNullPinWidget(mNullPinWidget, card);
const auto errMsg = card->errorMsg();
if (!errMsg.isEmpty()) {
mErrorWidget->setMessageType(KMessageWidget::Error);
mErrorWidget->setCloseButtonVisible(false);
mErrorWidget->setText(i18nc("@info", "Error: %1", errMsg));
mErrorWidget->setVisible(true);
} else {
mErrorWidget->setVisible(false);
}
if (firstSetup && (mAppType == AppType::OpenPGPApp || mAppType == AppType::P15App)) {
retrieveOpenPGPCertificate();
}
mCardKeysView->setCard(mCard);
}
const Kleo::SmartCard::Card *SmartCardWidget::card() const
{
return mCard.get();
}
Kleo::SmartCard::AppType SmartCardWidget::cardType() const
{
return mCard ? mCard->appType() : AppType::NoApp;
}
std::string SmartCardWidget::serialNumber() const
{
return mCard ? mCard->serialNumber() : std::string{};
}
std::string SmartCardWidget::currentCardSlot() const
{
if (mCardKeysView) {
return mCardKeysView->currentCardSlot();
}
return {};
}
GpgME::Key SmartCardWidget::currentCertificate() const
{
if (mCardKeysView) {
return mCardKeysView->currentCertificate();
}
return {};
}
void SmartCardWidget::retrieveOpenPGPCertificate()
{
Q_ASSERT(mCard);
Q_ASSERT(mAppType == AppType::OpenPGPApp || mAppType == AppType::P15App);
Q_ASSERT(!mJob);
- mStatusLabel->setVisible(false);
+ // clear the status message
+ Q_EMIT statusMessage({});
// Auto import the OpenPGP key for the card keys only from LDAP or if explicitly enabled
if (!(Kleo::keyserver().startsWith("ldap"_L1) || //
(Settings().alwaysSearchCardOnKeyserver() && Kleo::haveKeyserverConfigured()))) {
return;
}
const auto sigInfo = mCard->keyInfo(mCard->signingKeyRef());
if (sigInfo.grip.empty()) {
return;
}
const auto key = KeyCache::instance()->findSubkeyByKeyGrip(sigInfo.grip, GpgME::OpenPGP).parent();
if (!key.isNull()) {
return;
}
qCDebug(KLEOPATRA_LOG) << __func__ << "No key found for key grip" << sigInfo.grip;
const auto fpr = mCard->keyFingerprint(mCard->signingKeyRef());
if (fpr.empty()) {
return;
}
qCDebug(KLEOPATRA_LOG) << __func__ << "Should be OpenPGP key" << fpr;
- mStatusLabel->setText(i18n("Searching matching certificate in directory service..."));
- mStatusLabel->setVisible(true);
+ Q_EMIT statusMessage(i18n("Searching matching certificate in directory service..."));
qCDebug(KLEOPATRA_LOG) << __func__ << "Looking for" << fpr << "on key server" << Kleo::keyserver();
auto keyListJob = QGpgME::openpgp()->keyListJob(/* remote = */ true);
mJob = keyListJob;
connect(keyListJob, &QGpgME::KeyListJob::result, this, [this](GpgME::KeyListResult, std::vector<GpgME::Key> keys, QString, GpgME::Error) {
mJob.clear();
if (keys.size() == 1) {
qCDebug(KLEOPATRA_LOG) << "retrieveOpenPGPCertificate - Importing" << keys[0].primaryFingerprint();
auto importJob = QGpgME::openpgp()->importFromKeyserverJob();
mJob = importJob;
connect(importJob, &QGpgME::ImportFromKeyserverJob::result, this, [this](GpgME::ImportResult, QString, GpgME::Error) {
mJob.clear();
qCDebug(KLEOPATRA_LOG) << "retrieveOpenPGPCertificate - import job done";
- mStatusLabel->setText(i18n("The matching certificate was imported successfully."));
+ Q_EMIT statusMessage(i18n("The matching certificate was imported successfully."));
});
importJob->start(keys);
} else if (keys.size() > 1) {
qCDebug(KLEOPATRA_LOG) << "retrieveOpenPGPCertificate - Multiple keys found on server";
- mStatusLabel->setText(i18n("Multiple matching certificates were found in directory service."));
+ Q_EMIT statusMessage(i18n("Multiple matching certificates were found in directory service."));
} else {
qCDebug(KLEOPATRA_LOG) << "retrieveOpenPGPCertificate - No key found on server";
- mStatusLabel->setText(i18n("No matching certificate was found in directory service."));
+ Q_EMIT statusMessage(i18n("No matching certificate was found in directory service."));
}
});
keyListJob->start({QString::fromStdString(fpr)});
}
diff --git a/src/view/smartcardwidget.h b/src/view/smartcardwidget.h
index 0490e0103..dcce7fb81 100644
--- a/src/view/smartcardwidget.h
+++ b/src/view/smartcardwidget.h
@@ -1,78 +1,80 @@
/* view/smartcardwidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2024 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QPointer>
#include <QWidget>
#include <memory>
#include <string>
class KMessageWidget;
class QGridLayout;
class QLabel;
class QToolButton;
class QVBoxLayout;
namespace GpgME
{
class Key;
}
namespace QGpgME
{
class Job;
}
namespace Kleo
{
class CardKeysView;
class InfoField;
}
namespace Kleo::SmartCard
{
enum class AppType;
class Card;
}
class SmartCardWidget : public QWidget
{
Q_OBJECT
protected:
SmartCardWidget(Kleo::SmartCard::AppType appType, QWidget *parent = nullptr);
public:
~SmartCardWidget() override;
void setCard(const Kleo::SmartCard::Card *card);
const Kleo::SmartCard::Card *card() const;
Kleo::SmartCard::AppType cardType() const;
std::string serialNumber() const;
std::string currentCardSlot() const;
GpgME::Key currentCertificate() const;
+Q_SIGNALS:
+ void statusMessage(const QString &message);
+
private:
void retrieveOpenPGPCertificate();
private:
Kleo::SmartCard::AppType mAppType;
std::shared_ptr<const Kleo::SmartCard::Card> mCard;
QPointer<QGpgME::Job> mJob;
std::unique_ptr<Kleo::InfoField> mCardTypeField;
std::unique_ptr<Kleo::InfoField> mSerialNumberField;
std::unique_ptr<Kleo::InfoField> mCardholderField;
std::unique_ptr<Kleo::InfoField> mPublicKeyUrlField;
std::unique_ptr<Kleo::InfoField> mPinCountersField;
QToolButton *mCardActionsButton = nullptr;
KMessageWidget *mNullPinWidget = nullptr;
- QLabel *mStatusLabel = nullptr;
KMessageWidget *mErrorWidget = nullptr;
Kleo::CardKeysView *mCardKeysView = nullptr;
};

File Metadata

Mime Type
text/x-diff
Expires
Thu, Oct 16, 4:46 AM (20 h, 44 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
cd/23/5e040156cba8cef04bd5e23ea00c

Event Timeline