1 /* view/smartcardwidget.cpp
2
3 This file is part of Kleopatra, the KDE keymanager
4 SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
5 SPDX-FileContributor: Intevation GmbH
6 SPDX-FileCopyrightText: 2020 g10 Code GmbH
7 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include "smartcardwidget.h"
13
14 #include "smartcard/readerstatus.h"
15 #include "smartcard/openpgpcard.h"
16 #include "smartcard/netkeycard.h"
17 #include "smartcard/p15card.h"
18 #include "smartcard/pivcard.h"
19 #include "smartcard/utils.h"
20
21 #include "view/pgpcardwidget.h"
22 #include "view/netkeywidget.h"
23 #include "view/pivcardwidget.h"
24 #include "view/p15cardwidget.h"
25
26 #include "kleopatra_debug.h"
27
28 #include <KLocalizedString>
29
30 #include <QHBoxLayout>
31 #include <QLabel>
32 #include <QPointer>
33 #include <QPushButton>
34 #include <QTabWidget>
35 #include <QVBoxLayout>
36 #include <QStackedWidget>
37
38 using namespace Kleo;
39 using namespace Kleo::SmartCard;
40
41 namespace {
42 class PlaceHolderWidget: public QWidget
43 {
44 Q_OBJECT
45 public:
PlaceHolderWidget(QWidget * parent=nullptr)46 explicit PlaceHolderWidget(QWidget *parent = nullptr)
47 : QWidget{parent}
48 {
49 auto lay = new QVBoxLayout;
50 lay->addStretch(-1);
51
52 const QStringList supported = QStringList() << i18nc("OpenPGP refers to a smartcard protocol", "OpenPGP v2.0 - v3.3")
53 << i18nc("Gnuk is a cryptographic token for GnuPG", "Gnuk")
54 << i18nc("NetKey refers to a smartcard protocol", "NetKey v3")
55 << i18nc("PIV refers to a smartcard protocol", "PIV (requires GnuPG 2.3 or later)")
56 << i18nc("CardOS is a smartcard operating system", "CardOS 5 (various apps)");
57 lay->addWidget(new QLabel(QStringLiteral("\t\t<h3>") +
58 i18n("Please insert a compatible smartcard.") + QStringLiteral("</h3>"), this));
59 lay->addSpacing(10);
60 lay->addWidget(new QLabel(QStringLiteral("\t\t") +
61 i18n("Kleopatra currently supports the following card types:") +
62 QStringLiteral("<ul><li>") + supported.join(QLatin1String("</li><li>")) +
63 QStringLiteral("</li></ul>"), this));
64 lay->addSpacing(10);
65 lay->addWidget(new QLabel(i18n("Refresh the view (F5) to update the smartcard status."), this));
66 lay->addStretch(-1);
67
68 auto hLay = new QHBoxLayout(this);
69 hLay->addStretch(-1);
70 hLay->addLayout(lay);
71 hLay->addStretch(-1);
72 lay->addStretch(-1);
73 }
74 };
75 } // namespace
76
77 class SmartCardWidget::Private
78 {
79 public:
80 Private(SmartCardWidget *qq);
81
82 void cardAddedOrChanged(const std::string &serialNumber, const std::string &appName);
83 void cardRemoved(const std::string &serialNumber, const std::string &appName);
84
85 private:
86 template <typename C, typename W>
87 void cardAddedOrChanged(const std::string &serialNumber);
88
89 private:
90 SmartCardWidget *const q;
91 QMap<std::pair<std::string, std::string>, QPointer<QWidget>> mCardWidgets;
92 PlaceHolderWidget *mPlaceHolderWidget;
93 QStackedWidget *mStack;
94 QTabWidget *mTabWidget;
95 };
96
Private(SmartCardWidget * qq)97 SmartCardWidget::Private::Private(SmartCardWidget *qq)
98 : q{qq}
99 {
100 auto vLay = new QVBoxLayout(q);
101
102 vLay->addWidget(new QLabel(QStringLiteral("<h2>") + i18n("Smartcard Management") +
103 QStringLiteral("</h2>")));
104
105 mStack = new QStackedWidget;
106 vLay->addWidget(mStack);
107
108 mPlaceHolderWidget = new PlaceHolderWidget;
109 mStack->addWidget(mPlaceHolderWidget);
110
111 mTabWidget = new QTabWidget;
112 mStack->addWidget(mTabWidget);
113
114 mStack->setCurrentWidget(mPlaceHolderWidget);
115
116 connect(ReaderStatus::instance(), &ReaderStatus::cardAdded,
__anonb88aeace0202(const std::string &serialNumber, const std::string &appName) 117 q, [this] (const std::string &serialNumber, const std::string &appName) { cardAddedOrChanged(serialNumber, appName); });
118 connect(ReaderStatus::instance(), &ReaderStatus::cardChanged,
__anonb88aeace0302(const std::string &serialNumber, const std::string &appName) 119 q, [this] (const std::string &serialNumber, const std::string &appName) { cardAddedOrChanged(serialNumber, appName); });
120 connect(ReaderStatus::instance(), &ReaderStatus::cardRemoved,
__anonb88aeace0402(const std::string &serialNumber, const std::string &appName) 121 q, [this] (const std::string &serialNumber, const std::string &appName) { cardRemoved(serialNumber, appName); });
122 }
123
cardAddedOrChanged(const std::string & serialNumber,const std::string & appName)124 void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber, const std::string &appName)
125 {
126 if (appName == SmartCard::NetKeyCard::AppName) {
127 cardAddedOrChanged<NetKeyCard, NetKeyWidget>(serialNumber);
128 } else if (appName == SmartCard::OpenPGPCard::AppName) {
129 cardAddedOrChanged<OpenPGPCard, PGPCardWidget>(serialNumber);
130 } else if (appName == SmartCard::PIVCard::AppName) {
131 cardAddedOrChanged<PIVCard, PIVCardWidget>(serialNumber);
132 } else if (appName == SmartCard::P15Card::AppName) {
133 cardAddedOrChanged<P15Card, P15CardWidget>(serialNumber);
134 } else {
135 qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:"
136 << "App" << appName.c_str() << "is not supported";
137 }
138 }
139
140 namespace {
getCardLabel(const std::shared_ptr<Card> & card)141 static QString getCardLabel(const std::shared_ptr<Card> &card) {
142 if (!card->cardHolder().isEmpty()) {
143 return i18nc("@title:tab smartcard application - name of card holder - serial number of smartcard", "%1 - %2 - %3",
144 displayAppName(card->appName()), card->cardHolder(), card->displaySerialNumber());
145 } else {
146 return i18nc("@title:tab smartcard application - serial number of smartcard", "%1 - %2",
147 displayAppName(card->appName()), card->displaySerialNumber());
148 }
149 }
150 }
151
152 template <typename C, typename W>
cardAddedOrChanged(const std::string & serialNumber)153 void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber)
154 {
155 const auto card = ReaderStatus::instance()->getCard<C>(serialNumber);
156 if (!card) {
157 qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:"
158 << "New or changed card" << serialNumber.c_str() << "with app" << C::AppName.c_str() << "not found";
159 return;
160 }
161 W *cardWidget = dynamic_cast<W *>(mCardWidgets.value({serialNumber, C::AppName}).data());
162 if (!cardWidget) {
163 cardWidget = new W;
164 mCardWidgets.insert({serialNumber, C::AppName}, cardWidget);
165 mTabWidget->addTab(cardWidget, getCardLabel(card));
166 if (mCardWidgets.size() == 1) {
167 mStack->setCurrentWidget(mTabWidget);
168 }
169 }
170 cardWidget->setCard(card.get());
171 }
172
cardRemoved(const std::string & serialNumber,const std::string & appName)173 void SmartCardWidget::Private::cardRemoved(const std::string &serialNumber, const std::string &appName)
174 {
175 QWidget * cardWidget = mCardWidgets.take({serialNumber, appName});
176 if (cardWidget) {
177 const int index = mTabWidget->indexOf(cardWidget);
178 if (index != -1) {
179 mTabWidget->removeTab(index);
180 }
181 delete cardWidget;
182 }
183 if (mCardWidgets.empty()) {
184 mStack->setCurrentWidget(mPlaceHolderWidget);
185 }
186 }
187
SmartCardWidget(QWidget * parent)188 SmartCardWidget::SmartCardWidget(QWidget *parent)
189 : QWidget{parent}
190 , d{std::make_unique<Private>(this)}
191 {
192 }
193
194 Kleo::SmartCardWidget::~SmartCardWidget() = default;
195
reload()196 void SmartCardWidget::reload()
197 {
198 ReaderStatus::mutableInstance()->updateStatus();
199 }
200
201 #include "smartcardwidget.moc"
202