Page MenuHome GnuPG

No OneTemporary

diff --git a/src/dialogs/subkeyswidget.cpp b/src/dialogs/subkeyswidget.cpp
index 301b20e88..e7bb9a58a 100644
--- a/src/dialogs/subkeyswidget.cpp
+++ b/src/dialogs/subkeyswidget.cpp
@@ -1,317 +1,321 @@
/*
dialogs/subkeyswidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2022 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 "subkeyswidget.h"
#include "commands/changeexpirycommand.h"
#include "commands/exportsecretsubkeycommand.h"
#include "commands/importpaperkeycommand.h"
#include "commands/keytocardcommand.h"
#include "exportdialog.h"
#include <kleopatra_debug.h>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <Libkleo/NavigatableTreeWidget>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QLabel>
#include <QMenu>
#include <QPushButton>
#include <QTreeWidgetItem>
#include <QVBoxLayout>
#include <gpgme++/context.h>
#include <gpgme++/key.h>
Q_DECLARE_METATYPE(GpgME::Subkey)
using namespace Kleo;
using namespace Kleo::Commands;
class SubKeysWidget::Private
{
SubKeysWidget *const q;
public:
Private(SubKeysWidget *qq)
: q{qq}
, ui{qq}
{
ui.subkeysTree->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui.subkeysTree, &QAbstractItemView::customContextMenuRequested, q, [this](const QPoint &p) {
tableContextMenuRequested(p);
});
connect(Kleo::KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, q, [this]() {
keysMayHaveChanged();
});
}
private:
void tableContextMenuRequested(const QPoint &p);
void keysMayHaveChanged();
public:
GpgME::Key key;
public:
struct UI {
QVBoxLayout *mainLayout;
NavigatableTreeWidget *subkeysTree;
UI(QWidget *widget)
{
mainLayout = new QVBoxLayout{widget};
mainLayout->setContentsMargins(0, 0, 0, 0);
auto subkeysTreeLabel = new QLabel{i18nc("@label", "Subkeys:"), widget};
mainLayout->addWidget(subkeysTreeLabel);
subkeysTree = new NavigatableTreeWidget{widget};
subkeysTreeLabel->setBuddy(subkeysTree);
subkeysTree->setAccessibleName(i18nc("@label", "Subkeys"));
subkeysTree->setRootIsDecorated(false);
subkeysTree->setHeaderLabels({
i18nc("@title:column", "ID"),
i18nc("@title:column", "Type"),
i18nc("@title:column", "Valid From"),
i18nc("@title:column", "Valid Until"),
i18nc("@title:column", "Status"),
i18nc("@title:column", "Strength"),
i18nc("@title:column", "Usage"),
i18nc("@title:column", "Primary"),
i18nc("@title:column", "Storage"),
});
mainLayout->addWidget(subkeysTree);
}
} ui;
};
void SubKeysWidget::Private::tableContextMenuRequested(const QPoint &p)
{
auto item = ui.subkeysTree->itemAt(p);
if (!item) {
return;
}
const auto subkey = item->data(0, Qt::UserRole).value<GpgME::Subkey>();
const bool isOwnKey = subkey.parent().hasSecret();
const bool secretSubkeyStoredInKeyRing = subkey.isSecret() && !subkey.isCardKey();
auto menu = new QMenu(q);
connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
if (isOwnKey) {
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("change-date-symbolic")), i18n("Change End of Validity Period..."), q, [this, subkey]() {
auto cmd = new ChangeExpiryCommand(subkey.parent());
cmd->setSubkey(subkey);
ui.subkeysTree->setEnabled(false);
connect(cmd, &ChangeExpiryCommand::finished, q, [this]() {
ui.subkeysTree->setEnabled(true);
key.update();
q->setKey(key);
});
cmd->setParentWidget(q);
cmd->start();
});
action->setEnabled(canBeUsedForSecretKeyOperations(subkey.parent()));
}
if (subkey.canAuthenticate()) {
menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-export")), i18n("Export OpenSSH key"), q, [this, subkey]() {
QScopedPointer<ExportDialog> dlg(new ExportDialog(q));
dlg->setKey(subkey, static_cast<unsigned int>(GpgME::Context::ExportSSH));
dlg->exec();
});
}
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-import")), i18n("Restore printed backup"), q, [this, subkey]() {
auto cmd = new ImportPaperKeyCommand(subkey.parent());
ui.subkeysTree->setEnabled(false);
connect(cmd, &ImportPaperKeyCommand::finished, q, [this]() {
ui.subkeysTree->setEnabled(true);
});
cmd->setParentWidget(q);
cmd->start();
});
action->setEnabled(!secretSubkeyStoredInKeyRing);
if (isOwnKey) {
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("send-to-symbolic")), i18n("Transfer to smartcard"), q, [this, subkey]() {
auto cmd = new KeyToCardCommand(subkey);
ui.subkeysTree->setEnabled(false);
connect(cmd, &KeyToCardCommand::finished, q, [this]() {
ui.subkeysTree->setEnabled(true);
});
cmd->setParentWidget(q);
cmd->start();
});
action->setEnabled(secretSubkeyStoredInKeyRing && !KeyToCardCommand::getSuitableCards(subkey).empty());
}
const bool isPrimarySubkey = subkey.keyID() == key.keyID();
if (isOwnKey && !isPrimarySubkey) {
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-export")), i18n("Export secret subkey"), q, [this, subkey]() {
auto cmd = new ExportSecretSubkeyCommand{{subkey}};
ui.subkeysTree->setEnabled(false);
connect(cmd, &ExportSecretSubkeyCommand::finished, q, [this]() {
ui.subkeysTree->setEnabled(true);
});
cmd->setParentWidget(q);
cmd->start();
});
action->setEnabled(secretSubkeyStoredInKeyRing);
}
menu->popup(ui.subkeysTree->viewport()->mapToGlobal(p));
}
void SubKeysWidget::Private::keysMayHaveChanged()
{
qCDebug(KLEOPATRA_LOG) << q << __func__;
const auto updatedKey = Kleo::KeyCache::instance()->findByFingerprint(key.primaryFingerprint());
if (!updatedKey.isNull()) {
q->setKey(updatedKey);
}
}
SubKeysWidget::SubKeysWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
SubKeysWidget::~SubKeysWidget() = default;
void SubKeysWidget::setKey(const GpgME::Key &key)
{
if (key.protocol() != GpgME::OpenPGP) {
return;
}
d->key = key;
const auto currentItem = d->ui.subkeysTree->currentItem();
const QByteArray selectedKeyFingerprint = currentItem ? QByteArray(currentItem->data(0, Qt::UserRole).value<GpgME::Subkey>().fingerprint()) : QByteArray();
d->ui.subkeysTree->clear();
const auto subkeys = key.subkeys();
for (const auto &subkey : subkeys) {
auto item = new QTreeWidgetItem;
item->setData(0, Qt::DisplayRole, Formatting::prettyID(subkey.keyID()));
item->setData(0, Qt::AccessibleTextRole, Formatting::accessibleHexID(subkey.keyID()));
item->setData(0, Qt::UserRole, QVariant::fromValue(subkey));
item->setData(1, Qt::DisplayRole, Kleo::Formatting::type(subkey));
item->setData(2, Qt::DisplayRole, Kleo::Formatting::creationDateString(subkey));
item->setData(2, Qt::AccessibleTextRole, Formatting::accessibleCreationDate(subkey));
- item->setData(3, Qt::DisplayRole, Kleo::Formatting::expirationDateString(subkey));
- item->setData(3, Qt::AccessibleTextRole, Formatting::accessibleExpirationDate(subkey));
+ item->setData(3,
+ Qt::DisplayRole,
+ subkey.neverExpires() ? Kleo::Formatting::expirationDateString(subkey.parent()) : Kleo::Formatting::expirationDateString(subkey));
+ item->setData(3,
+ Qt::AccessibleTextRole,
+ subkey.neverExpires() ? Kleo::Formatting::expirationDateString(subkey.parent()) : Kleo::Formatting::expirationDateString(subkey));
item->setData(4, Qt::DisplayRole, Kleo::Formatting::validityShort(subkey));
switch (subkey.publicKeyAlgorithm()) {
case GpgME::Subkey::AlgoECDSA:
case GpgME::Subkey::AlgoEDDSA:
case GpgME::Subkey::AlgoECDH:
item->setData(5, Qt::DisplayRole, QString::fromStdString(subkey.algoName()));
break;
default:
item->setData(5, Qt::DisplayRole, QString::number(subkey.length()));
}
item->setData(6, Qt::DisplayRole, Kleo::Formatting::usageString(subkey));
const auto isPrimary = subkey.keyID() == key.keyID();
item->setData(7, Qt::DisplayRole, isPrimary ? QStringLiteral("✓") : QString());
item->setData(7, Qt::AccessibleTextRole, isPrimary ? i18nc("yes, is primary key", "yes") : i18nc("no, is not primary key", "no"));
if (subkey.isCardKey()) {
if (const char *serialNo = subkey.cardSerialNumber()) {
item->setData(8, Qt::DisplayRole, i18nc("smart card <serial number>", "smart card %1", QString::fromUtf8(serialNo)));
} else {
item->setData(8, Qt::DisplayRole, i18n("smart card"));
}
} else if (isPrimary && key.hasSecret() && !subkey.isSecret()) {
item->setData(8, Qt::DisplayRole, i18nc("key is 'offline key', i.e. secret key is not stored on this computer", "offline"));
} else if (subkey.isSecret()) {
item->setData(8, Qt::DisplayRole, i18n("on this computer"));
} else {
item->setData(8, Qt::DisplayRole, i18nc("unknown storage location", "unknown"));
}
d->ui.subkeysTree->addTopLevelItem(item);
if (subkey.fingerprint() == selectedKeyFingerprint) {
d->ui.subkeysTree->setCurrentItem(item);
}
}
if (!key.hasSecret()) {
// hide information about storage location for keys of other people
d->ui.subkeysTree->hideColumn(8);
}
d->ui.subkeysTree->header()->resizeSections(QHeaderView::ResizeToContents);
}
GpgME::Key SubKeysWidget::key() const
{
return d->key;
}
SubKeysDialog::SubKeysDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle(i18nc("@title:window", "Subkeys Details"));
auto l = new QVBoxLayout(this);
l->addWidget(new SubKeysWidget(this));
auto bbox = new QDialogButtonBox(this);
auto btn = bbox->addButton(QDialogButtonBox::Close);
connect(btn, &QPushButton::clicked, this, &QDialog::accept);
l->addWidget(bbox);
readConfig();
}
SubKeysDialog::~SubKeysDialog()
{
writeConfig();
}
void SubKeysDialog::readConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), "SubKeysDialog");
const QSize size = dialog.readEntry("Size", QSize(820, 280));
if (size.isValid()) {
resize(size);
}
}
void SubKeysDialog::writeConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), "SubKeysDialog");
dialog.writeEntry("Size", size());
dialog.sync();
}
void SubKeysDialog::setKey(const GpgME::Key &key)
{
auto w = findChild<SubKeysWidget *>();
Q_ASSERT(w);
w->setKey(key);
}
GpgME::Key SubKeysDialog::key() const
{
auto w = findChild<SubKeysWidget *>();
Q_ASSERT(w);
return w->key();
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jan 17, 2:25 AM (10 h, 39 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
17/64/30424fe12c794146ae1e9785a375

Event Timeline