Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/changeexpirycommand.cpp b/src/commands/changeexpirycommand.cpp
index 5f226c613..bd4f1a304 100644
--- a/src/commands/changeexpirycommand.cpp
+++ b/src/commands/changeexpirycommand.cpp
@@ -1,310 +1,324 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/changeexpirycommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021 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 "changeexpirycommand.h"
#include "command_p.h"
#include "dialogs/expirydialog.h"
#include "utils/expiration.h"
#include <Libkleo/Formatting>
#include <KLocalizedString>
#include <QGpgME/ChangeExpiryJob>
#include <QGpgME/Protocol>
#include <QDateTime>
#include <gpgme++/key.h>
+#include <algorithm>
+
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::Dialogs;
using namespace GpgME;
using namespace QGpgME;
namespace
{
bool subkeyHasSameExpirationAsPrimaryKey(const Subkey &subkey)
{
// we allow for a difference in expiration of up to 1 hour (= 3600 seconds)
static const auto maxExpirationDifference = 3600;
Q_ASSERT(!subkey.isNull());
const auto key = subkey.parent();
const auto primaryKey = key.subkey(0);
const auto primaryExpiration = quint32(primaryKey.expirationTime());
const auto subkeyExpiration = quint32(subkey.expirationTime());
if (primaryExpiration != 0 && subkeyExpiration != 0) {
return (primaryExpiration == subkeyExpiration) //
|| ((primaryExpiration > subkeyExpiration) && (primaryExpiration - subkeyExpiration <= maxExpirationDifference)) //
|| ((primaryExpiration < subkeyExpiration) && (subkeyExpiration - primaryExpiration <= maxExpirationDifference));
}
return primaryKey.neverExpires() && subkey.neverExpires();
}
bool allNotRevokedSubkeysHaveSameExpirationAsPrimaryKey(const Key &key)
{
Q_ASSERT(!key.isNull() && key.numSubkeys() > 0);
const auto subkeys = key.subkeys();
return std::all_of(std::begin(subkeys), std::end(subkeys), [](const auto &subkey) {
// revoked subkeys are ignored by gpg --quick-set-expire when updating the expiration of all subkeys;
// check if expiration of subkey is (more or less) the same as the expiration of the primary key
return subkey.isRevoked() || subkeyHasSameExpirationAsPrimaryKey(subkey);
});
}
}
class ChangeExpiryCommand::Private : public Command::Private
{
friend class ::Kleo::Commands::ChangeExpiryCommand;
ChangeExpiryCommand *q_func() const
{
return static_cast<ChangeExpiryCommand *>(q);
}
public:
explicit Private(ChangeExpiryCommand *qq, KeyListController *c);
~Private() override;
private:
void slotDialogAccepted();
void slotDialogRejected();
void slotResult(const Error &err);
private:
void ensureDialogCreated(ExpiryDialog::Mode mode);
void createJob();
void showErrorDialog(const Error &error);
void showSuccessDialog();
private:
GpgME::Key key;
GpgME::Subkey subkey;
QPointer<ExpiryDialog> dialog;
QPointer<ChangeExpiryJob> job;
};
ChangeExpiryCommand::Private *ChangeExpiryCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ChangeExpiryCommand::Private *ChangeExpiryCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
ChangeExpiryCommand::Private::Private(ChangeExpiryCommand *qq, KeyListController *c)
: Command::Private{qq, c}
{
}
ChangeExpiryCommand::Private::~Private() = default;
void ChangeExpiryCommand::Private::slotDialogAccepted()
{
Q_ASSERT(dialog);
static const QTime END_OF_DAY{23, 59, 00};
const QDateTime expiry{dialog->dateOfExpiry(), END_OF_DAY};
qCDebug(KLEOPATRA_LOG) << "expiry" << expiry;
createJob();
Q_ASSERT(job);
std::vector<Subkey> subkeysToUpdate;
if (!subkey.isNull()) {
// change expiration of a single subkey
if (subkey.keyID() != key.keyID()) { // ignore the primary subkey
subkeysToUpdate.push_back(subkey);
}
} else {
// change expiration of the (primary) key and, optionally, of some subkeys
job->setOptions(ChangeExpiryJob::UpdatePrimaryKey);
if (dialog->updateExpirationOfAllSubkeys() && key.numSubkeys() > 1) {
// explicitly list the subkeys for which the expiration should be changed
// together with the expiration of the (primary) key, so that already expired
// subkeys are also updated
const auto subkeys = key.subkeys();
std::copy_if(std::next(subkeys.begin()), subkeys.end(), std::back_inserter(subkeysToUpdate), [](const auto &subkey) {
// skip revoked subkeys which would anyway be ignored by gpg;
// also skip subkeys without explicit expiration because they inherit the primary key's expiration;
// include all subkeys that are not yet expired or that expired around the same time as the primary key
return !subkey.isRevoked() //
&& !subkey.neverExpires() //
&& (!subkey.isExpired() || subkeyHasSameExpirationAsPrimaryKey(subkey));
});
}
}
if (const Error err = job->start(key, expiry, subkeysToUpdate)) {
showErrorDialog(err);
finished();
}
}
void ChangeExpiryCommand::Private::slotDialogRejected()
{
Q_EMIT q->canceled();
finished();
}
void ChangeExpiryCommand::Private::slotResult(const Error &err)
{
if (err.isCanceled())
;
else if (err) {
showErrorDialog(err);
} else {
showSuccessDialog();
}
finished();
}
void ChangeExpiryCommand::Private::ensureDialogCreated(ExpiryDialog::Mode mode)
{
if (dialog) {
return;
}
dialog = new ExpiryDialog{mode};
applyWindowID(dialog);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, &QDialog::accepted, q, [this]() {
slotDialogAccepted();
});
connect(dialog, &QDialog::rejected, q, [this]() {
slotDialogRejected();
});
}
void ChangeExpiryCommand::Private::createJob()
{
Q_ASSERT(!job);
const auto backend = (key.protocol() == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
if (!backend) {
return;
}
ChangeExpiryJob *const j = backend->changeExpiryJob();
if (!j) {
return;
}
connect(j, &QGpgME::Job::jobProgress, q, &Command::progress);
connect(j, &ChangeExpiryJob::result, q, [this](const auto &err) {
slotResult(err);
});
job = j;
}
void ChangeExpiryCommand::Private::showErrorDialog(const Error &err)
{
error(
i18n("<p>An error occurred while trying to change "
"the end of the validity period for <b>%1</b>:</p><p>%2</p>",
Formatting::formatForComboBox(key),
Formatting::errorAsString(err)));
}
void ChangeExpiryCommand::Private::showSuccessDialog()
{
success(i18n("End of validity period changed successfully."));
}
ChangeExpiryCommand::ChangeExpiryCommand(KeyListController *c)
: Command{new Private{this, c}}
{
}
ChangeExpiryCommand::ChangeExpiryCommand(QAbstractItemView *v, KeyListController *c)
: Command{v, new Private{this, c}}
{
}
ChangeExpiryCommand::ChangeExpiryCommand(const GpgME::Key &key)
: Command{key, new Private{this, nullptr}}
{
}
ChangeExpiryCommand::~ChangeExpiryCommand() = default;
void ChangeExpiryCommand::setSubkey(const GpgME::Subkey &subkey)
{
d->subkey = subkey;
}
void ChangeExpiryCommand::doStart()
{
const std::vector<Key> keys = d->keys();
if (keys.size() != 1 //
|| keys.front().protocol() != GpgME::OpenPGP //
|| !keys.front().hasSecret() //
|| keys.front().subkey(0).isNull()) {
d->finished();
return;
}
d->key = keys.front();
if (!d->subkey.isNull() && d->subkey.parent().primaryFingerprint() != d->key.primaryFingerprint()) {
qDebug() << "Invalid subkey" << d->subkey.fingerprint() << ": Not a subkey of key" << d->key.primaryFingerprint();
d->finished();
return;
}
ExpiryDialog::Mode mode;
if (!d->subkey.isNull()) {
mode = ExpiryDialog::Mode::UpdateIndividualSubkey;
- } else if (d->key.numSubkeys() == 1) {
- mode = ExpiryDialog::Mode::UpdateCertificateWithoutSubkeys;
} else {
- mode = ExpiryDialog::Mode::UpdateCertificateWithSubkeys;
+ // count the number of subkeys for which the user has the choice to update
+ // the expiration together with the primary key
+ const auto numSubkeys = std::ranges::count_if(d->key.subkeys(), [](const auto &subkey) {
+ // skip revoked subkeys which would anyway be ignored by gpg;
+ // also skip subkeys without explicit expiration because they inherit the primary key's expiration;
+ // include all subkeys that are not yet expired or that expired around the same time as the primary key
+ return !subkey.isRevoked() //
+ && !subkey.neverExpires() //
+ && (!subkey.isExpired() || subkeyHasSameExpirationAsPrimaryKey(subkey));
+ });
+ if (numSubkeys == 1) {
+ mode = ExpiryDialog::Mode::UpdateCertificateWithoutSubkeys;
+ } else {
+ mode = ExpiryDialog::Mode::UpdateCertificateWithSubkeys;
+ }
}
d->ensureDialogCreated(mode);
Q_ASSERT(d->dialog);
const Subkey subkey = !d->subkey.isNull() ? d->subkey : d->key.subkey(0);
d->dialog->setDateOfExpiry((subkey.neverExpires() //
? QDate{} //
: defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration)));
if (mode == ExpiryDialog::Mode::UpdateIndividualSubkey && subkey.keyID() != subkey.parent().keyID()) {
d->dialog->setPrimaryKey(subkey.parent());
} else if (mode == ExpiryDialog::Mode::UpdateCertificateWithSubkeys) {
d->dialog->setUpdateExpirationOfAllSubkeys(allNotRevokedSubkeysHaveSameExpirationAsPrimaryKey(d->key));
}
d->dialog->show();
}
void ChangeExpiryCommand::doCancel()
{
if (d->job) {
d->job->slotCancel();
}
}
#undef d
#undef q
#include "moc_changeexpirycommand.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 17, 12:45 AM (1 d, 8 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
dd/5a/5cbfc903940c56c7a5c441d83ab1

Event Timeline