diff --git a/src/dialogs/cardinfotab.cpp b/src/dialogs/cardinfotab.cpp index c41bfe7b0..77203a08d 100644 --- a/src/dialogs/cardinfotab.cpp +++ b/src/dialogs/cardinfotab.cpp @@ -1,200 +1,178 @@ // SPDX-FileCopyrightText: 2024 g10 Code GmbH // SPDX-FileContributor: Tobias Fella // SPDX-License-Identifier: GPL-2.0-or-later #include "cardinfotab.h" #include "smartcard/openpgpcard.h" #include "smartcard/readerstatus.h" #include "view/smartcardwidget.h" #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 SmartCard; class CardInfoTab::Private { friend class ::Kleo::CardInfoTab; CardInfoTab *const q; void loadData(); private: GpgME::Key key; TreeWidget *subkeysTree = nullptr; QLabel *placeholderLabel = nullptr; QPushButton *reloadButton = nullptr; - QPushButton *smartCardManagerButton = nullptr; public: Private(CardInfoTab *qq) : q{qq} { auto vLay = new QVBoxLayout(q); vLay->setContentsMargins({}); vLay->setSpacing(0); subkeysTree = new TreeWidget{q}; subkeysTree->setAccessibleName(i18n("Subkeys")); subkeysTree->setAllColumnsShowFocus(false); subkeysTree->setSelectionMode(QAbstractItemView::SingleSelection); subkeysTree->setRootIsDecorated(false); subkeysTree->setHeaderLabels({ i18nc("@title:column", "Keygrip"), i18nc("@title:column", "Token"), i18nc("@title:column", "Type"), i18nc("@title:column", "Serial Number"), i18nc("@title:column", "Owner"), }); vLay->addWidget(subkeysTree); placeholderLabel = new QLabel(i18nc("@info", "Smartcard information is only available for your own certificates.")); placeholderLabel->setVisible(false); placeholderLabel->setAlignment(Qt::AlignHCenter); vLay->addWidget(placeholderLabel); auto separator = new KSeparator(q); vLay->addWidget(separator); auto bbox = new QHBoxLayout; reloadButton = new QPushButton(i18nc("@action:button", "Reload")); bbox->addWidget(reloadButton); - smartCardManagerButton = new QPushButton(i18nc("@action:button", "Open Smartcard manager")); - bbox->addWidget(smartCardManagerButton); - bbox->addStretch(1); vLay->addLayout(bbox); } }; CardInfoTab::CardInfoTab(QWidget *parent) : QWidget{parent} , d{std::make_unique(this)} { connect(d->reloadButton, &QPushButton::clicked, this, []() { ReaderStatus::mutableInstance()->updateStatus(); }); - connect(d->smartCardManagerButton, &QPushButton::clicked, this, [this]() { - SmartCardDialog *dialog = nullptr; - if (d->subkeysTree->selectedItems().size() > 0) { - const auto item = d->subkeysTree->selectedItems()[0]; - const auto appName = item->data(5, Qt::UserRole).toString(); - const auto serialNumber = item->data(1, Qt::DisplayRole).toString(); - dialog = new SmartCardDialog(this, serialNumber, appName); - } else if (d->subkeysTree->topLevelItemCount() > 0) { - const auto item = d->subkeysTree->topLevelItem(0); - const auto appName = item->data(5, Qt::UserRole).toString(); - const auto serialNumber = item->data(1, Qt::DisplayRole).toString(); - dialog = new SmartCardDialog(this, serialNumber, appName); - } else { - dialog = new SmartCardDialog(this); - } - dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->show(); - }); connect(ReaderStatus::instance(), &ReaderStatus::cardAdded, this, [this]() { d->loadData(); }); connect(ReaderStatus::instance(), &ReaderStatus::cardChanged, this, [this]() { d->loadData(); }); connect(ReaderStatus::instance(), &ReaderStatus::cardRemoved, this, [this]() { d->loadData(); }); } CardInfoTab::~CardInfoTab() = default; GpgME::Key CardInfoTab::key() const { return d->key; } void CardInfoTab::Private::loadData() { subkeysTree->clear(); for (const auto &subkey : key.subkeys()) { const auto &cards = KeyCache::instance()->cardsForSubkey(subkey); for (const auto &cardSerial : cards) { std::shared_ptr availableCard; for (const auto &c : SmartCard::ReaderStatus::instance()->getCards()) { if (c->serialNumber() == cardSerial.toStdString()) { availableCard = c; break; } } auto item = new QTreeWidgetItem; item->setData(0, Qt::DisplayRole, QString::fromLatin1(subkey.keyGrip())); item->setData(1, Qt::DisplayRole, cardSerial); if (availableCard) { const QString manufacturer = QString::fromStdString(availableCard->manufacturer()); const bool manufacturerIsUnknown = manufacturer.isEmpty() || manufacturer == QLatin1StringView("unknown"); item->setData(2, Qt::DisplayRole, manufacturerIsUnknown ? i18nc("Placeholder is a version number", "Unknown OpenPGP v%1 card", availableCard->displayAppVersion()) : i18nc("First placeholder is manufacturer, second placeholder is a version number", "%1 OpenPGP v%2 card", manufacturer, availableCard->displayAppVersion())); item->setData(3, Qt::DisplayRole, availableCard->displaySerialNumber()); item->setData(4, Qt::DisplayRole, availableCard->cardHolder()); item->setData(5, Qt::UserRole, QString::fromStdString(availableCard->appName())); } else { item->setData(2, Qt::DisplayRole, i18n("n/a")); item->setData(3, Qt::DisplayRole, i18n("n/a")); item->setData(4, Qt::DisplayRole, i18n("n/a")); } subkeysTree->addTopLevelItem(item); } } for (int i = 0; i < subkeysTree->columnCount(); i++) { subkeysTree->resizeColumnToContents(i); } } void CardInfoTab::setKey(const GpgME::Key &key) { if (!key.hasSecret()) { d->subkeysTree->setVisible(false); d->placeholderLabel->setVisible(true); d->reloadButton->setEnabled(false); return; } d->key = key; d->subkeysTree->header()->resizeSections(QHeaderView::ResizeToContents); d->subkeysTree->restoreColumnLayout(QStringLiteral("CardInfoTab")); d->loadData(); for (int i = 0; i < d->subkeysTree->columnCount(); i++) { d->subkeysTree->resizeColumnToContents(i); } } #include "moc_cardinfotab.cpp" diff --git a/src/view/smartcardwidget.cpp b/src/view/smartcardwidget.cpp index 01d5b56eb..11ab1b029 100644 --- a/src/view/smartcardwidget.cpp +++ b/src/view/smartcardwidget.cpp @@ -1,283 +1,249 @@ /* view/smartcardwidget.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 SPDX-License-Identifier: GPL-2.0-or-later */ #include "smartcardwidget.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 "view/netkeywidget.h" #include "view/p15cardwidget.h" #include "view/pgpcardwidget.h" #include "view/pivcardwidget.h" #include "kleopatra_debug.h" #include -#include -#include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::SmartCard; 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 = QStringList() << i18nc("OpenPGP refers to a smartcard protocol", "OpenPGP v2.0 - v3.3") << i18nc("Gnuk is a cryptographic token for GnuPG", "Gnuk") << i18nc("NetKey refers to a smartcard protocol", "NetKey v3") << 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

