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