Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F27326002
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
21 KB
Subscribers
None
View Options
diff --git a/src/dialogs/revokerswidget.cpp b/src/dialogs/revokerswidget.cpp
index ac26b3dce..3d928d93e 100644
--- a/src/dialogs/revokerswidget.cpp
+++ b/src/dialogs/revokerswidget.cpp
@@ -1,164 +1,164 @@
// SPDX-FileCopyrightText: 2024 g10 Code GmbH
// SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "revokerswidget.h"
#include "commands/command.h"
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyList>
#include <Libkleo/TreeWidget>
#include <gpgme++/key.h>
#include <gpgme.h>
#include <KLocalizedString>
#include <KStandardAction>
#include <QClipboard>
#include <QGuiApplication>
#include <QKeyEvent>
#include <QMenu>
#include <QVBoxLayout>
using namespace Kleo;
class RevokersWidget::Private
{
public:
RevokersWidget *const q;
enum Column {
Fingerprint,
Name,
Email,
};
Private(RevokersWidget *qq)
: q(qq)
, ui{qq}
{
connect(ui.revokersTree, &QTreeWidget::doubleClicked, q, [this]() {
const auto index = ui.revokersTree->currentIndex();
if (!index.isValid()) {
return;
}
#if GPGME_VERSION_NUMBER >= 0x011800 // 1.24.0
const auto fingerprint = QString::fromLatin1(key.revocationKey(ui.revokersTree->currentIndex().row()).fingerprint());
auto cmd = Command::commandForQuery(fingerprint);
- cmd->setParentWId(q->winId());
+ cmd->setParentWidget(q->window());
cmd->start();
#endif
});
}
public:
GpgME::Key key;
public:
struct UI {
QVBoxLayout *mainLayout;
TreeWidget *revokersTree;
UI(QWidget *widget)
{
mainLayout = new QVBoxLayout{widget};
mainLayout->setContentsMargins({});
revokersTree = new TreeWidget{widget};
revokersTree->setProperty("_breeze_force_frame", true);
revokersTree->setHeaderLabels({
i18nc("@title:column", "Fingerprint"),
i18nc("@title:column", "Name"),
i18nc("@title:column", "Email"),
});
revokersTree->setAccessibleName(i18nc("@label", "Revokers"));
revokersTree->setContextMenuPolicy(Qt::CustomContextMenu);
revokersTree->setRootIsDecorated(false);
mainLayout->addWidget(revokersTree);
connect(revokersTree, &QTreeWidget::customContextMenuRequested, widget, [widget, this](const auto &pos) {
auto menu = new QMenu;
menu->setAttribute(Qt::WA_DeleteOnClose, true);
menu->addAction(KStandardAction::copy(
widget,
[this]() {
QGuiApplication::clipboard()->setText(revokersTree->currentIndex().data(KeyList::ClipboardRole).toString());
},
widget));
menu->popup(widget->mapToGlobal(pos));
});
}
} ui;
};
RevokersWidget::RevokersWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
RevokersWidget::~RevokersWidget() = default;
void RevokersWidget::setKey(const GpgME::Key &key)
{
if (key.protocol() != GpgME::OpenPGP) {
return;
}
d->key = key;
d->ui.revokersTree->clear();
#if GPGME_VERSION_NUMBER >= 0x011800 // 1.24.0
for (size_t i = 0; i < key.numRevocationKeys(); i++) {
auto item = new QTreeWidgetItem;
auto revoker = key.revocationKey(i);
auto revokerKey = Kleo::KeyCache::instance()->findByFingerprint(revoker.fingerprint());
item->setData(Private::Fingerprint, Qt::DisplayRole, Formatting::prettyID(revoker.fingerprint()));
item->setData(Private::Fingerprint, Qt::AccessibleTextRole, Formatting::accessibleHexID(revoker.fingerprint()));
item->setData(Private::Fingerprint, KeyList::ClipboardRole, QString::fromLatin1(revoker.fingerprint()));
if (!revokerKey.isNull()) {
item->setData(Private::Name, Qt::DisplayRole, Formatting::prettyName(revokerKey));
item->setData(Private::Name, KeyList::ClipboardRole, Formatting::prettyName(revokerKey));
item->setData(Private::Email, Qt::DisplayRole, Formatting::prettyEMail(revokerKey));
item->setData(Private::Email, KeyList::ClipboardRole, Formatting::prettyEMail(revokerKey));
} else {
item->setData(Private::Name, Qt::DisplayRole, {});
item->setData(Private::Email, Qt::DisplayRole, {});
item->setData(Private::Name, Qt::AccessibleTextRole, i18nc("text for screen readers for an unknown name", "unknown name"));
item->setData(Private::Email, Qt::AccessibleTextRole, i18nc("text for screen readers for an unknown email", "unknown email"));
item->setData(Private::Name, KeyList::ClipboardRole, {});
item->setData(Private::Email, KeyList::ClipboardRole, {});
}
d->ui.revokersTree->addTopLevelItem(item);
}
#endif
QMetaObject::invokeMethod(
this,
[this]() {
if (!d->ui.revokersTree->restoreColumnLayout(QStringLiteral("RevokersWidget"))) {
for (int i = 0; i < d->ui.revokersTree->columnCount(); i++) {
d->ui.revokersTree->resizeColumnToContents(i);
}
}
},
Qt::QueuedConnection);
}
GpgME::Key RevokersWidget::key() const
{
return d->key;
}
void RevokersWidget::keyPressEvent(QKeyEvent *event)
{
if (event == QKeySequence::Copy) {
QGuiApplication::clipboard()->setText(d->ui.revokersTree->currentIndex().data(KeyList::ClipboardRole).toString());
}
}
#include "moc_revokerswidget.cpp"
diff --git a/src/dialogs/weboftrustwidget.cpp b/src/dialogs/weboftrustwidget.cpp
index dd147c63f..03b37818c 100644
--- a/src/dialogs/weboftrustwidget.cpp
+++ b/src/dialogs/weboftrustwidget.cpp
@@ -1,440 +1,440 @@
/*
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Intevation GmbH
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "weboftrustwidget.h"
#include "commands/certifycertificatecommand.h"
#include "commands/importcertificatefromkeyservercommand.h"
#include "commands/revokecertificationcommand.h"
#include "utils/tags.h"
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <Libkleo/TreeView>
#include <Libkleo/UserIDListModel>
#include <Libkleo/UserIDListProxyModel>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSeparator>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QLabel>
#include <QMenu>
#include <QPushButton>
#include <QVBoxLayout>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include "kleopatra_debug.h"
using namespace Kleo;
namespace
{
void addActionButton(QLayout *buttonBox, QAction *action)
{
if (!action) {
return;
}
auto button = new QPushButton(buttonBox->parentWidget());
button->setText(action->text());
buttonBox->addWidget(button);
button->setEnabled(action->isEnabled());
QObject::connect(action, &QAction::changed, button, [action, button]() {
button->setEnabled(action->isEnabled());
});
QObject::connect(button, &QPushButton::clicked, action, &QAction::trigger);
}
}
class WebOfTrustWidget::Private
{
friend class ::Kleo::WebOfTrustWidget;
WebOfTrustWidget *const q;
private:
GpgME::Key key;
UserIDListModel certificationsModel;
UserIDListProxyModel proxyModel;
QGpgME::KeyListJob *keyListJob = nullptr;
TreeView *certificationsTV = nullptr;
QAction *detailsAction = nullptr;
QAction *certifyAction = nullptr;
QAction *revokeAction = nullptr;
QAction *fetchAction = nullptr;
QLabel *notAvailableLabel = nullptr;
QPushButton *moreButton = nullptr;
QCheckBox *showOnlyOwnCheck = nullptr;
public:
Private(WebOfTrustWidget *qq)
: q{qq}
{
certificationsModel.enableRemarks(Tags::tagsEnabled());
auto vLay = new QVBoxLayout(q);
vLay->setContentsMargins({});
proxyModel.setSourceModel(&certificationsModel);
certificationsTV = new TreeView{q};
certificationsTV->setAccessibleName(i18n("User IDs and certifications"));
certificationsTV->setModel(&proxyModel);
certificationsTV->setAllColumnsShowFocus(false);
certificationsTV->setSelectionMode(QAbstractItemView::SingleSelection);
if (!Tags::tagsEnabled()) {
certificationsTV->hideColumn(static_cast<int>(UserIDListModel::Column::Tags));
}
vLay->addWidget(certificationsTV);
notAvailableLabel = new QLabel(i18nc("@info", "Certifications are not available before the certificate is imported."));
notAvailableLabel->setAlignment(Qt::AlignHCenter);
notAvailableLabel->setVisible(false);
vLay->addWidget(notAvailableLabel);
detailsAction = new QAction{QIcon::fromTheme(QStringLiteral("dialog-information")), i18nc("@action", "Show Certificate Details"), q};
connect(detailsAction, &QAction::triggered, q, [this]() {
showCertificateDetails();
});
certifyAction = new QAction{QIcon::fromTheme(QStringLiteral("view-certificate-sign")), i18nc("@action", "Add Certification"), q};
connect(certifyAction, &QAction::triggered, q, [this]() {
addCertification();
});
if (Kleo::Commands::RevokeCertificationCommand::isSupported()) {
revokeAction = new QAction{QIcon::fromTheme(QStringLiteral("view-certificate-revoke")), i18nc("@action", "Revoke Certification"), q};
connect(revokeAction, &QAction::triggered, q, [this]() {
revokeCertification();
});
}
fetchAction = new QAction(QIcon::fromTheme(QStringLiteral("download")), i18nc("@action:button", "Fetch Missing Keys"));
fetchAction->setToolTip(i18nc("@info:tooltip", "Look up and import all keys that were used to certify the user IDs of this key"));
connect(fetchAction, &QAction::triggered, q, [this]() {
fetchMissingKeys();
});
auto bbox = new QHBoxLayout;
addActionButton(bbox, certifyAction);
addActionButton(bbox, revokeAction);
moreButton = new QPushButton(QIcon::fromTheme(QStringLiteral("application-menu")), {});
moreButton->setToolTip(i18nc("@info:tooltip", "Show more options"));
bbox->addWidget(moreButton);
connect(moreButton, &QPushButton::clicked, q, [this]() {
auto menu = new QMenu(q);
menu->addAction(detailsAction);
menu->addAction(fetchAction);
menu->popup(moreButton->mapToGlobal(QPoint()));
});
showOnlyOwnCheck = new QCheckBox(i18nc("label:checkbox", "Show only my own certifications"));
bbox->addWidget(showOnlyOwnCheck);
connect(showOnlyOwnCheck, &QCheckBox::toggled, &proxyModel, &UserIDListProxyModel::setShowOnlyOwnCertifications);
bbox->addStretch(1);
vLay->addLayout(bbox);
connect(certificationsTV, &QAbstractItemView::doubleClicked, q, [this]() {
certificationDblClicked();
});
certificationsTV->setContextMenuPolicy(Qt::CustomContextMenu);
connect(certificationsTV, &QWidget::customContextMenuRequested, q, [this](const QPoint &p) {
contextMenuRequested(p);
});
connect(certificationsTV->selectionModel(), &QItemSelectionModel::currentRowChanged, q, [this]() {
updateActions();
});
updateActions();
}
GpgME::UserID selectedUserID()
{
return proxyModel.userID(certificationsTV->currentIndex());
}
GpgME::UserID::Signature selectedCertification()
{
return proxyModel.signature(certificationsTV->currentIndex());
}
void certificationDblClicked()
{
showCertificateDetails();
}
void showCertificateDetails()
{
const auto signature = selectedCertification();
if (signature.isNull()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "- no certification selected";
return;
}
auto cmd = Command::commandForQuery(QString::fromUtf8(signature.signerKeyID()));
- cmd->setParentWId(q->winId());
+ cmd->setParentWidget(q->window());
cmd->start();
}
void addCertification()
{
auto userID = selectedUserID();
if (userID.isNull()) {
userID = selectedCertification().parent();
}
if (userID.isNull()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "- no user ID or certification selected";
return;
}
auto cmd = new Kleo::Commands::CertifyCertificateCommand(userID);
cmd->setParentWidget(q);
certificationsTV->setEnabled(false);
connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished, q, [this]() {
certificationsTV->setEnabled(true);
// Trigger an update when done
q->setKey(key);
});
cmd->start();
}
void revokeCertification()
{
Command *cmd = nullptr;
if (const auto signature = selectedCertification(); !signature.isNull()) {
cmd = new Kleo::Commands::RevokeCertificationCommand(signature);
} else if (const auto userID = selectedUserID(); !userID.isNull()) {
cmd = new Kleo::Commands::RevokeCertificationCommand(userID);
} else {
qCDebug(KLEOPATRA_LOG) << __func__ << "- no user ID or certification selected";
return;
}
cmd->setParentWidget(q);
certificationsTV->setEnabled(false);
connect(cmd, &Kleo::Commands::RevokeCertificationCommand::finished, q, [this]() {
certificationsTV->setEnabled(true);
// Trigger an update when done
q->setKey(key);
});
cmd->start();
}
void addActionsForUserID(QMenu *menu)
{
menu->addAction(certifyAction);
if (revokeAction) {
menu->addAction(revokeAction);
}
}
void addActionsForSignature(QMenu *menu)
{
menu->addAction(detailsAction);
menu->addAction(certifyAction);
if (revokeAction) {
menu->addAction(revokeAction);
if (!revokeAction->isEnabled()) {
menu->setToolTipsVisible(true);
}
}
}
void updateActions()
{
const auto userCanSignUserIDs = userHasCertificationKey();
const auto keyCanBeCertified = Kleo::canBeCertified(key);
const auto userID = selectedUserID();
const auto signature = selectedCertification();
detailsAction->setEnabled(!signature.isNull());
certifyAction->setEnabled(keyCanBeCertified && userCanSignUserIDs && (!userID.isNull() || !signature.isNull()));
if (revokeAction) {
revokeAction->setToolTip({});
if (!signature.isNull()) {
const auto revocationFeasibility = userCanRevokeCertification(signature);
revokeAction->setEnabled(revocationFeasibility == CertificationCanBeRevoked);
switch (revocationFeasibility) {
case CertificationCanBeRevoked:
break;
case CertificationNotMadeWithOwnKey:
revokeAction->setToolTip(
i18n("You cannot revoke this certification because it wasn't made with one of your keys (or the required secret key is missing)."));
break;
case CertificationIsSelfSignature:
revokeAction->setToolTip(i18nc("@info:tooltip", "Revocation of self-certifications is currently not possible."));
break;
case CertificationIsRevocation:
revokeAction->setToolTip(
i18nc("@info:tooltip", "You cannot revoke this revocation certification. (But you can re-certify the corresponding user ID.)"));
break;
case CertificationIsExpired:
revokeAction->setToolTip(i18nc("@info:tooltip", "You cannot revoke this expired certification."));
break;
case CertificationIsInvalid:
revokeAction->setToolTip(i18nc("@info:tooltip", "You cannot revoke this invalid certification."));
break;
case CertificationKeyNotAvailable:
revokeAction->setToolTip(i18nc("@info:tooltip", "You cannot revoke this certification because the required secret key is not available."));
break;
};
} else if (!userID.isNull()) {
const bool canRevokeCertification = userCanRevokeCertifications(userID);
revokeAction->setEnabled(canRevokeCertification);
if (!canRevokeCertification) {
revokeAction->setToolTip(
i18n("You cannot revoke any of the certifications of this user ID. Select any of the certifications for details."));
}
} else {
revokeAction->setEnabled(false);
}
}
}
void contextMenuRequested(const QPoint &p)
{
const auto userID = proxyModel.userID(certificationsTV->indexAt(p));
const auto signature = proxyModel.signature(certificationsTV->indexAt(p));
if (userID.isNull() && signature.isNull()) {
return;
}
auto menu = new QMenu(q);
if (!userID.isNull()) {
addActionsForUserID(menu);
} else if (!signature.isNull()) {
addActionsForSignature(menu);
}
connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
menu->popup(certificationsTV->viewport()->mapToGlobal(p));
}
void startSignatureListing()
{
if (keyListJob) {
return;
}
QGpgME::KeyListJob *const job = QGpgME::openpgp()->keyListJob(/*remote*/ false, /*includeSigs*/ true, /*validate*/ true);
if (!job) {
return;
}
if (Tags::tagsEnabled()) {
job->addMode(GpgME::SignatureNotations);
}
connect(job, &QGpgME::KeyListJob::result, q, &WebOfTrustWidget::signatureListingDone);
connect(job, &QGpgME::KeyListJob::nextKey, q, &WebOfTrustWidget::signatureListingNextKey);
job->start(QStringList(QString::fromLatin1(key.primaryFingerprint())));
keyListJob = job;
}
void fetchMissingKeys()
{
if (q->key().isNull()) {
return;
}
const auto missingSignerKeyIds = Kleo::getMissingSignerKeyIds(q->key().userIDs());
auto cmd = new Kleo::ImportCertificateFromKeyserverCommand{QStringList{std::begin(missingSignerKeyIds), std::end(missingSignerKeyIds)}};
cmd->setParentWidget(q);
fetchAction->setEnabled(false);
connect(cmd, &Kleo::ImportCertificateFromKeyserverCommand::finished, q, [this]() {
// Trigger an update when done
q->setKey(q->key());
fetchAction->setEnabled(true);
});
cmd->start();
}
};
WebOfTrustWidget::WebOfTrustWidget(QWidget *parent)
: QWidget{parent}
, d{std::make_unique<Private>(this)}
{
}
WebOfTrustWidget::~WebOfTrustWidget() = default;
QAction *WebOfTrustWidget::detailsAction() const
{
return d->detailsAction;
}
QAction *WebOfTrustWidget::certifyAction() const
{
return d->certifyAction;
}
QAction *WebOfTrustWidget::revokeAction() const
{
return d->revokeAction;
}
GpgME::Key WebOfTrustWidget::key() const
{
return d->key;
}
void WebOfTrustWidget::setKey(const GpgME::Key &key)
{
if (key.protocol() != GpgME::OpenPGP) {
qCDebug(KLEOPATRA_LOG) << "List of Certifications is only supported for OpenPGP keys";
return;
}
if (isRemoteKey(key)) {
d->certificationsTV->setVisible(false);
d->notAvailableLabel->setVisible(true);
d->moreButton->setEnabled(false);
}
d->key = key;
d->certificationsModel.setKey(key);
d->updateActions();
d->certificationsTV->expandAll();
d->certificationsTV->header()->resizeSections(QHeaderView::ResizeToContents);
d->startSignatureListing();
d->certificationsTV->restoreColumnLayout(QStringLiteral("WebOfTrustWidget"));
for (int i = 0; i < d->certificationsModel.columnCount(); i++) {
d->certificationsTV->resizeColumnToContents(i);
}
d->fetchAction->setEnabled(!key.isBad());
}
void WebOfTrustWidget::signatureListingNextKey(const GpgME::Key &key)
{
GpgME::Key merged = key;
merged.mergeWith(d->key);
setKey(merged);
}
void WebOfTrustWidget::signatureListingDone(const GpgME::KeyListResult &result)
{
if (result.error()) {
KMessageBox::information(this,
xi18nc("@info",
"<para>An error occurred while loading the certifications: "
"<message>%1</message></para>",
Formatting::errorAsString(result.error())),
i18nc("@title", "Certifications Loading Failed"));
}
d->keyListJob = nullptr;
}
#include "moc_weboftrustwidget.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Aug 29, 7:59 AM (1 d, 9 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
75/3a/c2789e6fccda7e62a198cc09ff0b
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment