Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/signencryptfilescommand.cpp b/src/commands/signencryptfilescommand.cpp
index 6de25656e..7d33f3f72 100644
--- a/src/commands/signencryptfilescommand.cpp
+++ b/src/commands/signencryptfilescommand.cpp
@@ -1,291 +1,291 @@
/*
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 "signencryptfilescommand.h"
#include "command_p.h"
#include <crypto/signencryptfilescontroller.h>
#include <utils/filedialog.h>
#include <Libkleo/Stl_Util>
#include "kleopatra_debug.h"
#include <KLocalizedString>
#include <exception>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::Crypto;
class SignEncryptFilesCommand::Private : public Command::Private
{
friend class ::Kleo::Commands::SignEncryptFilesCommand;
SignEncryptFilesCommand *q_func() const
{
return static_cast<SignEncryptFilesCommand *>(q);
}
public:
explicit Private(SignEncryptFilesCommand *qq, KeyListController *c);
~Private() override;
QStringList selectFiles() const;
void init();
private:
void slotControllerDone()
{
finished();
}
void slotControllerError(int, const QString &)
{
finished();
}
private:
QStringList files;
std::shared_ptr<const ExecutionContext> shared_qq;
SignEncryptFilesController controller;
};
SignEncryptFilesCommand::Private *SignEncryptFilesCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const SignEncryptFilesCommand::Private *SignEncryptFilesCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
SignEncryptFilesCommand::Private::Private(SignEncryptFilesCommand *qq, KeyListController *c)
: Command::Private(qq, c)
, files()
, shared_qq(qq, [](SignEncryptFilesCommand *) {})
, controller()
{
- controller.setOperationMode(SignEncryptFilesController::SignSelected //
- | SignEncryptFilesController::EncryptSelected //
+ controller.setOperationMode(SignEncryptFilesController::SignAllowed //
+ | SignEncryptFilesController::EncryptAllowed //
| SignEncryptFilesController::ArchiveAllowed);
}
SignEncryptFilesCommand::Private::~Private()
{
qCDebug(KLEOPATRA_LOG) << q << __func__;
}
SignEncryptFilesCommand::SignEncryptFilesCommand(KeyListController *c)
: Command(new Private(this, c))
{
d->init();
}
SignEncryptFilesCommand::SignEncryptFilesCommand(QAbstractItemView *v, KeyListController *c)
: Command(v, new Private(this, c))
{
d->init();
}
SignEncryptFilesCommand::SignEncryptFilesCommand(const QStringList &files, KeyListController *c)
: Command(new Private(this, c))
{
d->init();
d->files = files;
}
SignEncryptFilesCommand::SignEncryptFilesCommand(const QStringList &files, QAbstractItemView *v, KeyListController *c)
: Command(v, new Private(this, c))
{
d->init();
d->files = files;
}
void SignEncryptFilesCommand::Private::init()
{
controller.setExecutionContext(shared_qq);
connect(&controller, &Controller::done, q, [this]() {
slotControllerDone();
});
connect(&controller, &Controller::error, q, [this](int err, const QString &details) {
slotControllerError(err, details);
});
}
SignEncryptFilesCommand::~SignEncryptFilesCommand()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
}
void SignEncryptFilesCommand::setFiles(const QStringList &files)
{
d->files = files;
}
void SignEncryptFilesCommand::setSigningPolicy(Policy policy)
{
unsigned int mode = d->controller.operationMode();
mode &= ~SignEncryptFilesController::SignMask;
switch (policy) {
case NoPolicy:
case Allow:
mode |= SignEncryptFilesController::SignAllowed;
break;
case Deny:
mode |= SignEncryptFilesController::SignDisallowed;
break;
case Force:
mode |= SignEncryptFilesController::SignSelected;
break;
}
try {
d->controller.setOperationMode(mode);
} catch (...) {
}
}
Policy SignEncryptFilesCommand::signingPolicy() const
{
const unsigned int mode = d->controller.operationMode();
switch (mode & SignEncryptFilesController::SignMask) {
default:
Q_ASSERT(!"This should not happen!");
return NoPolicy;
case SignEncryptFilesController::SignAllowed:
return Allow;
case SignEncryptFilesController::SignSelected:
return Force;
case SignEncryptFilesController::SignDisallowed:
return Deny;
}
}
void SignEncryptFilesCommand::setEncryptionPolicy(Policy policy)
{
unsigned int mode = d->controller.operationMode();
mode &= ~SignEncryptFilesController::EncryptMask;
switch (policy) {
case NoPolicy:
case Allow:
mode |= SignEncryptFilesController::EncryptAllowed;
break;
case Deny:
mode |= SignEncryptFilesController::EncryptDisallowed;
break;
case Force:
mode |= SignEncryptFilesController::EncryptSelected;
break;
}
try {
d->controller.setOperationMode(mode);
} catch (...) {
}
}
Policy SignEncryptFilesCommand::encryptionPolicy() const
{
const unsigned int mode = d->controller.operationMode();
switch (mode & SignEncryptFilesController::EncryptMask) {
default:
Q_ASSERT(!"This should not happen!");
return NoPolicy;
case SignEncryptFilesController::EncryptAllowed:
return Allow;
case SignEncryptFilesController::EncryptSelected:
return Force;
case SignEncryptFilesController::EncryptDisallowed:
return Deny;
}
}
void SignEncryptFilesCommand::setArchivePolicy(Policy policy)
{
unsigned int mode = d->controller.operationMode();
mode &= ~SignEncryptFilesController::ArchiveMask;
switch (policy) {
case NoPolicy:
case Allow:
mode |= SignEncryptFilesController::ArchiveAllowed;
break;
case Deny:
mode |= SignEncryptFilesController::ArchiveDisallowed;
break;
case Force:
mode |= SignEncryptFilesController::ArchiveForced;
break;
}
d->controller.setOperationMode(mode);
}
Policy SignEncryptFilesCommand::archivePolicy() const
{
const unsigned int mode = d->controller.operationMode();
switch (mode & SignEncryptFilesController::ArchiveMask) {
case SignEncryptFilesController::ArchiveAllowed:
return Allow;
case SignEncryptFilesController::ArchiveForced:
return Force;
case SignEncryptFilesController::ArchiveDisallowed:
return Deny;
default:
Q_ASSERT(!"This should not happen!");
return NoPolicy;
}
}
void SignEncryptFilesCommand::setProtocol(GpgME::Protocol proto)
{
d->controller.setProtocol(proto);
}
GpgME::Protocol SignEncryptFilesCommand::protocol() const
{
return d->controller.protocol();
}
void SignEncryptFilesCommand::doStart()
{
try {
if (d->files.empty()) {
d->files = selectFiles();
}
if (d->files.empty()) {
d->finished();
return;
}
d->controller.setFiles(d->files);
d->controller.start();
} catch (const std::exception &e) {
d->information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Sign/Encrypt Files Error"));
d->finished();
}
}
void SignEncryptFilesCommand::doCancel()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
d->controller.cancel();
}
QStringList SignEncryptFilesCommand::selectFiles() const
{
return FileDialog::getOpenFileNames(d->parentWidgetOrView(), i18n("Select One or More Files to Sign and/or Encrypt"), QStringLiteral("enc"));
}
#undef d
#undef q
#include "moc_signencryptfilescommand.cpp"
diff --git a/src/conf/cryptooperationsconfigwidget.cpp b/src/conf/cryptooperationsconfigwidget.cpp
index 48475786e..28f033376 100644
--- a/src/conf/cryptooperationsconfigwidget.cpp
+++ b/src/conf/cryptooperationsconfigwidget.cpp
@@ -1,230 +1,240 @@
/*
This file is part of kleopatra, the KDE key manager
SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 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 "cryptooperationsconfigwidget.h"
#include "kleopatra_debug.h"
#include <settings.h>
#include <Libkleo/ChecksumDefinition>
#include <Libkleo/KeyFilterManager>
#include <libkleo/classifyconfig.h>
#include <KConfig>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <QCheckBox>
#include <QComboBox>
#include <QDir>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QRegularExpression>
#include <QVBoxLayout>
#include <memory>
using namespace Kleo;
using namespace Kleo::Config;
CryptoOperationsConfigWidget::CryptoOperationsConfigWidget(QWidget *p, Qt::WindowFlags f)
: QWidget{p, f}
{
setupGui();
}
void CryptoOperationsConfigWidget::setupGui()
{
auto baseLay = new QVBoxLayout(this);
mPGPFileExtCB = new QCheckBox(i18nc("@option:check", R"(Create OpenPGP encrypted files with ".pgp" file extensions instead of ".gpg")"));
mASCIIArmorCB = new QCheckBox(i18nc("@option:check", "Create signed or encrypted files as text files."));
mASCIIArmorCB->setToolTip(i18nc("@info",
"Set this option to encode encrypted or signed files as base64 encoded text. "
"So that they can be opened with an editor or sent in a mail body. "
"This will increase file size by one third."));
mTreatP7mEmailCB = new QCheckBox(i18nc("@option:check", "Treat .p7m files without extensions as mails."));
mAutoExtractArchivesCB = new QCheckBox(i18nc("@option:check", "Automatically extract file archives after decryption"));
mTmpDirCB = new QCheckBox(i18nc("@option:check", "Create temporary decrypted files in the folder of the encrypted file."));
mTmpDirCB->setToolTip(i18nc("@info", "Set this option to avoid using the users temporary directory."));
+
+ auto encryptionBox = new QGroupBox(i18nc("@title", "Encryption Settings"));
+ auto encryptionLayout = new QVBoxLayout(encryptionBox);
mSymmetricOnlyCB = new QCheckBox(i18nc("@option:check", "Use symmetric encryption only."));
mSymmetricOnlyCB->setToolTip(i18nc("@info", "Set this option to disable public key encryption."));
mPublicKeyOnlyCB = new QCheckBox(i18nc("@option:check", "Use public-key encryption only."));
mPublicKeyOnlyCB->setToolTip(i18nc("@info", "Set this option to disable password-based encryption."));
+ mRememberCB = new QCheckBox(i18nc("@option:check", "Remember settings from last sign / encrypt operation"));
+
+ encryptionLayout->addWidget(mSymmetricOnlyCB);
+ encryptionLayout->addWidget(mPublicKeyOnlyCB);
+ encryptionLayout->addWidget(mRememberCB);
connect(mSymmetricOnlyCB, &QCheckBox::toggled, this, [this]() {
if (mSymmetricOnlyCB->isChecked()) {
mPublicKeyOnlyCB->setChecked(false);
}
});
connect(mPublicKeyOnlyCB, &QCheckBox::toggled, this, [this]() {
if (mPublicKeyOnlyCB->isChecked()) {
mSymmetricOnlyCB->setChecked(false);
}
});
baseLay->addWidget(mPGPFileExtCB);
baseLay->addWidget(mTreatP7mEmailCB);
baseLay->addWidget(mAutoExtractArchivesCB);
baseLay->addWidget(mASCIIArmorCB);
baseLay->addWidget(mTmpDirCB);
- baseLay->addWidget(mSymmetricOnlyCB);
- baseLay->addWidget(mPublicKeyOnlyCB);
+ baseLay->addWidget(encryptionBox);
auto comboLay = new QGridLayout;
mChecksumDefinitionCB.createWidgets(this);
mChecksumDefinitionCB.label()->setText(i18n("Checksum program to use when creating checksum files:"));
comboLay->addWidget(mChecksumDefinitionCB.label(), 0, 0);
comboLay->addWidget(mChecksumDefinitionCB.widget(), 0, 1);
mArchiveDefinitionCB.createWidgets(this);
mArchiveDefinitionCB.label()->setText(i18n("Archive command to use when archiving files:"));
comboLay->addWidget(mArchiveDefinitionCB.label(), 1, 0);
comboLay->addWidget(mArchiveDefinitionCB.widget(), 1, 1);
baseLay->addLayout(comboLay);
baseLay->addStretch(1);
for (const auto checkboxes = findChildren<QCheckBox *>(); auto checkbox : checkboxes) {
connect(checkbox, &QCheckBox::toggled, this, &CryptoOperationsConfigWidget::changed);
}
for (const auto comboboxes = findChildren<QComboBox *>(); auto combobox : comboboxes) {
connect(combobox, &QComboBox::currentIndexChanged, this, &CryptoOperationsConfigWidget::changed);
}
}
CryptoOperationsConfigWidget::~CryptoOperationsConfigWidget()
{
}
void CryptoOperationsConfigWidget::defaults()
{
Settings settings;
settings.setUsePGPFileExt(settings.findItem(QStringLiteral("UsePGPFileExt"))->getDefault().toBool());
settings.setAutoExtractArchives(settings.findItem(QStringLiteral("AutoExtractArchives"))->getDefault().toBool());
settings.setAddASCIIArmor(settings.findItem(QStringLiteral("AddASCIIArmor"))->getDefault().toBool());
settings.setDontUseTmpDir(settings.findItem(QStringLiteral("DontUseTmpDir"))->getDefault().toBool());
settings.setSymmetricEncryptionOnly(settings.findItem(QStringLiteral("SymmetricEncryptionOnly"))->getDefault().toBool());
settings.setPublicKeyEncryptionOnly(settings.findItem(QStringLiteral("PublicKeyEncryptionOnly"))->getDefault().toBool());
settings.setArchiveCommand(settings.findItem(QStringLiteral("ArchiveCommand"))->getDefault().toString());
settings.setChecksumDefinitionId(settings.findItem(QStringLiteral("ChecksumDefinitionId"))->getDefault().toString());
ClassifyConfig classifyConfig;
classifyConfig.setP7mWithoutExtensionAreEmail(classifyConfig.defaultP7mWithoutExtensionAreEmailValue());
load(settings, classifyConfig);
}
void CryptoOperationsConfigWidget::load(const Kleo::Settings &settings, const Kleo::ClassifyConfig &classifyConfig)
{
mPGPFileExtCB->setChecked(settings.usePGPFileExt());
mPGPFileExtCB->setEnabled(!settings.isImmutable(QStringLiteral("UsePGPFileExt")));
mAutoExtractArchivesCB->setChecked(settings.autoExtractArchives());
mAutoExtractArchivesCB->setEnabled(!settings.isImmutable(QStringLiteral("AutoExtractArchives")));
mASCIIArmorCB->setChecked(settings.addASCIIArmor());
mASCIIArmorCB->setEnabled(!settings.isImmutable(QStringLiteral("AddASCIIArmor")));
mTmpDirCB->setChecked(settings.dontUseTmpDir());
mTmpDirCB->setEnabled(!settings.isImmutable(QStringLiteral("DontUseTmpDir")));
mSymmetricOnlyCB->setChecked(settings.symmetricEncryptionOnly());
mSymmetricOnlyCB->setEnabled(!settings.isImmutable(QStringLiteral("SymmetricEncryptionOnly")));
mPublicKeyOnlyCB->setChecked(settings.publicKeyEncryptionOnly());
mPublicKeyOnlyCB->setEnabled(!settings.isPublicKeyEncryptionOnlyImmutable());
mTreatP7mEmailCB->setChecked(classifyConfig.p7mWithoutExtensionAreEmail());
mTreatP7mEmailCB->setEnabled(!classifyConfig.isP7mWithoutExtensionAreEmailImmutable());
+ mRememberCB->setChecked(settings.restoreSignEncryptValues());
+ mRememberCB->setEnabled(!settings.isRestoreSignEncryptValuesImmutable());
const auto defaultChecksumDefinitionId = settings.checksumDefinitionId();
{
const auto index = mChecksumDefinitionCB.widget()->findData(defaultChecksumDefinitionId);
if (index >= 0) {
mChecksumDefinitionCB.widget()->setCurrentIndex(index);
} else {
qCWarning(KLEOPATRA_LOG) << "No checksum definition found with id" << defaultChecksumDefinitionId;
}
}
mChecksumDefinitionCB.setEnabled(!settings.isImmutable(QStringLiteral("ChecksumDefinitionId")));
const auto ad_default_id = settings.archiveCommand();
{
const auto index = mArchiveDefinitionCB.widget()->findData(ad_default_id);
if (index >= 0) {
mArchiveDefinitionCB.widget()->setCurrentIndex(index);
} else {
qCWarning(KLEOPATRA_LOG) << "No archive definition found with id" << ad_default_id;
}
}
mArchiveDefinitionCB.setEnabled(!settings.isImmutable(QStringLiteral("ArchiveCommand")));
}
void CryptoOperationsConfigWidget::load()
{
mChecksumDefinitionCB.widget()->clear();
const auto cds = ChecksumDefinition::getChecksumDefinitions();
for (const std::shared_ptr<ChecksumDefinition> &cd : cds) {
mChecksumDefinitionCB.widget()->addItem(cd->label(), QVariant{cd->id()});
}
// This is a weird hack but because we are a KCM we can't link
// against ArchiveDefinition which pulls in loads of other classes.
// So we do the parsing which archive definitions exist here ourself.
mArchiveDefinitionCB.widget()->clear();
if (KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"))) {
const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Archive Definition #")));
for (const QString &group : groups) {
const KConfigGroup cGroup(config, group);
const QString id = cGroup.readEntryUntranslated(QStringLiteral("id"));
const QString name = cGroup.readEntry("Name");
mArchiveDefinitionCB.widget()->addItem(name, QVariant(id));
}
}
load(Settings{}, ClassifyConfig{});
}
void CryptoOperationsConfigWidget::save()
{
Settings settings;
settings.setUsePGPFileExt(mPGPFileExtCB->isChecked());
settings.setAutoExtractArchives(mAutoExtractArchivesCB->isChecked());
settings.setAddASCIIArmor(mASCIIArmorCB->isChecked());
settings.setDontUseTmpDir(mTmpDirCB->isChecked());
settings.setSymmetricEncryptionOnly(mSymmetricOnlyCB->isChecked());
settings.setPublicKeyEncryptionOnly(mPublicKeyOnlyCB->isChecked());
+ settings.setRestoreSignEncryptValues(mRememberCB->isChecked());
const int idx = mChecksumDefinitionCB.widget()->currentIndex();
if (idx >= 0) {
const auto id = mChecksumDefinitionCB.widget()->itemData(idx).toString();
settings.setChecksumDefinitionId(id);
}
const int aidx = mArchiveDefinitionCB.widget()->currentIndex();
if (aidx >= 0) {
const QString id = mArchiveDefinitionCB.widget()->itemData(aidx).toString();
settings.setArchiveCommand(id);
}
settings.save();
ClassifyConfig classifyConfig;
classifyConfig.setP7mWithoutExtensionAreEmail(mTreatP7mEmailCB->isChecked());
classifyConfig.save();
}
#include "moc_cryptooperationsconfigwidget.cpp"
diff --git a/src/conf/cryptooperationsconfigwidget.h b/src/conf/cryptooperationsconfigwidget.h
index 8e3405622..57c684278 100644
--- a/src/conf/cryptooperationsconfigwidget.h
+++ b/src/conf/cryptooperationsconfigwidget.h
@@ -1,59 +1,60 @@
/*
This file is part of kleopatra, the KDE key manager
SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "labelledwidget.h"
#include <QWidget>
class QCheckBox;
class QComboBox;
class QPushButton;
namespace Kleo
{
class Settings;
class ClassifyConfig;
namespace Config
{
class CryptoOperationsConfigWidget : public QWidget
{
Q_OBJECT
public:
explicit CryptoOperationsConfigWidget(QWidget *parent = nullptr, Qt::WindowFlags f = {});
~CryptoOperationsConfigWidget() override;
void load();
void save();
void defaults();
Q_SIGNALS:
void changed();
private:
void setupGui();
void load(const Kleo::Settings &settings, const Kleo::ClassifyConfig &classifyConfig);
private:
QCheckBox *mPGPFileExtCB = nullptr;
QCheckBox *mTreatP7mEmailCB = nullptr;
QCheckBox *mAutoExtractArchivesCB = nullptr;
QCheckBox *mASCIIArmorCB = nullptr;
QCheckBox *mTmpDirCB = nullptr;
QCheckBox *mSymmetricOnlyCB = nullptr;
QCheckBox *mPublicKeyOnlyCB = nullptr;
+ QCheckBox *mRememberCB = nullptr;
Kleo::LabelledWidget<QComboBox> mChecksumDefinitionCB;
Kleo::LabelledWidget<QComboBox> mArchiveDefinitionCB;
QPushButton *mApplyBtn = nullptr;
};
}
}
diff --git a/src/crypto/gui/signencryptwidget.cpp b/src/crypto/gui/signencryptwidget.cpp
index 3bed5e0d9..14e9c9edb 100644
--- a/src/crypto/gui/signencryptwidget.cpp
+++ b/src/crypto/gui/signencryptwidget.cpp
@@ -1,1049 +1,1070 @@
/*
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 "signencryptwidget.h"
#include "kleopatra_debug.h"
#include "certificatelineedit.h"
#include "kleopatraapplication.h"
#include "unknownrecipientwidget.h"
#include "utils/gui-helper.h"
#include <settings.h>
#include <QButtonGroup>
#include <QCheckBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QRadioButton>
#include <QScrollArea>
#include <QScrollBar>
#include <QVBoxLayout>
#include <Libkleo/Algorithm>
#include <Libkleo/Compliance>
#include <Libkleo/DefaultKeyFilter>
#include <Libkleo/ExpiryChecker>
#include <Libkleo/ExpiryCheckerConfig>
#include <Libkleo/ExpiryCheckerSettings>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/GnuPG>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageWidget>
#include <KSeparator>
#include <KSharedConfig>
using namespace Kleo;
using namespace Kleo::Dialogs;
using namespace GpgME;
+using namespace Qt::Literals::StringLiterals;
namespace
{
class SignCertificateFilter : public DefaultKeyFilter
{
public:
SignCertificateFilter(GpgME::Protocol proto)
: DefaultKeyFilter()
{
setIsBad(DefaultKeyFilter::NotSet);
setHasSecret(DefaultKeyFilter::Set);
setCanSign(DefaultKeyFilter::Set);
setValidIfSMIME(DefaultKeyFilter::Set);
if (proto == GpgME::OpenPGP) {
setIsOpenPGP(DefaultKeyFilter::Set);
} else if (proto == GpgME::CMS) {
setIsOpenPGP(DefaultKeyFilter::NotSet);
}
}
};
class EncryptCertificateFilter : public DefaultKeyFilter
{
public:
EncryptCertificateFilter(GpgME::Protocol proto)
: DefaultKeyFilter()
{
setIsBad(DefaultKeyFilter::NotSet);
setCanEncrypt(DefaultKeyFilter::Set);
setValidIfSMIME(DefaultKeyFilter::Set);
if (proto == GpgME::OpenPGP) {
setIsOpenPGP(DefaultKeyFilter::Set);
} else if (proto == GpgME::CMS) {
setIsOpenPGP(DefaultKeyFilter::NotSet);
}
}
};
class EncryptSelfCertificateFilter : public EncryptCertificateFilter
{
public:
EncryptSelfCertificateFilter(GpgME::Protocol proto)
: EncryptCertificateFilter(proto)
{
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setCanEncrypt(DefaultKeyFilter::Set);
setHasSecret(DefaultKeyFilter::Set);
setValidIfSMIME(DefaultKeyFilter::Set);
}
};
}
class SignEncryptWidget::Private
{
SignEncryptWidget *const q;
public:
struct RecipientWidgets {
CertificateLineEdit *edit;
KMessageWidget *expiryMessage;
};
explicit Private(SignEncryptWidget *qq, bool sigEncExclusive)
: q{qq}
, mModel{AbstractKeyListModel::createFlatKeyListModel(qq)}
, mIsExclusive{sigEncExclusive}
{
}
CertificateLineEdit *addRecipientWidget();
/* Inserts a new recipient widget after widget @p after or at the end
* if @p after is null. */
CertificateLineEdit *insertRecipientWidget(CertificateLineEdit *after);
void recpRemovalRequested(const RecipientWidgets &recipient);
void onProtocolChanged();
void updateCheckBoxes();
ExpiryChecker *expiryChecker();
void updateExpiryMessages(KMessageWidget *w, const GpgME::UserID &userID, ExpiryChecker::CheckFlags flags);
void updateAllExpiryMessages();
public:
UserIDSelectionCombo *mSigSelect = nullptr;
KMessageWidget *mSignKeyExpiryMessage = nullptr;
UserIDSelectionCombo *mSelfSelect = nullptr;
KMessageWidget *mEncryptToSelfKeyExpiryMessage = nullptr;
std::vector<RecipientWidgets> mRecpWidgets;
QList<UnknownRecipientWidget *> mUnknownWidgets;
QList<GpgME::Key> mAddedKeys;
QList<KeyGroup> mAddedGroups;
QVBoxLayout *mRecpLayout = nullptr;
Operations mOp;
AbstractKeyListModel *mModel = nullptr;
QCheckBox *mSymmetric = nullptr;
QCheckBox *mSigChk = nullptr;
QCheckBox *mEncOtherChk = nullptr;
QCheckBox *mEncSelfChk = nullptr;
GpgME::Protocol mCurrentProto = GpgME::UnknownProtocol;
const bool mIsExclusive;
std::unique_ptr<ExpiryChecker> mExpiryChecker;
QRadioButton *mPGPRB = nullptr;
QRadioButton *mCMSRB = nullptr;
QLabel *mSymmetricLabel = nullptr;
+ KSharedConfig::Ptr mStateConfig = KSharedConfig::openStateConfig();
};
SignEncryptWidget::SignEncryptWidget(QWidget *parent, bool sigEncExclusive)
: QWidget{parent}
, d{new Private{this, sigEncExclusive}}
{
auto lay = new QVBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
d->mModel->useKeyCache(true, KeyList::IncludeGroups);
const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
const bool havePublicKeys = !KeyCache::instance()->keys().empty();
const bool symmetricOnly = Settings().symmetricEncryptionOnly();
const bool publicKeyOnly = Settings().publicKeyEncryptionOnly();
bool pgpOnly = KeyCache::instance()->pgpOnly();
if (!pgpOnly && Settings{}.cmsEnabled() && sigEncExclusive) {
auto protocolSelectionLay = new QHBoxLayout;
auto protocolLabel = new QLabel(i18nc("@label", "Protocol:"));
auto font = protocolLabel->font();
font.setWeight(QFont::DemiBold);
protocolLabel->setFont(font);
lay->addWidget(protocolLabel);
lay->addLayout(protocolSelectionLay);
lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3);
auto grp = new QButtonGroup;
d->mPGPRB = new QRadioButton(i18nc("@option:radio", "OpenPGP"));
d->mCMSRB = new QRadioButton(i18nc("@option:radio", "S/MIME"));
grp->addButton(d->mPGPRB);
grp->addButton(d->mCMSRB);
protocolSelectionLay->addWidget(d->mPGPRB);
protocolSelectionLay->addWidget(d->mCMSRB);
connect(d->mPGPRB, &QRadioButton::toggled, this, [this](bool value) {
if (value) {
setProtocol(GpgME::OpenPGP);
KConfigGroup(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptWidget")).writeEntry("wasCMS", false);
}
});
connect(d->mCMSRB, &QRadioButton::toggled, this, [this](bool value) {
if (value) {
setProtocol(GpgME::CMS);
KConfigGroup(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptWidget")).writeEntry("wasCMS", true);
}
});
}
/* The signature selection */
{
d->mSigChk = new QCheckBox{i18n("Sign &as:"), this};
d->mSigChk->setEnabled(haveSecretKeys);
- d->mSigChk->setChecked(haveSecretKeys);
+ d->mSigChk->setChecked(
+ haveSecretKeys && (!Settings().restoreSignEncryptValues() || d->mStateConfig->group(u"SignEncryptWidget"_s).readEntry(u"SignAsChecked"_s, true)));
auto checkFont = d->mSigChk->font();
checkFont.setWeight(QFont::DemiBold);
d->mSigChk->setFont(checkFont);
lay->addWidget(d->mSigChk);
d->mSigSelect = new UserIDSelectionCombo{KeyUsage::Sign, this};
d->mSigSelect->setEnabled(d->mSigChk->isChecked());
lay->addWidget(d->mSigSelect);
d->mSignKeyExpiryMessage = new KMessageWidget{this};
d->mSignKeyExpiryMessage->setVisible(false);
lay->addWidget(d->mSignKeyExpiryMessage);
connect(d->mSigSelect, &UserIDSelectionCombo::certificateSelectionRequested, this, [this]() {
ownCertificateSelectionRequested(CertificateSelectionDialog::SignOnly, d->mSigSelect);
});
connect(d->mSigSelect, &UserIDSelectionCombo::customItemSelected, this, [this]() {
updateOp();
d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey);
});
connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool checked) {
d->mSigSelect->setEnabled(checked);
updateOp();
d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey);
+ d->mStateConfig->group(u"SignEncryptWidget"_s).writeEntry(u"SignAsChecked"_s, checked);
+ d->mStateConfig->sync();
});
connect(d->mSigSelect, &UserIDSelectionCombo::currentKeyChanged, this, [this]() {
updateOp();
d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey);
});
lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3);
}
// Recipient selection
{
// Own key
d->mEncSelfChk = new QCheckBox{i18n("Encrypt for me:"), this};
d->mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly);
- d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly);
+ d->mEncSelfChk->setChecked(
+ haveSecretKeys && !symmetricOnly
+ && (!Settings().restoreSignEncryptValues() || d->mStateConfig->group(u"SignEncryptWidget"_s).readEntry(u"EncryptForMeChecked"_s, true)));
auto checkFont = d->mEncSelfChk->font();
checkFont.setWeight(QFont::DemiBold);
d->mEncSelfChk->setFont(checkFont);
lay->addWidget(d->mEncSelfChk);
d->mSelfSelect = new UserIDSelectionCombo{KeyUsage::Encrypt, this};
d->mSelfSelect->setEnabled(d->mEncSelfChk->isChecked());
lay->addWidget(d->mSelfSelect);
d->mEncryptToSelfKeyExpiryMessage = new KMessageWidget{this};
d->mEncryptToSelfKeyExpiryMessage->setVisible(false);
lay->addWidget(d->mEncryptToSelfKeyExpiryMessage);
connect(d->mSelfSelect, &UserIDSelectionCombo::certificateSelectionRequested, this, [this]() {
ownCertificateSelectionRequested(CertificateSelectionDialog::EncryptOnly, d->mSelfSelect);
});
connect(d->mSelfSelect, &UserIDSelectionCombo::customItemSelected, this, [this]() {
updateOp();
d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey);
});
connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool checked) {
d->mSelfSelect->setEnabled(checked);
+ d->mStateConfig->group(u"SignEncryptWidget"_s).writeEntry(u"EncryptForMeChecked"_s, checked);
+ d->mStateConfig->sync();
});
lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3);
// Checkbox for other keys
d->mEncOtherChk = new QCheckBox(i18nc("@label", "Encrypt for others:"), this);
d->mEncOtherChk->setEnabled(havePublicKeys && !symmetricOnly);
- d->mEncOtherChk->setChecked(d->mEncOtherChk->isEnabled());
+ d->mEncOtherChk->setChecked(
+ d->mEncOtherChk->isEnabled()
+ && (!Settings().restoreSignEncryptValues() || d->mStateConfig->group(u"SignEncryptWidget"_s).readEntry(u"EncryptForOthersChecked"_s, true)));
d->mEncOtherChk->setFont(checkFont);
lay->addWidget(d->mEncOtherChk);
d->mRecpLayout = new QVBoxLayout;
lay->addLayout(d->mRecpLayout);
d->addRecipientWidget();
lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3);
// Checkbox for password
d->mSymmetric = new QCheckBox(i18nc("@option:check", "Encrypt with password:"));
d->mSymmetric->setFont(checkFont);
d->mSymmetric->setToolTip(i18nc("Tooltip information for symmetric encryption",
"Additionally to the keys of the recipients you can encrypt your data with a password. "
"Anyone who has the password can read the data without any secret key. "
"Using a password is <b>less secure</b> then public key cryptography. Even if you pick a very strong password."));
- d->mSymmetric->setChecked((symmetricOnly || !havePublicKeys) && !publicKeyOnly);
+ d->mSymmetric->setChecked(
+ ((symmetricOnly || !havePublicKeys) && !publicKeyOnly)
+ || (Settings().restoreSignEncryptValues() && d->mStateConfig->group(u"SignEncryptWidget"_s).readEntry(u"EncryptSymmetricChecked"_s, true)));
d->mSymmetric->setEnabled(!publicKeyOnly);
lay->addWidget(d->mSymmetric);
d->mSymmetricLabel = new QLabel(i18nc("@info", "Anyone you share the password with can read the data."));
d->mSymmetricLabel->setEnabled(!publicKeyOnly);
lay->addWidget(d->mSymmetricLabel);
lay->addStretch();
// Connect it
connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool checked) {
d->mSelfSelect->setEnabled(checked);
updateOp();
d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey);
});
connect(d->mSelfSelect, &UserIDSelectionCombo::currentKeyChanged, this, [this]() {
updateOp();
d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey);
});
connect(d->mEncOtherChk, &QCheckBox::toggled, this, [this](const auto enabled) {
for (const auto &widget : d->mRecpWidgets) {
widget.edit->setEnabled(enabled);
}
+ d->mStateConfig->group(u"SignEncryptWidget"_s).writeEntry(u"EncryptForOthersChecked"_s, enabled);
+ d->mStateConfig->sync();
+ updateOp();
+ });
+ connect(d->mSymmetric, &QCheckBox::toggled, this, [this](bool checked) {
updateOp();
+ d->mStateConfig->group(u"SignEncryptWidget"_s).writeEntry(u"EncryptSymmetricChecked"_s, checked);
+ d->mStateConfig->sync();
});
- connect(d->mSymmetric, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp);
if (d->mIsExclusive) {
connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool value) {
if (d->mCurrentProto != GpgME::CMS) {
return;
}
if (value) {
d->mSigChk->setChecked(false);
}
});
connect(d->mEncOtherChk, &QCheckBox::toggled, this, [this](bool value) {
if (d->mCurrentProto != GpgME::CMS || !value) {
return;
}
d->mSigChk->setChecked(false);
});
connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool value) {
if (d->mCurrentProto != GpgME::CMS) {
return;
}
if (value) {
d->mEncSelfChk->setChecked(false);
d->mEncOtherChk->setChecked(false);
// Copying the vector makes sure that all items are actually deleted.
for (const auto &widget : std::vector(d->mRecpWidgets)) {
d->recpRemovalRequested(widget);
}
d->addRecipientWidget();
d->mRecpWidgets[0].edit->setEnabled(false);
}
});
}
// Ensure that the d->mSigChk is aligned together with the encryption check boxes.
d->mSigChk->setMinimumWidth(qMax(d->mEncOtherChk->width(), d->mEncSelfChk->width()));
}
connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() {
d->updateCheckBoxes();
d->updateAllExpiryMessages();
});
connect(KleopatraApplication::instance(), &KleopatraApplication::configurationChanged, this, [this]() {
d->updateCheckBoxes();
d->mExpiryChecker.reset();
d->updateAllExpiryMessages();
});
if (!pgpOnly && Settings{}.cmsEnabled() && sigEncExclusive) {
- KConfigGroup config(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptWidget"));
+ KConfigGroup config(d->mStateConfig, QStringLiteral("SignEncryptWidget"));
if (config.readEntry("wasCMS", false)) {
d->mCMSRB->setChecked(true);
setProtocol(GpgME::CMS);
} else {
d->mPGPRB->setChecked(true);
setProtocol(GpgME::OpenPGP);
}
}
if (pgpOnly || !Settings{}.cmsEnabled()) {
setProtocol(GpgME::OpenPGP);
}
loadKeys();
d->onProtocolChanged();
updateOp();
}
SignEncryptWidget::~SignEncryptWidget() = default;
void SignEncryptWidget::setSignAsText(const QString &text)
{
d->mSigChk->setText(text);
}
void SignEncryptWidget::setEncryptForMeText(const QString &text)
{
d->mEncSelfChk->setText(text);
}
void SignEncryptWidget::setEncryptForOthersText(const QString &text)
{
d->mEncOtherChk->setText(text);
}
void SignEncryptWidget::setEncryptWithPasswordText(const QString &text)
{
d->mSymmetric->setText(text);
}
CertificateLineEdit *SignEncryptWidget::Private::addRecipientWidget()
{
return insertRecipientWidget(nullptr);
}
CertificateLineEdit *SignEncryptWidget::Private::insertRecipientWidget(CertificateLineEdit *after)
{
Q_ASSERT(!after || mRecpLayout->indexOf(after) != -1);
const auto index = after ? mRecpLayout->indexOf(after) + 2 : mRecpLayout->count();
const RecipientWidgets recipient{new CertificateLineEdit{mModel, KeyUsage::Encrypt, new EncryptCertificateFilter{mCurrentProto}, q}, new KMessageWidget{q}};
recipient.edit->setAccessibleNameOfLineEdit(i18nc("text for screen readers", "recipient key"));
recipient.edit->setEnabled(!KeyCache::instance()->keys().empty() && !Settings().symmetricEncryptionOnly());
recipient.expiryMessage->setVisible(false);
if (static_cast<unsigned>(index / 2) < mRecpWidgets.size()) {
mRecpWidgets.insert(mRecpWidgets.begin() + index / 2, recipient);
} else {
mRecpWidgets.push_back(recipient);
}
if (mRecpLayout->count() > 0) {
auto prevWidget = after ? after : mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget();
Kleo::forceSetTabOrder(prevWidget, recipient.edit);
Kleo::forceSetTabOrder(recipient.edit, recipient.expiryMessage);
} else {
Kleo::forceSetTabOrder(mEncryptToSelfKeyExpiryMessage, recipient.edit);
Kleo::forceSetTabOrder(recipient.edit, recipient.expiryMessage);
}
Kleo::forceSetTabOrder(recipient.expiryMessage, mSymmetric);
mRecpLayout->insertWidget(index, recipient.edit);
mRecpLayout->insertWidget(index + 1, recipient.expiryMessage);
connect(recipient.edit, &CertificateLineEdit::keyChanged, q, &SignEncryptWidget::recipientsChanged);
connect(recipient.edit, &CertificateLineEdit::editingStarted, q, &SignEncryptWidget::recipientsChanged);
connect(recipient.edit, &CertificateLineEdit::cleared, q, &SignEncryptWidget::recipientsChanged);
connect(recipient.edit, &CertificateLineEdit::certificateSelectionRequested, q, [this, recipient]() {
q->certificateSelectionRequested(recipient.edit);
});
if (mIsExclusive) {
connect(recipient.edit, &CertificateLineEdit::keyChanged, q, [this]() {
if (mCurrentProto != GpgME::CMS) {
return;
}
mSigChk->setChecked(false);
});
}
updateAllExpiryMessages();
return recipient.edit;
}
void SignEncryptWidget::addRecipient(const Key &key)
{
CertificateLineEdit *certSel = d->addRecipientWidget();
if (!key.isNull()) {
certSel->setKey(key);
d->mAddedKeys << key;
}
}
void SignEncryptWidget::addRecipient(const KeyGroup &group)
{
CertificateLineEdit *certSel = d->addRecipientWidget();
if (!group.isNull()) {
certSel->setGroup(group);
d->mAddedGroups << group;
}
}
void SignEncryptWidget::ownCertificateSelectionRequested(CertificateSelectionDialog::Options options, UserIDSelectionCombo *combo)
{
CertificateSelectionDialog dialog{this};
dialog.setOptions(CertificateSelectionDialog::Options( //
CertificateSelectionDialog::SingleSelection | //
CertificateSelectionDialog::SecretKeys | //
CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto))
| //
options);
auto keyFilter = std::make_shared<DefaultKeyFilter>();
keyFilter->setMatchContexts(KeyFilter::Filtering);
keyFilter->setIsBad(DefaultKeyFilter::NotSet);
if (options & CertificateSelectionDialog::SignOnly) {
keyFilter->setName(i18n("Usable for Signing"));
keyFilter->setDescription(i18n("Certificates that can be used for signing"));
keyFilter->setId(QStringLiteral("CanSignFilter"));
} else {
keyFilter->setName(i18n("Usable for Encryption"));
keyFilter->setDescription(i18n("Certificates that can be used for encryption"));
keyFilter->setId(QStringLiteral("CanEncryptFilter"));
}
dialog.addCustomKeyFilter(keyFilter);
dialog.setKeyFilter(keyFilter);
if (dialog.exec()) {
auto userId = dialog.selectedUserIDs()[0];
auto index = combo->findUserId(userId);
if (index == -1) {
combo->appendCustomItem(Formatting::errorIcon(), Formatting::summaryLine(userId), QVariant::fromValue(userId));
index = combo->combo()->count() - 1;
}
combo->combo()->setCurrentIndex(index);
}
recipientsChanged();
}
void SignEncryptWidget::certificateSelectionRequested(CertificateLineEdit *certificateLineEdit)
{
CertificateSelectionDialog dlg{this};
dlg.setOptions(CertificateSelectionDialog::Options( //
CertificateSelectionDialog::MultiSelection | //
CertificateSelectionDialog::EncryptOnly | //
CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto) | //
CertificateSelectionDialog::IncludeGroups));
auto keyFilter = std::make_shared<DefaultKeyFilter>();
keyFilter->setMatchContexts(KeyFilter::Filtering);
keyFilter->setIsBad(DefaultKeyFilter::NotSet);
keyFilter->setName(i18n("Usable for Encryption"));
keyFilter->setDescription(i18n("Certificates that can be used for encryption"));
keyFilter->setId(QStringLiteral("CanEncryptFilter"));
dlg.addCustomKeyFilter(keyFilter);
dlg.setKeyFilter(keyFilter);
if (!certificateLineEdit->key().isNull()) {
const auto key = certificateLineEdit->key();
const auto name = QString::fromUtf8(key.userID(0).name());
const auto email = QString::fromUtf8(key.userID(0).email());
dlg.setStringFilter(!name.isEmpty() ? name : email);
} else if (!certificateLineEdit->group().isNull()) {
dlg.setStringFilter(certificateLineEdit->group().name());
} else if (!certificateLineEdit->userID().isNull()) {
const auto userID = certificateLineEdit->userID();
const auto name = QString::fromUtf8(userID.name());
const auto email = QString::fromUtf8(userID.email());
dlg.setStringFilter(!name.isEmpty() ? name : email);
} else {
dlg.setStringFilter(certificateLineEdit->text());
}
if (dlg.exec()) {
const std::vector<UserID> userIds = dlg.selectedUserIDs();
const std::vector<KeyGroup> groups = dlg.selectedGroups();
if (userIds.size() == 0 && groups.size() == 0) {
return;
}
CertificateLineEdit *certWidget = nullptr;
for (const auto &userId : userIds) {
if (!certWidget) {
certWidget = certificateLineEdit;
} else {
certWidget = d->insertRecipientWidget(certWidget);
}
certWidget->setUserID(userId);
}
for (const KeyGroup &group : groups) {
if (!certWidget) {
certWidget = certificateLineEdit;
} else {
certWidget = d->insertRecipientWidget(certWidget);
}
certWidget->setGroup(group);
}
}
recipientsChanged();
}
void SignEncryptWidget::clearAddedRecipients()
{
for (auto w : std::as_const(d->mUnknownWidgets)) {
d->mRecpLayout->removeWidget(w);
delete w;
}
for (auto &key : std::as_const(d->mAddedKeys)) {
removeRecipient(key);
}
for (auto &group : std::as_const(d->mAddedGroups)) {
removeRecipient(group);
}
}
void SignEncryptWidget::addUnknownRecipient(const char *keyID)
{
auto unknownWidget = new UnknownRecipientWidget(keyID);
d->mUnknownWidgets << unknownWidget;
if (d->mRecpLayout->count() > 0) {
auto lastWidget = d->mRecpLayout->itemAt(d->mRecpLayout->count() - 1)->widget();
setTabOrder(lastWidget, unknownWidget);
}
d->mRecpLayout->addWidget(unknownWidget);
connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() {
// Check if any unknown recipient can now be found.
for (auto w : std::as_const(d->mUnknownWidgets)) {
auto key = KeyCache::instance()->findByKeyIDOrFingerprint(w->keyID().toLatin1().constData());
if (key.isNull()) {
std::vector<std::string> subids;
subids.push_back(std::string(w->keyID().toLatin1().constData()));
for (const auto &subkey : KeyCache::instance()->findSubkeysByKeyID(subids)) {
key = subkey.parent();
}
}
if (key.isNull()) {
continue;
}
// Key is now available replace by line edit.
qCDebug(KLEOPATRA_LOG) << "Removing widget for keyid: " << w->keyID();
d->mRecpLayout->removeWidget(w);
d->mUnknownWidgets.removeAll(w);
delete w;
addRecipient(key);
}
});
}
void SignEncryptWidget::recipientsChanged()
{
const bool hasEmptyRecpWidget = std::any_of(std::cbegin(d->mRecpWidgets), std::cend(d->mRecpWidgets), [](auto w) {
return w.edit->isEmpty();
});
if (!hasEmptyRecpWidget) {
d->addRecipientWidget();
}
updateOp();
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
if (!recipient.edit->isEditingInProgress() || recipient.edit->isEmpty()) {
d->updateExpiryMessages(recipient.expiryMessage, recipient.edit->userID(), ExpiryChecker::EncryptionKey);
}
}
}
UserID SignEncryptWidget::signUserId() const
{
if (d->mSigSelect->isEnabled()) {
return d->mSigSelect->currentUserID();
}
return UserID();
}
UserID SignEncryptWidget::selfUserId() const
{
if (d->mSelfSelect->isEnabled()) {
return d->mSelfSelect->currentUserID();
}
return UserID();
}
std::vector<Key> SignEncryptWidget::recipients() const
{
std::vector<Key> ret;
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
const auto *const w = recipient.edit;
if (!w->isEnabled()) {
// If one is disabled, all are disabled.
break;
}
const Key k = w->key();
const KeyGroup g = w->group();
const UserID u = w->userID();
if (!k.isNull()) {
ret.push_back(k);
} else if (!g.isNull()) {
const auto keys = g.keys();
std::copy(keys.begin(), keys.end(), std::back_inserter(ret));
} else if (!u.isNull()) {
ret.push_back(u.parent());
}
}
const Key k = selfUserId().parent();
if (!k.isNull()) {
ret.push_back(k);
}
return ret;
}
bool SignEncryptWidget::isDeVsAndValid() const
{
if (!signUserId().isNull() && !DeVSCompliance::userIDIsCompliant(signUserId())) {
return false;
}
if (!selfUserId().isNull() && !DeVSCompliance::userIDIsCompliant(selfUserId())) {
return false;
}
for (const auto &key : recipients()) {
if (!DeVSCompliance::keyIsCompliant(key)) {
return false;
}
}
return true;
}
static QString expiryMessage(const ExpiryChecker::Result &result)
{
if (result.expiration.certificate.isNull()) {
return {};
}
switch (result.expiration.status) {
case ExpiryChecker::Expired:
return i18nc("@info", "This certificate is expired.");
case ExpiryChecker::ExpiresSoon: {
if (result.expiration.duration.count() == 0) {
return i18nc("@info", "This certificate expires today.");
} else {
return i18ncp("@info", "This certificate expires tomorrow.", "This certificate expires in %1 days.", result.expiration.duration.count());
}
}
case ExpiryChecker::NoSuitableSubkey:
if (result.checkFlags & ExpiryChecker::EncryptionKey) {
return i18nc("@info", "This certificate cannot be used for encryption.");
} else {
return i18nc("@info", "This certificate cannot be used for signing.");
}
case ExpiryChecker::InvalidKey:
case ExpiryChecker::InvalidCheckFlags:
break; // wrong usage of ExpiryChecker; can be ignored
case ExpiryChecker::NotNearExpiry:;
}
return {};
}
void SignEncryptWidget::updateOp()
{
Operations op = NoOperation;
if (!signUserId().isNull()) {
op |= Sign;
}
if (d->mEncSelfChk->isChecked() || d->mEncOtherChk->isChecked() || encryptSymmetric()) {
op |= Encrypt;
}
d->mOp = op;
Q_EMIT operationChanged(d->mOp);
Q_EMIT keysChanged();
}
SignEncryptWidget::Operations SignEncryptWidget::currentOp() const
{
return d->mOp;
}
namespace
{
bool recipientWidgetHasFocus(QWidget *w)
{
// check if w (or its focus proxy) or a child widget of w has focus
return w->hasFocus() || w->isAncestorOf(qApp->focusWidget());
}
}
void SignEncryptWidget::Private::recpRemovalRequested(const RecipientWidgets &recipient)
{
if (!recipient.edit) {
return;
}
if (recipientWidgetHasFocus(recipient.edit) || recipientWidgetHasFocus(recipient.expiryMessage)) {
const int index = mRecpLayout->indexOf(recipient.edit);
const auto focusWidget = (index < mRecpLayout->count() - 2) ? //
mRecpLayout->itemAt(index + 2)->widget()
: mRecpLayout->itemAt(mRecpLayout->count() - 3)->widget();
focusWidget->setFocus();
}
mRecpLayout->removeWidget(recipient.expiryMessage);
mRecpLayout->removeWidget(recipient.edit);
const auto it = std::find_if(std::begin(mRecpWidgets), std::end(mRecpWidgets), [recipient](const auto &r) {
return r.edit == recipient.edit;
});
mRecpWidgets.erase(it);
recipient.expiryMessage->deleteLater();
recipient.edit->deleteLater();
}
void SignEncryptWidget::removeRecipient(const GpgME::Key &key)
{
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
const auto editKey = recipient.edit->key();
if (key.isNull() && editKey.isNull()) {
d->recpRemovalRequested(recipient);
return;
}
if (editKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp(editKey.primaryFingerprint(), key.primaryFingerprint())) {
d->recpRemovalRequested(recipient);
return;
}
}
}
void SignEncryptWidget::removeRecipient(const KeyGroup &group)
{
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
const auto editGroup = recipient.edit->group();
if (group.isNull() && editGroup.isNull()) {
d->recpRemovalRequested(recipient);
return;
}
if (editGroup.name() == group.name()) {
d->recpRemovalRequested(recipient);
return;
}
}
}
bool SignEncryptWidget::encryptSymmetric() const
{
return d->mSymmetric->isChecked();
}
void SignEncryptWidget::loadKeys()
{
KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys"));
auto cache = KeyCache::instance();
d->mSigSelect->setDefaultKey(keys.readEntry("SigningKey", QString()));
d->mSelfSelect->setDefaultKey(keys.readEntry("EncryptKey", QString()));
}
void SignEncryptWidget::saveOwnKeys() const
{
KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys"));
auto sigKey = d->mSigSelect->currentKey();
auto encKey = d->mSelfSelect->currentKey();
if (!sigKey.isNull()) {
keys.writeEntry("SigningKey", sigKey.primaryFingerprint());
}
if (!encKey.isNull()) {
keys.writeEntry("EncryptKey", encKey.primaryFingerprint());
}
}
void SignEncryptWidget::setSigningChecked(bool value)
{
d->mSigChk->setChecked(value && !KeyCache::instance()->secretKeys().empty());
}
void SignEncryptWidget::setEncryptionChecked(bool checked)
{
if (checked) {
const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
const bool havePublicKeys = !KeyCache::instance()->keys().empty();
const bool symmetricOnly = Settings().symmetricEncryptionOnly();
const bool publicKeyOnly = Settings().publicKeyEncryptionOnly();
d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly);
+ d->mEncOtherChk->setChecked(!symmetricOnly && havePublicKeys);
d->mSymmetric->setChecked((symmetricOnly || !havePublicKeys) && !publicKeyOnly);
} else {
d->mEncSelfChk->setChecked(false);
+ d->mEncOtherChk->setChecked(false);
d->mSymmetric->setChecked(false);
}
}
void SignEncryptWidget::setProtocol(GpgME::Protocol proto)
{
if (d->mCurrentProto == proto) {
return;
}
d->mCurrentProto = proto;
if (d->mPGPRB) {
d->mPGPRB->setChecked(proto == GpgME::OpenPGP);
d->mCMSRB->setChecked(proto == GpgME::CMS);
}
d->onProtocolChanged();
}
void Kleo::SignEncryptWidget::Private::onProtocolChanged()
{
auto encryptForOthers = q->recipients().size() > 0;
mSigSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new SignCertificateFilter(mCurrentProto)));
mSelfSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new EncryptSelfCertificateFilter(mCurrentProto)));
const auto encFilter = std::shared_ptr<KeyFilter>(new EncryptCertificateFilter(mCurrentProto));
for (const auto &widget : std::vector(mRecpWidgets)) {
recpRemovalRequested(widget);
}
addRecipientWidget();
for (const auto &recipient : std::as_const(mRecpWidgets)) {
recipient.edit->setKeyFilter(encFilter);
}
if (mIsExclusive) {
mSymmetric->setDisabled(mCurrentProto == GpgME::CMS || Settings{}.publicKeyEncryptionOnly());
mSymmetricLabel->setDisabled(mCurrentProto == GpgME::CMS || Settings{}.publicKeyEncryptionOnly());
if (mSymmetric->isChecked() && mCurrentProto == GpgME::CMS) {
mSymmetric->setChecked(false);
}
if (mSigChk->isChecked() && mCurrentProto == GpgME::CMS && (mEncSelfChk->isChecked() || encryptForOthers)) {
mSigChk->setChecked(false);
}
}
}
static bool recipientIsOkay(const CertificateLineEdit *edit)
{
if (!edit->isEnabled() || edit->isEmpty()) {
return true;
}
if (!edit->hasAcceptableInput()) {
return false;
}
if (const auto userID = edit->userID(); !userID.isNull()) {
return Kleo::canBeUsedForEncryption(userID.parent()) && !userID.isBad();
}
if (const auto key = edit->key(); !key.isNull()) {
return Kleo::canBeUsedForEncryption(key);
}
if (const auto group = edit->group(); !group.isNull()) {
return std::ranges::all_of(group.keys(), Kleo::canBeUsedForEncryption);
}
// we should never reach this point
return false;
}
bool SignEncryptWidget::isComplete() const
{
if (currentOp() == NoOperation) {
return false;
}
if ((currentOp() & SignEncryptWidget::Sign) && !Kleo::canBeUsedForSigning(signUserId().parent())) {
return false;
}
if (currentOp() & SignEncryptWidget::Encrypt) {
if (!selfUserId().isNull() && !Kleo::canBeUsedForEncryption(selfUserId().parent())) {
return false;
}
const bool allOtherRecipientsAreOkay = std::ranges::all_of(d->mRecpWidgets, [](const auto &r) {
return recipientIsOkay(r.edit);
});
if (!allOtherRecipientsAreOkay) {
return false;
}
}
return true;
}
bool SignEncryptWidget::validate()
{
if (d->mEncOtherChk->isChecked()) {
if (std::ranges::all_of(d->mRecpWidgets, [](const auto &widget) {
return widget.edit->isEmpty();
})) {
KMessageBox::error(this,
xi18nc("@info translate 'Encrypt for others' the same way as for the other string in this file",
"Please choose at least one recipient or deselect <interface>Encrypt for others</interface>."));
return false;
}
CertificateLineEdit *firstUnresolvedRecipient = nullptr;
QStringList unresolvedRecipients;
for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
if (recipient.edit->isEnabled() && !recipient.edit->hasAcceptableInput()) {
if (!firstUnresolvedRecipient) {
firstUnresolvedRecipient = recipient.edit;
}
unresolvedRecipients.push_back(recipient.edit->text().toHtmlEscaped());
}
}
if (!unresolvedRecipients.isEmpty()) {
KMessageBox::errorList(this,
i18n("Could not find a key for the following recipients:"),
unresolvedRecipients,
i18nc("@title:window", "Failed to find some keys"));
}
if (firstUnresolvedRecipient) {
firstUnresolvedRecipient->setFocus();
}
return unresolvedRecipients.isEmpty();
}
return true;
}
void SignEncryptWidget::Private::updateCheckBoxes()
{
const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
const bool symmetricOnly = Settings().symmetricEncryptionOnly();
mSigChk->setEnabled(haveSecretKeys);
mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly);
if (symmetricOnly) {
mEncSelfChk->setChecked(false);
mSymmetric->setChecked(true);
}
}
ExpiryChecker *Kleo::SignEncryptWidget::Private::expiryChecker()
{
if (!mExpiryChecker) {
mExpiryChecker.reset(new ExpiryChecker{ExpiryCheckerConfig{}.settings()});
}
return mExpiryChecker.get();
}
void SignEncryptWidget::Private::updateExpiryMessages(KMessageWidget *messageWidget, const GpgME::UserID &userID, ExpiryChecker::CheckFlags flags)
{
messageWidget->setCloseButtonVisible(false);
if (userID.isNull()) {
messageWidget->setVisible(false);
} else if (userID.parent().isExpired()) {
messageWidget->setText(i18nc("@info", "This certificate is expired."));
messageWidget->setVisible(true);
} else if (userID.isRevoked() || userID.parent().isRevoked()) {
messageWidget->setText(i18nc("@info", "This certificate is revoked."));
messageWidget->setVisible(true);
} else if (Settings{}.showExpiryNotifications()) {
const auto result = expiryChecker()->checkKey(userID.parent(), flags);
const auto message = expiryMessage(result);
messageWidget->setText(message);
messageWidget->setVisible(!message.isEmpty());
} else {
messageWidget->setVisible(false);
}
}
void SignEncryptWidget::Private::updateAllExpiryMessages()
{
updateExpiryMessages(mSignKeyExpiryMessage, q->signUserId(), ExpiryChecker::OwnSigningKey);
updateExpiryMessages(mEncryptToSelfKeyExpiryMessage, q->selfUserId(), ExpiryChecker::OwnEncryptionKey);
for (const auto &recipient : std::as_const(mRecpWidgets)) {
if (recipient.edit->isEnabled()) {
updateExpiryMessages(recipient.expiryMessage, recipient.edit->userID(), ExpiryChecker::EncryptionKey);
}
}
}
#include "moc_signencryptwidget.cpp"
diff --git a/src/crypto/signencryptfilescontroller.cpp b/src/crypto/signencryptfilescontroller.cpp
index e1af0dae4..c682e1e28 100644
--- a/src/crypto/signencryptfilescontroller.cpp
+++ b/src/crypto/signencryptfilescontroller.cpp
@@ -1,808 +1,812 @@
/*
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "signencryptfilescontroller.h"
#include "signencrypttask.h"
#include "crypto/gui/signencryptfilesdialog.h"
#include "crypto/taskcollection.h"
#include <settings.h>
#include "utils/archivedefinition.h"
#include "utils/input.h"
#include "utils/kleo_assert.h"
#include "utils/output.h"
#include "utils/path-helper.h"
#include <Libkleo/Classify>
#include <Libkleo/KleoException>
#include "kleopatra_debug.h"
#include <KLocalizedString>
#include <QGpgME/SignEncryptArchiveJob>
#include <QDir>
#include <QFileInfo>
#include <QPointer>
#include <QTimer>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
class SignEncryptFilesController::Private
{
friend class ::Kleo::Crypto::SignEncryptFilesController;
SignEncryptFilesController *const q;
public:
explicit Private(SignEncryptFilesController *qq);
~Private();
private:
void slotWizardOperationPrepared();
void slotWizardCanceled();
private:
void ensureWizardCreated();
void ensureWizardVisible();
void updateWizardMode();
void cancelAllTasks();
void reportError(int err, const QString &details)
{
q->setLastError(err, details);
q->emitDoneOrError();
}
void schedule();
std::shared_ptr<SignEncryptTask> takeRunnable(GpgME::Protocol proto);
static void assertValidOperation(unsigned int);
static QString titleForOperation(unsigned int op);
private:
std::vector<std::shared_ptr<SignEncryptTask>> runnable, completed;
std::shared_ptr<SignEncryptTask> cms, openpgp;
QPointer<SignEncryptFilesDialog> wizard;
QStringList files;
unsigned int operation;
Protocol protocol;
};
SignEncryptFilesController::Private::Private(SignEncryptFilesController *qq)
: q(qq)
, runnable()
, cms()
, openpgp()
, wizard()
, files()
, operation(SignAllowed | EncryptAllowed | ArchiveAllowed)
, protocol(UnknownProtocol)
{
}
SignEncryptFilesController::Private::~Private()
{
qCDebug(KLEOPATRA_LOG) << q << __func__;
}
QString SignEncryptFilesController::Private::titleForOperation(unsigned int op)
{
- const bool signDisallowed = (op & SignMask) == SignDisallowed;
- const bool encryptDisallowed = (op & EncryptMask) == EncryptDisallowed;
+ const bool signDisallowed = (op & SignMask) & SignDisallowed;
+ const bool encryptDisallowed = (op & EncryptMask) & EncryptDisallowed;
const bool archiveSelected = (op & ArchiveMask) == ArchiveForced;
kleo_assert(!signDisallowed || !encryptDisallowed);
if (!signDisallowed && encryptDisallowed) {
if (archiveSelected) {
return i18n("Archive and Sign Files");
} else {
return i18n("Sign Files");
}
}
if (signDisallowed && !encryptDisallowed) {
if (archiveSelected) {
return i18n("Archive and Encrypt Files");
} else {
return i18n("Encrypt Files");
}
}
if (archiveSelected) {
return i18n("Archive and Sign/Encrypt Files");
} else {
return i18n("Sign/Encrypt Files");
}
}
SignEncryptFilesController::SignEncryptFilesController(QObject *p)
: Controller(p)
, d(new Private(this))
{
}
SignEncryptFilesController::SignEncryptFilesController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *p)
: Controller(ctx, p)
, d(new Private(this))
{
}
SignEncryptFilesController::~SignEncryptFilesController()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
if (d->wizard && !d->wizard->isVisible()) {
delete d->wizard;
}
// d->wizard->close(); ### ?
}
void SignEncryptFilesController::setProtocol(Protocol proto)
{
kleo_assert(d->protocol == UnknownProtocol || d->protocol == proto);
d->protocol = proto;
d->ensureWizardCreated();
}
Protocol SignEncryptFilesController::protocol() const
{
return d->protocol;
}
// static
void SignEncryptFilesController::Private::assertValidOperation(unsigned int op)
{
kleo_assert((op & SignMask) == SignDisallowed || //
(op & SignMask) == SignAllowed || //
(op & SignMask) == SignSelected);
kleo_assert((op & EncryptMask) == EncryptDisallowed || //
(op & EncryptMask) == EncryptAllowed || //
(op & EncryptMask) == EncryptSelected);
kleo_assert((op & ArchiveMask) == ArchiveDisallowed || //
(op & ArchiveMask) == ArchiveAllowed || //
(op & ArchiveMask) == ArchiveForced);
kleo_assert((op & ~(SignMask | EncryptMask | ArchiveMask)) == 0);
}
void SignEncryptFilesController::setOperationMode(unsigned int mode)
{
Private::assertValidOperation(mode);
d->operation = mode;
d->updateWizardMode();
}
void SignEncryptFilesController::Private::updateWizardMode()
{
if (!wizard) {
return;
}
wizard->setWindowTitle(titleForOperation(operation));
const unsigned int signOp = (operation & SignMask);
const unsigned int encrOp = (operation & EncryptMask);
const unsigned int archOp = (operation & ArchiveMask);
if (signOp == SignDisallowed) {
wizard->setSigningUserMutable(false);
wizard->setSigningPreset(false);
} else {
wizard->setSigningUserMutable(true);
- wizard->setSigningPreset(signOp == SignSelected);
+ if (signOp == SignSelected || encrOp == EncryptSelected) {
+ wizard->setSigningPreset(signOp == SignSelected);
+ }
}
if (encrOp == EncryptDisallowed) {
wizard->setEncryptionPreset(false);
wizard->setEncryptionUserMutable(false);
} else {
wizard->setEncryptionUserMutable(true);
- wizard->setEncryptionPreset(encrOp == EncryptSelected);
+ if (encrOp == EncryptSelected || signOp == SignSelected) {
+ wizard->setEncryptionPreset(encrOp == EncryptSelected);
+ }
}
wizard->setArchiveForced(archOp == ArchiveForced);
wizard->setArchiveMutable(archOp == ArchiveAllowed);
}
unsigned int SignEncryptFilesController::operationMode() const
{
return d->operation;
}
static QString extension(bool pgp, bool sign, bool encrypt, bool ascii, bool detached)
{
unsigned int cls = pgp ? Class::OpenPGP : Class::CMS;
if (encrypt) {
cls |= Class::CipherText;
} else if (sign) {
cls |= detached ? Class::DetachedSignature : Class::OpaqueSignature;
}
cls |= ascii ? Class::Ascii : Class::Binary;
const bool usePGPFileExt = Settings().usePGPFileExt();
const auto ext = outputFileExtension(cls, usePGPFileExt);
if (!ext.isEmpty()) {
return ext;
} else {
return QStringLiteral("out");
}
}
static std::shared_ptr<ArchiveDefinition> getDefaultAd()
{
const std::vector<std::shared_ptr<ArchiveDefinition>> ads = ArchiveDefinition::getArchiveDefinitions();
Q_ASSERT(!ads.empty());
std::shared_ptr<ArchiveDefinition> ad = ads.front();
const QString archiveCmd = Settings{}.archiveCommand();
auto it = std::find_if(ads.cbegin(), ads.cend(), [&archiveCmd](const std::shared_ptr<ArchiveDefinition> &toCheck) {
return toCheck->id() == archiveCmd;
});
if (it != ads.cend()) {
ad = *it;
}
return ad;
}
static QMap<int, QString> buildOutputNames(const QStringList &files, const bool archive)
{
QMap<int, QString> nameMap;
// Build the default names for the wizard.
QString baseNameCms;
QString baseNamePgp;
const QFileInfo firstFile(files.first());
if (archive) {
QString baseName = files.size() > 1 ? i18nc("base name of an archive file, e.g. archive.zip or archive.tar.gz", "archive") : firstFile.baseName();
baseName = QDir(heuristicBaseDirectory(files)).absoluteFilePath(baseName);
const auto ad = getDefaultAd();
baseNamePgp = baseName + QLatin1Char('.') + ad->extensions(GpgME::OpenPGP).first() + QLatin1Char('.');
baseNameCms = baseName + QLatin1Char('.') + ad->extensions(GpgME::CMS).first() + QLatin1Char('.');
} else {
baseNameCms = baseNamePgp = files.first() + QLatin1Char('.');
}
const bool ascii = Settings{}.addASCIIArmor();
nameMap.insert(SignEncryptFilesDialog::SignatureCMS, baseNameCms + extension(false, true, false, ascii, true));
nameMap.insert(SignEncryptFilesDialog::EncryptedCMS, baseNameCms + extension(false, false, true, ascii, false));
nameMap.insert(SignEncryptFilesDialog::CombinedPGP, baseNamePgp + extension(true, true, true, ascii, false));
nameMap.insert(SignEncryptFilesDialog::EncryptedPGP, baseNamePgp + extension(true, false, true, ascii, false));
nameMap.insert(SignEncryptFilesDialog::SignaturePGP, baseNamePgp + extension(true, true, false, ascii, true));
nameMap.insert(SignEncryptFilesDialog::Directory, heuristicBaseDirectory(files));
return nameMap;
}
static QMap<int, QString> buildOutputNamesForDir(const QString &file, const QMap<int, QString> &orig)
{
QMap<int, QString> ret;
const QString dir = orig.value(SignEncryptFilesDialog::Directory);
if (dir.isEmpty()) {
return orig;
}
// Build the default names for the wizard.
const QFileInfo fi(file);
const QString baseName = dir + QLatin1Char('/') + fi.fileName() + QLatin1Char('.');
const bool ascii = Settings{}.addASCIIArmor();
ret.insert(SignEncryptFilesDialog::SignatureCMS, baseName + extension(false, true, false, ascii, true));
ret.insert(SignEncryptFilesDialog::EncryptedCMS, baseName + extension(false, false, true, ascii, false));
ret.insert(SignEncryptFilesDialog::CombinedPGP, baseName + extension(true, true, true, ascii, false));
ret.insert(SignEncryptFilesDialog::EncryptedPGP, baseName + extension(true, false, true, ascii, false));
ret.insert(SignEncryptFilesDialog::SignaturePGP, baseName + extension(true, true, false, ascii, true));
return ret;
}
// strips all trailing slashes from the filename, but keeps filename "/"
static QString stripTrailingSlashes(const QString &fileName)
{
if (fileName.size() < 2 || !fileName.endsWith(QLatin1Char('/'))) {
return fileName;
}
auto tmp = QStringView{fileName}.chopped(1);
while (tmp.size() > 1 && tmp.endsWith(QLatin1Char('/'))) {
tmp.chop(1);
}
return tmp.toString();
}
static QStringList stripTrailingSlashesForAll(const QStringList &fileNames)
{
QStringList result;
result.reserve(fileNames.size());
std::transform(fileNames.begin(), fileNames.end(), std::back_inserter(result), &stripTrailingSlashes);
return result;
}
void SignEncryptFilesController::setFiles(const QStringList &files)
{
kleo_assert(!files.empty());
d->files = stripTrailingSlashesForAll(files);
bool archive = false;
if (d->files.size() > 1) {
setOperationMode((operationMode() & ~ArchiveMask) | ArchiveAllowed);
archive = true;
}
for (const auto &file : std::as_const(d->files)) {
if (QFileInfo(file).isDir()) {
setOperationMode((operationMode() & ~ArchiveMask) | ArchiveForced);
archive = true;
break;
}
}
d->ensureWizardCreated();
d->wizard->setSingleFile(!archive);
d->wizard->setOutputNames(buildOutputNames(d->files, archive));
}
void SignEncryptFilesController::Private::slotWizardCanceled()
{
qCDebug(KLEOPATRA_LOG) << q << __func__;
q->cancel();
reportError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel"));
}
void SignEncryptFilesController::start()
{
d->ensureWizardVisible();
}
static std::shared_ptr<SignEncryptTask> createSignEncryptTaskForFileInfo(const QFileInfo &fi,
bool ascii,
const std::vector<Key> &recipients,
const std::vector<Key> &signers,
const QString &outputName,
bool symmetric)
{
const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
task->setAsciiArmor(ascii);
if (!signers.empty()) {
task->setSign(true);
task->setSigners(signers);
task->setDetachedSignature(true);
} else {
task->setSign(false);
}
if (!recipients.empty()) {
task->setEncrypt(true);
task->setRecipients(recipients);
task->setDetachedSignature(false);
} else {
task->setEncrypt(false);
}
task->setEncryptSymmetric(symmetric);
const QString input = fi.absoluteFilePath();
task->setInputFileName(input);
#if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (task->protocol() != GpgME::OpenPGP) {
task->setInput(Input::createFromFile(input));
}
#else
task->setInput(Input::createFromFile(input));
#endif
task->setOutputFileName(outputName);
return task;
}
static bool archiveJobsCanBeUsed([[maybe_unused]] GpgME::Protocol protocol)
{
return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported();
}
static std::shared_ptr<SignEncryptTask> createArchiveSignEncryptTaskForFiles(const QStringList &files,
const std::shared_ptr<ArchiveDefinition> &ad,
bool pgp,
bool ascii,
const std::vector<Key> &recipients,
const std::vector<Key> &signers,
const QString &outputName,
bool symmetric)
{
const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
task->setCreateArchive(true);
task->setEncryptSymmetric(symmetric);
Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
task->setAsciiArmor(ascii);
if (!signers.empty()) {
task->setSign(true);
task->setSigners(signers);
task->setDetachedSignature(false);
} else {
task->setSign(false);
}
if (!recipients.empty()) {
task->setEncrypt(true);
task->setRecipients(recipients);
} else {
task->setEncrypt(false);
}
const Protocol proto = pgp ? OpenPGP : CMS;
task->setInputFileNames(files);
if (!archiveJobsCanBeUsed(proto)) {
// use legacy archive creation
kleo_assert(ad);
task->setInput(ad->createInputFromPackCommand(proto, files));
}
task->setOutputFileName(outputName);
return task;
}
static std::vector<std::shared_ptr<SignEncryptTask>> createSignEncryptTasksForFileInfo(const QFileInfo &fi,
bool ascii,
const std::vector<Key> &pgpRecipients,
const std::vector<Key> &pgpSigners,
const std::vector<Key> &cmsRecipients,
const std::vector<Key> &cmsSigners,
const QMap<int, QString> &outputNames,
bool symmetric)
{
std::vector<std::shared_ptr<SignEncryptTask>> result;
const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
result.reserve(pgp + cms);
if (pgp || symmetric) {
// Symmetric encryption is only supported for PGP
int outKind = 0;
if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) {
outKind = SignEncryptFilesDialog::CombinedPGP;
} else if (!pgpRecipients.empty() || symmetric) {
outKind = SignEncryptFilesDialog::EncryptedPGP;
} else {
outKind = SignEncryptFilesDialog::SignaturePGP;
}
result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
}
if (cms) {
// There is no combined sign / encrypt in gpgsm so we create one sign task
// and one encrypt task. Which leaves us with the age old dilemma, encrypt
// then sign, or sign then encrypt. Ugly.
if (!cmsSigners.empty()) {
result.push_back(
createSignEncryptTaskForFileInfo(fi, ascii, std::vector<Key>(), cmsSigners, outputNames[SignEncryptFilesDialog::SignatureCMS], false));
}
if (!cmsRecipients.empty()) {
result.push_back(
createSignEncryptTaskForFileInfo(fi, ascii, cmsRecipients, std::vector<Key>(), outputNames[SignEncryptFilesDialog::EncryptedCMS], false));
}
}
return result;
}
static std::vector<std::shared_ptr<SignEncryptTask>> createArchiveSignEncryptTasksForFiles(const QStringList &files,
const std::shared_ptr<ArchiveDefinition> &ad,
bool ascii,
const std::vector<Key> &pgpRecipients,
const std::vector<Key> &pgpSigners,
const std::vector<Key> &cmsRecipients,
const std::vector<Key> &cmsSigners,
const QMap<int, QString> &outputNames,
bool symmetric)
{
std::vector<std::shared_ptr<SignEncryptTask>> result;
const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
result.reserve(pgp + cms);
if (pgp || symmetric) {
int outKind = 0;
if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) {
outKind = SignEncryptFilesDialog::CombinedPGP;
} else if (!pgpRecipients.empty() || symmetric) {
outKind = SignEncryptFilesDialog::EncryptedPGP;
} else {
outKind = SignEncryptFilesDialog::SignaturePGP;
}
result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, true, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
}
if (cms) {
if (!cmsSigners.empty()) {
result.push_back(createArchiveSignEncryptTaskForFiles(files,
ad,
false,
ascii,
std::vector<Key>(),
cmsSigners,
outputNames[SignEncryptFilesDialog::SignatureCMS],
false));
}
if (!cmsRecipients.empty()) {
result.push_back(createArchiveSignEncryptTaskForFiles(files,
ad,
false,
ascii,
cmsRecipients,
std::vector<Key>(),
outputNames[SignEncryptFilesDialog::EncryptedCMS],
false));
}
}
return result;
}
namespace
{
static bool isDetachedOpenPGPSignature(const QString &fileName)
{
const auto classification = Kleo::classify(fileName);
return Kleo::isOpenPGP(classification) && Kleo::isDetachedSignature(classification);
}
static auto resolveFileNameConflicts(const std::vector<std::shared_ptr<SignEncryptTask>> &tasks, QWidget *parent)
{
std::vector<std::shared_ptr<SignEncryptTask>> resolvedTasks;
OverwritePolicy overwritePolicy{parent, tasks.size() > 1 ? OverwritePolicy::MultipleFiles : OverwritePolicy::Options{}};
for (auto &task : tasks) {
// by default, do not overwrite existing files
task->setOverwritePolicy(std::make_shared<OverwritePolicy>(OverwritePolicy::Skip));
const auto outputFileName = task->outputFileName();
if (QFile::exists(outputFileName)) {
const auto isDetachedOpenPGPSigningTask = (task->protocol() == GpgME::OpenPGP) && task->detachedSignatureEnabled();
const auto extraOptions = (isDetachedOpenPGPSigningTask && isDetachedOpenPGPSignature(outputFileName) //
? OverwritePolicy::AllowAppend //
: OverwritePolicy::Options{});
const auto policyAndFileName = overwritePolicy.obtainOverwritePermission(outputFileName, extraOptions);
if (policyAndFileName.policy == OverwritePolicy::Cancel) {
resolvedTasks.clear();
break;
}
switch (policyAndFileName.policy) {
case OverwritePolicy::Skip:
// do not add task to the final task list
continue;
case OverwritePolicy::Rename: {
task->setOutputFileName(policyAndFileName.fileName);
break;
}
case OverwritePolicy::Append: {
task->setOverwritePolicy(std::make_shared<OverwritePolicy>(OverwritePolicy::Append));
break;
}
case OverwritePolicy::Overwrite: {
task->setOverwritePolicy(std::make_shared<OverwritePolicy>(OverwritePolicy::Overwrite));
break;
}
case OverwritePolicy::Cancel:
// already handled outside of switch
break;
case OverwritePolicy::None:
case OverwritePolicy::Ask:
qCDebug(KLEOPATRA_LOG) << "Unexpected OverwritePolicy result" << policyAndFileName.policy << "for" << outputFileName;
};
}
resolvedTasks.push_back(task);
}
return resolvedTasks;
}
}
void SignEncryptFilesController::Private::slotWizardOperationPrepared()
{
try {
kleo_assert(wizard);
kleo_assert(!files.empty());
const bool archive = ((wizard->outputNames().value(SignEncryptFilesDialog::Directory).isNull() && files.size() > 1) //
|| ((operation & ArchiveMask) == ArchiveForced));
const std::vector<Key> recipients = wizard->resolvedRecipients();
const std::vector<Key> signers = wizard->resolvedSigners();
const bool ascii = Settings{}.addASCIIArmor();
std::vector<Key> pgpRecipients, cmsRecipients, pgpSigners, cmsSigners;
for (const Key &k : recipients) {
if (k.protocol() == GpgME::OpenPGP) {
pgpRecipients.push_back(k);
} else {
cmsRecipients.push_back(k);
}
}
for (const Key &k : signers) {
if (k.protocol() == GpgME::OpenPGP) {
pgpSigners.push_back(k);
} else {
cmsSigners.push_back(k);
}
}
std::vector<std::shared_ptr<SignEncryptTask>> tasks;
if (!archive) {
tasks.reserve(files.size());
}
if (archive) {
tasks = createArchiveSignEncryptTasksForFiles(files,
getDefaultAd(),
ascii,
pgpRecipients,
pgpSigners,
cmsRecipients,
cmsSigners,
wizard->outputNames(),
wizard->encryptSymmetric());
} else {
for (const QString &file : std::as_const(files)) {
const std::vector<std::shared_ptr<SignEncryptTask>> created =
createSignEncryptTasksForFileInfo(QFileInfo(file),
ascii,
pgpRecipients,
pgpSigners,
cmsRecipients,
cmsSigners,
buildOutputNamesForDir(file, wizard->outputNames()),
wizard->encryptSymmetric());
tasks.insert(tasks.end(), created.begin(), created.end());
}
}
tasks = resolveFileNameConflicts(tasks, wizard);
if (tasks.empty()) {
q->cancel();
return;
}
kleo_assert(runnable.empty());
runnable.swap(tasks);
for (const auto &task : std::as_const(runnable)) {
q->connectTask(task);
}
std::shared_ptr<TaskCollection> coll(new TaskCollection);
std::vector<std::shared_ptr<Task>> tmp;
std::copy(runnable.begin(), runnable.end(), std::back_inserter(tmp));
coll->setTasks(tmp);
wizard->setTaskCollection(coll);
QTimer::singleShot(0, q, SLOT(schedule()));
} catch (const Kleo::Exception &e) {
reportError(e.error().encodedError(), e.message());
} catch (const std::exception &e) {
reportError(
gpg_error(GPG_ERR_UNEXPECTED),
i18n("Caught unexpected exception in SignEncryptFilesController::Private::slotWizardOperationPrepared: %1", QString::fromLocal8Bit(e.what())));
} catch (...) {
reportError(gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in SignEncryptFilesController::Private::slotWizardOperationPrepared"));
}
}
void SignEncryptFilesController::Private::schedule()
{
if (!cms)
if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(CMS)) {
t->start();
cms = t;
}
if (!openpgp)
if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(OpenPGP)) {
t->start();
openpgp = t;
}
if (!cms && !openpgp) {
kleo_assert(runnable.empty());
q->emitDoneOrError();
}
}
std::shared_ptr<SignEncryptTask> SignEncryptFilesController::Private::takeRunnable(GpgME::Protocol proto)
{
const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr<Task> &task) {
return task->protocol() == proto;
});
if (it == runnable.end()) {
return std::shared_ptr<SignEncryptTask>();
}
const std::shared_ptr<SignEncryptTask> result = *it;
runnable.erase(it);
return result;
}
void SignEncryptFilesController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
{
Q_UNUSED(result)
Q_ASSERT(task);
// We could just delete the tasks here, but we can't use
// Qt::QueuedConnection here (we need sender()) and other slots
// might not yet have executed. Therefore, we push completed tasks
// into a burial container
if (task == d->cms.get()) {
d->completed.push_back(d->cms);
d->cms.reset();
} else if (task == d->openpgp.get()) {
d->completed.push_back(d->openpgp);
d->openpgp.reset();
}
QTimer::singleShot(0, this, SLOT(schedule()));
}
void SignEncryptFilesController::cancel()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
try {
if (d->wizard) {
d->wizard->close();
}
d->cancelAllTasks();
} catch (const std::exception &e) {
qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
}
}
void SignEncryptFilesController::Private::cancelAllTasks()
{
// we just kill all runnable tasks - this will not result in
// signal emissions.
runnable.clear();
// a cancel() will result in a call to
if (cms) {
cms->cancel();
}
if (openpgp) {
openpgp->cancel();
}
}
void SignEncryptFilesController::Private::ensureWizardCreated()
{
if (wizard) {
return;
}
std::unique_ptr<SignEncryptFilesDialog> w(new SignEncryptFilesDialog);
w->setAttribute(Qt::WA_DeleteOnClose);
connect(w.get(), SIGNAL(operationPrepared()), q, SLOT(slotWizardOperationPrepared()), Qt::QueuedConnection);
connect(w.get(), SIGNAL(rejected()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection);
wizard = w.release();
updateWizardMode();
}
void SignEncryptFilesController::Private::ensureWizardVisible()
{
ensureWizardCreated();
q->bringToForeground(wizard);
}
#include "moc_signencryptfilescontroller.cpp"
diff --git a/src/kcfg/settings.kcfg b/src/kcfg/settings.kcfg
index 6668b63fa..1f00f96ff 100644
--- a/src/kcfg/settings.kcfg
+++ b/src/kcfg/settings.kcfg
@@ -1,285 +1,289 @@
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<group name="CertificateCreationWizard">
<entry key="CN_placeholder" name="cnPlaceholder" type="String">
<label>Placeholder for CN</label>
<whatsthis>This text will be used as placeholder text for the common name (CN) field of S/MIME certificates.</whatsthis>
<default></default>
</entry>
<entry key="CN_prefill" name="prefillCN" type="Bool">
<label>Prefill CN automatically</label>
<whatsthis>If true, then the common name (CN) field of S/MIME certificates will be prefilled with information gathered from the system,
e.g., from the email settings of the desktop or, on Windows, from the Active Directory.</whatsthis>
<default>true</default>
</entry>
<entry key="EMAIL_placeholder" name="emailPlaceholder" type="String">
<label>Hint for EMAIL</label>
<whatsthis>This text will be shown above the email address field of OpenPGP certificates and as placeholder in that field for S/MIME certificates.</whatsthis>
<default></default>
</entry>
<entry key="EMAIL_prefill" name="prefillEmail" type="Bool">
<label>Prefill EMAIL automatically</label>
<whatsthis>If true, then the email address field of OpenPGP and S/MIME certificates will be prefilled with information gathered from the system,
e.g., from the email settings of the desktop or, on Windows, from the Active Directory.</whatsthis>
<default>true</default>
</entry>
<entry key="EMAIL" name="email" type="String">
<label>Value of EMAIL</label>
<whatsthis>Prefilled value for the email address field of OpenPGP and S/MIME certificates. This will override EMAIL_prefill. It is useful if no or unsuitable system settings are found for EMAIL_prefill.</whatsthis>
<default></default>
</entry>
<entry key="NAME_placeholder" name="namePlaceholder" type="String">
<label>Hint for NAME</label>
<whatsthis>This text will be shown above the name field of OpenPGP certificates.</whatsthis>
<default></default>
</entry>
<entry key="NAME_prefill" name="prefillName" type="Bool">
<label>Prefill NAME automatically</label>
<whatsthis>If true, then the name field of OpenPGP certificates will be prefilled with information gathered from the system,
e.g., from the email settings of the desktop or, on Windows, from the Active Directory.</whatsthis>
<default>true</default>
</entry>
<entry key="NAME" name="name" type="String">
<label>Value of NAME</label>
<whatsthis>Prefilled value for the name field of OpenPGP certificates. This will override NAME_prefill. It is useful if no or an unsuitable system setting is found for NAME_prefill.</whatsthis>
<default></default>
</entry>
<entry key="ValidityPeriodInDays" type="Int">
<label>Default validity period</label>
<tooltip>Specifies the default validity period of new or extended OpenPGP keys in days.</tooltip>
<whatsthis>This setting specifies how many days an OpenPGP key will be valid by default at creation or change of validity, or, in other words, after how many days the key will expire. Set this to 0 for unlimited validity. If this setting is not set or is set to a negative value, then new or extended OpenPGP keys will be valid for three years (possibly clamped to the allowed minimum or maximum validity period) by default.</whatsthis>
<default>-1</default>
</entry>
<entry key="ValidityPeriodInDaysMin" type="Int">
<label>Minimum validity period</label>
<tooltip>Specifies the minimum allowed validity period of new or extended OpenPGP keys in days.</tooltip>
<whatsthis>This setting specifies the minimum number of days a user can choose for the validity period of OpenPGP certificates. It applies at key creation and change of validity.</whatsthis>
<default>1</default>
</entry>
<entry key="ValidityPeriodInDaysMax" type="Int">
<label>Maximum validity period</label>
<tooltip>Specifies the maximum allowed validity period of new or extended OpenPGP keys in days.</tooltip>
<whatsthis>This setting specifies the maximum number of days a user can choose for the validity period of OpenPGP certificates. It applies at key creation and change of validity. If this setting is not set or is set to a negative value, then unlimited validity is allowed.</whatsthis>
<default>-1</default>
</entry>
<entry key="HideAdvanced" type="Bool">
<label>Hide advanced settings</label>
<whatsthis>If true, hides the advanced settings button in the new certificate wizard.</whatsthis>
<default>false</default>
</entry>
</group>
<group name="Certification">
<entry key="CertificationValidityInDays" type="Int">
<label>Default certification validity</label>
<tooltip>Specifies the default validity of certifications in days.</tooltip>
<whatsthis>This setting specifies how many days a certification is valid by default, or, in other words, after how many days a new certification will expire. Set this to 0 for unlimited validity of certifications.</whatsthis>
<default>0</default>
</entry>
</group>
<group name="ChecksumOperations">
<entry key="checksum-definition-id" name="ChecksumDefinitionId" type="String">
<label>Checksum program to use when creating checksum files</label>
<default>sha256sum</default>
</entry>
</group>
<group name="Clipboard">
<entry key="ShowResultsAfterSigning" name="ShowResultsAfterSigningClipboard" type="Bool">
<label>Show results after signing</label>
<whatsthis>If true, then the results are shown after successfully signing the clipboard.</whatsthis>
<default>true</default>
</entry>
<entry key="ShowResultsAfterEncryption" name="ShowResultsAfterEncryptingClipboard" type="Bool">
<label>Show results after encryption</label>
<whatsthis>If true, then the results are shown after successfully encrypting the clipboard.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="CMS">
<entry key="Enabled" name="cmsEnabled" type="Bool">
<label>Enable S/MIME</label>
<tooltip>Enables support for S/MIME (CMS).</tooltip>
<whatsthis>If false, then Kleopatra's main UI will not offer any functionality related to S/MIME (CMS).</whatsthis>
<default>true</default>
</entry>
<entry key="AllowCertificateCreation" name="cmsCertificateCreationAllowed" type="Bool">
<label>Allow S/MIME certificate creation</label>
<tooltip>Allows the creation of S/MIME certificate signing requests.</tooltip>
<whatsthis>If false, then Kleopatra will not offer the creation of S/MIME certificate signing requests.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="ConfigurationDialog">
<entry name="ShowAppearanceConfiguration" type="Bool">
<label>Show appearance configuration</label>
<default>true</default>
</entry>
<entry name="ShowCryptoOperationsConfiguration" type="Bool">
<label>Show crypto operations configuration</label>
<default>true</default>
</entry>
<entry name="ShowDirectoryServicesConfiguration" type="Bool">
<label>Show directory services configuration</label>
<default>true</default>
</entry>
<entry name="ShowGnuPGSystemConfiguration" type="Bool">
<label>Show GnuPG system configuration</label>
<default>true</default>
</entry>
<entry name="ShowSmartCardsConfiguration" type="Bool">
<label>Show smart cards configuration</label>
<default>true</default>
</entry>
<entry name="ShowSMimeValidationConfiguration" type="Bool">
<label>Show S/MIME validation configuration</label>
<default>true</default>
</entry>
</group>
<group name="DN">
<entry name="AttributeOrder" type="StringList">
<label>DN-Attribute Order</label>
<tooltip>Specifies the display order of the DN attributes of X.509 certificates.</tooltip>
<default></default>
</entry>
</group>
<group name="Export">
<entry name="AllowPublicKeyUpload" type="Bool">
<label>Allow upload of public keys to keyservers</label>
<whatsthis>If this option is disabled then Kleopatra only allows the upload of OpenPGP keys for which the user has the secret key.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="FileOperations">
<entry name="UsePGPFileExt" key="file-extension-pgp" type="Bool">
<label>Use pgp as the default extension for generated OpenPGP files</label>
<whatsthis>Set this to make kleopatra default to pgp file extensions for OpenPGP files.</whatsthis>
<default>false</default>
</entry>
<entry name="AutoExtractArchives" key="auto-extract-archives" type="Bool">
<label>Automatically extract archives after decryption.</label>
<whatsthis>If this option is set, then Kleopatra automatically extracts file archives after decryption.</whatsthis>
<default>true</default>
</entry>
<entry name="ArchiveCommand" key="default-archive-cmd" type="String">
<label>Use this command to create file archives.</label>
<whatsthis>When encrypting multiple files or a folder Kleopatra creates an encrypted archive with this command.</whatsthis>
<default>tar</default>
</entry>
<entry name="AddASCIIArmor" key="ascii-armor" type="Bool">
<label>Create signed or encrypted files as text files.</label>
<whatsthis>Set this option to encode encrypted or signed files as base64 encoded text. So that they can be opened with an editor or sent in a mail body. This will increase file size by one third.</whatsthis>
<default>false</default>
</entry>
<entry name="DontUseTmpDir" key="dont-use-tmp-dir" type="Bool">
<label>Create temporary decrypted files in the folder of the encrypted file.</label>
<whatsthis>Set this option to avoid using the users temporary directory.</whatsthis>
<default>false</default>
</entry>
<entry name="SymmetricEncryptionOnly" key="symmetric-encryption-only" type="Bool">
<label>Use symmetric encryption only.</label>
<whatsthis>Set this option to disable public key encryption.</whatsthis>
<default>false</default>
</entry>
<entry name="PublicKeyEncryptionOnly" key="public-key-encryption-only" type="Bool">
<label>Use public key encryption only.</label>
<whatsthis>Set this option to disable password-based encryption.</whatsthis>
<default>false</default>
</entry>
</group>
<group name="Groups">
<entry name="GroupsEnabled" type="Bool">
<label>Enable Groups</label>
<tooltip>Enable usage of groups of keys.</tooltip>
<whatsthis>Enable usage of groups of keys to create lists of recipients.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="Import">
<entry name="RetrieveSignerKeysAfterImport" type="Bool">
<label>Retrieve signer keys after import</label>
<whatsthis>If enabled, then Kleopatra will automatically try to retrieve the keys
that were used to certify the user ids of newly imported OpenPGP keys. This is
useful in combination with trusted introducers.</whatsthis>
<default>false</default>
</entry>
<entry name="QueryWKDsForAllUserIDs" type="Bool">
<label>Query certificate directories of providers for all user IDs</label>
<whatsthis>By default, Kleopatra only queries the certificate directories of providers (WKD)
for user IDs that were originally retrieved from a WKD when you update an OpenPGP certificate.
If this option is enabled, then Kleopatra will query WKDs for all user IDs.</whatsthis>
<default>false</default>
</entry>
</group>
<group name="Notifications">
<entry name="ShowExpiryNotifications" type="Bool">
<label>Notify about upcoming certificate expiration</label>
<whatsthis>If enabled, then Kleopatra will show notifications in some place when using
certificates that are about to expire soon.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="Privacy">
<entry name="BlockedUrlSchemes" type="StringList">
<label>URL schemes to block</label>
<whatsthis>This is a list of URL schemes that shall be blocked by the application.
This can be used to prevent the application from opening external applications for certain URLs.</whatsthis>
<default></default>
</entry>
</group>
<group name="Smartcard">
<entry name="AlwaysSearchCardOnKeyserver" type="Bool">
<label>Always search smartcard certificates on keyserver</label>
<tooltip>Searches for the certificates belonging the smartcard keys on the configured keyserver.</tooltip>
<whatsthis>Searches on keyservers regardless of the protocol for the smartcards key, regardless
of the keyserver protocol. Default behavior is to only do this for LDAP keyservers.</whatsthis>
<default>false</default>
</entry>
<entry key="AutoLoadP15Certs" name="autoLoadP15Certs" type="Bool">
<label>Try to load S/MIME certificates from PKCS#15 smartcards</label>
<tooltip>Automatically load S/MIME certificates from PKCS#15 (CardOS) smartcards</tooltip>
<whatsthis>If true, then Kleopatra will call gpgsm --learn if a PKCS#15 Smartcard is inserted with unknown certificates. This can take a while and blocks the smartcard while the command is running.</whatsthis>
<default>true</default>
</entry>
</group>
<group name="SMime Validation">
<entry name="RefreshInterval" key="refresh-interval" type="UInt" >
<label>Certificate refresh interval (in hours). Zero (0) disables.</label>
<whatsthis>This option enables interval checking of certificate validity. You can also choose the checking interval (in hours). Note that validation is performed implicitly whenever significant files in ~/.gnupg change. This option therefore only affects external factors of certificate validity.</whatsthis>
<default>1</default>
<max>24</max>
</entry>
</group>
<group name="General">
<entry name="ProfilesDisabled" type="Bool">
<label>Disable profile settings</label>
<default>false</default>
</entry>
<entry name="AllowCustomDebugCommands" type="Bool">
<label>Allow custom debug commands</label>
<default>true</default>
</entry>
<entry name="ShowResultsAfterSignEncrypt" type="Bool">
<label>Show results after signing and encrypting files</label>
<default>true</default>
</entry>
+ <entry name="RestoreSignEncryptValues" type="Bool">
+ <label>Use last-used values for the sign/encrypt configuration</label>
+ <default>true</default>
+ </entry>
</group>
<group name="Tooltip" >
<entry name="ShowValidity" key="show-validity" type="Bool" >
<label>Show certificate validity</label>
<whatsthis>Show validity information for certificates in tooltip, such as whether the certificate is expired or revoked.</whatsthis>
<default>true</default>
</entry>
<entry name="ShowOwnerInformation" key="show-owner-information" type="Bool" >
<label>Show certificate owner information</label>
<whatsthis>Show owner information for certificates in tooltip, such as User IDs, subject and issuers.</whatsthis>
<default>false</default>
</entry>
<entry name="ShowCertificateDetails" key="show-certificate-details" type="Bool" >
<label>Show certificate details</label>
<whatsthis>Show more certificate details, such as fingerprint, key length and expiration dates</whatsthis>
<default>false</default>
</entry>
</group>
</kcfg>

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jan 24, 11:28 PM (1 d, 10 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
7c/43/22b970b5e98c2b53efb8f2ba7fbc

Event Timeline