Page MenuHome GnuPG

No OneTemporary

diff --git a/src/conf/appearanceconfigpage.cpp b/src/conf/appearanceconfigpage.cpp
index 7568b7c0b..f43f368f1 100644
--- a/src/conf/appearanceconfigpage.cpp
+++ b/src/conf/appearanceconfigpage.cpp
@@ -1,60 +1,59 @@
/* -*- mode: c++; c-basic-offset:4 -*-
conf/appearanceconfigpage.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2004, 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "appearanceconfigpage.h"
#include "appearanceconfigwidget.h"
-#include <KMessageBox>
#include <QVBoxLayout>
using namespace Kleo;
using namespace Kleo::Config;
AppearanceConfigurationPage::AppearanceConfigurationPage(QWidget *parent, const QVariantList &args)
: KCModule(parent, args)
{
QVBoxLayout *lay = new QVBoxLayout(this);
mWidget = new AppearanceConfigWidget(this);
lay->addWidget(mWidget);
connect(mWidget, &AppearanceConfigWidget::changed, this, &Kleo::Config::AppearanceConfigurationPage::markAsChanged);
load();
}
void AppearanceConfigurationPage::load()
{
mWidget->load();
}
void AppearanceConfigurationPage::save()
{
mWidget->save();
}
void AppearanceConfigurationPage::defaults()
{
mWidget->defaults();
}
extern "C"
{
Q_DECL_EXPORT KCModule *create_kleopatra_config_appear(QWidget *parent = nullptr, const QVariantList &args = QVariantList())
{
AppearanceConfigurationPage *page =
new AppearanceConfigurationPage(parent, args);
page->setObjectName(QStringLiteral("kleopatra_config_appear"));
return page;
}
}
diff --git a/src/conf/configuredialog.cpp b/src/conf/configuredialog.cpp
index 910a64b42..63b5bd7aa 100644
--- a/src/conf/configuredialog.cpp
+++ b/src/conf/configuredialog.cpp
@@ -1,72 +1,70 @@
/*
configuredialog.cpp
This file is part of kleopatra
SPDX-FileCopyrightText: 2000 Espen Sand <espen@kde.org>
SPDX-FileCopyrightText: 2001-2002 Marc Mutz <mutz@kde.org>
SPDX-FileCopyrightText: 2004, 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-only
*/
#include "configuredialog.h"
#include <KConfig>
#include <KLocalizedString>
#include <KConfigGroup>
#include <KSharedConfig>
-#include <QIcon>
-#include <QApplication>
#if HAVE_KCMUTILS
# include <KCMultiDialog>
#else
# include "kleopageconfigdialog.h"
#endif
ConfigureDialog::ConfigureDialog(QWidget *parent)
#if HAVE_KCMUTILS
: KCMultiDialog(parent)
#else
: KleoPageConfigDialog(parent)
#endif
{
setFaceType(KPageDialog::List);
setWindowTitle(i18nc("@title:window", "Configure"));
addModule(QStringLiteral("kleopatra_config_dirserv"));
addModule(QStringLiteral("kleopatra_config_appear"));
addModule(QStringLiteral("kleopatra_config_cryptooperations"));
addModule(QStringLiteral("kleopatra_config_smimevalidation"));
addModule(QStringLiteral("kleopatra_config_gnupgsystem"));
// We store the minimum size of the dialog on hide, because otherwise
// the KCMultiDialog starts with the size of the first kcm, not
// the largest one. This way at least after the first showing of
// the largest kcm the size is kept.
const KConfigGroup geometry(KSharedConfig::openConfig(), "Geometry");
const int width = geometry.readEntry("ConfigureDialogWidth", 0);
const int height = geometry.readEntry("ConfigureDialogHeight", 0);
if (width != 0 && height != 0) {
setMinimumSize(width, height);
}
}
void ConfigureDialog::hideEvent(QHideEvent *e)
{
const QSize minSize = minimumSizeHint();
KConfigGroup geometry(KSharedConfig::openConfig(), "Geometry");
geometry.writeEntry("ConfigureDialogWidth", minSize.width());
geometry.writeEntry("ConfigureDialogHeight", minSize.height());
#if HAVE_KCMUTILS
KCMultiDialog::hideEvent(e);
#else
KleoPageConfigDialog::hideEvent(e);
#endif
}
ConfigureDialog::~ConfigureDialog()
{
}
diff --git a/src/conf/dirservconfigpage.cpp b/src/conf/dirservconfigpage.cpp
index b61c45f6a..66615377b 100644
--- a/src/conf/dirservconfigpage.cpp
+++ b/src/conf/dirservconfigpage.cpp
@@ -1,369 +1,369 @@
/* -*- mode: c++; c-basic-offset:4 -*-
conf/dirservconfigpage.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2004, 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "dirservconfigpage.h"
#include <Libkleo/DirectoryServicesWidget>
#include <Libkleo/CryptoConfigModule>
#include <QGpgME/Protocol>
#include <KMessageBox>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <KConfig>
#include <QSpinBox>
#include <QLabel>
-#include <QDateTimeEdit>
#include <QCheckBox>
#include <QLayout>
+#include <QTimeEdit>
using namespace Kleo;
#if 0 // disabled, since it is apparently confusing
// For sync'ing kabldaprc
class KABSynchronizer
{
public:
KABSynchronizer()
: mConfig("kabldaprc")
{
mConfig.setGroup("LDAP");
}
KUrl::List readCurrentList() const
{
KUrl::List lst;
// stolen from kabc/ldapclient.cpp
const uint numHosts = mConfig.readEntry("NumSelectedHosts");
for (uint j = 0; j < numHosts; j++) {
const QString num = QString::number(j);
KUrl url;
url.setProtocol("ldap");
url.setPath("/"); // workaround KUrl parsing bug
const QString host = mConfig.readEntry(QString("SelectedHost") + num).trimmed();
url.setHost(host);
const int port = mConfig.readEntry(QString("SelectedPort") + num);
if (port != 0) {
url.setPort(port);
}
const QString base = mConfig.readEntry(QString("SelectedBase") + num).trimmed();
url.setQuery(base);
const QString bindDN = mConfig.readEntry(QString("SelectedBind") + num).trimmed();
url.setUser(bindDN);
const QString pwdBindDN = mConfig.readEntry(QString("SelectedPwdBind") + num).trimmed();
url.setPass(pwdBindDN);
lst.append(url);
}
return lst;
}
void writeList(const KUrl::List &lst)
{
mConfig.writeEntry("NumSelectedHosts", lst.count());
KUrl::List::const_iterator it = lst.begin();
KUrl::List::const_iterator end = lst.end();
unsigned j = 0;
for (; it != end; ++it, ++j) {
const QString num = QString::number(j);
KUrl url = *it;
Q_ASSERT(url.scheme() == "ldap");
mConfig.writeEntry(QString("SelectedHost") + num, url.host());
mConfig.writeEntry(QString("SelectedPort") + num, url.port());
// KUrl automatically encoded the query (e.g. for spaces inside it),
// so decode it before writing it out
const QString base = KUrl::decode_string(url.query().mid(1));
mConfig.writeEntry(QString("SelectedBase") + num, base);
mConfig.writeEntry(QString("SelectedBind") + num, url.user());
mConfig.writeEntry(QString("SelectedPwdBind") + num, url.pass());
}
mConfig.sync();
}
private:
KConfig mConfig;
};
#endif
static const char s_x509services_componentName[] = "dirmngr";
static const char s_x509services_groupName[] = "LDAP";
static const char s_x509services_entryName[] = "LDAP Server";
static const char s_x509services_new_componentName[] = "gpgsm";
static const char s_x509services_new_groupName[] = "Configuration";
static const char s_x509services_new_entryName[] = "keyserver";
static const char s_pgpservice_componentName[] = "gpg";
static const char s_pgpservice_groupName[] = "Keyserver";
static const char s_pgpservice_entryName[] = "keyserver";
static const char s_timeout_componentName[] = "dirmngr";
static const char s_timeout_groupName[] = "LDAP";
static const char s_timeout_entryName[] = "ldaptimeout";
static const char s_maxitems_componentName[] = "dirmngr";
static const char s_maxitems_groupName[] = "LDAP";
static const char s_maxitems_entryName[] = "max-replies";
#ifdef NOT_USEFUL_CURRENTLY
static const char s_addnewservers_componentName[] = "dirmngr";
static const char s_addnewservers_groupName[] = "LDAP";
static const char s_addnewservers_entryName[] = "add-servers";
#endif
DirectoryServicesConfigurationPage::DirectoryServicesConfigurationPage(QWidget *parent, const QVariantList &args)
: KCModule(parent, args)
{
mConfig = QGpgME::cryptoConfig();
QGridLayout *glay = new QGridLayout(this);
glay->setContentsMargins(0, 0, 0, 0);
int row = 0;
mWidget = new Kleo::DirectoryServicesWidget(this);
if (QLayout *l = mWidget->layout()) {
l->setContentsMargins(0, 0, 0, 0);
}
glay->addWidget(mWidget, row, 0, 1, 3);
connect(mWidget, SIGNAL(changed()), this, SLOT(changed()));
// LDAP timeout
++row;
QLabel *label = new QLabel(i18n("LDAP &timeout (minutes:seconds):"), this);
mTimeout = new QTimeEdit(this);
mTimeout->setDisplayFormat(QStringLiteral("mm:ss"));
connect(mTimeout, SIGNAL(timeChanged(QTime)), this, SLOT(changed()));
label->setBuddy(mTimeout);
glay->addWidget(label, row, 0);
glay->addWidget(mTimeout, row, 1);
// Max number of items returned by queries
++row;
mMaxItemsLabel = new QLabel(i18n("&Maximum number of items returned by query:"), this);
mMaxItems = new QSpinBox(this);
mMaxItems->setMinimum(0);
mMaxItemsLabel->setBuddy(mMaxItems);
connect(mMaxItems, SIGNAL(valueChanged(int)), this, SLOT(changed()));
glay->addWidget(mMaxItemsLabel, row, 0);
glay->addWidget(mMaxItems, row, 1);
#ifdef NOT_USEFUL_CURRENTLY
++row
mAddNewServersCB = new QCheckBox(i18n("Automatically add &new servers discovered in CRL distribution points"), this);
connect(mAddNewServersCB, SIGNAL(clicked()), this, SLOT(changed()));
glay->addWidget(mAddNewServersCB, row, 0, 1, 3);
#endif
glay->setRowStretch(++row, 1);
glay->setColumnStretch(2, 1);
load();
}
static QList<QUrl> string2urls(const QString &str)
{
QList<QUrl> ret;
if (str.isEmpty()) {
return ret;
}
ret << QUrl::fromEncoded(str.toLocal8Bit());
return ret;
}
void DirectoryServicesConfigurationPage::load()
{
mWidget->clear();
// gpgsm/Configuration/keyserver is not provided by older gpgconf versions;
if ((mX509ServicesEntry = configEntry(s_x509services_new_componentName, s_x509services_new_groupName, s_x509services_new_entryName,
QGpgME::CryptoConfigEntry::ArgType_LDAPURL, /*isList=*/true, /*showError=*/false))) {
mWidget->addX509Services(mX509ServicesEntry->urlValueList());
} else if ((mX509ServicesEntry = configEntry(s_x509services_componentName, s_x509services_groupName, s_x509services_entryName,
QGpgME::CryptoConfigEntry::ArgType_LDAPURL, true))) {
mWidget->addX509Services(mX509ServicesEntry->urlValueList());
}
mWidget->setX509ReadOnly(mX509ServicesEntry && mX509ServicesEntry->isReadOnly());
mOpenPGPServiceEntry = configEntry(s_pgpservice_componentName, s_pgpservice_groupName, s_pgpservice_entryName,
QGpgME::CryptoConfigEntry::ArgType_String, false);
if (mOpenPGPServiceEntry) {
mWidget->addOpenPGPServices(string2urls(parseKeyserver(mOpenPGPServiceEntry->stringValue()).url));
}
mWidget->setOpenPGPReadOnly(mOpenPGPServiceEntry && mOpenPGPServiceEntry->isReadOnly());
if (mX509ServicesEntry)
if (mOpenPGPServiceEntry) {
mWidget->setAllowedProtocols(DirectoryServicesWidget::AllProtocols);
} else {
mWidget->setAllowedProtocols(DirectoryServicesWidget::X509Protocol);
}
else if (mOpenPGPServiceEntry) {
mWidget->setAllowedProtocols(DirectoryServicesWidget::OpenPGPProtocol);
} else {
mWidget->setDisabled(true);
}
DirectoryServicesWidget::Protocols readOnlyProtocols;
if (mX509ServicesEntry && mX509ServicesEntry->isReadOnly()) {
readOnlyProtocols = DirectoryServicesWidget::X509Protocol;
}
mTimeoutConfigEntry = configEntry(s_timeout_componentName, s_timeout_groupName, s_timeout_entryName, QGpgME::CryptoConfigEntry::ArgType_UInt, false);
if (mTimeoutConfigEntry) {
QTime time = QTime(0, 0, 0, 0).addSecs(mTimeoutConfigEntry->uintValue());
//qCDebug(KLEOPATRA_LOG) <<"timeout:" << mTimeoutConfigEntry->uintValue() <<" ->" << time;
mTimeout->setTime(time);
}
mMaxItemsConfigEntry = configEntry(s_maxitems_componentName, s_maxitems_groupName, s_maxitems_entryName, QGpgME::CryptoConfigEntry::ArgType_UInt, false);
if (mMaxItemsConfigEntry) {
mMaxItems->blockSignals(true); // KNumInput emits valueChanged from setValue!
mMaxItems->setValue(mMaxItemsConfigEntry->uintValue());
mMaxItems->blockSignals(false);
}
const bool maxItemsEnabled = mMaxItemsConfigEntry && !mMaxItemsConfigEntry->isReadOnly();
mMaxItems->setEnabled(maxItemsEnabled);
mMaxItemsLabel->setEnabled(maxItemsEnabled);
#ifdef NOT_USEFUL_CURRENTLY
mAddNewServersConfigEntry = configEntry(s_addnewservers_componentName, s_addnewservers_groupName, s_addnewservers_entryName, QGpgME::CryptoConfigEntry::ArgType_None, false);
if (mAddNewServersConfigEntry) {
mAddNewServersCB->setChecked(mAddNewServersConfigEntry->boolValue());
}
#endif
}
void DirectoryServicesConfigurationPage::save()
{
if (mX509ServicesEntry) {
mX509ServicesEntry->setURLValueList(mWidget->x509Services());
}
if (mOpenPGPServiceEntry) {
const QList<QUrl> serv = mWidget->openPGPServices();
if (serv.empty()) {
mOpenPGPServiceEntry->setStringValue(QString());
} else {
ParsedKeyserver pks = parseKeyserver(mOpenPGPServiceEntry->stringValue());
pks.url = serv.front().url();
mOpenPGPServiceEntry->setStringValue(assembleKeyserver(pks));
}
}
QTime time(mTimeout->time());
unsigned int timeout = time.minute() * 60 + time.second();
if (mTimeoutConfigEntry && mTimeoutConfigEntry->uintValue() != timeout) {
mTimeoutConfigEntry->setUIntValue(timeout);
}
if (mMaxItemsConfigEntry && mMaxItemsConfigEntry->uintValue() != (uint)mMaxItems->value()) {
mMaxItemsConfigEntry->setUIntValue(mMaxItems->value());
}
#ifdef NOT_USEFUL_CURRENTLY
if (mAddNewServersConfigEntry && mAddNewServersConfigEntry->boolValue() != mAddNewServersCB->isChecked()) {
mAddNewServersConfigEntry->setBoolValue(mAddNewServersCB->isChecked());
}
#endif
mConfig->sync(true);
#if 0
// Also write the LDAP URLs to kabldaprc so that they are used by kaddressbook
KABSynchronizer sync;
const KUrl::List toAdd = mWidget->urlList();
KUrl::List currentList = sync.readCurrentList();
KUrl::List::const_iterator it = toAdd.begin();
KUrl::List::const_iterator end = toAdd.end();
for (; it != end; ++it) {
// check if the URL is already in currentList
if (currentList.find(*it) == currentList.end())
// if not, add it
{
currentList.append(*it);
}
}
sync.writeList(currentList);
#endif
}
void DirectoryServicesConfigurationPage::defaults()
{
// these guys don't have a default, to clear them:
if (mX509ServicesEntry) {
mX509ServicesEntry->setURLValueList(QList<QUrl>());
}
if (mOpenPGPServiceEntry) {
mOpenPGPServiceEntry->setStringValue(QString());
}
// these presumably have a default, use that one:
if (mTimeoutConfigEntry) {
mTimeoutConfigEntry->resetToDefault();
}
if (mMaxItemsConfigEntry) {
mMaxItemsConfigEntry->resetToDefault();
}
#ifdef NOT_USEFUL_CURRENTLY
if (mAddNewServersConfigEntry) {
mAddNewServersConfigEntry->resetToDefault();
}
#endif
load();
}
extern "C"
{
Q_DECL_EXPORT KCModule *create_kleopatra_config_dirserv(QWidget *parent = nullptr, const QVariantList &args = QVariantList())
{
DirectoryServicesConfigurationPage *page =
new DirectoryServicesConfigurationPage(parent, args);
page->setObjectName(QStringLiteral("kleopatra_config_dirserv"));
return page;
}
}
// Find config entry for ldap servers. Implements runtime checks on the configuration option.
QGpgME::CryptoConfigEntry *DirectoryServicesConfigurationPage::configEntry(const char *componentName,
const char *groupName,
const char *entryName,
QGpgME::CryptoConfigEntry::ArgType argType,
bool isList,
bool showError)
{
QGpgME::CryptoConfigEntry *entry = mConfig->entry(QLatin1String(componentName), QLatin1String(groupName), QLatin1String(entryName));
if (!entry) {
if (showError) {
KMessageBox::error(this, i18n("Backend error: gpgconf does not seem to know the entry for %1/%2/%3", QLatin1String(componentName), QLatin1String(groupName), QLatin1String(entryName)));
}
return nullptr;
}
if (entry->argType() != argType || entry->isList() != isList) {
if (showError) {
KMessageBox::error(this, i18n("Backend error: gpgconf has wrong type for %1/%2/%3: %4 %5", QLatin1String(componentName), QLatin1String(groupName), QLatin1String(entryName), entry->argType(), entry->isList()));
}
return nullptr;
}
return entry;
}
diff --git a/src/conf/smimevalidationconfigurationpage.cpp b/src/conf/smimevalidationconfigurationpage.cpp
index 14692470e..13da2bb01 100644
--- a/src/conf/smimevalidationconfigurationpage.cpp
+++ b/src/conf/smimevalidationconfigurationpage.cpp
@@ -1,55 +1,54 @@
/* -*- mode: c++; c-basic-offset:4 -*-
conf/smimevalidationconfigurationpage.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "smimevalidationconfigurationpage.h"
#include "smimevalidationconfigurationwidget.h"
#include <QVBoxLayout>
-#include <KMessageBox>
using namespace Kleo::Config;
SMimeValidationConfigurationPage::SMimeValidationConfigurationPage(QWidget *parent, const QVariantList &args)
: KCModule(parent, args)
{
QVBoxLayout *lay = new QVBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
mWidget = new SMimeValidationConfigurationWidget(this);
lay->addWidget(mWidget);
connect(mWidget, &SMimeValidationConfigurationWidget::changed, this, &Kleo::Config::SMimeValidationConfigurationPage::markAsChanged);
load();
}
void SMimeValidationConfigurationPage::load()
{
mWidget->load();
}
void SMimeValidationConfigurationPage::save()
{
mWidget->save();
}
void SMimeValidationConfigurationPage::defaults()
{
mWidget->defaults();
}
extern "C" Q_DECL_EXPORT KCModule *create_kleopatra_config_smimevalidation(QWidget *parent, const QVariantList &args)
{
SMimeValidationConfigurationPage *page =
new SMimeValidationConfigurationPage(parent, args);
page->setObjectName(QStringLiteral("kleopatra_config_smimevalidation"));
return page;
}
diff --git a/src/crypto/gui/certificatelineedit.cpp b/src/crypto/gui/certificatelineedit.cpp
index 9942c2972..ebf21b416 100644
--- a/src/crypto/gui/certificatelineedit.cpp
+++ b/src/crypto/gui/certificatelineedit.cpp
@@ -1,248 +1,247 @@
/* crypto/gui/certificatelineedit.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "certificatelineedit.h"
#include <QCompleter>
-#include <QFontMetrics>
#include <QPushButton>
#include <QAction>
#include <QSignalBlocker>
#include "kleopatra_debug.h"
#include "commands/detailscommand.h"
#include <Libkleo/KeyCache>
#include <Libkleo/KeyFilter>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/Formatting>
#include <KLocalizedString>
#include <KIconLoader>
#include <gpgme++/key.h>
#include <QGpgME/KeyForMailboxJob>
#include <QGpgME/Protocol>
using namespace Kleo;
using namespace Kleo::Dialogs;
using namespace GpgME;
Q_DECLARE_METATYPE(GpgME::Key)
static QStringList s_lookedUpKeys;
namespace
{
class ProxyModel : public KeyListSortFilterProxyModel
{
Q_OBJECT
public:
ProxyModel(QObject *parent = nullptr)
: KeyListSortFilterProxyModel(parent)
{
}
QVariant data(const QModelIndex &index, int role) const override
{
if (!index.isValid()) {
return QVariant();
}
switch (role) {
case Qt::DecorationRole: {
const auto key = KeyListSortFilterProxyModel::data(index,
Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
Q_ASSERT(!key.isNull());
if (key.isNull()) {
return QVariant();
}
return Kleo::Formatting::iconForUid(key.userID(0));
}
default:
return KeyListSortFilterProxyModel::data(index, role);
}
}
};
} // namespace
CertificateLineEdit::CertificateLineEdit(AbstractKeyListModel *model,
QWidget *parent,
KeyFilter *filter)
: QLineEdit(parent),
mFilterModel(new KeyListSortFilterProxyModel(this)),
mFilter(std::shared_ptr<KeyFilter>(filter)),
mLineAction(new QAction(this))
{
setPlaceholderText(i18n("Please enter a name or email address..."));
setClearButtonEnabled(true);
addAction(mLineAction, QLineEdit::LeadingPosition);
auto *completer = new QCompleter(this);
auto *completeFilterModel = new ProxyModel(completer);
completeFilterModel->setKeyFilter(mFilter);
completeFilterModel->setSourceModel(model);
completer->setModel(completeFilterModel);
completer->setCompletionColumn(KeyListModelInterface::Summary);
completer->setFilterMode(Qt::MatchContains);
completer->setCaseSensitivity(Qt::CaseInsensitive);
setCompleter(completer);
mFilterModel->setSourceModel(model);
mFilterModel->setFilterKeyColumn(KeyListModelInterface::Summary);
if (filter) {
mFilterModel->setKeyFilter(mFilter);
}
connect(KeyCache::instance().get(), &Kleo::KeyCache::keyListingDone,
this, &CertificateLineEdit::updateKey);
connect(this, &QLineEdit::editingFinished,
this, &CertificateLineEdit::updateKey);
connect(this, &QLineEdit::textChanged,
this, &CertificateLineEdit::editChanged);
connect(mLineAction, &QAction::triggered,
this, &CertificateLineEdit::dialogRequested);
connect(this, &QLineEdit::editingFinished, this,
&CertificateLineEdit::checkLocate);
updateKey();
/* Take ownership of the model to prevent double deletion when the
* filter models are deleted */
model->setParent(parent ? parent : this);
}
void CertificateLineEdit::editChanged()
{
updateKey();
if (!mEditStarted) {
Q_EMIT editingStarted();
mEditStarted = true;
}
mEditFinished = false;
}
void CertificateLineEdit::checkLocate()
{
if (!key().isNull()) {
// Already have a key
return;
}
// Only check once per mailbox
const auto mailText = text();
if (s_lookedUpKeys.contains(mailText)) {
return;
}
s_lookedUpKeys << mailText;
qCDebug(KLEOPATRA_LOG) << "Lookup job for" << mailText;
QGpgME::KeyForMailboxJob *job = QGpgME::openpgp()->keyForMailboxJob();
job->start(mailText);
}
void CertificateLineEdit::updateKey()
{
const auto mailText = text();
auto newKey = Key();
if (mailText.isEmpty()) {
mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("resource-group-new")));
mLineAction->setToolTip(i18n("Open selection dialog."));
} else {
mFilterModel->setFilterFixedString(mailText);
if (mFilterModel->rowCount() > 1) {
if (mEditFinished) {
mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("question")).pixmap(KIconLoader::SizeSmallMedium));
mLineAction->setToolTip(i18n("Multiple certificates"));
}
} else if (mFilterModel->rowCount() == 1) {
newKey = mFilterModel->data(mFilterModel->index(0, 0), KeyListModelInterface::KeyRole).value<Key>();
mLineAction->setToolTip(Formatting::validity(newKey.userID(0)) +
QStringLiteral("<br/>Click here for details."));
/* FIXME: This needs to be solved by a multiple UID supporting model */
mLineAction->setIcon(Formatting::iconForUid(newKey.userID(0)));
} else {
mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("emblem-error")));
mLineAction->setToolTip(i18n("No matching certificates found.<br/>Click here to import a certificate."));
}
}
mKey = newKey;
if (mKey.isNull()) {
setToolTip(QString());
} else {
setToolTip(Formatting::toolTip(newKey, Formatting::ToolTipOption::AllOptions));
}
Q_EMIT keyChanged();
if (mailText.isEmpty()) {
Q_EMIT wantsRemoval(this);
}
}
Key CertificateLineEdit::key() const
{
if (isEnabled()) {
return mKey;
} else {
return Key();
}
}
void CertificateLineEdit::dialogRequested()
{
if (!mKey.isNull()) {
auto cmd = new Commands::DetailsCommand(mKey, nullptr);
cmd->start();
return;
}
CertificateSelectionDialog *const dlg = new CertificateSelectionDialog(this);
dlg->setKeyFilter(mFilter);
if (dlg->exec()) {
const std::vector<Key> keys = dlg->selectedCertificates();
if (!keys.size()) {
return;
}
for (unsigned int i = 0; i < keys.size(); i++) {
if (!i) {
setKey(keys[i]);
} else {
Q_EMIT addRequested(keys[i]);
}
}
}
delete dlg;
updateKey();
}
void CertificateLineEdit::setKey(const Key &k)
{
QSignalBlocker blocky(this);
qCDebug(KLEOPATRA_LOG) << "Setting Key. " << Formatting::summaryLine(k);
setText(Formatting::summaryLine(k));
updateKey();
}
bool CertificateLineEdit::isEmpty() const
{
return text().isEmpty();
}
void CertificateLineEdit::setKeyFilter(const std::shared_ptr<KeyFilter> &filter)
{
mFilter = filter;
mFilterModel->setKeyFilter(filter);
}
#include "certificatelineedit.moc"
diff --git a/src/dialogs/pivcardapplicationadministrationkeyinputdialog.cpp b/src/dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
index ee6594e48..785b45cd2 100644
--- a/src/dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
+++ b/src/dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
@@ -1,103 +1,102 @@
/* dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "pivcardapplicationadministrationkeyinputdialog.h"
#include <QDialogButtonBox>
#include <QFontDatabase>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
-#include <KLocalizedString>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Dialogs;
class PIVCardApplicationAdministrationKeyInputDialog::Private
{
friend class ::Kleo::Dialogs::PIVCardApplicationAdministrationKeyInputDialog;
public:
explicit Private(PIVCardApplicationAdministrationKeyInputDialog *qq);
void checkAcceptable();
PIVCardApplicationAdministrationKeyInputDialog *const q;
QLabel *mLabel;
QLineEdit *mHexEncodedAdminKeyEdit;
QPushButton *mOkButton;
QByteArray adminKey;
};
PIVCardApplicationAdministrationKeyInputDialog::Private::Private(PIVCardApplicationAdministrationKeyInputDialog *qq): q(qq),
mLabel(new QLabel(qq)),
mHexEncodedAdminKeyEdit(new QLineEdit(qq)),
mOkButton(nullptr)
{
auto *vBox = new QVBoxLayout(q);
{
mLabel->setWordWrap(true);
vBox->addWidget(mLabel);
}
{
const QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
mHexEncodedAdminKeyEdit->setInputMask(QStringLiteral("HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH;_"));
mHexEncodedAdminKeyEdit->setFont(fixedFont);
mHexEncodedAdminKeyEdit->setMinimumWidth(QFontMetrics(fixedFont).horizontalAdvance(QStringLiteral("HH:")) * 24);
connect(mHexEncodedAdminKeyEdit, &QLineEdit::textChanged, q, [this] () { checkAcceptable(); });
vBox->addWidget(mHexEncodedAdminKeyEdit);
}
auto bbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, qq);
mOkButton = bbox->button(QDialogButtonBox::Ok);
mOkButton->setDefault(true);
mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return);
connect(bbox, &QDialogButtonBox::rejected, q, [this]() {q->reject();});
connect(bbox, &QDialogButtonBox::accepted, q, [this]() {q->accept();});
vBox->addWidget(bbox);
q->setMinimumWidth(400);
checkAcceptable();
}
void PIVCardApplicationAdministrationKeyInputDialog::Private::checkAcceptable()
{
mOkButton->setEnabled(mHexEncodedAdminKeyEdit->hasAcceptableInput());
}
PIVCardApplicationAdministrationKeyInputDialog::PIVCardApplicationAdministrationKeyInputDialog(QWidget *parent) : QDialog(parent),
d(new Private(this))
{
}
void PIVCardApplicationAdministrationKeyInputDialog::setLabelText(const QString& text)
{
d->mLabel->setText(text);
}
QString PIVCardApplicationAdministrationKeyInputDialog::labelText() const
{
return d->mLabel->text();
}
QByteArray PIVCardApplicationAdministrationKeyInputDialog::adminKey() const
{
return QByteArray::fromHex(d->mHexEncodedAdminKeyEdit->text().toUtf8());
}
diff --git a/src/kwatchgnupg/main.cpp b/src/kwatchgnupg/main.cpp
index d951ba250..49700b4d9 100644
--- a/src/kwatchgnupg/main.cpp
+++ b/src/kwatchgnupg/main.cpp
@@ -1,49 +1,48 @@
/*
main.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2001, 2002, 2004 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "aboutdata.h"
#include "kwatchgnupgmainwin.h"
#include <Kdelibs4ConfigMigrator>
#include "utils/kuniqueservice.h"
-#include <KMessageBox>
#include <KLocalizedString>
#include <KCrash>
#include "kwatchgnupg_debug.h"
#include <QCommandLineParser>
#include <QApplication>
int main(int argc, char **argv)
{
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
QApplication app(argc, argv);
KCrash::initialize();
Kdelibs4ConfigMigrator migrate(QStringLiteral("kwatchgnupg"));
migrate.setConfigFiles(QStringList() << QStringLiteral("kwatchgnupgrc"));
migrate.setUiFiles(QStringList() << QStringLiteral("kwatchgnupgui.rc"));
migrate.migrate();
KLocalizedString::setApplicationDomain("kwatchgnupg");
AboutData aboutData;
KAboutData::setApplicationData(aboutData);
QCommandLineParser parser;
aboutData.setupCommandLine(&parser);
parser.process(app);
aboutData.processCommandLine(&parser);
KUniqueService service;
KWatchGnuPGMainWindow *mMainWin = new KWatchGnuPGMainWindow();
mMainWin->show();
return app.exec();
}
diff --git a/src/main.cpp b/src/main.cpp
index 0bf7aa95b..0d116e7e0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,243 +1,242 @@
/*
main.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2001, 2002, 2004, 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "aboutdata.h"
#include "kleopatraapplication.h"
#include "mainwindow.h"
#include <Kdelibs4ConfigMigrator>
#include <commands/reloadkeyscommand.h>
#include <commands/selftestcommand.h>
#include <Libkleo/GnuPG>
#include <utils/archivedefinition.h>
#include "utils/kuniqueservice.h"
#include <uiserver/uiserver.h>
#include <uiserver/assuancommand.h>
#include <uiserver/echocommand.h>
#include <uiserver/decryptcommand.h>
#include <uiserver/verifycommand.h>
#include <uiserver/decryptverifyfilescommand.h>
#include <uiserver/decryptfilescommand.h>
#include <uiserver/verifyfilescommand.h>
#include <uiserver/prepencryptcommand.h>
#include <uiserver/prepsigncommand.h>
#include <uiserver/encryptcommand.h>
#include <uiserver/signcommand.h>
#include <uiserver/signencryptfilescommand.h>
#include <uiserver/selectcertificatecommand.h>
#include <uiserver/importfilescommand.h>
#include <uiserver/createchecksumscommand.h>
#include <uiserver/verifychecksumscommand.h>
#include <Libkleo/ChecksumDefinition>
#include "kleopatra_debug.h"
#include "kleopatra_options.h"
#include <KLocalizedString>
-#include <KIconLoader>
#include <KMessageBox>
#include <KCrash>
#include <QTextDocument> // for Qt::escape
#include <QMessageBox>
#include <QTimer>
#include <QTime>
#include <QEventLoop>
#include <QThreadPool>
#include <QElapsedTimer>
#include <gpgme++/global.h>
#include <gpgme++/error.h>
#include <memory>
#include <iostream>
#include <QCommandLineParser>
static bool selfCheck()
{
Kleo::Commands::SelfTestCommand cmd(nullptr);
cmd.setAutoDelete(false);
cmd.setAutomaticMode(true);
QEventLoop loop;
QObject::connect(&cmd, &Kleo::Commands::SelfTestCommand::finished, &loop, &QEventLoop::quit);
QTimer::singleShot(0, &cmd, &Kleo::Command::start); // start() may Q_EMIT finished()...
loop.exec();
if (cmd.isCanceled()) {
return false;
} else {
return true;
}
}
static void fillKeyCache(Kleo::UiServer *server)
{
Kleo::ReloadKeysCommand *cmd = new Kleo::ReloadKeysCommand(nullptr);
QObject::connect(cmd, SIGNAL(finished()), server, SLOT(enableCryptoCommands()));
cmd->start();
}
int main(int argc, char **argv)
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
KleopatraApplication app(argc, argv);
KCrash::initialize();
QElapsedTimer timer;
timer.start();
KLocalizedString::setApplicationDomain("kleopatra");
KUniqueService service;
QObject::connect(&service, &KUniqueService::activateRequested,
&app, &KleopatraApplication::slotActivateRequested);
QObject::connect(&app, &KleopatraApplication::setExitValue,
&service, [&service](int i) {
service.setExitValue(i);
});
// Delay init after KUniqueservice call as this might already
// have terminated us and so we can avoid overhead (e.g. keycache
// setup / systray icon).
qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Service created";
app.init();
qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Application initialized";
AboutData aboutData;
KAboutData::setApplicationData(aboutData);
QCommandLineParser parser;
aboutData.setupCommandLine(&parser);
kleopatra_options(&parser);
parser.process(QApplication::arguments());
aboutData.processCommandLine(&parser);
Kdelibs4ConfigMigrator migrate(QStringLiteral("kleopatra"));
migrate.setConfigFiles(QStringList() << QStringLiteral("kleopatrarc")
<< QStringLiteral("libkleopatrarc"));
migrate.setUiFiles(QStringList() << QStringLiteral("kleopatra.rc"));
migrate.migrate();
qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Application created";
// Initialize GpgME
const GpgME::Error gpgmeInitError = GpgME::initializeLibrary(0);
{
const unsigned int threads = QThreadPool::globalInstance()->maxThreadCount();
QThreadPool::globalInstance()->setMaxThreadCount(qMax(2U, threads));
}
if (gpgmeInitError) {
KMessageBox::sorry(nullptr, xi18nc("@info",
"<para>The version of the <application>GpgME</application> library you are running against "
"is older than the one that the <application>GpgME++</application> library was built against.</para>"
"<para><application>Kleopatra</application> will not function in this setting.</para>"
"<para>Please ask your administrator for help in resolving this issue.</para>"),
i18nc("@title", "GpgME Too Old"));
return EXIT_FAILURE;
}
qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: GPGME Initialized";
Kleo::ChecksumDefinition::setInstallPath(Kleo::gpg4winInstallPath());
Kleo::ArchiveDefinition::setInstallPath(Kleo::gnupgInstallPath());
int rc;
Kleo::UiServer server(parser.value(QStringLiteral("uiserver-socket")));
try {
qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: UiServer created";
QObject::connect(&server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow);
QObject::connect(&server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog);
#define REGISTER( Command ) server.registerCommandFactory( std::shared_ptr<Kleo::AssuanCommandFactory>( new Kleo::GenericAssuanCommandFactory<Kleo::Command> ) )
REGISTER(CreateChecksumsCommand);
REGISTER(DecryptCommand);
REGISTER(DecryptFilesCommand);
REGISTER(DecryptVerifyFilesCommand);
REGISTER(EchoCommand);
REGISTER(EncryptCommand);
REGISTER(EncryptFilesCommand);
REGISTER(EncryptSignFilesCommand);
REGISTER(ImportFilesCommand);
REGISTER(PrepEncryptCommand);
REGISTER(PrepSignCommand);
REGISTER(SelectCertificateCommand);
REGISTER(SignCommand);
REGISTER(SignEncryptFilesCommand);
REGISTER(SignFilesCommand);
REGISTER(VerifyChecksumsCommand);
REGISTER(VerifyCommand);
REGISTER(VerifyFilesCommand);
#undef REGISTER
server.start();
qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: UiServer started";
} catch (const std::exception &e) {
qCDebug(KLEOPATRA_LOG) << "Failed to start UI Server: " << e.what();
#ifdef Q_OS_WIN
// Once there actually is a plugin for other systems then Windows this
// error should probably be shown, too. But currently only Windows users need
// to care.
QMessageBox::information(nullptr, i18n("GPG UI Server Error"),
i18n("<qt>The Kleopatra GPG UI Server Module could not be initialized.<br/>"
"The error given was: <b>%1</b><br/>"
"You can use Kleopatra as a certificate manager, but cryptographic plugins that "
"rely on a GPG UI Server being present might not work correctly, or at all.</qt>",
QString::fromUtf8(e.what()).toHtmlEscaped()));
#endif
}
const bool daemon = parser.isSet(QStringLiteral("daemon"));
if (!daemon && app.isSessionRestored()) {
app.restoreMainWindow();
}
if (!selfCheck()) {
return EXIT_FAILURE;
}
qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: SelfCheck completed";
fillKeyCache(&server);
#ifndef QT_NO_SYSTEMTRAYICON
app.startMonitoringSmartCard();
#endif
app.setIgnoreNewInstance(false);
if (!daemon) {
const QString err = app.newInstance(parser);
if (!err.isEmpty()) {
std::cerr << i18n("Invalid arguments: %1", err).toLocal8Bit().constData() << "\n";
return EXIT_FAILURE;
}
qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: new instance created";
}
rc = app.exec();
app.setIgnoreNewInstance(true);
QObject::disconnect(&server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow);
QObject::disconnect(&server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog);
server.stop();
server.waitForStopped();
return rc;
}
diff --git a/src/smartcard/readerstatus.cpp b/src/smartcard/readerstatus.cpp
index e8caa26da..77891ecce 100644
--- a/src/smartcard/readerstatus.cpp
+++ b/src/smartcard/readerstatus.cpp
@@ -1,1083 +1,1082 @@
/* -*- mode: c++; c-basic-offset:4 -*-
smartcard/readerstatus.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E01 // 1.14.1
# define QGPGME_HAS_DEBUG
# define GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER
#endif
#include "readerstatus.h"
#ifdef GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER
# include "deviceinfowatcher.h"
#endif
#include "keypairinfo.h"
#include <Libkleo/GnuPG>
#include <Libkleo/FileSystemWatcher>
#include <Libkleo/Stl_Util>
#ifdef QGPGME_HAS_DEBUG
# include <QGpgME/Debug>
#endif
#include <gpgme++/context.h>
#include <gpgme++/defaultassuantransaction.h>
#include <gpgme++/key.h>
#include <gpg-error.h>
#include "openpgpcard.h"
#include "netkeycard.h"
#include "pivcard.h"
#include <QStringList>
-#include <QFileInfo>
#include <QMutex>
#include <QWaitCondition>
#include <QThread>
#include <QPointer>
#include <QRegularExpression>
#include <set>
#include <list>
#include <algorithm>
#include <iterator>
#include <utility>
#include <cstdlib>
#include "utils/kdtoolsglobal.h"
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::SmartCard;
using namespace GpgME;
static ReaderStatus *self = nullptr;
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
static const char *flags[] = {
"NOCARD",
"PRESENT",
"ACTIVE",
"USABLE",
};
static_assert(sizeof flags / sizeof * flags == Card::_NumScdStates, "");
static const char *prettyFlags[] = {
"NoCard",
"CardPresent",
"CardActive",
"CardUsable",
"CardError",
};
static_assert(sizeof prettyFlags / sizeof * prettyFlags == Card::NumStates, "");
Q_DECLARE_METATYPE(GpgME::Error)
namespace
{
static bool gpgHasMultiCardMultiAppSupport()
{
return !(engineInfo(GpgME::GpgEngine).engineVersion() < "2.3.0");
}
static QDebug operator<<(QDebug s, const std::string &string)
{
return s << QString::fromStdString(string);
}
#ifndef QGPGME_HAS_DEBUG
static QDebug operator<<(QDebug s, const GpgME::Error &err)
{
const bool oldSetting = s.autoInsertSpaces();
s.nospace() << err.asString() << " (code: " << err.code() << ", source: " << err.source() << ")";
s.setAutoInsertSpaces(oldSetting);
return s.maybeSpace();
}
#endif
static QDebug operator<<(QDebug s, const std::vector< std::pair<std::string, std::string> > &v)
{
typedef std::pair<std::string, std::string> pair;
s << '(';
for (const pair &p : v) {
s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << '\n';
}
return s << ')';
}
struct CardApp {
std::string serialNumber;
std::string appName;
};
static void logUnexpectedStatusLine(const std::pair<std::string, std::string> &line,
const std::string &prefix = std::string(),
const std::string &command = std::string())
{
qCWarning(KLEOPATRA_LOG) << (!prefix.empty() ? QString::fromStdString(prefix + ": ") : QString())
<< "Unexpected status line"
<< (!command.empty() ? QString::fromStdString(" on " + command + ":") : QLatin1String(":"))
<< QString::fromStdString(line.first)
<< QString::fromStdString(line.second);
}
static int parse_app_version(const std::string &s)
{
return std::atoi(s.c_str());
}
static Card::PinState parse_pin_state(const QString &s)
{
bool ok;
int i = s.toInt(&ok);
if (!ok) {
qCDebug(KLEOPATRA_LOG) << "Failed to parse pin state" << s;
return Card::UnknownPinState;
}
switch (i) {
case -4: return Card::NullPin;
case -3: return Card::PinBlocked;
case -2: return Card::NoPin;
case -1: return Card::UnknownPinState;
default:
if (i < 0) {
return Card::UnknownPinState;
} else {
return Card::PinOk;
}
}
}
template<typename T>
static std::unique_ptr<T> gpgagent_transact(std::shared_ptr<Context> &gpgAgent, const char *command, std::unique_ptr<T> transaction, Error &err)
{
qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << ")";
err = gpgAgent->assuanTransact(command, std::move(transaction));
if (err.code()) {
qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << "): Error:" << err;
if (err.code() >= GPG_ERR_ASS_GENERAL && err.code() <= GPG_ERR_ASS_UNKNOWN_INQUIRE) {
qCDebug(KLEOPATRA_LOG) << "Assuan problem, killing context";
gpgAgent.reset();
}
return std::unique_ptr<T>();
}
std::unique_ptr<AssuanTransaction> t = gpgAgent->takeLastAssuanTransaction();
return std::unique_ptr<T>(dynamic_cast<T*>(t.release()));
}
static std::unique_ptr<DefaultAssuanTransaction> gpgagent_default_transact(std::shared_ptr<Context> &gpgAgent, const char *command, Error &err)
{
return gpgagent_transact(gpgAgent, command, std::unique_ptr<DefaultAssuanTransaction>(new DefaultAssuanTransaction), err);
}
static const std::string gpgagent_data(std::shared_ptr<Context> gpgAgent, const char *command, Error &err)
{
const std::unique_ptr<DefaultAssuanTransaction> t = gpgagent_default_transact(gpgAgent, command, err);
if (t.get()) {
qCDebug(KLEOPATRA_LOG) << "gpgagent_data(" << command << "): got" << QString::fromStdString(t->data());
return t->data();
} else {
qCDebug(KLEOPATRA_LOG) << "gpgagent_data(" << command << "): t == NULL";
return std::string();
}
}
static const std::vector< std::pair<std::string, std::string> > gpgagent_statuslines(std::shared_ptr<Context> gpgAgent, const char *what, Error &err)
{
const std::unique_ptr<DefaultAssuanTransaction> t = gpgagent_default_transact(gpgAgent, what, err);
if (t.get()) {
qCDebug(KLEOPATRA_LOG) << "agent_getattr_status(" << what << "): got" << t->statusLines();
return t->statusLines();
} else {
qCDebug(KLEOPATRA_LOG) << "agent_getattr_status(" << what << "): t == NULL";
return std::vector<std::pair<std::string, std::string> >();
}
}
static const std::string gpgagent_status(const std::shared_ptr<Context> &gpgAgent, const char *what, Error &err)
{
const auto lines = gpgagent_statuslines (gpgAgent, what, err);
// The status is only the last attribute
// e.g. for SCD SERIALNO it would only be "SERIALNO" and for SCD GETATTR FOO
// it would only be FOO
const char *p = strrchr(what, ' ');
const char *needle = (p + 1) ? (p + 1) : what;
for (const auto &pair: lines) {
if (pair.first == needle) {
return pair.second;
}
}
return std::string();
}
static const std::string scd_getattr_status(std::shared_ptr<Context> &gpgAgent, const char *what, Error &err)
{
std::string cmd = "SCD GETATTR ";
cmd += what;
return gpgagent_status(gpgAgent, cmd.c_str(), err);
}
static std::vector<CardApp> getCardsAndApps(std::shared_ptr<Context> &gpgAgent, Error &err)
{
std::vector<CardApp> result;
if (gpgHasMultiCardMultiAppSupport()) {
const std::string command = "SCD GETINFO all_active_apps";
const auto statusLines = gpgagent_statuslines(gpgAgent, command.c_str(), err);
if (err) {
return result;
}
for (const auto &statusLine: statusLines) {
if (statusLine.first == "SERIALNO") {
const auto serialNumberAndApps = QByteArray::fromStdString(statusLine.second).split(' ');
if (serialNumberAndApps.size() >= 2) {
const auto serialNumber = serialNumberAndApps[0];
auto apps = serialNumberAndApps.mid(1);
// sort the apps to get a stable order independently of the currently selected application
std::sort(apps.begin(), apps.end());
for (const auto &app: apps) {
qCDebug(KLEOPATRA_LOG) << "getCardsAndApps(): Found card" << serialNumber << "with app" << app;
result.push_back({ serialNumber.toStdString(), app.toStdString() });
}
} else {
logUnexpectedStatusLine(statusLine, "getCardsAndApps()", command);
}
} else {
logUnexpectedStatusLine(statusLine, "getCardsAndApps()", command);
}
}
} else {
// use SCD SERIALNO to get the currently active card
const auto serialNumber = gpgagent_status(gpgAgent, "SCD SERIALNO", err);
if (err) {
return result;
}
// use SCD GETATTR APPTYPE to find out which app is active
auto appName = scd_getattr_status(gpgAgent, "APPTYPE", err);
std::transform(appName.begin(), appName.end(), appName.begin(),
[](unsigned char c){ return std::tolower(c); });
if (err) {
return result;
}
result.push_back({ serialNumber, appName });
}
return result;
}
static std::string switchCard(std::shared_ptr<Context> &gpgAgent, const std::string &serialNumber, Error &err)
{
const std::string command = "SCD SWITCHCARD " + serialNumber;
const auto statusLines = gpgagent_statuslines(gpgAgent, command.c_str(), err);
if (err) {
return std::string();
}
if (statusLines.size() == 1 && statusLines[0].first == "SERIALNO" && statusLines[0].second == serialNumber) {
return serialNumber;
}
qCWarning(KLEOPATRA_LOG) << "switchCard():" << command << "returned" << statusLines
<< "(expected:" << "SERIALNO " + serialNumber << ")";
return std::string();
}
static std::string switchApp(std::shared_ptr<Context> &gpgAgent, const std::string &serialNumber,
const std::string &appName, Error &err)
{
const std::string command = "SCD SWITCHAPP " + appName;
const auto statusLines = gpgagent_statuslines(gpgAgent, command.c_str(), err);
if (err) {
return std::string();
}
if (statusLines.size() == 1 && statusLines[0].first == "SERIALNO" &&
statusLines[0].second.find(serialNumber + ' ' + appName) == 0) {
return appName;
}
qCWarning(KLEOPATRA_LOG) << "switchApp():" << command << "returned" << statusLines
<< "(expected:" << "SERIALNO " + serialNumber + ' ' + appName + "..." << ")";
return std::string();
}
static const char * get_openpgp_card_manufacturer_from_serial_number(const std::string &serialno)
{
qCDebug(KLEOPATRA_LOG) << "get_openpgp_card_manufacturer_from_serial_number(" << serialno.c_str() << ")";
const bool isProperOpenPGPCardSerialNumber =
serialno.size() == 32 && serialno.substr(0, 12) == "D27600012401";
if (isProperOpenPGPCardSerialNumber) {
const char *sn = serialno.c_str();
const int manufacturerId = xtoi_2(sn + 16)*256 + xtoi_2(sn + 18);
switch (manufacturerId) {
case 0x0001: return "PPC Card Systems";
case 0x0002: return "Prism";
case 0x0003: return "OpenFortress";
case 0x0004: return "Wewid";
case 0x0005: return "ZeitControl";
case 0x0006: return "Yubico";
case 0x0007: return "OpenKMS";
case 0x0008: return "LogoEmail";
case 0x002A: return "Magrathea";
case 0x1337: return "Warsaw Hackerspace";
case 0xF517: return "FSIJ";
/* 0x0000 and 0xFFFF are defined as test cards per spec,
0xFF00 to 0xFFFE are assigned for use with randomly created
serial numbers. */
case 0x0000:
case 0xffff: return "test card";
default: return (manufacturerId & 0xff00) == 0xff00 ? "unmanaged S/N range" : "unknown";
}
} else {
return "unknown";
}
}
static const std::string get_manufacturer(std::shared_ptr<Context> &gpgAgent, Error &err)
{
// The result of SCD GETATTR MANUFACTURER is the manufacturer ID as unsigned number
// optionally followed by the name of the manufacturer, e.g.
// 6 Yubico
// 65534 unmanaged S/N range
const auto manufacturerIdAndName = scd_getattr_status(gpgAgent, "MANUFACTURER", err);
if (err.code()) {
if (err.code() == GPG_ERR_INV_NAME) {
qCDebug(KLEOPATRA_LOG) << "get_manufacturer(): Querying for attribute MANUFACTURER not yet supported; needs GnuPG 2.2.21+";
} else {
qCWarning(KLEOPATRA_LOG) << "Running SCD GETATTR MANUFACTURER failed:" << err;
}
return std::string();
}
const auto startOfManufacturerName = manufacturerIdAndName.find(' ');
if (startOfManufacturerName == std::string::npos) {
// only ID without manufacturer name
return "unknown";
}
const auto manufacturerName = manufacturerIdAndName.substr(startOfManufacturerName + 1);
return manufacturerName;
}
static bool isOpenPGPCardSerialNumber(const std::string &serialNumber)
{
return serialNumber.size() == 32 && serialNumber.substr(0, 12) == "D27600012401";
}
static const std::string getDisplaySerialNumber(std::shared_ptr<Context> &gpgAgent, Error &err)
{
const auto displaySerialNumber = scd_getattr_status(gpgAgent, "$DISPSERIALNO", err);
if (err && err.code() != GPG_ERR_INV_NAME) {
qCWarning(KLEOPATRA_LOG) << "Running SCD GETATTR $DISPSERIALNO failed:" << err;
}
return displaySerialNumber;
}
static void setDisplaySerialNumber(Card *card, std::shared_ptr<Context> &gpgAgent)
{
static const QRegularExpression leadingZeros(QStringLiteral("^0*"));
Error err;
const QString displaySerialNumber = QString::fromStdString(getDisplaySerialNumber(gpgAgent, err));
if (err) {
card->setDisplaySerialNumber(QString::fromStdString(card->serialNumber()));
return;
}
if (isOpenPGPCardSerialNumber(card->serialNumber()) && displaySerialNumber.size() == 12) {
// add a space between manufacturer id and card id for OpenPGP cards
card->setDisplaySerialNumber(displaySerialNumber.left(4) + QLatin1Char(' ') + displaySerialNumber.right(8));
} else {
card->setDisplaySerialNumber(displaySerialNumber);
}
return;
}
static void handle_openpgp_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
auto pgpCard = new OpenPGPCard(*ci);
pgpCard->setManufacturer(get_manufacturer(gpg_agent, err));
if (err.code()) {
// fallback, e.g. if gpg does not yet support querying for the MANUFACTURER attribute
pgpCard->setManufacturer(get_openpgp_card_manufacturer_from_serial_number(ci->serialNumber()));
}
const auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --force", err);
if (err.code()) {
ci->setStatus(Card::CardError);
return;
}
pgpCard->setCardInfo(info);
setDisplaySerialNumber(pgpCard, gpg_agent);
ci.reset(pgpCard);
}
static void readKeyPairInfoFromPIVCard(const std::string &keyRef, PIVCard *pivCard, const std::shared_ptr<Context> &gpg_agent)
{
Error err;
const std::string command = std::string("SCD READKEY --info-only -- ") + keyRef;
const auto keyPairInfoLines = gpgagent_statuslines(gpg_agent, command.c_str(), err);
if (err) {
qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err;
return;
}
for (const auto &pair: keyPairInfoLines) {
if (pair.first == "KEYPAIRINFO") {
const KeyPairInfo info = KeyPairInfo::fromStatusLine(pair.second);
if (info.grip.empty()) {
qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO status line"
<< QString::fromStdString(pair.second);
continue;
}
pivCard->setKeyAlgorithm(keyRef, info.algorithm);
} else {
logUnexpectedStatusLine(pair, "readKeyPairInfoFromPIVCard()", command);
}
}
}
static void readCertificateFromPIVCard(const std::string &keyRef, PIVCard *pivCard, const std::shared_ptr<Context> &gpg_agent)
{
Error err;
const std::string command = std::string("SCD READCERT ") + keyRef;
const std::string certificateData = gpgagent_data(gpg_agent, command.c_str(), err);
if (err && err.code() != GPG_ERR_NOT_FOUND) {
qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err;
return;
}
if (certificateData.empty()) {
qCDebug(KLEOPATRA_LOG) << "readCertificateFromPIVCard(" << QString::fromStdString(keyRef) << "): No certificate stored on card";
return;
}
qCDebug(KLEOPATRA_LOG) << "readCertificateFromPIVCard(" << QString::fromStdString(keyRef) << "): Found certificate stored on card";
pivCard->setCertificateData(keyRef, certificateData);
}
static void handle_piv_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
auto pivCard = new PIVCard(*ci);
const auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --force", err);
if (err) {
ci->setStatus(Card::CardError);
return;
}
pivCard->setCardInfo(info);
setDisplaySerialNumber(pivCard, gpg_agent);
for (const std::string &keyRef : PIVCard::supportedKeys()) {
if (!pivCard->keyGrip(keyRef).empty()) {
readKeyPairInfoFromPIVCard(keyRef, pivCard, gpg_agent);
readCertificateFromPIVCard(keyRef, pivCard, gpg_agent);
}
}
ci.reset(pivCard);
}
static void handle_netkey_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
auto nkCard = new NetKeyCard(*ci);
ci.reset(nkCard);
ci->setAppVersion(parse_app_version(scd_getattr_status(gpg_agent, "NKS-VERSION", err)));
if (err.code()) {
qCWarning(KLEOPATRA_LOG) << "Running SCD GETATTR NKS-VERSION failed:" << err;
ci->setErrorMsg(QStringLiteral ("NKS-VERSION failed: ") + QString::fromUtf8(err.asString()));
return;
}
if (ci->appVersion() != 3) {
qCDebug(KLEOPATRA_LOG) << "not a NetKey v3 card, giving up. Version:" << ci->appVersion();
ci->setErrorMsg(QStringLiteral("NetKey v%1 cards are not supported.").arg(ci->appVersion()));
return;
}
setDisplaySerialNumber(nkCard, gpg_agent);
// the following only works for NKS v3...
const auto chvStatus = QString::fromStdString(
scd_getattr_status(gpg_agent, "CHV-STATUS", err)).split(QLatin1Char(' '));
if (err.code()) {
qCDebug(KLEOPATRA_LOG) << "Running SCD GETATTR CHV-STATUS failed:" << err;
ci->setErrorMsg(QStringLiteral ("CHV-Status failed: ") + QString::fromUtf8(err.asString()));
return;
}
std::vector<Card::PinState> states;
states.reserve(chvStatus.count());
// CHV Status for NKS v3 is
// Pin1 (Normal pin) Pin2 (Normal PUK)
// SigG1 SigG PUK.
int num = 0;
for (const auto &state: chvStatus) {
const auto parsed = parse_pin_state (state);
states.push_back(parsed);
if (parsed == Card::NullPin) {
if (num == 0) {
ci->setHasNullPin(true);
}
}
++num;
}
nkCard->setPinStates(states);
// check for keys to learn:
const std::unique_ptr<DefaultAssuanTransaction> result = gpgagent_default_transact(gpg_agent, "SCD LEARN --keypairinfo", err);
if (err.code() || !result.get()) {
if (err) {
ci->setErrorMsg(QString::fromLatin1(err.asString()));
} else {
ci->setErrorMsg(QStringLiteral("Invalid internal state. No result."));
}
return;
}
const std::vector<std::string> keyPairInfos = result->statusLine("KEYPAIRINFO");
if (keyPairInfos.empty()) {
return;
}
nkCard->setKeyPairInfo(keyPairInfos);
}
static std::shared_ptr<Card> get_card_status(const std::string &serialNumber, const std::string &appName, std::shared_ptr<Context> &gpg_agent)
{
qCDebug(KLEOPATRA_LOG) << "get_card_status(" << serialNumber << ',' << appName << ',' << gpg_agent.get() << ')';
auto ci = std::shared_ptr<Card>(new Card());
if (gpgHasMultiCardMultiAppSupport()) {
// select card
Error err;
const auto result = switchCard(gpg_agent, serialNumber, err);
if (err) {
if (err.code() == GPG_ERR_CARD_NOT_PRESENT || err.code() == GPG_ERR_CARD_REMOVED) {
ci->setStatus(Card::NoCard);
} else {
ci->setStatus(Card::CardError);
}
return ci;
}
if (result.empty()) {
qCWarning(KLEOPATRA_LOG) << "get_card_status: switching card failed";
ci->setStatus(Card::CardError);
return ci;
}
ci->setStatus(Card::CardPresent);
} else {
ci->setStatus(Card::CardPresent);
}
if (gpgHasMultiCardMultiAppSupport()) {
// select app
Error err;
const auto result = switchApp(gpg_agent, serialNumber, appName, err);
if (err) {
if (err.code() == GPG_ERR_CARD_NOT_PRESENT || err.code() == GPG_ERR_CARD_REMOVED) {
ci->setStatus(Card::NoCard);
} else {
ci->setStatus(Card::CardError);
}
return ci;
}
if (result.empty()) {
qCWarning(KLEOPATRA_LOG) << "get_card_status: switching app failed";
ci->setStatus(Card::CardError);
return ci;
}
}
ci->setSerialNumber(serialNumber);
// Handle different card types
if (appName == NetKeyCard::AppName) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: found Netkey card" << ci->serialNumber().c_str() << "end";
handle_netkey_card(ci, gpg_agent);
return ci;
} else if (appName == OpenPGPCard::AppName) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: found OpenPGP card" << ci->serialNumber().c_str() << "end";
handle_openpgp_card(ci, gpg_agent);
return ci;
} else if (appName == PIVCard::AppName) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: found PIV card" << ci->serialNumber().c_str() << "end";
handle_piv_card(ci, gpg_agent);
return ci;
} else {
qCDebug(KLEOPATRA_LOG) << "get_card_status: unhandled application:" << appName;
return ci;
}
return ci;
}
static bool isCardNotPresentError(const GpgME::Error &err)
{
// see fixup_scd_errors() in gpg-card.c
return err && ((err.code() == GPG_ERR_CARD_NOT_PRESENT) ||
((err.code() == GPG_ERR_ENODEV || err.code() == GPG_ERR_CARD_REMOVED) &&
(err.sourceID() == GPG_ERR_SOURCE_SCD)));
}
static std::vector<std::shared_ptr<Card> > update_cardinfo(std::shared_ptr<Context> &gpgAgent)
{
qCDebug(KLEOPATRA_LOG) << "update_cardinfo()";
// ensure that a card is present and that all cards are properly set up
{
Error err;
const char *command = (gpgHasMultiCardMultiAppSupport()) ? "SCD SERIALNO --all" : "SCD SERIALNO";
const std::string serialno = gpgagent_status(gpgAgent, command, err);
if (err) {
if (isCardNotPresentError(err)) {
qCDebug(KLEOPATRA_LOG) << "update_cardinfo: No card present";
return std::vector<std::shared_ptr<Card> >();
} else {
qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err;
auto ci = std::shared_ptr<Card>(new Card());
ci->setStatus(Card::CardError);
return std::vector<std::shared_ptr<Card> >(1, ci);
}
}
}
Error err;
const std::vector<CardApp> cardApps = getCardsAndApps(gpgAgent, err);
if (err) {
if (isCardNotPresentError(err)) {
qCDebug(KLEOPATRA_LOG) << "update_cardinfo: No card present";
return std::vector<std::shared_ptr<Card> >();
} else {
qCWarning(KLEOPATRA_LOG) << "Getting active apps on all inserted cards failed:" << err;
auto ci = std::shared_ptr<Card>(new Card());
ci->setStatus(Card::CardError);
return std::vector<std::shared_ptr<Card> >(1, ci);
}
}
std::vector<std::shared_ptr<Card> > cards;
for (const auto &cardApp: cardApps) {
const auto card = get_card_status(cardApp.serialNumber, cardApp.appName, gpgAgent);
cards.push_back(card);
}
return cards;
}
} // namespace
struct Transaction {
CardApp cardApp;
QByteArray command;
QPointer<QObject> receiver;
const char *slot;
AssuanTransaction* assuanTransaction;
};
static const Transaction updateTransaction = { { "__all__", "__all__" }, "__update__", nullptr, nullptr, nullptr };
static const Transaction quitTransaction = { { "__all__", "__all__" }, "__quit__", nullptr, nullptr, nullptr };
namespace
{
class ReaderStatusThread : public QThread
{
Q_OBJECT
public:
explicit ReaderStatusThread(QObject *parent = nullptr)
: QThread(parent),
m_gnupgHomePath(Kleo::gnupgHomeDirectory()),
m_transactions(1, updateTransaction) // force initial scan
{
connect(this, &ReaderStatusThread::oneTransactionFinished,
this, &ReaderStatusThread::slotOneTransactionFinished);
}
std::vector<std::shared_ptr<Card> > cardInfos() const
{
const QMutexLocker locker(&m_mutex);
return m_cardInfos;
}
Card::Status cardStatus(unsigned int slot) const
{
const QMutexLocker locker(&m_mutex);
if (slot < m_cardInfos.size()) {
return m_cardInfos[slot]->status();
} else {
return Card::NoCard;
}
}
void addTransaction(const Transaction &t)
{
const QMutexLocker locker(&m_mutex);
m_transactions.push_back(t);
m_waitForTransactions.wakeOne();
}
Q_SIGNALS:
void firstCardWithNullPinChanged(const std::string &serialNumber);
void anyCardCanLearnKeysChanged(bool);
void cardAdded(const std::string &serialNumber, const std::string &appName);
void cardChanged(const std::string &serialNumber, const std::string &appName);
void cardRemoved(const std::string &serialNumber, const std::string &appName);
void oneTransactionFinished(const GpgME::Error &err);
public Q_SLOTS:
void deviceStatusChanged(const QByteArray &details)
{
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[GUI]::deviceStatusChanged(" << details << ")";
addTransaction(updateTransaction);
}
void ping()
{
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[GUI]::ping()";
addTransaction(updateTransaction);
}
void stop()
{
const QMutexLocker locker(&m_mutex);
m_transactions.push_front(quitTransaction);
m_waitForTransactions.wakeOne();
}
private Q_SLOTS:
void slotOneTransactionFinished(const GpgME::Error &err)
{
std::list<Transaction> ft;
KDAB_SYNCHRONIZED(m_mutex)
ft.splice(ft.begin(), m_finishedTransactions);
Q_FOREACH (const Transaction &t, ft)
if (t.receiver && t.slot && *t.slot) {
QMetaObject::invokeMethod(t.receiver, t.slot, Qt::DirectConnection, Q_ARG(GpgME::Error, err));
}
}
private:
void run() override {
while (true) {
std::shared_ptr<Context> gpgAgent;
CardApp cardApp;
QByteArray command;
bool nullSlot = false;
AssuanTransaction* assuanTransaction = nullptr;
std::list<Transaction> item;
std::vector<std::shared_ptr<Card> > oldCards;
Error err;
std::unique_ptr<Context> c = Context::createForEngine(AssuanEngine, &err);
if (err.code() == GPG_ERR_NOT_SUPPORTED) {
return;
}
gpgAgent = std::shared_ptr<Context>(c.release());
KDAB_SYNCHRONIZED(m_mutex) {
while (m_transactions.empty()) {
// go to sleep waiting for more work:
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: waiting for commands";
m_waitForTransactions.wait(&m_mutex);
}
// splice off the first transaction without
// copying, so we own it without really importing
// it into this thread (the QPointer isn't
// thread-safe):
item.splice(item.end(),
m_transactions, m_transactions.begin());
// make local copies of the interesting stuff so
// we can release the mutex again:
cardApp = item.front().cardApp;
command = item.front().command;
nullSlot = !item.front().slot;
// we take ownership of the assuan transaction
std::swap(assuanTransaction, item.front().assuanTransaction);
oldCards = m_cardInfos;
}
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: new iteration command=" << command << " ; nullSlot=" << nullSlot;
// now, let's see what we got:
if (nullSlot && command == quitTransaction.command) {
return; // quit
}
if ((nullSlot && command == updateTransaction.command)) {
std::vector<std::shared_ptr<Card> > newCards = update_cardinfo(gpgAgent);
KDAB_SYNCHRONIZED(m_mutex)
m_cardInfos = newCards;
bool anyLC = false;
std::string firstCardWithNullPin;
bool anyError = false;
for (const auto &newCard: newCards) {
const auto serialNumber = newCard->serialNumber();
const auto appName = newCard->appName();
const auto matchingOldCard = std::find_if(oldCards.cbegin(), oldCards.cend(),
[serialNumber, appName] (const std::shared_ptr<Card> &card) {
return card->serialNumber() == serialNumber && card->appName() == appName;
});
if (matchingOldCard == oldCards.cend()) {
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread: Card" << serialNumber << "with app" << appName << "was added";
Q_EMIT cardAdded(serialNumber, appName);
} else {
if (*newCard != **matchingOldCard) {
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread: Card" << serialNumber << "with app" << appName << "changed";
Q_EMIT cardChanged(serialNumber, appName);
}
oldCards.erase(matchingOldCard);
}
if (newCard->canLearnKeys()) {
anyLC = true;
}
if (newCard->hasNullPin() && firstCardWithNullPin.empty()) {
firstCardWithNullPin = newCard->serialNumber();
}
if (newCard->status() == Card::CardError) {
anyError = true;
}
}
for (const auto &oldCard: oldCards) {
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread: Card" << oldCard->serialNumber() << "with app" << oldCard->appName() << "was removed";
Q_EMIT cardRemoved(oldCard->serialNumber(), oldCard->appName());
}
Q_EMIT firstCardWithNullPinChanged(firstCardWithNullPin);
Q_EMIT anyCardCanLearnKeysChanged(anyLC);
if (anyError) {
gpgAgent.reset();
}
} else {
GpgME::Error err;
if (gpgHasMultiCardMultiAppSupport()) {
switchCard(gpgAgent, cardApp.serialNumber, err);
if (!err) {
switchApp(gpgAgent, cardApp.serialNumber, cardApp.appName, err);
}
}
if (!err) {
if (assuanTransaction) {
(void)gpgagent_transact(gpgAgent, command.constData(), std::unique_ptr<AssuanTransaction>(assuanTransaction), err);
} else {
(void)gpgagent_default_transact(gpgAgent, command.constData(), err);
}
}
KDAB_SYNCHRONIZED(m_mutex)
// splice 'item' into m_finishedTransactions:
m_finishedTransactions.splice(m_finishedTransactions.end(), item);
Q_EMIT oneTransactionFinished(err);
}
}
}
private:
mutable QMutex m_mutex;
QWaitCondition m_waitForTransactions;
const QString m_gnupgHomePath;
// protected by m_mutex:
std::vector<std::shared_ptr<Card> > m_cardInfos;
std::list<Transaction> m_transactions, m_finishedTransactions;
};
}
class ReaderStatus::Private : ReaderStatusThread
{
friend class Kleo::SmartCard::ReaderStatus;
ReaderStatus *const q;
public:
explicit Private(ReaderStatus *qq)
: ReaderStatusThread(qq),
q(qq),
watcher()
{
KDAB_SET_OBJECT_NAME(watcher);
qRegisterMetaType<Card::Status>("Kleo::SmartCard::Card::Status");
qRegisterMetaType<GpgME::Error>("GpgME::Error");
connect(this, &::ReaderStatusThread::cardAdded,
q, &ReaderStatus::cardAdded);
connect(this, &::ReaderStatusThread::cardChanged,
q, &ReaderStatus::cardChanged);
connect(this, &::ReaderStatusThread::cardRemoved,
q, &ReaderStatus::cardRemoved);
connect(this, &::ReaderStatusThread::firstCardWithNullPinChanged,
q, &ReaderStatus::firstCardWithNullPinChanged);
connect(this, &::ReaderStatusThread::anyCardCanLearnKeysChanged,
q, &ReaderStatus::anyCardCanLearnKeysChanged);
#ifdef GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER
if (DeviceInfoWatcher::isSupported()) {
qCDebug(KLEOPATRA_LOG) << "ReaderStatus::Private: Using new DeviceInfoWatcher";
connect(&devInfoWatcher, &DeviceInfoWatcher::statusChanged, this, &::ReaderStatusThread::deviceStatusChanged);
} else
#endif
{
qCDebug(KLEOPATRA_LOG) << "ReaderStatus::Private: Using deprecated FileSystemWatcher";
watcher.whitelistFiles(QStringList(QStringLiteral("reader_*.status")));
watcher.addPath(Kleo::gnupgHomeDirectory());
watcher.setDelay(100);
connect(&watcher, &FileSystemWatcher::triggered, this, &::ReaderStatusThread::ping);
}
}
~Private()
{
stop();
if (!wait(100)) {
terminate();
wait();
}
}
private:
std::string firstCardWithNullPinImpl() const
{
const auto cis = cardInfos();
const auto firstWithNullPin = std::find_if(cis.cbegin(), cis.cend(),
[](const std::shared_ptr<Card> &ci) { return ci->hasNullPin(); });
return firstWithNullPin != cis.cend() ? (*firstWithNullPin)->serialNumber() : std::string();
}
bool anyCardCanLearnKeysImpl() const
{
const auto cis = cardInfos();
return std::any_of(cis.cbegin(), cis.cend(),
[](const std::shared_ptr<Card> &ci) { return ci->canLearnKeys(); });
}
private:
FileSystemWatcher watcher;
#ifdef GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER
DeviceInfoWatcher devInfoWatcher;
#endif
};
ReaderStatus::ReaderStatus(QObject *parent)
: QObject(parent), d(new Private(this))
{
self = this;
qRegisterMetaType<std::string>("std::string");
}
ReaderStatus::~ReaderStatus()
{
self = nullptr;
}
// slot
void ReaderStatus::startMonitoring()
{
d->start();
#ifdef GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER
if (DeviceInfoWatcher::isSupported()) {
d->devInfoWatcher.start();
}
#endif
}
// static
ReaderStatus *ReaderStatus::mutableInstance()
{
return self;
}
// static
const ReaderStatus *ReaderStatus::instance()
{
return self;
}
Card::Status ReaderStatus::cardStatus(unsigned int slot) const
{
return d->cardStatus(slot);
}
std::string ReaderStatus::firstCardWithNullPin() const
{
return d->firstCardWithNullPinImpl();
}
bool ReaderStatus::anyCardCanLearnKeys() const
{
return d->anyCardCanLearnKeysImpl();
}
void ReaderStatus::startSimpleTransaction(const std::shared_ptr<Card> &card, const QByteArray &command, QObject *receiver, const char *slot)
{
const CardApp cardApp = { card->serialNumber(), card->appName() };
const Transaction t = { cardApp, command, receiver, slot, nullptr };
d->addTransaction(t);
}
void ReaderStatus::startTransaction(const std::shared_ptr<Card> &card, const QByteArray &command, QObject *receiver, const char *slot,
std::unique_ptr<AssuanTransaction> transaction)
{
const CardApp cardApp = { card->serialNumber(), card->appName() };
const Transaction t = { cardApp, command, receiver, slot, transaction.release() };
d->addTransaction(t);
}
void ReaderStatus::updateStatus()
{
d->ping();
}
std::vector <std::shared_ptr<Card> > ReaderStatus::getCards() const
{
return d->cardInfos();
}
std::shared_ptr<Card> ReaderStatus::getCard(const std::string &serialNumber, const std::string &appName) const
{
for (const auto &card: d->cardInfos()) {
if (card->serialNumber() == serialNumber && card->appName() == appName) {
qCDebug(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Found card with serial number" << serialNumber << "and app" << appName;
return card;
}
}
qCWarning(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Did not find card with serial number" << serialNumber << "and app" << appName;
return std::shared_ptr<Card>();
}
// static
std::string ReaderStatus::switchCard(std::shared_ptr<Context>& ctx, const std::string& serialNumber, Error& err)
{
return ::switchCard(ctx, serialNumber, err);
}
// static
std::string ReaderStatus::switchApp(std::shared_ptr<Context>& ctx, const std::string& serialNumber, const std::string& appName, Error& err)
{
return ::switchApp(ctx, serialNumber, appName, err);
}
#include "readerstatus.moc"
diff --git a/src/uiserver/echocommand.cpp b/src/uiserver/echocommand.cpp
index ea850e09e..f33f9b38f 100644
--- a/src/uiserver/echocommand.cpp
+++ b/src/uiserver/echocommand.cpp
@@ -1,191 +1,190 @@
/* -*- mode: c++; c-basic-offset:4 -*-
uiserver/echocommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "echocommand.h"
#include <utils/input.h>
#include <utils/output.h>
#include <Libkleo/KleoException>
#include <gpg-error.h>
#include <KLocalizedString>
#include <QByteArray>
#include <QIODevice>
-#include <QVariant>
#include <string>
#include <algorithm>
using namespace Kleo;
static const char option_prefix[] = "prefix";
class EchoCommand::Private
{
public:
int operationsInFlight = 0;
QByteArray buffer;
};
EchoCommand::EchoCommand()
: QObject(), AssuanCommandMixin<EchoCommand>(), d(new Private) {}
EchoCommand::~EchoCommand() {}
int EchoCommand::doStart()
{
const std::vector< std::shared_ptr<Input> > in = inputs(), msg = messages();
const std::vector< std::shared_ptr<Output> > out = outputs();
if (!in.empty() && out.empty()) {
return makeError(GPG_ERR_NOT_SUPPORTED);
}
if (!msg.empty()) {
return makeError(GPG_ERR_NOT_SUPPORTED);
}
if (hasOption(option_prefix) && !option(option_prefix).toByteArray().isEmpty()) {
return makeError(GPG_ERR_NOT_IMPLEMENTED);
}
std::string keyword;
if (hasOption("inquire")) {
keyword = option("inquire").toString().toStdString();
if (keyword.empty()) {
return makeError(GPG_ERR_INV_ARG);
}
}
const std::string output = option("text").toString().toStdString();
// aaand ACTION:
// 1. echo the command line though the status channel
sendStatus("ECHO", output.empty() ? QString() : QLatin1String(output.c_str()));
// 2. if --inquire was given, inquire more data from the client:
if (!keyword.empty()) {
if (const int err = inquire(keyword.c_str(), this,
SLOT(slotInquireData(int,QByteArray)))) {
return err;
} else {
++d->operationsInFlight;
}
}
// 3. if INPUT was given, start the data pump for input->output
if (const std::shared_ptr<QIODevice> i = in.at(0)->ioDevice()) {
const std::shared_ptr<QIODevice> o = out.at(0)->ioDevice();
++d->operationsInFlight;
connect(i.get(), &QIODevice::readyRead, this, &EchoCommand::slotInputReadyRead);
connect(o.get(), &QIODevice::bytesWritten, this, &EchoCommand::slotOutputBytesWritten);
if (i->bytesAvailable()) {
slotInputReadyRead();
}
}
if (!d->operationsInFlight) {
done();
}
return 0;
}
void EchoCommand::doCanceled()
{
}
void EchoCommand::slotInquireData(int rc, const QByteArray &data)
{
--d->operationsInFlight;
if (rc) {
done(rc);
return;
}
try {
sendStatus("ECHOINQ", QLatin1String(data));
if (!d->operationsInFlight) {
done();
}
} catch (const Exception &e) {
done(e.error(), e.message());
} catch (const std::exception &e) {
done(makeError(GPG_ERR_UNEXPECTED),
i18n("Caught unexpected exception in SignCommand::Private::slotMicAlgDetermined: %1",
QString::fromLocal8Bit(e.what())));
} catch (...) {
done(makeError(GPG_ERR_UNEXPECTED),
i18n("Caught unknown exception in SignCommand::Private::slotMicAlgDetermined"));
}
}
void EchoCommand::slotInputReadyRead()
{
const std::shared_ptr<QIODevice> in = inputs().at(0)->ioDevice();
Q_ASSERT(in);
QByteArray buffer;
buffer.resize(in->bytesAvailable());
const qint64 read = in->read(buffer.data(), buffer.size());
if (read == - 1) {
done(makeError(GPG_ERR_EIO));
return;
}
if (read == 0 || (!in->isSequential() && read == in->size())) {
in->close();
}
buffer.resize(read);
d->buffer += buffer;
slotOutputBytesWritten();
}
void EchoCommand::slotOutputBytesWritten()
{
const std::shared_ptr<QIODevice> out = outputs().at(0)->ioDevice();
Q_ASSERT(out);
if (!d->buffer.isEmpty()) {
if (out->bytesToWrite()) {
return;
}
const qint64 written = out->write(d->buffer);
if (written == -1) {
done(makeError(GPG_ERR_EIO));
return;
}
d->buffer.remove(0, written);
}
if (out->isOpen() && d->buffer.isEmpty() && !inputs().at(0)->ioDevice()->isOpen()) {
out->close();
if (!--d->operationsInFlight) {
done();
}
}
}
diff --git a/src/view/pivcardwidget.cpp b/src/view/pivcardwidget.cpp
index 7bbb8318f..725f40b5f 100644
--- a/src/view/pivcardwidget.cpp
+++ b/src/view/pivcardwidget.cpp
@@ -1,362 +1,361 @@
/* view/pivcardwiget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "pivcardwidget.h"
#include "tooltippreferences.h"
#include "commands/certificatetopivcardcommand.h"
#include "commands/changepincommand.h"
#include "commands/createopenpgpkeyfromcardkeyscommand.h"
#include "commands/importcertificatefrompivcardcommand.h"
#include "commands/keytocardcommand.h"
#include "commands/pivgeneratecardkeycommand.h"
#include "commands/setpivcardapplicationadministrationkeycommand.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <KLocalizedString>
-#include <KMessageBox>
#include <QFrame>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include <QVBoxLayout>
using namespace GpgME;
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
namespace {
static void layoutKeyWidgets(QGridLayout *grid, const QString &keyName, const PIVCardWidget::KeyWidgets &keyWidgets)
{
int row = grid->rowCount();
grid->addWidget(new QLabel(keyName), row, 0);
grid->addWidget(keyWidgets.certificateInfo, row, 1, 1, 2);
grid->addWidget(keyWidgets.generateButton, row, 3);
if (keyWidgets.writeKeyButton) {
grid->addWidget(keyWidgets.writeKeyButton, row, 4);
}
row++;
grid->addWidget(keyWidgets.keyGrip, row, 1);
grid->addWidget(keyWidgets.keyAlgorithm, row, 2);
grid->addWidget(keyWidgets.writeCertificateButton, row, 3);
grid->addWidget(keyWidgets.importCertificateButton, row, 4);
}
static int toolTipOptions()
{
using namespace Kleo::Formatting;
static const int validityFlags = Validity | Issuer | ExpiryDates | CertificateUsage;
static const int ownerFlags = Subject | UserIDs | OwnerTrust;
static const int detailsFlags = StorageLocation | CertificateType | SerialNumber | Fingerprint;
const TooltipPreferences prefs;
int flags = KeyID;
flags |= prefs.showValidity() ? validityFlags : 0;
flags |= prefs.showOwnerInformation() ? ownerFlags : 0;
flags |= prefs.showCertificateDetails() ? detailsFlags : 0;
return flags;
}
}
PIVCardWidget::PIVCardWidget(QWidget *parent)
: QWidget(parent)
, mSerialNumber(new QLabel(this))
, mVersionLabel(new QLabel(this))
, mKeyForCardKeysButton(new QPushButton(this))
{
// Set up the scroll area
auto myLayout = new QVBoxLayout(this);
myLayout->setContentsMargins(0, 0, 0, 0);
auto area = new QScrollArea;
area->setFrameShape(QFrame::NoFrame);
area->setWidgetResizable(true);
myLayout->addWidget(area);
auto areaWidget = new QWidget;
area->setWidget(areaWidget);
auto areaVLay = new QVBoxLayout(areaWidget);
auto grid = new QGridLayout;
areaVLay->addLayout(grid);
areaVLay->addStretch(1);
const int columnCount = 5;
int row = 0;
// Version and Serialnumber
grid->addWidget(mVersionLabel, row++, 0, 1, 2);
mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
grid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
grid->addWidget(mSerialNumber, row++, 1);
mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);
{
auto line = new QFrame();
line->setFrameShape(QFrame::HLine);
grid->addWidget(line, row++, 0, 1, columnCount);
}
// The keys
grid->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Keys:"))), row++, 0);
{
KeyWidgets keyWidgets = createKeyWidgets(PIVCard::pivAuthenticationKeyRef());
layoutKeyWidgets(grid, i18n("PIV authentication:"), keyWidgets);
}
{
KeyWidgets keyWidgets = createKeyWidgets(PIVCard::cardAuthenticationKeyRef());
layoutKeyWidgets(grid, i18n("Card authentication:"), keyWidgets);
}
{
KeyWidgets keyWidgets = createKeyWidgets(PIVCard::digitalSignatureKeyRef());
layoutKeyWidgets(grid, i18n("Digital signature:"), keyWidgets);
}
{
KeyWidgets keyWidgets = createKeyWidgets(PIVCard::keyManagementKeyRef());
layoutKeyWidgets(grid, i18n("Key management:"), keyWidgets);
}
row = grid->rowCount();
{
auto line = new QFrame();
line->setFrameShape(QFrame::HLine);
grid->addWidget(line, row++, 0, 1, columnCount);
}
auto actionLayout = new QHBoxLayout;
mKeyForCardKeysButton->setText(i18nc("@action:button", "Create OpenPGP Key"));
mKeyForCardKeysButton->setToolTip(i18nc("@info:tooltip", "Create an OpenPGP key for the keys stored on the card."));
actionLayout->addWidget(mKeyForCardKeysButton);
connect(mKeyForCardKeysButton, &QPushButton::clicked, this, &PIVCardWidget::createKeyFromCardKeys);
{
auto button = new QPushButton(i18nc("@action:button", "Change PIN"));
button->setToolTip(i18nc("@info:tooltip", "Change the PIV Card Application PIN that activates the PIV Card "
"and enables private key operations using the stored keys."));
actionLayout->addWidget(button);
connect(button, &QPushButton::clicked, this, [this] () { changePin(PIVCard::pinKeyRef()); });
}
{
auto button = new QPushButton(i18nc("@action:button", "Change PUK"));
button->setToolTip(i18nc("@info:tooltip", "Change the PIN Unblocking Key that enables a reset of the PIN."));
actionLayout->addWidget(button);
connect(button, &QPushButton::clicked, this, [this] () { changePin(PIVCard::pukKeyRef()); });
}
{
auto button = new QPushButton(i18nc("@action:button", "Change Admin Key"));
button->setToolTip(i18nc("@info:tooltip", "Change the PIV Card Application Administration Key that is used by the "
"PIV Card Application to authenticate the PIV Card Application Administrator and by the "
"administrator (resp. Kleopatra) to authenticate the PIV Card Application."));
actionLayout->addWidget(button);
connect(button, &QPushButton::clicked, this, [this] () { setAdminKey(); });
}
actionLayout->addStretch(-1);
grid->addLayout(actionLayout, row++, 0, 1, columnCount);
grid->setColumnStretch(4, -1);
}
PIVCardWidget::KeyWidgets PIVCardWidget::createKeyWidgets(const std::string &keyRef)
{
KeyWidgets keyWidgets;
keyWidgets.keyGrip = new QLabel(this);
keyWidgets.keyGrip->setTextInteractionFlags(Qt::TextBrowserInteraction);
keyWidgets.keyAlgorithm = new QLabel(this);
keyWidgets.keyAlgorithm->setTextInteractionFlags(Qt::TextSelectableByMouse);
keyWidgets.certificateInfo = new QLabel(this);
keyWidgets.certificateInfo->setTextInteractionFlags(Qt::TextBrowserInteraction);
keyWidgets.generateButton = new QPushButton(i18nc("@action:button", "Generate"), this);
keyWidgets.generateButton->setEnabled(false);
connect(keyWidgets.generateButton, &QPushButton::clicked,
this, [this, keyRef] () { generateKey(keyRef); });
keyWidgets.writeCertificateButton = new QPushButton(i18nc("@action:button", "Write Certificate"));
keyWidgets.writeCertificateButton->setToolTip(i18nc("@info:tooltip", "Write the certificate corresponding to this key to the card"));
keyWidgets.writeCertificateButton->setEnabled(false);
connect(keyWidgets.writeCertificateButton, &QPushButton::clicked,
this, [this, keyRef] () { writeCertificateToCard(keyRef); });
keyWidgets.importCertificateButton = new QPushButton(i18nc("@action:button", "Import Certificate"));
keyWidgets.importCertificateButton->setToolTip(i18nc("@info:tooltip", "Import the certificate stored on the card"));
keyWidgets.importCertificateButton->setEnabled(false);
connect(keyWidgets.importCertificateButton, &QPushButton::clicked,
this, [this, keyRef] () { importCertificateFromCard(keyRef); });
if (keyRef == PIVCard::cardAuthenticationKeyRef() || keyRef == PIVCard::keyManagementKeyRef()) {
keyWidgets.writeKeyButton = new QPushButton(i18nc("@action:button", "Write Key"));
keyWidgets.writeKeyButton->setToolTip(i18nc("@info:tooltip", "Write the key pair of a certificate to the card"));
keyWidgets.writeKeyButton->setEnabled(true);
connect(keyWidgets.writeKeyButton, &QPushButton::clicked,
this, [this, keyRef] () { writeKeyToCard(keyRef); });
}
mKeyWidgets.insert(keyRef, keyWidgets);
return keyWidgets;
}
PIVCardWidget::~PIVCardWidget()
{
}
void PIVCardWidget::setCard(const PIVCard *card)
{
mCardSerialNumber = card->serialNumber();
mVersionLabel->setText(i18nc("%1 version number", "PIV v%1 card", card->displayAppVersion()));
mSerialNumber->setText(card->displaySerialNumber());
mSerialNumber->setToolTip(QString::fromStdString(card->serialNumber()));
updateKeyWidgets(PIVCard::pivAuthenticationKeyRef(), card);
updateKeyWidgets(PIVCard::cardAuthenticationKeyRef(), card);
updateKeyWidgets(PIVCard::digitalSignatureKeyRef(), card);
updateKeyWidgets(PIVCard::keyManagementKeyRef(), card);
const bool signingKeyAvailable = !card->keyGrip(PIVCard::digitalSignatureKeyRef()).empty();
const bool encryptionKeyAvailable = !card->keyGrip(PIVCard::keyManagementKeyRef()).empty();
mKeyForCardKeysButton->setEnabled(signingKeyAvailable && encryptionKeyAvailable);
}
void PIVCardWidget::updateKeyWidgets(const std::string &keyRef, const PIVCard *card)
{
KeyWidgets widgets = mKeyWidgets.value(keyRef);
const std::string grip = card ? card->keyGrip(keyRef) : widgets.keyGrip->text().toStdString();
if (grip.empty()) {
widgets.certificateInfo->setText(i18nc("@info", "<em>slot empty</em>"));
widgets.certificateInfo->setToolTip(QString());
widgets.keyGrip->setText(QString());
widgets.keyAlgorithm->setText(QString());
widgets.generateButton->setText(i18nc("@action:button", "Generate"));
widgets.generateButton->setToolTip(
i18nc("@info:tooltip %1 display name of a key", "Generate %1", PIVCard::keyDisplayName(keyRef)));
widgets.writeCertificateButton->setEnabled(false);
widgets.importCertificateButton->setEnabled(false);
} else {
const Key certificate = KeyCache::instance()->findSubkeyByKeyGrip(grip, GpgME::CMS).parent();
if (!certificate.isNull()) {
widgets.certificateInfo->setText(
i18nc("X.509 certificate DN (validity, created: date)", "%1 (%2, created: %3)",
DN(certificate.userID(0).id()).prettyDN(),
Formatting::complianceStringShort(certificate),
Formatting::creationDateString(certificate)));
widgets.certificateInfo->setToolTip(Formatting::toolTip(certificate, toolTipOptions()));
widgets.writeCertificateButton->setEnabled(true);
} else {
widgets.certificateInfo->setText(i18nc("@info", "<em>no matching certificate</em>"));
widgets.certificateInfo->setToolTip(QString());
widgets.writeCertificateButton->setEnabled(false);
}
if (card) {
// update information if called with card
widgets.keyGrip->setText(QString::fromStdString(grip));
const std::string algo = card->keyAlgorithm(keyRef);
widgets.keyAlgorithm->setText(algo.empty() ? i18nc("@info unknown key algorithm", "unknown") : QString::fromStdString(algo));
widgets.importCertificateButton->setEnabled(!card->certificateData(keyRef).empty());
}
widgets.generateButton->setText(i18nc("@action:button", "Replace"));
widgets.generateButton->setToolTip(
i18nc("@info:tooltip %1 display name of a key", "Replace %1 with new key", PIVCard::keyDisplayName(keyRef)));
}
widgets.generateButton->setEnabled(true);
}
void PIVCardWidget::generateKey(const std::string &keyref)
{
auto cmd = new PIVGenerateCardKeyCommand(mCardSerialNumber, this);
this->setEnabled(false);
connect(cmd, &PIVGenerateCardKeyCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->setKeyRef(keyref);
cmd->start();
}
void PIVCardWidget::writeCertificateToCard(const std::string &keyref)
{
auto cmd = new CertificateToPIVCardCommand(keyref, mCardSerialNumber);
this->setEnabled(false);
connect(cmd, &CertificateToPIVCardCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->setParentWidget(this);
cmd->start();
}
void PIVCardWidget::importCertificateFromCard(const std::string &keyref)
{
auto cmd = new ImportCertificateFromPIVCardCommand(keyref, mCardSerialNumber);
this->setEnabled(false);
connect(cmd, &ImportCertificateFromPIVCardCommand::finished,
this, [this, keyref] () {
this->updateKeyWidgets(keyref, nullptr);
this->setEnabled(true);
});
cmd->setParentWidget(this);
cmd->start();
}
void PIVCardWidget::writeKeyToCard(const std::string &keyref)
{
auto cmd = new KeyToCardCommand(keyref, mCardSerialNumber, PIVCard::AppName);
this->setEnabled(false);
connect(cmd, &KeyToCardCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->setParentWidget(this);
cmd->start();
}
void PIVCardWidget::createKeyFromCardKeys()
{
auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(mCardSerialNumber, PIVCard::AppName, this);
this->setEnabled(false);
connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->start();
}
void PIVCardWidget::changePin(const std::string &keyRef)
{
auto cmd = new ChangePinCommand(mCardSerialNumber, PIVCard::AppName, this);
this->setEnabled(false);
connect(cmd, &ChangePinCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->setKeyRef(keyRef);
cmd->start();
}
void PIVCardWidget::setAdminKey()
{
auto cmd = new SetPIVCardApplicationAdministrationKeyCommand(mCardSerialNumber, this);
this->setEnabled(false);
connect(cmd, &SetPIVCardApplicationAdministrationKeyCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->start();
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, May 5, 6:13 AM (1 d, 23 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
1f/02/71f92c8bbe568b47e8c922998238

Event Timeline