") + i18n("Please insert a compatible smartcard.") + QStringLiteral("

"), this)); lay->addSpacing(10); lay->addWidget(new QLabel(QStringLiteral("\t\t") + i18n("Kleopatra currently supports the following card types:") + QStringLiteral("
  • ") + supported.join(QLatin1StringView("
  • ")) + QStringLiteral("
"), this)); lay->addSpacing(10); { auto hbox = new QHBoxLayout; hbox->addStretch(1); mReloadButton = new QPushButton{i18n("Reload"), this}; 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(mReloadButton, &QPushButton::clicked, this, &PlaceHolderWidget::reload); } Q_SIGNALS: void reload(); private: QPushButton *mReloadButton = nullptr; }; } // namespace class SmartCardWidget::Private { friend class ::Kleo::SmartCardWidget; public: - Private(SmartCardWidget *qq, const QString &preferredSerialNumber, const QString &appName); + Private(SmartCardWidget *qq); void cardAddedOrChanged(const std::string &serialNumber, const std::string &appName); void cardRemoved(const std::string &serialNumber, const std::string &appName); private: template void cardAddedOrChanged(const std::string &serialNumber); private: SmartCardWidget *const q; QMap, QPointer> mCardWidgets; PlaceHolderWidget *mPlaceHolderWidget; QStackedWidget *mStack; QTabWidget *mTabWidget; - QString preferredSerialNumber; - QString preferredAppName; }; -SmartCardWidget::Private::Private(SmartCardWidget *qq, const QString &preferredSerialNumber, const QString &preferredAppName) +SmartCardWidget::Private::Private(SmartCardWidget *qq) : q{qq} - , preferredSerialNumber(preferredSerialNumber) - , preferredAppName(preferredAppName) { auto vLay = new QVBoxLayout(q); vLay->addWidget(new QLabel(QStringLiteral("

") + i18n("Smartcard Management") + QStringLiteral("

