Commit 92de59e6 authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

List available smartcard readers

This allows the user to get a list of smartcard readers that have been
detected by GnuPG. The readers are listed with the reader IDs used by
GnuPG to identify certain readers, e.g. for the reader-port option of
scdaemon.

GnuPG-bug-id: 5662
parent 5b170051
Pipeline #89800 passed with stage
in 14 minutes and 39 seconds
......@@ -260,6 +260,7 @@ set(_kleopatra_SRCS
commands/importcertificatefrompivcardcommand.cpp
commands/createopenpgpkeyfromcardkeyscommand.cpp
commands/createcsrforcardkeycommand.cpp
commands/listreaderscommand.cpp
${_kleopatra_uiserver_files}
......
/* commands/listreaderscommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "listreaderscommand.h"
#include "command_p.h"
#include "smartcard/readerstatus.h"
#include <KLocalizedString>
#include <KMessageBox>
using namespace Kleo;
using namespace Kleo::Commands;
class ListReadersCommand::Private : public Command::Private
{
friend class ::Kleo::Commands::ListReadersCommand;
ListReadersCommand *q_func() const
{
return static_cast<ListReadersCommand *>(q);
}
public:
explicit Private(ListReadersCommand *qq, QWidget *parent);
~Private() override;
private:
void start();
};
ListReadersCommand::Private *ListReadersCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ListReadersCommand::Private *ListReadersCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
ListReadersCommand::Private::Private(ListReadersCommand *qq, QWidget *parent)
: Command::Private(qq, parent)
{
}
ListReadersCommand::Private::~Private() = default;
void ListReadersCommand::Private::start()
{
GpgME::Error err;
const auto readers = SmartCard::ReaderStatus::getReaders(err);
QString message;
if (err) {
message = i18nc("@info", "Reading the list of readers failed:") + QLatin1Char{'\n'} +
QString::fromUtf8(err.asString()).toHtmlEscaped();
} else if (readers.empty()) {
message = i18nc("@info", "Available smartcard readers:") + QLatin1String{"<p>"} +
i18nc("@info No smartcard readers have been found", "<em>None</em>") + QLatin1String{"</p>"};
} else {
QStringList l;
std::transform(std::cbegin(readers), std::cend(readers),
std::back_inserter(l),
[](const auto &s) { return QString::fromStdString(s).toHtmlEscaped(); });
message = i18nc("@info", "Available smartcard readers:") +
QLatin1String{"<ul><li>"} +
l.join(QLatin1String{"</li><li>"}) +
QLatin1String{"</li></ul>"};
}
KMessageBox::information(parentWidgetOrView(),
QLatin1String{"<html>"} + message + QLatin1String{"</html>"},
i18nc("@title", "Smartcard Readers"));
finished();
}
ListReadersCommand::ListReadersCommand(QWidget *parent)
: Command(new Private(this, parent))
{
}
ListReadersCommand::~ListReadersCommand() = default;
void ListReadersCommand::doStart()
{
d->start();
}
void ListReadersCommand::doCancel()
{
}
#undef d
#undef q
/* commands/listreaderscommand.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "command.h"
namespace Kleo
{
namespace Commands
{
class ListReadersCommand : public Command
{
Q_OBJECT
public:
explicit ListReadersCommand(QWidget *parent = nullptr);
~ListReadersCommand() override;
private:
void doStart() override;
void doCancel() override;
private:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
};
} // namespace Commands
} // namespace Kleo
<!DOCTYPE gui >
<gui name="kleopatra" version="503" >
<gui name="kleopatra" version="504" >
<MenuBar>
<Menu name="file">
<text>&amp;File</text>
......@@ -72,6 +72,7 @@
<Menu name="settings">
<text>&amp;Settings</text>
<Action name="settings_self_test"/>
<Action name="settings_list_readers"/>
<Action name="configure_groups" append="configure_merge"/>
</Menu>
<Menu name="window" append="settings_merge">
......
......@@ -25,6 +25,7 @@
#include "commands/importcertificatefromfilecommand.h"
#include "commands/decryptverifyfilescommand.h"
#include "commands/signencryptfilescommand.h"
#include "commands/listreaderscommand.h"
#include "conf/groupsconfigdialog.h"
......@@ -281,6 +282,12 @@ public:
ui.stackWidget->resize(ui.padWidget->sizeHint());
}
void listSmartcardReaders()
{
auto command = new ListReadersCommand(q);
command->start();
}
private:
void setupActions();
......@@ -436,6 +443,10 @@ void MainWindow::Private::setupActions()
"settings_self_test", i18n("Perform Self-Test"), QString(),
nullptr, q, SLOT(selfTest()), QString(), false, true
},
{
"settings_list_readers", i18n("List Smartcard Readers"), QString(),
nullptr, q, SLOT(listSmartcardReaders()), QString(), false, true
},
{
"configure_groups", i18n("Configure Groups..."), QString(),
"group", q, SLOT(configureGroups()), QString(), false, true
......
......@@ -54,5 +54,6 @@ private:
Q_PRIVATE_SLOT(d, void showSmartcardView())
Q_PRIVATE_SLOT(d, void forceUpdateCheck())
Q_PRIVATE_SLOT(d, void openCompendium())
Q_PRIVATE_SLOT(d, void listSmartcardReaders())
};
......@@ -24,6 +24,8 @@
#endif
#include "keypairinfo.h"
#include "utils/hex.h"
#include <Libkleo/GnuPG>
#include <Libkleo/FileSystemWatcher>
......@@ -1135,4 +1137,47 @@ Error ReaderStatus::switchCardAndApp(const std::string &serialNumber, const std:
return err;
}
namespace
{
static auto split(const std::string &s, char c)
{
std::vector<std::string> result;
auto start = 0;
auto end = s.find(c, start);
while (end != s.npos) {
result.push_back(s.substr(start, end - start));
start = end + 1;
end = s.find(c, start);
}
result.push_back(s.substr(start));
return result;
}
}
// static
std::vector<std::string> ReaderStatus::getReaders(Error &err)
{
std::vector<std::string> result;
auto c = Context::createForEngine(AssuanEngine, &err);
if (err) {
qCDebug(KLEOPATRA_LOG) << "Creating context for Assuan engine failed:" << err;
return result;
}
auto assuanContext = std::shared_ptr<Context>(c.release());
const std::string command = "SCD GETINFO reader_list";
const auto readersData = gpgagent_data(assuanContext, command.c_str(), err);
if (err) {
return result;
}
const auto readers = hexdecode(readersData);
result = split(readers, '\n');
return result;
}
#include "readerstatus.moc"
......@@ -34,7 +34,7 @@ class ReaderStatus : public QObject
Q_OBJECT
public:
explicit ReaderStatus(QObject *parent = nullptr);
~ReaderStatus();
~ReaderStatus() override;
static const ReaderStatus *instance();
static ReaderStatus *mutableInstance();
......@@ -62,6 +62,10 @@ public:
const std::string &appName, GpgME::Error &err);
static GpgME::Error switchCardAndApp(const std::string &serialNumber, const std::string &appName);
// Returns a list of reader IDs of the form "%04X:%04X:%s:0"
// with USB device vendor ID & 0xffff, USB device product ID & 0xffff, USB device serial number or "X"
static std::vector<std::string> getReaders(GpgME::Error &err);
public Q_SLOTS:
void updateStatus();
void startMonitoring();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment