File indexing completed on 2024-06-16 04:56:17
0001 /* view/smartcardwidget.cpp 0002 0003 This file is part of Kleopatra, the KDE keymanager 0004 SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik 0005 SPDX-FileContributor: Intevation GmbH 0006 SPDX-FileCopyrightText: 2020 g10 Code GmbH 0007 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "smartcardwidget.h" 0013 0014 #include "smartcard/netkeycard.h" 0015 #include "smartcard/openpgpcard.h" 0016 #include "smartcard/p15card.h" 0017 #include "smartcard/pivcard.h" 0018 #include "smartcard/readerstatus.h" 0019 #include "smartcard/utils.h" 0020 0021 #include "view/netkeywidget.h" 0022 #include "view/p15cardwidget.h" 0023 #include "view/pgpcardwidget.h" 0024 #include "view/pivcardwidget.h" 0025 0026 #include "kleopatra_debug.h" 0027 0028 #include <KLocalizedString> 0029 0030 #include <QHBoxLayout> 0031 #include <QLabel> 0032 #include <QPointer> 0033 #include <QPushButton> 0034 #include <QStackedWidget> 0035 #include <QTabWidget> 0036 #include <QVBoxLayout> 0037 0038 using namespace Kleo; 0039 using namespace Kleo::SmartCard; 0040 0041 namespace 0042 { 0043 class PlaceHolderWidget : public QWidget 0044 { 0045 Q_OBJECT 0046 public: 0047 explicit PlaceHolderWidget(QWidget *parent = nullptr) 0048 : QWidget{parent} 0049 { 0050 auto lay = new QVBoxLayout; 0051 lay->addStretch(-1); 0052 0053 const QStringList supported = QStringList() << i18nc("OpenPGP refers to a smartcard protocol", "OpenPGP v2.0 - v3.3") 0054 << i18nc("Gnuk is a cryptographic token for GnuPG", "Gnuk") 0055 << i18nc("NetKey refers to a smartcard protocol", "NetKey v3") 0056 << i18nc("PIV refers to a smartcard protocol", "PIV (requires GnuPG 2.3 or later)") 0057 << i18nc("CardOS is a smartcard operating system", "CardOS 5 (various apps)"); 0058 lay->addWidget(new QLabel(QStringLiteral("\t\t<h3>") + i18n("Please insert a compatible smartcard.") + QStringLiteral("</h3>"), this)); 0059 lay->addSpacing(10); 0060 lay->addWidget(new QLabel(QStringLiteral("\t\t") + i18n("Kleopatra currently supports the following card types:") + QStringLiteral("<ul><li>") 0061 + supported.join(QLatin1StringView("</li><li>")) + QStringLiteral("</li></ul>"), 0062 this)); 0063 lay->addSpacing(10); 0064 { 0065 auto hbox = new QHBoxLayout; 0066 hbox->addStretch(1); 0067 mReloadButton = new QPushButton{i18n("Reload"), this}; 0068 hbox->addWidget(mReloadButton); 0069 hbox->addStretch(1); 0070 lay->addLayout(hbox); 0071 } 0072 lay->addStretch(-1); 0073 0074 auto hLay = new QHBoxLayout(this); 0075 hLay->addStretch(-1); 0076 hLay->addLayout(lay); 0077 hLay->addStretch(-1); 0078 lay->addStretch(-1); 0079 0080 connect(mReloadButton, &QPushButton::clicked, this, &PlaceHolderWidget::reload); 0081 } 0082 0083 Q_SIGNALS: 0084 void reload(); 0085 0086 private: 0087 QPushButton *mReloadButton = nullptr; 0088 }; 0089 } // namespace 0090 0091 class SmartCardWidget::Private 0092 { 0093 friend class ::Kleo::SmartCardWidget; 0094 0095 public: 0096 Private(SmartCardWidget *qq); 0097 0098 void cardAddedOrChanged(const std::string &serialNumber, const std::string &appName); 0099 void cardRemoved(const std::string &serialNumber, const std::string &appName); 0100 0101 private: 0102 template<typename C, typename W> 0103 void cardAddedOrChanged(const std::string &serialNumber); 0104 0105 private: 0106 SmartCardWidget *const q; 0107 QMap<std::pair<std::string, std::string>, QPointer<QWidget>> mCardWidgets; 0108 PlaceHolderWidget *mPlaceHolderWidget; 0109 QStackedWidget *mStack; 0110 QTabWidget *mTabWidget; 0111 }; 0112 0113 SmartCardWidget::Private::Private(SmartCardWidget *qq) 0114 : q{qq} 0115 { 0116 auto vLay = new QVBoxLayout(q); 0117 0118 vLay->addWidget(new QLabel(QStringLiteral("<h2>") + i18n("Smartcard Management") + QStringLiteral("</h2>"))); 0119 0120 mStack = new QStackedWidget; 0121 vLay->addWidget(mStack); 0122 0123 mPlaceHolderWidget = new PlaceHolderWidget; 0124 mStack->addWidget(mPlaceHolderWidget); 0125 0126 mTabWidget = new QTabWidget; 0127 mStack->addWidget(mTabWidget); 0128 0129 mStack->setCurrentWidget(mPlaceHolderWidget); 0130 0131 connect(mPlaceHolderWidget, &PlaceHolderWidget::reload, q, &SmartCardWidget::reload); 0132 connect(ReaderStatus::instance(), &ReaderStatus::cardAdded, q, [this](const std::string &serialNumber, const std::string &appName) { 0133 cardAddedOrChanged(serialNumber, appName); 0134 }); 0135 connect(ReaderStatus::instance(), &ReaderStatus::cardChanged, q, [this](const std::string &serialNumber, const std::string &appName) { 0136 cardAddedOrChanged(serialNumber, appName); 0137 }); 0138 connect(ReaderStatus::instance(), &ReaderStatus::cardRemoved, q, [this](const std::string &serialNumber, const std::string &appName) { 0139 cardRemoved(serialNumber, appName); 0140 }); 0141 } 0142 0143 void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber, const std::string &appName) 0144 { 0145 if (appName == SmartCard::NetKeyCard::AppName) { 0146 cardAddedOrChanged<NetKeyCard, NetKeyWidget>(serialNumber); 0147 } else if (appName == SmartCard::OpenPGPCard::AppName) { 0148 cardAddedOrChanged<OpenPGPCard, PGPCardWidget>(serialNumber); 0149 } else if (appName == SmartCard::PIVCard::AppName) { 0150 cardAddedOrChanged<PIVCard, PIVCardWidget>(serialNumber); 0151 } else if (appName == SmartCard::P15Card::AppName) { 0152 cardAddedOrChanged<P15Card, P15CardWidget>(serialNumber); 0153 } else { 0154 qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:" 0155 << "App" << appName.c_str() << "is not supported"; 0156 } 0157 } 0158 0159 namespace 0160 { 0161 static QString getCardLabel(const std::shared_ptr<Card> &card) 0162 { 0163 if (!card->cardHolder().isEmpty()) { 0164 return i18nc("@title:tab smartcard application - name of card holder - serial number of smartcard", 0165 "%1 - %2 - %3", 0166 displayAppName(card->appName()), 0167 card->cardHolder(), 0168 card->displaySerialNumber()); 0169 } else { 0170 return i18nc("@title:tab smartcard application - serial number of smartcard", "%1 - %2", displayAppName(card->appName()), card->displaySerialNumber()); 0171 } 0172 } 0173 } 0174 0175 template<typename C, typename W> 0176 void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber) 0177 { 0178 const auto card = ReaderStatus::instance()->getCard<C>(serialNumber); 0179 if (!card) { 0180 qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:" 0181 << "New or changed card" << serialNumber.c_str() << "with app" << C::AppName.c_str() << "not found"; 0182 return; 0183 } 0184 W *cardWidget = dynamic_cast<W *>(mCardWidgets.value({serialNumber, C::AppName}).data()); 0185 if (!cardWidget) { 0186 cardWidget = new W; 0187 mCardWidgets.insert({serialNumber, C::AppName}, cardWidget); 0188 mTabWidget->addTab(cardWidget, getCardLabel(card)); 0189 if (mCardWidgets.size() == 1) { 0190 mStack->setCurrentWidget(mTabWidget); 0191 } 0192 } 0193 cardWidget->setCard(card.get()); 0194 } 0195 0196 void SmartCardWidget::Private::cardRemoved(const std::string &serialNumber, const std::string &appName) 0197 { 0198 QWidget *cardWidget = mCardWidgets.take({serialNumber, appName}); 0199 if (cardWidget) { 0200 const int index = mTabWidget->indexOf(cardWidget); 0201 if (index != -1) { 0202 mTabWidget->removeTab(index); 0203 } 0204 delete cardWidget; 0205 } 0206 if (mCardWidgets.empty()) { 0207 mStack->setCurrentWidget(mPlaceHolderWidget); 0208 } 0209 } 0210 0211 SmartCardWidget::SmartCardWidget(QWidget *parent) 0212 : QWidget{parent} 0213 , d{std::make_unique<Private>(this)} 0214 { 0215 } 0216 0217 Kleo::SmartCardWidget::~SmartCardWidget() = default; 0218 0219 QWidget *getFirstEnabledFocusWidget(QWidget *parent) 0220 { 0221 for (auto w = parent->nextInFocusChain(); w != parent; w = w->nextInFocusChain()) { 0222 if (w->isEnabled() && (w->focusPolicy() != Qt::NoFocus)) { 0223 return w; 0224 } 0225 } 0226 return nullptr; 0227 } 0228 0229 void SmartCardWidget::focusFirstChild(Qt::FocusReason reason) 0230 { 0231 if (d->mStack->currentWidget() == d->mPlaceHolderWidget) { 0232 if (auto w = getFirstEnabledFocusWidget(d->mPlaceHolderWidget)) { 0233 w->setFocus(reason); 0234 } 0235 } else if (auto cardWidget = d->mTabWidget->currentWidget()) { 0236 if (auto w = getFirstEnabledFocusWidget(cardWidget)) { 0237 w->setFocus(reason); 0238 } 0239 } 0240 } 0241 0242 void SmartCardWidget::reload() 0243 { 0244 ReaderStatus::mutableInstance()->updateStatus(); 0245 } 0246 0247 #include "smartcardwidget.moc" 0248 0249 #include "moc_smartcardwidget.cpp"