"))); mStack = new QStackedWidget; vLay->addWidget(mStack); mPlaceHolderWidget = new PlaceHolderWidget; mStack->addWidget(mPlaceHolderWidget); mTabWidget = new QTabWidget; mStack->addWidget(mTabWidget); mStack->setCurrentWidget(mPlaceHolderWidget); connect(mPlaceHolderWidget, &PlaceHolderWidget::reload, q, &SmartCardWidget::reload); - for (const auto &card : ReaderStatus::instance()->getCards()) { - cardAddedOrChanged(card->serialNumber(), card->appName()); - } 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); }); } void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber, const std::string &appName) { if (appName == SmartCard::NetKeyCard::AppName) { cardAddedOrChanged(serialNumber); } else if (appName == SmartCard::OpenPGPCard::AppName) { cardAddedOrChanged(serialNumber); } else if (appName == SmartCard::PIVCard::AppName) { cardAddedOrChanged(serialNumber); } else if (appName == SmartCard::P15Card::AppName) { cardAddedOrChanged(serialNumber); } else { qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:" << "App" << appName.c_str() << "is not supported"; } } namespace { static QString getCardLabel(const std::shared_ptr &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 void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber) { const auto card = ReaderStatus::instance()->getCard(serialNumber); if (!card) { qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:" << "New or changed card" << serialNumber.c_str() << "with app" << C::AppName.c_str() << "not found"; return; } W *cardWidget = dynamic_cast(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()); - if (preferredAppName == QString::fromStdString(C::AppName) && preferredSerialNumber == QString::fromStdString(serialNumber)) { - auto index = mTabWidget->indexOf(cardWidget); - mTabWidget->setCurrentIndex(index); - } } void SmartCardWidget::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); } } -SmartCardWidget::SmartCardWidget(QWidget *parent, const QString &preferredSerialNumber, const QString &preferredAppName) +SmartCardWidget::SmartCardWidget(QWidget *parent) : QWidget{parent} - , d{std::make_unique(this, preferredSerialNumber, preferredAppName)} + , d{std::make_unique(this)} { - d->preferredSerialNumber = preferredSerialNumber; - d->preferredAppName = preferredAppName; } Kleo::SmartCardWidget::~SmartCardWidget() = default; QWidget *getFirstEnabledFocusWidget(QWidget *parent) { for (auto w = parent->nextInFocusChain(); w != parent; w = w->nextInFocusChain()) { if (w->isEnabled() && (w->focusPolicy() != Qt::NoFocus)) { return w; } } return nullptr; } void SmartCardWidget::focusFirstChild(Qt::FocusReason reason) { if (d->mStack->currentWidget() == d->mPlaceHolderWidget) { if (auto w = getFirstEnabledFocusWidget(d->mPlaceHolderWidget)) { w->setFocus(reason); } } else if (auto cardWidget = d->mTabWidget->currentWidget()) { if (auto w = getFirstEnabledFocusWidget(cardWidget)) { w->setFocus(reason); } } } void SmartCardWidget::reload() { ReaderStatus::mutableInstance()->updateStatus(); } -SmartCardDialog::SmartCardDialog(QWidget *parent, const QString &serialNumber, const QString &appName) - : QDialog(parent) -{ - auto layout = new QVBoxLayout(this); - setLayout(layout); - setWindowTitle(i18nc("@title:window", "Manage Smartcards")); - - auto smartCardWidget = new SmartCardWidget(this, serialNumber, appName); - smartCardWidget->layout()->setContentsMargins(0, 0, 0, 0); - layout->addWidget(smartCardWidget); - - layout->addWidget(new KSeparator); - auto bbox = new QDialogButtonBox(this); - layout->addWidget(bbox); - auto closeButton = bbox->addButton(QDialogButtonBox::Close); - connect(closeButton, &QPushButton::clicked, this, &QDialog::close); - resize(820, 600); -} - #include "smartcardwidget.moc" #include "moc_smartcardwidget.cpp" diff --git a/src/view/smartcardwidget.h b/src/view/smartcardwidget.h index b9edb4d95..481a8a089 100644 --- a/src/view/smartcardwidget.h +++ b/src/view/smartcardwidget.h @@ -1,46 +1,38 @@ /* view/smartcardwidget.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 -#include #include #include namespace Kleo { /* SmartCardWidget a generic widget to interact with smartcards */ class SmartCardWidget : public QWidget, public FocusFirstChild { Q_OBJECT public: - explicit SmartCardWidget(QWidget *parent = nullptr, const QString &preferredSerialNumber = {}, const QString &preferredAppName = {}); + explicit SmartCardWidget(QWidget *parent = nullptr); ~SmartCardWidget() override; void focusFirstChild(Qt::FocusReason reason = Qt::OtherFocusReason) override; public Q_SLOTS: void reload(); private: class Private; std::unique_ptr d; }; -class SmartCardDialog : public QDialog -{ - Q_OBJECT -public: - explicit SmartCardDialog(QWidget *parent = nullptr, const QString &serialNumber = {}, const QString &application = {}); -}; - } // namespace Kleo