diff --git a/src/dialogs/revokecertificationdialog.cpp b/src/dialogs/revokecertificationdialog.cpp index 04cac5a54..615c99aae 100644 --- a/src/dialogs/revokecertificationdialog.cpp +++ b/src/dialogs/revokecertificationdialog.cpp @@ -1,143 +1,156 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/revokecertificationdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "revokecertificationdialog.h" #include "revokecertificationwidget.h" #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace GpgME; using namespace Kleo; class RevokeCertificationDialog::Private { friend class ::Kleo::RevokeCertificationDialog; RevokeCertificationDialog *const q; public: explicit Private(RevokeCertificationDialog *qq); - ~Private(); + + void updateOkButton(); private: void saveGeometry(); void restoreGeometry(const QSize &defaultSize); private: RevokeCertificationWidget *mainWidget = nullptr; + QPushButton *okButton = nullptr; }; RevokeCertificationDialog::Private::Private(RevokeCertificationDialog *qq) : q(qq) { } -RevokeCertificationDialog::Private::~Private() +void RevokeCertificationDialog::Private::updateOkButton() { + okButton->setEnabled(!mainWidget->certificationKey().isNull() + && !mainWidget->selectedUserIDs().empty()); } void RevokeCertificationDialog::Private::saveGeometry() { KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), "RevokeCertificationDialog"); cfgGroup.writeEntry("geometry", q->saveGeometry()); cfgGroup.sync(); } void RevokeCertificationDialog::Private::restoreGeometry(const QSize &defaultSize) { KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), "RevokeCertificationDialog"); const QByteArray geometry = cfgGroup.readEntry("geometry", QByteArray()); if (!geometry.isEmpty()) { q->restoreGeometry(geometry); } else { q->resize(defaultSize); } } RevokeCertificationDialog::RevokeCertificationDialog(QWidget *p, Qt::WindowFlags f) : QDialog(p, f) , d(new Private(this)) { setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); auto mainLay = new QVBoxLayout(this); d->mainWidget = new RevokeCertificationWidget(this); mainLay->addWidget(d->mainWidget); - auto buttonBox = new QDialogButtonBox(); + auto buttonBox = new QDialogButtonBox(this); mainLay->addWidget(buttonBox); buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); - KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); - KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); - buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("Revoke Certification")); - connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, + d->okButton = buttonBox->button(QDialogButtonBox::Ok); + auto cancelButton = buttonBox->button(QDialogButtonBox::Cancel); + KGuiItem::assign(d->okButton, KStandardGuiItem::ok()); + KGuiItem::assign(cancelButton, KStandardGuiItem::cancel()); + d->okButton->setText(i18n("Revoke Certification")); + + connect(d->mainWidget, &RevokeCertificationWidget::certificationKeyChanged, + this, [this]() { d->updateOkButton(); }); + connect(d->mainWidget, &RevokeCertificationWidget::selectedUserIDsChanged, + this, [this]() { d->updateOkButton(); }); + d->updateOkButton(); + + connect(d->okButton, &QAbstractButton::clicked, this, [this] () { d->mainWidget->saveConfig(); accept(); }); - connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, + connect(cancelButton, &QAbstractButton::clicked, this, [this] () { close(); }); d->restoreGeometry(QSize(640, 480)); } RevokeCertificationDialog::~RevokeCertificationDialog() { d->saveGeometry(); } void RevokeCertificationDialog::setCertificateToRevoke(const Key &key) { setWindowTitle(i18nc("@title:window arg is name, email of certificate holder", "Revoke Certification: %1", Formatting::prettyName(key))); d->mainWidget->setTarget(key); } void RevokeCertificationDialog::setSelectedUserIDs(const std::vector &uids) { d->mainWidget->setSelectUserIDs(uids); } std::vector RevokeCertificationDialog::selectedUserIDs() const { return d->mainWidget->selectedUserIDs(); } void Kleo::RevokeCertificationDialog::setSelectedCertificationKey(const GpgME::Key &key) { d->mainWidget->setCertificationKey(key); } Key RevokeCertificationDialog::selectedCertificationKey() const { return d->mainWidget->certificationKey(); } bool RevokeCertificationDialog::sendToServer() const { return d->mainWidget->publishSelected(); } diff --git a/src/dialogs/revokecertificationwidget.cpp b/src/dialogs/revokecertificationwidget.cpp index 8c3fdefe6..334637ba8 100644 --- a/src/dialogs/revokecertificationwidget.cpp +++ b/src/dialogs/revokecertificationwidget.cpp @@ -1,263 +1,270 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/revokecertificationwidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "revokecertificationwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; namespace { class CertificationKeyFilter: public DefaultKeyFilter { public: CertificationKeyFilter(const GpgME::Key &certificationTarget); bool matches(const GpgME::Key &key, Kleo::KeyFilter::MatchContexts contexts) const override; private: GpgME::Key mCertificationTarget; // the key to certify or to revoke the certification of }; CertificationKeyFilter::CertificationKeyFilter(const GpgME::Key &certificationTarget) : DefaultKeyFilter() , mCertificationTarget(certificationTarget) { setIsOpenPGP(DefaultKeyFilter::Set); setHasSecret(DefaultKeyFilter::Set); setCanCertify(DefaultKeyFilter::Set); setIsBad(DefaultKeyFilter::NotSet); } bool CertificationKeyFilter::matches(const GpgME::Key &key, Kleo::KeyFilter::MatchContexts contexts) const { if (!(availableMatchContexts() & contexts)) { return false; } // exclude certification target from list of certification keys if (qstrcmp(key.primaryFingerprint(), mCertificationTarget.primaryFingerprint()) == 0) { return false; } return DefaultKeyFilter::matches(key, contexts); } static bool uidsAreEqual(const GpgME::UserID &lhs, const GpgME::UserID &rhs) { // use uidhash if available if (lhs.uidhash() && rhs.uidhash()) { return strcmp(lhs.uidhash(), rhs.uidhash()) == 0; } // compare actual user ID string and primary key; this is not unique, but it's all we can do if uidhash is missing return qstrcmp(lhs.id(), rhs.id()) == 0 && qstrcmp(lhs.parent().primaryFingerprint(), rhs.parent().primaryFingerprint()) == 0; } class UserIDModel : public QStandardItemModel { Q_OBJECT public: explicit UserIDModel(QObject *parent = nullptr) : QStandardItemModel(parent) { } void setKey(const GpgME::Key &key) { mKey = key; clear(); const std::vector uids = key.userIDs(); for (const auto &uid : uids) { auto const item = new QStandardItem; item->setText(Formatting::prettyUserID(uid)); item->setCheckable(true); item->setEditable(false); item->setCheckState(Qt::Checked); appendRow(item); } } void setCheckedUserIDs(const std::vector &checkedUids) { const auto keyUids = mKey.userIDs(); Q_ASSERT(rowCount() == static_cast(keyUids.size())); for (int i = 0; i < rowCount(); ++i) { const auto &keyUid = keyUids[i]; const bool uidIsChecked = std::find_if(checkedUids.cbegin(), checkedUids.cend(), [keyUid](const GpgME::UserID &checkedUid) { return uidsAreEqual(keyUid, checkedUid); }) != checkedUids.cend(); item(i)->setCheckState(uidIsChecked ? Qt::Checked : Qt::Unchecked); } } std::vector checkedUserIDs() const { const auto keyUids = mKey.userIDs(); Q_ASSERT(rowCount() == static_cast(keyUids.size())); std::vector checkedUids; for (int i = 0; i < rowCount(); ++i) { if (item(i)->checkState() == Qt::Checked) { checkedUids.push_back(keyUids[i]); } } return checkedUids; } private: GpgME::Key mKey; }; } // unnamed namespace class RevokeCertificationWidget::Private { friend class ::Kleo::RevokeCertificationWidget; RevokeCertificationWidget *const q; QLabel *mFprLabel; KeySelectionCombo *mCertificationKeySelect; QCheckBox *mPublishCB; UserIDModel mUserIDModel; GpgME::Key mTarget; public: Private(RevokeCertificationWidget *qq) : q(qq) , mFprLabel(new QLabel) , mCertificationKeySelect(new KeySelectionCombo(/* secretOnly = */ true)) , mPublishCB(new QCheckBox) { auto mainLayout = new QVBoxLayout(q); mainLayout->addWidget(mFprLabel); auto certKeyLayout = new QHBoxLayout; { auto label = new QLabel(i18n("Certification key:")); label->setToolTip(i18n("The key whose certifications shall be revoke")); certKeyLayout->addWidget(label); } certKeyLayout->addWidget(mCertificationKeySelect, 1); mainLayout->addLayout(certKeyLayout); auto splitLine = new QFrame; splitLine->setFrameShape(QFrame::HLine); splitLine->setFrameShadow(QFrame::Sunken); splitLine->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); mainLayout->addWidget(splitLine); auto listView = new QListView; listView->setModel(&mUserIDModel); mainLayout->addWidget(listView, 1); mPublishCB = new QCheckBox(i18n("Publish revocations on keyserver")); mainLayout->addWidget(mPublishCB); loadConfig(); + + connect(mCertificationKeySelect, &KeySelectionCombo::currentKeyChanged, + q, &RevokeCertificationWidget::certificationKeyChanged); + connect(&mUserIDModel, &UserIDModel::itemChanged, + q, [this]() { + Q_EMIT q->selectedUserIDsChanged(q->selectedUserIDs()); + }); } ~Private() { } void saveConfig() { KConfigGroup conf(KSharedConfig::openConfig(), "RevokeCertificationSettings"); const auto certificationKey = mCertificationKeySelect->currentKey(); if (!certificationKey.isNull()) { conf.writeEntry("LastKey", certificationKey.primaryFingerprint()); } conf.writeEntry("PublishCheckState", mPublishCB->isChecked()); } void loadConfig() { const KConfigGroup conf(KSharedConfig::openConfig(), "RevokeCertificationSettings"); mCertificationKeySelect->setDefaultKey(conf.readEntry("LastKey", QString())); mPublishCB->setChecked(conf.readEntry("PublishCheckState", false)); } }; RevokeCertificationWidget::RevokeCertificationWidget(QWidget *parent) : QWidget(parent) , d(new Private(this)) { } RevokeCertificationWidget::~RevokeCertificationWidget() { } void RevokeCertificationWidget::setTarget(const GpgME::Key &key) { d->mTarget = key; d->mFprLabel->setText(i18n("Fingerprint: %1", Formatting::prettyID(d->mTarget.primaryFingerprint())) + QStringLiteral("
") + i18n("Only the fingerprint clearly identifies the key and its owner.")); d->mCertificationKeySelect->setKeyFilter(std::shared_ptr(new CertificationKeyFilter(d->mTarget))); d->mUserIDModel.setKey(d->mTarget); } GpgME::Key RevokeCertificationWidget::target() const { return d->mTarget; } void RevokeCertificationWidget::setSelectUserIDs(const std::vector &uids) { d->mUserIDModel.setCheckedUserIDs(uids); } std::vector RevokeCertificationWidget::selectedUserIDs() const { return d->mUserIDModel.checkedUserIDs(); } void RevokeCertificationWidget::setCertificationKey(const GpgME::Key &key) { d->mCertificationKeySelect->setDefaultKey(QString::fromLatin1(key.primaryFingerprint())); } GpgME::Key RevokeCertificationWidget::certificationKey() const { return d->mCertificationKeySelect->currentKey(); } bool RevokeCertificationWidget::publishSelected() const { return d->mPublishCB->isChecked(); } void Kleo::RevokeCertificationWidget::saveConfig() const { d->saveConfig(); } // for UserIDModel #include "revokecertificationwidget.moc" diff --git a/src/dialogs/revokecertificationwidget.h b/src/dialogs/revokecertificationwidget.h index 3cce84d92..261106063 100644 --- a/src/dialogs/revokecertificationwidget.h +++ b/src/dialogs/revokecertificationwidget.h @@ -1,63 +1,67 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/revokecertificationwidget.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include #include namespace GpgME { class Key; class UserID; } namespace Kleo { /** Widget for revoking OpenPGP certifications. */ class RevokeCertificationWidget : public QWidget { Q_OBJECT public: explicit RevokeCertificationWidget(QWidget *parent = nullptr); ~RevokeCertificationWidget() override; /* Set the key to revoke certifications of */ void setTarget(const GpgME::Key &key); /* Get the key to revoke certifications of */ GpgME::Key target() const; /* Select specific user ids. Default: all */ void setSelectUserIDs(const std::vector &uids); /* The user ids whose certifications shall be revoked */ std::vector selectedUserIDs() const; /* Set the selected certification key. Default: last used key */ void setCertificationKey(const GpgME::Key &key); /* The selected certification key */ GpgME::Key certificationKey() const; /* Whether the revocations shall be published */ bool publishSelected() const; void saveConfig() const; +Q_SIGNALS: + void certificationKeyChanged(const GpgME::Key &key); + void selectedUserIDsChanged(const std::vector &uids); + private: class Private; const std::unique_ptr d; }; } // namespace Kleo