Page MenuHome GnuPG

No OneTemporary

diff --git a/src/view/pgpcardwidget.cpp b/src/view/pgpcardwidget.cpp
index d2e7f64ac..2690269b3 100644
--- a/src/view/pgpcardwidget.cpp
+++ b/src/view/pgpcardwidget.cpp
@@ -1,312 +1,224 @@
/* view/pgpcardwiget.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, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "pgpcardwidget.h"
#include "kleopatra_debug.h"
-#include <commands/generateopenpgpcardkeysandcertificatecommand.h>
-
#include "smartcard/openpgpcard.h"
#include "smartcard/readerstatus.h"
#include <view/cardkeysview.h>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <KLocalizedString>
#include <KMessageBox>
#include <Libkleo/Formatting>
using namespace Kleo;
-using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
PGPCardWidget::PGPCardWidget(QWidget *parent)
: SmartCardWidget(parent)
{
{
mInfoGridLayout->setColumnStretch(mInfoGridLayout->columnCount() - 1, 0); // undo stretch set by base widget
int row = mInfoGridLayout->rowCount();
// Cardholder Row
mInfoGridLayout->addWidget(new QLabel(i18nc("The owner of a smartcard. GnuPG refers to this as cardholder.", "Cardholder:")), row, 0);
mCardHolderLabel = new QLabel{this};
mCardHolderLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
mInfoGridLayout->addWidget(mCardHolderLabel, row, 1);
{
auto button = new QPushButton{this};
button->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
button->setAccessibleName(i18nc("@action:button", "Edit"));
button->setToolTip(i18nc("@info:tooltip", "Change"));
mInfoGridLayout->addWidget(button, row, 2);
connect(button, &QPushButton::clicked, this, &PGPCardWidget::changeNameRequested);
}
row++;
// URL Row
mInfoGridLayout->addWidget(new QLabel(i18nc("The URL under which a public key that "
"corresponds to a smartcard can be downloaded",
"Pubkey URL:")),
row,
0);
mUrlLabel = new QLabel{this};
mUrlLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
mInfoGridLayout->addWidget(mUrlLabel, row, 1);
{
auto button = new QPushButton{this};
button->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
button->setAccessibleName(i18nc("@action:button", "Edit"));
button->setToolTip(i18nc("@info:tooltip", "Change"));
mInfoGridLayout->addWidget(button, row, 2);
connect(button, &QPushButton::clicked, this, &PGPCardWidget::changeUrlRequested);
}
row++;
// PIN counters row
{
mInfoGridLayout->addWidget(new QLabel(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:")),
row,
0);
mPinCounterLabel = new QLabel{this};
mPinCounterLabel->setToolTip(xi18nc("@info:tooltip", "Shows the number of remaining attempts for entering the correct PIN or PUK."));
mPinCounterLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
mInfoGridLayout->addWidget(mPinCounterLabel, row, 1);
}
mInfoGridLayout->setColumnStretch(mInfoGridLayout->columnCount(), 1);
}
mCardKeysView = new CardKeysView{this};
mContentLayout->addWidget(mCardKeysView, 1);
-
- auto actionLayout = new QHBoxLayout;
-
- {
- auto generateButton = new QPushButton(i18nc("@action:button", "Generate New Keys"), this);
- generateButton->setToolTip(xi18nc("@info:tooltip",
- "<para>Generate three new keys on the smart card and create a new OpenPGP "
- "certificate with those keys. Optionally, the encryption key is generated "
- "off-card and a backup is created so that you can still access data encrypted "
- "with this key in case the card is lost or damaged.</para>"
- "<para><emphasis strong='true'>"
- "Existing keys on the smart card will be overwritten."
- "</emphasis></para>"));
- actionLayout->addWidget(generateButton);
- connect(generateButton, &QPushButton::clicked, this, &PGPCardWidget::genkeyRequested);
- }
- {
- auto pinButton = new QPushButton(i18nc("@action:button", "Change PIN"), this);
- pinButton->setToolTip(i18nc("@info:tooltip",
- "Change the PIN required for using the keys on the smart card. "
- "The PIN must contain at least six characters."));
- actionLayout->addWidget(pinButton);
- connect(pinButton, &QPushButton::clicked, this, [this]() {
- doChangePin(OpenPGPCard::pinKeyRef());
- });
- }
- {
- auto unblockButton = new QPushButton(i18nc("@action:button", "Unblock Card"), this);
- unblockButton->setToolTip(i18nc("@info:tooltip", "Unblock the smart card with the PUK (if available) or the Admin PIN."));
- actionLayout->addWidget(unblockButton);
- connect(unblockButton, &QPushButton::clicked, this, [this]() {
- if (mPUKIsAvailable) {
- // unblock card with the PUK
- doChangePin(OpenPGPCard::resetCodeKeyRef());
- } else {
- // unblock card with the Admin PIN
- doChangePin(OpenPGPCard::pinKeyRef(), ChangePinCommand::ResetMode);
- }
- });
- }
- {
- auto pukButton = new QPushButton(i18nc("@action:button", "Change Admin PIN"), this);
- pukButton->setToolTip(i18nc("@info:tooltip", "Change the PIN required for administrative operations."));
- actionLayout->addWidget(pukButton);
- connect(pukButton, &QPushButton::clicked, this, [this]() {
- doChangePin(OpenPGPCard::adminPinKeyRef());
- });
- }
- {
- mSetOrChangePUKButton = new QPushButton(i18nc("@action:button", "Set PUK"), this);
- mSetOrChangePUKButton->setToolTip(i18nc("@info:tooltip",
- "Set or change the PUK that can be used to unblock the smart card. "
- "The PUK must contain at least eight characters."));
- actionLayout->addWidget(mSetOrChangePUKButton);
- connect(mSetOrChangePUKButton, &QPushButton::clicked, this, [this]() {
- doChangePin(OpenPGPCard::resetCodeKeyRef(), ChangePinCommand::ResetMode);
- });
- }
-
- actionLayout->addStretch(-1);
- mContentLayout->addLayout(actionLayout);
}
void PGPCardWidget::setCard(const OpenPGPCard *card)
{
SmartCardWidget::setCard(card);
mIs21 = card->appVersion() >= 0x0201;
const auto holder = card->cardHolder();
const auto url = QString::fromStdString(card->pubkeyUrl());
mCardHolderLabel->setText(holder.isEmpty() ? i18n("not set") : holder);
mUrl = url;
mUrlLabel->setText(url.isEmpty() ? i18n("not set") : QStringLiteral("<a href=\"%1\">%1</a>").arg(url.toHtmlEscaped()));
mUrlLabel->setOpenExternalLinks(true);
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));
}
mPinCounterLabel->setText(countersWithLabels.join(QLatin1String(", ")));
- mPUKIsAvailable = (pinCounters.size() == 3) && (pinCounters[1] > 0);
- mSetOrChangePUKButton->setText(mPUKIsAvailable ? i18nc("@action:button", "Change PUK") : i18nc("@action:button", "Set PUK"));
-}
-
-void PGPCardWidget::doChangePin(const std::string &keyRef, ChangePinCommand::ChangePinMode mode)
-{
- auto cmd = new ChangePinCommand(serialNumber(), OpenPGPCard::AppName, this);
- this->setEnabled(false);
- connect(cmd, &ChangePinCommand::finished, this, [this]() {
- this->setEnabled(true);
- });
- cmd->setKeyRef(keyRef);
- cmd->setMode(mode);
- cmd->start();
-}
-
-void PGPCardWidget::genkeyRequested()
-{
- auto cmd = new GenerateOpenPGPCardKeysAndCertificateCommand(serialNumber(), this);
- this->setEnabled(false);
- connect(cmd, &Command::finished, this, [this]() {
- this->setEnabled(true);
- });
- cmd->start();
}
void PGPCardWidget::changeNameRequested()
{
QString text = mCardHolderLabel->text();
while (true) {
bool ok = false;
text = QInputDialog::getText(this, i18n("Change cardholder"), i18n("New name:"), QLineEdit::Normal, text, &ok, Qt::WindowFlags(), Qt::ImhLatinOnly);
if (!ok) {
return;
}
// Some additional restrictions imposed by gnupg
if (text.contains(QLatin1Char('<'))) {
KMessageBox::error(this, i18nc("@info", "The \"<\" character may not be used."));
continue;
}
if (text.contains(QLatin1StringView(" "))) {
KMessageBox::error(this, i18nc("@info", "Double spaces are not allowed"));
continue;
}
if (text.size() > 38) {
KMessageBox::error(this, i18nc("@info", "The size of the name may not exceed 38 characters."));
}
break;
}
auto parts = text.split(QLatin1Char(' '));
const auto lastName = parts.takeLast();
const QString formatted = lastName + QStringLiteral("<<") + parts.join(QLatin1Char('<'));
const auto pgpCard = ReaderStatus::instance()->getCard<OpenPGPCard>(serialNumber());
if (!pgpCard) {
KMessageBox::error(this, i18n("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, this, [this](const GpgME::Error &err) {
changeNameResult(err);
});
}
void PGPCardWidget::changeNameResult(const GpgME::Error &err)
{
if (err) {
KMessageBox::error(this, i18nc("@info", "Name change failed: %1", Formatting::errorAsString(err)));
return;
}
if (!err.isCanceled()) {
KMessageBox::information(this, i18nc("@info", "Name successfully changed."), i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
}
void PGPCardWidget::changeUrlRequested()
{
QString text = mUrl;
while (true) {
bool ok = false;
text = QInputDialog::getText(this,
i18n("Change the URL where the pubkey can be found"),
i18n("New pubkey URL:"),
QLineEdit::Normal,
text,
&ok,
Qt::WindowFlags(),
Qt::ImhLatinOnly);
if (!ok) {
return;
}
// Some additional restrictions imposed by gnupg
if (text.size() > 254) {
KMessageBox::error(this, i18nc("@info", "The size of the URL may not exceed 254 characters."));
}
break;
}
const auto pgpCard = ReaderStatus::instance()->getCard<OpenPGPCard>(serialNumber());
if (!pgpCard) {
KMessageBox::error(this, i18n("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, this, [this](const GpgME::Error &err) {
changeUrlResult(err);
});
}
void PGPCardWidget::changeUrlResult(const GpgME::Error &err)
{
if (err) {
KMessageBox::error(this, i18nc("@info", "URL change failed: %1", Formatting::errorAsString(err)));
return;
}
if (!err.isCanceled()) {
KMessageBox::information(this, i18nc("@info", "URL successfully changed."), i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
}
#include "moc_pgpcardwidget.cpp"
diff --git a/src/view/pgpcardwidget.h b/src/view/pgpcardwidget.h
index 3702e1e96..55591d957 100644
--- a/src/view/pgpcardwidget.h
+++ b/src/view/pgpcardwidget.h
@@ -1,62 +1,52 @@
/* view/pgpcardwiget.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-FileCopyrightText: 2020, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "smartcardwidget.h"
-#include "commands/changepincommand.h"
-
-#include <gpgme++/error.h>
-
#include <string>
class QLabel;
-class QPushButton;
-namespace Kleo
+namespace GpgME
{
-class GenCardKeyDialog;
+class Error;
+}
+namespace Kleo
+{
namespace SmartCard
{
class OpenPGPCard;
} // namespace SmartCard
class PGPCardWidget : public SmartCardWidget
{
Q_OBJECT
public:
explicit PGPCardWidget(QWidget *parent = nullptr);
void setCard(const SmartCard::OpenPGPCard *card);
- void doGenKey(GenCardKeyDialog *dlg);
- void genKeyDone(const GpgME::Error &err, const std::string &backup);
public Q_SLOTS:
- void genkeyRequested();
void changeNameRequested();
void changeNameResult(const GpgME::Error &err);
void changeUrlRequested();
void changeUrlResult(const GpgME::Error &err);
-private:
- void doChangePin(const std::string &keyRef, Commands::ChangePinCommand::ChangePinMode mode = Commands::ChangePinCommand::NormalMode);
-
private:
QLabel *mCardHolderLabel = nullptr;
QLabel *mUrlLabel = nullptr;
QLabel *mPinCounterLabel = nullptr;
- QPushButton *mSetOrChangePUKButton = nullptr;
QString mUrl;
bool mIs21 = false;
- bool mPUKIsAvailable = false;
};
} // namespace Kleo
diff --git a/src/view/smartcardactions.cpp b/src/view/smartcardactions.cpp
index ffe05e555..56c63975d 100644
--- a/src/view/smartcardactions.cpp
+++ b/src/view/smartcardactions.cpp
@@ -1,134 +1,162 @@
/* view/smartcardactions.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 "smartcardactions.h"
#include <commands/createopenpgpkeyfromcardkeyscommand.h>
#include <KActionCollection>
#include <KLocalizedString>
#include <algorithm>
using namespace Kleo::Commands;
using namespace Qt::Literals::StringLiterals;
SmartCardActions::SmartCardActions()
: mActionCollection{std::make_unique<KActionCollection>(nullptr, u"smartcards"_s)}
{
mActionCollection->setComponentDisplayName(i18n("Smart Card Management"));
// window actions
mActionCollection->addAction(KStandardAction::StandardAction::Close, u"window_close"_s);
// general actions
{
QAction *action = mActionCollection->addAction(KStandardAction::StandardAction::Redisplay, u"reload"_s);
action->setText(i18nc("@action", "Reload"));
action->setToolTip(i18nc("@info:tooltip", "Reload smart cards"));
}
// card actions
if (CreateOpenPGPKeyFromCardKeysCommand::isSupported()) {
addAction(u"card_all_create_openpgp_certificate"_s, //
i18nc("@action", "Create OpenPGP Certificate"),
i18nc("@info:tooltip", "Create an OpenPGP certificate for the keys stored on the card."));
}
// NetKey-specific card actions
addAction(u"card_netkey_set_nks_pin"_s, //
i18nc("@action NKS is an identifier for a type of keys on a NetKey card", "Set NKS PIN"));
addAction(u"card_netkey_set_sigg_pin"_s, //
i18nc("@action SigG is an identifier for a type of keys on a NetKey card", "Set SigG PIN"));
+ // OpenPGP-specific card actions
+ addAction(u"card_pgp_generate_keys_and_certificate"_s, //
+ i18nc("@action:button", "Generate New Keys"),
+ xi18nc("@info:tooltip",
+ "<para>Generate three new keys on the smart card and create a new OpenPGP "
+ "certificate with those keys. Optionally, the encryption key is generated "
+ "off-card and a backup is created so that you can still access data encrypted "
+ "with this key in case the card is lost or damaged.</para>"
+ "<para><emphasis strong='true'>"
+ "Existing keys on the smart card will be overwritten."
+ "</emphasis></para>"));
+ addAction(u"card_pgp_change_pin"_s, //
+ i18nc("@action:button", "Change PIN"),
+ i18nc("@info:tooltip",
+ "Change the PIN required for using the keys on the smart card. "
+ "The PIN must contain at least six characters."));
+ addAction(u"card_pgp_unblock_card"_s, //
+ i18nc("@action:button", "Unblock Card"),
+ i18nc("@info:tooltip", "Unblock the smart card with the PUK (if available) or the Admin PIN."));
+ addAction(u"card_pgp_change_admin_pin"_s, //
+ i18nc("@action:button", "Change Admin PIN"),
+ i18nc("@info:tooltip", "Change the PIN required for administrative operations."));
+ addAction(u"card_pgp_change_puk"_s, //
+ i18nc("@action:button", "Set PUK"),
+ i18nc("@info:tooltip",
+ "Set or change the PUK that can be used to unblock the smart card. "
+ "The PUK must contain at least eight characters."));
+
// PIV-specific card actions
addAction(u"card_piv_change_pin"_s, //
i18nc("@action", "Change PIN"),
i18nc("@info:tooltip", "Change the PIN required for using the keys on the smart card."));
addAction(u"card_piv_change_puk"_s, //
i18nc("@action", "Change PUK"),
i18nc("@info:tooltip",
"Change the PIN Unblocking Key (PUK) that can be used to unblock the smart card "
"after a wrong PIN has been entered too many times."));
addAction(u"card_piv_change_admin_key"_s, //
i18nc("@action", "Change Admin Key"),
i18nc("@info:tooltip",
"Change the PIV Card Application Administration Key that is used by the "
"PIV Card Application to authenticate the PIV Card Application Administrator and by the "
"administrator (resp. Kleopatra) to authenticate the PIV Card Application."));
// card slot actions
addAction(u"card_slot_show_certificate_details"_s, //
i18nc("@action", "Show Certificate Details"),
{},
QIcon::fromTheme(u"dialog-information"_s));
addAction(u"card_slot_generate_key"_s, //
i18nc("@action", "Generate New Key"),
i18nc("@info:tooltip", "If the card slot already contains a key then the new key will irrevocably replace the old key."),
QIcon::fromTheme(u"view-certificate-add"_s));
addAction(u"card_slot_write_key"_s, //
i18nc("@action", "Write Key to Card"),
i18nc("@info:tooltip", "Write the key pair of a certificate to the card"),
QIcon::fromTheme(u"view-certificate-export"_s));
addAction(u"card_slot_write_certificate"_s, //
i18nc("@action", "Write Certificate to Card"),
i18nc("@info:tooltip", "Write the certificate corresponding to this key to the card"),
QIcon::fromTheme(u"view-certificate-export"_s));
addAction(u"card_slot_read_certificate"_s, //
i18nc("@action", "Import Certificate from Card"),
i18nc("@info:tooltip", "Import the certificate stored on the card"),
QIcon::fromTheme(u"view-certificate-import"_s));
addAction(u"card_slot_create_csr"_s, //
i18nc("@action", "Create S/MIME Certification Request"),
i18nc("@info:tooltip", "Create an S/MIME certificate signing request for this key"),
QIcon::fromTheme(u"view-certificate-add"_s));
}
SmartCardActions::~SmartCardActions() = default;
void SmartCardActions::addAction(const QString &id, const QString &text, const QString &toolTip, const QIcon &icon)
{
QAction *action = mActionCollection->addAction(id);
action->setText(text);
action->setToolTip(toolTip);
action->setIcon(icon);
}
std::shared_ptr<const SmartCardActions> SmartCardActions::instance()
{
return mutableInstance();
}
std::shared_ptr<SmartCardActions> SmartCardActions::mutableInstance()
{
static std::weak_ptr<SmartCardActions> self;
if (std::shared_ptr<SmartCardActions> shared = self.lock()) {
return shared;
} else {
const std::shared_ptr<SmartCardActions> s{new SmartCardActions};
self = s;
return s;
}
}
QAction *SmartCardActions::action(const QString &name) const
{
return mActionCollection->action(name);
}
std::vector<QAction *> SmartCardActions::actions(const std::vector<QString> &names) const
{
std::vector<QAction *> result;
result.reserve(names.size());
std::ranges::transform(names, std::back_inserter(result), [this](const QString &name) {
return action(name);
});
std::erase(result, nullptr);
return result;
}
diff --git a/src/view/smartcardswidget.cpp b/src/view/smartcardswidget.cpp
index c9d2c03ff..83f9fedbb 100644
--- a/src/view/smartcardswidget.cpp
+++ b/src/view/smartcardswidget.cpp
@@ -1,523 +1,567 @@
/* 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 <KActionCollection>
#include <KLocalizedString>
#include <QHBoxLayout>
#include <QLabel>
#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;
};
} // 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();
// card actions
+ void generateCardKeysAndOpenPGPCertificate();
void createOpenPGPCertificate();
- void changePin(const std::string &keyRef);
+ void changePin(const std::string &keyRef, ChangePinCommand::ChangePinMode mode = ChangePinCommand::NormalMode);
+ void unblockOpenPGPCard();
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(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_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));
if (mCardWidgets.size() == 1) {
mStack->setCurrentWidget(mTabWidget);
}
}
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::generateCardKeysAndOpenPGPCertificate()
+{
+ Q_ASSERT(currentCardType() == AppType::OpenPGPApp);
+ auto cmd = new GenerateOpenPGPCardKeysAndCertificateCommand(currentSerialNumber(), q->window());
+ disableCurrentWidget();
+ connect(cmd, &Command::finished, q, [this]() {
+ enableCurrentWidget();
+ });
+ cmd->start();
+}
+
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());
auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(serialNumber, appName(app), q->window());
disableCurrentWidget();
connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
-void SmartCardsWidget::Private::changePin(const std::string &keyRef)
+void SmartCardsWidget::Private::changePin(const std::string &keyRef, ChangePinCommand::ChangePinMode mode)
{
const auto app = currentCardType();
- Q_ASSERT(app == AppType::NetKeyApp || app == AppType::PIVApp);
+ 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) {
auto netKeyCard = static_cast<const NetKeyCard *>(currentCardWidget()->card());
Q_ASSERT(netKeyCard);
if ((keyRef == NetKeyCard::nksPinKeyRef() && netKeyCard->hasNKSNullPin()) //
|| (keyRef == NetKeyCard::sigGPinKeyRef() && netKeyCard->hasSigGNullPin())) {
cmd->setMode(ChangePinCommand::NullPinMode);
}
}
disableCurrentWidget();
connect(cmd, &ChangePinCommand::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
+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::setPIVAdminKey()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
auto cmd = new SetPIVCardApplicationAdministrationKeyCommand(currentSerialNumber(), q->window());
disableCurrentWidget();
connect(cmd, &Command::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
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()
{
auto cmd = createGenerateKeyCommand(currentCardType(), currentSerialNumber(), currentCardSlot(), q->window());
disableCurrentWidget();
connect(cmd, &Command::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
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();
auto cmd = new CreateCSRForCardKeyCommand(keyRef, serialNumber, appName(app), q->window());
disableCurrentWidget();
connect(cmd, &CreateCSRForCardKeyCommand::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
void SmartCardsWidget::Private::writeCertificateToCard()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
auto cmd = new CertificateToPIVCardCommand(keyRef, serialNumber);
disableCurrentWidget();
connect(cmd, &CertificateToPIVCardCommand::finished, q, [this]() {
enableCurrentWidget();
});
cmd->setParentWidget(q->window());
cmd->start();
}
void SmartCardsWidget::Private::readCertificateFromCard()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
auto cmd = new ImportCertificateFromPIVCardCommand(keyRef, serialNumber);
disableCurrentWidget();
connect(cmd, &ImportCertificateFromPIVCardCommand::finished, q, [this, keyRef]() {
// this->updateKeyWidgets(keyRef); // this should happen automatically
enableCurrentWidget();
});
cmd->setParentWidget(q->window());
cmd->start();
}
void SmartCardsWidget::Private::writeKeyToCard()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
auto cmd = new KeyToCardCommand(keyRef, serialNumber, PIVCard::AppName);
disableCurrentWidget();
connect(cmd, &KeyToCardCommand::finished, q, [this]() {
enableCurrentWidget();
});
cmd->setParentWidget(q->window());
cmd->start();
}
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/smartcardwidget.cpp b/src/view/smartcardwidget.cpp
index 0ebc6e95c..5a71b0d44 100644
--- a/src/view/smartcardwidget.cpp
+++ b/src/view/smartcardwidget.cpp
@@ -1,260 +1,272 @@
/* 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 <view/cardkeysview.h>
#include <Libkleo/Compliance>
#include <KLocalizedString>
#include <QGridLayout>
#include <QLabel>
#include <QMenu>
#include <QScrollArea>
#include <QToolButton>
#include <QVBoxLayout>
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,
+ };
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 QAction *createProxyAction(QAction *action, QObject *parent)
{
// create a clone of the given action; for each card we use a different
// clone so that the clones can be enabled/disabled individually; the
// triggered signal is forwarded to the original action
Q_ASSERT(action);
auto proxyAction = new QAction{parent};
proxyAction->setObjectName(action->objectName());
proxyAction->setText(action->text());
proxyAction->setToolTip(action->toolTip());
proxyAction->setIcon(action->icon());
QObject::connect(proxyAction, &QAction::triggered, action, &QAction::trigger);
return proxyAction;
}
static void updateCardAction(QAction *action, const Card *card)
{
switch (card->appType()) {
case AppType::NetKeyApp: {
auto netKeyCard = static_cast<const NetKeyCard *>(card);
if (action->objectName() == "card_all_create_openpgp_certificate"_L1) {
action->setEnabled(!netKeyCard->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 (!netKeyCard->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 (!netKeyCard->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(createProxyAction(action, menu));
}
actionsButton->setMenu(menu);
}
for (auto action : actionsButton->menu()->actions()) {
updateCardAction(action, card);
}
}
SmartCardWidget::SmartCardWidget(QWidget *parent)
: QWidget{parent}
{
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);
mContentLayout = new QVBoxLayout{areaWidget};
auto contentLayout = mContentLayout;
auto upperLayout = new QHBoxLayout;
{
// auto gridLayout = new QGridLayout;
mInfoGridLayout = new QGridLayout;
auto gridLayout = mInfoGridLayout;
// 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);
gridLayout->setColumnStretch(gridLayout->columnCount(), 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);
}
SmartCardWidget::~SmartCardWidget() = default;
void SmartCardWidget::setCard(const Card *card)
{
mCard.reset(card->clone());
mCardTypeField->setValue(cardTypeForDisplay(card));
mSerialNumberField->setValue(card->displaySerialNumber());
updateCardActions(mCardActionsButton, card);
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 {};
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 29, 9:21 AM (23 h, 1 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e4/85/a7af0c4b0953ff7f3d1f30d170d5

Event Timeline