Page MenuHome GnuPG

No OneTemporary

diff --git a/src/crypto/gui/certificatelineedit.cpp b/src/crypto/gui/certificatelineedit.cpp
index d1802197a..a6af8aaed 100644
--- a/src/crypto/gui/certificatelineedit.cpp
+++ b/src/crypto/gui/certificatelineedit.cpp
@@ -1,391 +1,450 @@
/* crypto/gui/certificatelineedit.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "certificatelineedit.h"
#include <QCompleter>
#include <QPushButton>
#include <QAction>
#include <QSignalBlocker>
#include "kleopatra_debug.h"
#include <Libkleo/KeyCache>
#include <Libkleo/KeyFilter>
#include <Libkleo/KeyGroup>
#include <Libkleo/KeyList>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/Formatting>
#include <KLocalizedString>
#include <gpgme++/key.h>
#include <QGpgME/KeyForMailboxJob>
#include <QGpgME/Protocol>
+#include <QHBoxLayout>
#include <QLabel>
+#include <QLineEdit>
+#include <QToolButton>
using namespace Kleo;
using namespace GpgME;
Q_DECLARE_METATYPE(GpgME::Key)
Q_DECLARE_METATYPE(KeyGroup)
static QStringList s_lookedUpKeys;
namespace
{
class CompletionProxyModel : public KeyListSortFilterProxyModel
{
Q_OBJECT
public:
CompletionProxyModel(QObject *parent = nullptr)
: KeyListSortFilterProxyModel(parent)
{
}
int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
Q_UNUSED(parent)
// pretend that there is only one column to workaround a bug in
// QAccessibleTable which provides the accessibility interface for the
// completion pop-up
return 1;
}
QVariant data(const QModelIndex &idx, int role) const override
{
if (!idx.isValid()) {
return QVariant();
}
switch (role) {
case Qt::DecorationRole: {
const auto key = KeyListSortFilterProxyModel::data(idx, KeyList::KeyRole).value<GpgME::Key>();
if (!key.isNull()) {
return Kleo::Formatting::iconForUid(key.userID(0));
}
const auto group = KeyListSortFilterProxyModel::data(idx, KeyList::GroupRole).value<KeyGroup>();
if (!group.isNull()) {
return QIcon::fromTheme(QStringLiteral("group"));
}
Q_ASSERT(!key.isNull() || !group.isNull());
return QVariant();
}
default:
return KeyListSortFilterProxyModel::data(index(idx.row(), KeyList::Summary), role);
}
}
};
} // namespace
class CertificateLineEdit::Private
{
CertificateLineEdit *q;
public:
explicit Private(CertificateLineEdit *qq, AbstractKeyListModel *model, KeyFilter *filter);
+ QString text() const;
+
+ void setKey(const GpgME::Key &key);
+
+ void setGroup(const KeyGroup &group);
+
void setKeyFilter(const std::shared_ptr<KeyFilter> &filter);
void updateKey();
void editChanged();
void editFinished();
void checkLocate();
+private:
+ void setTextWithBlockedSignals(const QString &s);
+
public:
GpgME::Key mKey;
KeyGroup mGroup;
private:
+ struct Ui {
+ Ui(QWidget *parent)
+ : lineEdit{parent}
+ , button{parent}
+ {}
+
+ QLineEdit lineEdit;
+ QToolButton button;
+ } ui;
+
KeyListSortFilterProxyModel *const mFilterModel;
KeyListSortFilterProxyModel *const mCompleterFilterModel;
QCompleter *mCompleter = nullptr;
std::shared_ptr<KeyFilter> mFilter;
bool mEditStarted = false;
bool mEditFinished = false;
QAction *const mLineAction;
};
CertificateLineEdit::Private::Private(CertificateLineEdit *qq, AbstractKeyListModel *model, KeyFilter *filter)
: q{qq}
+ , ui{qq}
, mFilterModel{new KeyListSortFilterProxyModel{qq}}
, mCompleterFilterModel{new CompletionProxyModel{qq}}
, mCompleter{new QCompleter{qq}}
, mFilter{std::shared_ptr<KeyFilter>{filter}}
, mLineAction{new QAction{qq}}
{
- q->setPlaceholderText(i18n("Please enter a name or email address..."));
- q->setClearButtonEnabled(true);
- q->addAction(mLineAction, QLineEdit::LeadingPosition);
+ ui.lineEdit.setPlaceholderText(i18n("Please enter a name or email address..."));
+ ui.lineEdit.setClearButtonEnabled(true);
+ ui.lineEdit.addAction(mLineAction, QLineEdit::LeadingPosition);
mCompleterFilterModel->setKeyFilter(mFilter);
mCompleterFilterModel->setSourceModel(model);
mCompleter->setModel(mCompleterFilterModel);
mCompleter->setFilterMode(Qt::MatchContains);
mCompleter->setCaseSensitivity(Qt::CaseInsensitive);
- q->setCompleter(mCompleter);
+ ui.lineEdit.setCompleter(mCompleter);
+
+ ui.button.setIcon(QIcon::fromTheme(QStringLiteral("resource-group-new")));
+ ui.button.setToolTip(i18n("Show certificate list"));
+ ui.button.setAccessibleName(i18n("Show certificate list"));
+
+ auto l = new QHBoxLayout{q};
+ l->setContentsMargins(0, 0, 0, 0);
+ l->addWidget(&ui.lineEdit);
+ l->addWidget(&ui.button);
+
+ q->setFocusPolicy(ui.lineEdit.focusPolicy());
+ q->setFocusProxy(&ui.lineEdit);
+
mFilterModel->setSourceModel(model);
mFilterModel->setFilterKeyColumn(KeyList::Summary);
if (filter) {
mFilterModel->setKeyFilter(mFilter);
}
connect(KeyCache::instance().get(), &Kleo::KeyCache::keyListingDone,
q, [this]() { updateKey(); });
connect(KeyCache::instance().get(), &Kleo::KeyCache::groupUpdated,
q, [this](const KeyGroup &group) {
if (!mGroup.isNull() && mGroup.source() == group.source() && mGroup.id() == group.id()) {
- QSignalBlocker blocky{q};
- q->setText(Formatting::summaryLine(group));
+ setTextWithBlockedSignals(Formatting::summaryLine(group));
// queue the update to ensure that the model has been updated
QMetaObject::invokeMethod(q, [this]() { updateKey(); }, Qt::QueuedConnection);
}
});
connect(KeyCache::instance().get(), &Kleo::KeyCache::groupRemoved,
q, [this](const KeyGroup &group) {
if (!mGroup.isNull() && mGroup.source() == group.source() && mGroup.id() == group.id()) {
mGroup = KeyGroup();
- QSignalBlocker blocky{q};
- q->clear();
+ QSignalBlocker blocky{&ui.lineEdit};
+ ui.lineEdit.clear();
// queue the update to ensure that the model has been updated
QMetaObject::invokeMethod(q, [this]() { updateKey(); }, Qt::QueuedConnection);
}
});
- connect(q, &QLineEdit::editingFinished,
+ connect(&ui.lineEdit, &QLineEdit::editingFinished,
q, [this]() {
// queue the call of editFinished() to ensure that QCompleter::activated is handled first
QMetaObject::invokeMethod(q, [this]() { editFinished(); }, Qt::QueuedConnection);
});
- connect(q, &QLineEdit::textChanged,
+ connect(&ui.lineEdit, &QLineEdit::textChanged,
q, [this]() { editChanged(); });
connect(mLineAction, &QAction::triggered,
q, &CertificateLineEdit::dialogRequested);
+ connect(&ui.button, &QToolButton::clicked,
+ q, &CertificateLineEdit::certificateSelectionRequested);
connect(mCompleter, qOverload<const QModelIndex &>(&QCompleter::activated),
q, [this] (const QModelIndex &index) {
Key key = mCompleter->completionModel()->data(index, KeyList::KeyRole).value<Key>();
auto group = mCompleter->completionModel()->data(index, KeyList::GroupRole).value<KeyGroup>();
if (!key.isNull()) {
q->setKey(key);
} else if (!group.isNull()) {
q->setGroup(group);
} else {
qCDebug(KLEOPATRA_LOG) << "Activated item is neither key nor group";
}
});
updateKey();
}
+void CertificateLineEdit::Private::setTextWithBlockedSignals(const QString &s)
+{
+ QSignalBlocker blocky{&ui.lineEdit};
+ ui.lineEdit.setText(s);
+}
+
CertificateLineEdit::CertificateLineEdit(AbstractKeyListModel *model,
- QWidget *parent,
- KeyFilter *filter)
- : QLineEdit(parent)
+ KeyFilter *filter,
+ QWidget *parent)
+ : QWidget{parent}
, d{new Private{this, model, filter}}
{
/* Take ownership of the model to prevent double deletion when the
* filter models are deleted */
model->setParent(parent ? parent : this);
}
CertificateLineEdit::~CertificateLineEdit() = default;
void CertificateLineEdit::Private::editChanged()
{
mEditFinished = false;
updateKey();
if (!mEditStarted) {
Q_EMIT q->editingStarted();
mEditStarted = true;
}
}
void CertificateLineEdit::Private::editFinished()
{
mEditStarted = false;
mEditFinished = true;
updateKey();
if (!q->key().isNull()) {
- QSignalBlocker blocky{q};
- q->setText(Formatting::summaryLine(q->key()));
+ setTextWithBlockedSignals(Formatting::summaryLine(q->key()));
} else if (!q->group().isNull()) {
- QSignalBlocker blocky{q};
- q->setText(Formatting::summaryLine(q->group()));
+ setTextWithBlockedSignals(Formatting::summaryLine(q->group()));
} else {
checkLocate();
}
}
void CertificateLineEdit::Private::checkLocate()
{
if (!q->key().isNull() || !q->group().isNull()) {
// Already have a key or group
return;
}
// Only check once per mailbox
- const auto mailText = q->text();
+ const auto mailText = ui.lineEdit.text();
if (s_lookedUpKeys.contains(mailText)) {
return;
}
s_lookedUpKeys << mailText;
qCDebug(KLEOPATRA_LOG) << "Lookup job for" << mailText;
QGpgME::KeyForMailboxJob *job = QGpgME::openpgp()->keyForMailboxJob();
job->start(mailText);
}
void CertificateLineEdit::Private::updateKey()
{
static const _detail::ByFingerprint<std::equal_to> keysHaveSameFingerprint;
- const auto mailText = q->text();
+ const auto mailText = ui.lineEdit.text();
auto newKey = Key();
auto newGroup = KeyGroup();
if (mailText.isEmpty()) {
mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("resource-group-new")));
mLineAction->setToolTip(i18n("Open selection dialog."));
- q->setToolTip({});
+ ui.lineEdit.setToolTip({});
} else {
mFilterModel->setFilterFixedString(mailText);
if (mFilterModel->rowCount() > 1) {
// keep current key or group if they still match
if (!mKey.isNull()) {
for (int row = 0; row < mFilterModel->rowCount(); ++row) {
const QModelIndex index = mFilterModel->index(row, 0);
Key key = mFilterModel->key(index);
if (!key.isNull() && keysHaveSameFingerprint(key, mKey)) {
newKey = mKey;
break;
}
}
} else if (!mGroup.isNull()) {
newGroup = mGroup;
for (int row = 0; row < mFilterModel->rowCount(); ++row) {
const QModelIndex index = mFilterModel->index(row, 0);
KeyGroup group = mFilterModel->group(index);
if (!group.isNull() && group.source() == mGroup.source() && group.id() == mGroup.id()) {
newGroup = mGroup;
break;
}
}
}
if (newKey.isNull() && newGroup.isNull()) {
if (mEditFinished) {
mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("question")));
mLineAction->setToolTip(i18n("Multiple matching certificates found"));
- q->setToolTip(i18n("Multiple matching certificates found"));
+ ui.lineEdit.setToolTip(i18n("Multiple matching certificates found"));
} else {
mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("resource-group-new")));
mLineAction->setToolTip(i18n("Open selection dialog."));
- q->setToolTip({});
+ ui.lineEdit.setToolTip({});
}
}
} else if (mFilterModel->rowCount() == 1) {
const auto index = mFilterModel->index(0, 0);
newKey = mFilterModel->data(index, KeyList::KeyRole).value<Key>();
newGroup = mFilterModel->data(index, KeyList::GroupRole).value<KeyGroup>();
Q_ASSERT(!newKey.isNull() || !newGroup.isNull());
if (newKey.isNull() && newGroup.isNull()) {
mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("emblem-error")));
mLineAction->setToolTip(i18n("No matching certificates found.<br/>Click to import a certificate."));
- q->setToolTip(i18n("No matching certificates found"));
+ ui.lineEdit.setToolTip(i18n("No matching certificates found"));
}
} else {
mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("emblem-error")));
mLineAction->setToolTip(i18n("No matching certificates found.<br/>Click to import a certificate."));
- q->setToolTip(i18n("No matching certificates found"));
+ ui.lineEdit.setToolTip(i18n("No matching certificates found"));
}
}
mKey = newKey;
mGroup = newGroup;
if (!mKey.isNull()) {
/* FIXME: This needs to be solved by a multiple UID supporting model */
mLineAction->setIcon(Formatting::iconForUid(mKey.userID(0)));
mLineAction->setToolTip(Formatting::validity(mKey.userID(0)) +
QLatin1String("<br/>") + i18n("Click for details."));
- q->setToolTip(Formatting::toolTip(mKey, Formatting::ToolTipOption::AllOptions));
+ ui.lineEdit.setToolTip(Formatting::toolTip(mKey, Formatting::ToolTipOption::AllOptions));
} else if (!mGroup.isNull()) {
mLineAction->setIcon(Formatting::validityIcon(mGroup));
mLineAction->setToolTip(Formatting::validity(mGroup) +
QLatin1String("<br/>") + i18n("Click for details."));
- q->setToolTip(Formatting::toolTip(mGroup, Formatting::ToolTipOption::AllOptions));
+ ui.lineEdit.setToolTip(Formatting::toolTip(mGroup, Formatting::ToolTipOption::AllOptions));
}
Q_EMIT q->keyChanged();
if (mailText.isEmpty()) {
Q_EMIT q->wantsRemoval(q);
}
}
Key CertificateLineEdit::key() const
{
if (isEnabled()) {
return d->mKey;
} else {
return Key();
}
}
KeyGroup CertificateLineEdit::group() const
{
if (isEnabled()) {
return d->mGroup;
} else {
return KeyGroup();
}
}
-void CertificateLineEdit::setKey(const Key &key)
+QString CertificateLineEdit::Private::text() const
+{
+ return ui.lineEdit.text();
+}
+
+QString CertificateLineEdit::text() const
+{
+ return d->text();
+}
+
+void CertificateLineEdit::Private::setKey(const Key &key)
{
- d->mKey = key;
- d->mGroup = KeyGroup();
- QSignalBlocker blocky{this};
+ mKey = key;
+ mGroup = KeyGroup();
qCDebug(KLEOPATRA_LOG) << "Setting Key. " << Formatting::summaryLine(key);
- setText(Formatting::summaryLine(key));
- d->updateKey();
+ setTextWithBlockedSignals(Formatting::summaryLine(key));
+ updateKey();
}
-void CertificateLineEdit::setGroup(const KeyGroup &group)
+void CertificateLineEdit::setKey(const Key &key)
{
- d->mGroup = group;
- d->mKey = Key();
- QSignalBlocker blocky{this};
+ d->setKey(key);
+}
+
+void CertificateLineEdit::Private::setGroup(const KeyGroup &group)
+{
+ mGroup = group;
+ mKey = Key();
const QString summary = Formatting::summaryLine(group);
qCDebug(KLEOPATRA_LOG) << "Setting KeyGroup. " << summary;
- setText(summary);
- d->updateKey();
+ setTextWithBlockedSignals(summary);
+ updateKey();
+}
+
+void CertificateLineEdit::setGroup(const KeyGroup &group)
+{
+ d->setGroup(group);
}
bool CertificateLineEdit::isEmpty() const
{
return text().isEmpty();
}
void CertificateLineEdit::Private::setKeyFilter(const std::shared_ptr<KeyFilter> &filter)
{
mFilter = filter;
mFilterModel->setKeyFilter(filter);
mCompleterFilterModel->setKeyFilter(mFilter);
updateKey();
}
void CertificateLineEdit::setKeyFilter(const std::shared_ptr<KeyFilter> &filter)
{
d->setKeyFilter(filter);
}
#include "certificatelineedit.moc"
diff --git a/src/crypto/gui/certificatelineedit.h b/src/crypto/gui/certificatelineedit.h
index 94330db02..c581ed931 100644
--- a/src/crypto/gui/certificatelineedit.h
+++ b/src/crypto/gui/certificatelineedit.h
@@ -1,89 +1,95 @@
/* crypto/gui/certificatelineedit.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
-#include <QLineEdit>
+#include <QWidget>
#include <memory>
namespace GpgME
{
class Key;
}
namespace Kleo
{
class AbstractKeyListModel;
class KeyFilter;
class KeyGroup;
/** Line edit and completion based Certificate Selection Widget.
*
* Shows the status of the selection with a status label and icon.
*
* The widget will use a single line HBox Layout. For larger dialog
* see certificateslectiondialog.
*/
-class CertificateLineEdit: public QLineEdit
+class CertificateLineEdit: public QWidget
{
Q_OBJECT
public:
/** Create the certificate selection line.
*
* If parent is not NULL the model is not taken
* over but the parent argument used as the parent of the model.
*
* @param model: The keylistmodel to use.
* @param parent: The usual widget parent.
* @param filter: The filters to use. See certificateselectiondialog.
*/
explicit CertificateLineEdit(AbstractKeyListModel *model,
- QWidget *parent = nullptr,
- KeyFilter *filter = nullptr);
+ KeyFilter *filter = nullptr,
+ QWidget *parent = nullptr);
~CertificateLineEdit() override;
/** Get the selected key */
GpgME::Key key() const;
KeyGroup group() const;
+ /** The current text */
+ QString text() const;
+
/** Check if the text is empty */
bool isEmpty() const;
/** Set the preselected Key for this widget. */
void setKey(const GpgME::Key &key);
/** Set the preselected group for this widget. */
void setGroup(const KeyGroup &group);
/** Set the used keyfilter. */
void setKeyFilter(const std::shared_ptr<KeyFilter> &filter);
Q_SIGNALS:
/** Emitted when the selected key changed. */
void keyChanged();
/** Emitted when the entry is empty and editing is finished. */
void wantsRemoval(CertificateLineEdit *w);
/** Emitted when the entry is no longer empty. */
void editingStarted();
/** Emitted when the details dialog or the selection dialog is requested. */
void dialogRequested();
+ /** Emitted when the certificate selection dialog is requested. */
+ void certificateSelectionRequested();
+
private:
class Private;
std::unique_ptr<Private> const d;
};
}
diff --git a/src/crypto/gui/signencryptwidget.cpp b/src/crypto/gui/signencryptwidget.cpp
index 1fe448370..e83f1fedd 100644
--- a/src/crypto/gui/signencryptwidget.cpp
+++ b/src/crypto/gui/signencryptwidget.cpp
@@ -1,665 +1,673 @@
/* crypto/gui/signencryptwidget.cpp
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 "fileoperationspreferences.h"
#include "kleopatraapplication.h"
#include "settings.h"
#include "unknownrecipientwidget.h"
#include "commands/detailscommand.h"
#include "dialogs/certificateselectiondialog.h"
#include "dialogs/groupdetailsdialog.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGroupBox>
#include <QCheckBox>
#include <QScrollArea>
#include <QScrollBar>
#include <Libkleo/DefaultKeyFilter>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyGroup>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeySelectionCombo>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/GnuPG>
#include <KLocalizedString>
#include <KConfigGroup>
#include <KSharedConfig>
#include <KMessageBox>
using namespace Kleo;
using namespace Kleo::Dialogs;
using namespace GpgME;
namespace {
class SignCertificateFilter: public DefaultKeyFilter
{
public:
SignCertificateFilter(GpgME::Protocol proto) : DefaultKeyFilter()
{
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setHasSecret(DefaultKeyFilter::Set);
setCanSign(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()
{
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setCanEncrypt(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);
}
};
}
SignEncryptWidget::SignEncryptWidget(QWidget *parent, bool sigEncExclusive)
: QWidget(parent),
mModel(AbstractKeyListModel::createFlatKeyListModel(this)),
mIsExclusive(sigEncExclusive)
{
auto lay = new QVBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
mModel->useKeyCache(true, KeyList::IncludeGroups);
const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
const bool havePublicKeys = !KeyCache::instance()->keys().empty();
const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly();
/* The signature selection */
auto sigLay = new QHBoxLayout;
auto sigGrp = new QGroupBox(i18nc("@title:group", "Prove authenticity (sign)"));
mSigChk = new QCheckBox(i18n("Sign as:"));
mSigChk->setEnabled(haveSecretKeys);
mSigChk->setChecked(haveSecretKeys);
mSigSelect = new KeySelectionCombo();
mSigSelect->setEnabled(mSigChk->isChecked());
sigLay->addWidget(mSigChk);
sigLay->addWidget(mSigSelect, 1);
sigGrp->setLayout(sigLay);
lay->addWidget(sigGrp);
connect(mSigChk, &QCheckBox::toggled, mSigSelect, &QWidget::setEnabled);
connect(mSigChk, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp);
connect(mSigSelect, &KeySelectionCombo::currentKeyChanged,
this, &SignEncryptWidget::updateOp);
// Recipient selection
auto encBoxLay = new QVBoxLayout;
auto encBox = new QGroupBox(i18nc("@title:group", "Encrypt"));
encBox->setLayout(encBoxLay);
auto recipientGrid = new QGridLayout;
// Own key
mEncSelfChk = new QCheckBox(i18n("Encrypt for me:"));
mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly);
mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly);
mSelfSelect = new KeySelectionCombo();
mSelfSelect->setEnabled(mEncSelfChk->isChecked());
recipientGrid->addWidget(mEncSelfChk, 0, 0);
recipientGrid->addWidget(mSelfSelect, 0, 1);
// Checkbox for other keys
mEncOtherChk = new QCheckBox(i18n("Encrypt for others:"));
mEncOtherChk->setEnabled(havePublicKeys && !symmetricOnly);
mEncOtherChk->setChecked(havePublicKeys && !symmetricOnly);
recipientGrid->addWidget(mEncOtherChk, 1, 0, Qt::AlignTop);
connect(mEncOtherChk, &QCheckBox::toggled, this,
[this](bool toggled) {
for (CertificateLineEdit *edit : std::as_const(mRecpWidgets)) {
edit->setEnabled(toggled);
}
updateOp();
});
mRecpLayout = new QVBoxLayout;
recipientGrid->addLayout(mRecpLayout, 1, 1);
recipientGrid->setRowStretch(2, 1);
// Scroll area for other keys
auto recipientWidget = new QWidget;
auto recipientScroll = new QScrollArea;
recipientWidget->setLayout(recipientGrid);
recipientScroll->setWidget(recipientWidget);
recipientScroll->setWidgetResizable(true);
recipientScroll->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
recipientScroll->setFrameStyle(QFrame::NoFrame);
recipientScroll->setFocusPolicy(Qt::NoFocus);
recipientGrid->setContentsMargins(0, 0, 0, 0);
encBoxLay->addWidget(recipientScroll, 1);
auto bar = recipientScroll->verticalScrollBar();
connect (bar, &QScrollBar::rangeChanged, this, [bar] (int, int max) {
bar->setValue(max);
});
addRecipientWidget();
// Checkbox for password
mSymmetric = new QCheckBox(i18n("Encrypt with password. Anyone you share the password with can read the data."));
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."));
mSymmetric->setChecked(symmetricOnly || !havePublicKeys);
encBoxLay->addWidget(mSymmetric);
// Connect it
connect(mEncSelfChk, &QCheckBox::toggled, mSelfSelect, &QWidget::setEnabled);
connect(mEncSelfChk, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp);
connect(mSymmetric, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp);
connect(mSelfSelect, &KeySelectionCombo::currentKeyChanged,
this, &SignEncryptWidget::updateOp);
if (mIsExclusive) {
connect(mEncOtherChk, &QCheckBox::toggled, this, [this](bool value) {
if (mCurrentProto != GpgME::CMS) {
return;
}
if (value) {
mSigChk->setChecked(false);
}
});
connect(mEncSelfChk, &QCheckBox::toggled, this, [this](bool value) {
if (mCurrentProto != GpgME::CMS) {
return;
}
if (value) {
mSigChk->setChecked(false);
}
});
connect(mSigChk, &QCheckBox::toggled, this, [this](bool value) {
if (mCurrentProto != GpgME::CMS) {
return;
}
if (value) {
mEncSelfChk->setChecked(false);
mEncOtherChk->setChecked(false);
}
});
}
// Ensure that the mSigChk is aligned togehter with the encryption check boxes.
mSigChk->setMinimumWidth(qMax(mEncOtherChk->width(), mEncSelfChk->width()));
lay->addWidget(encBox);
connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged,
this, &SignEncryptWidget::updateCheckBoxes);
connect(KleopatraApplication::instance(), &KleopatraApplication::configurationChanged,
this, &SignEncryptWidget::updateCheckBoxes);
loadKeys();
onProtocolChanged();
updateOp();
}
void SignEncryptWidget::setSignAsText(const QString &text)
{
mSigChk->setText(text);
}
void SignEncryptWidget::setEncryptForMeText(const QString &text)
{
mEncSelfChk->setText(text);
}
void SignEncryptWidget::setEncryptForOthersText(const QString &text)
{
mEncOtherChk->setText(text);
}
void SignEncryptWidget::setEncryptWithPasswordText(const QString& text)
{
mSymmetric->setText(text);
}
CertificateLineEdit *SignEncryptWidget::addRecipientWidget()
{
- auto certSel = new CertificateLineEdit(mModel, this,
- new EncryptCertificateFilter(mCurrentProto));
+ auto certSel = new CertificateLineEdit(mModel,
+ new EncryptCertificateFilter(mCurrentProto),
+ this);
certSel->setAccessibleName(i18nc("text for screen readers", "recipient key"));
certSel->setEnabled(mEncOtherChk->isChecked());
mRecpWidgets << certSel;
if (mRecpLayout->count() > 0) {
auto lastWidget = mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget();
setTabOrder(lastWidget, certSel);
}
mRecpLayout->addWidget(certSel);
connect(certSel, &CertificateLineEdit::keyChanged,
this, &SignEncryptWidget::recipientsChanged);
connect(certSel, &CertificateLineEdit::wantsRemoval,
this, &SignEncryptWidget::recpRemovalRequested);
connect(certSel, &CertificateLineEdit::editingStarted,
this, &SignEncryptWidget::recipientsChanged);
connect(certSel, &CertificateLineEdit::dialogRequested,
this, [this, certSel] () { dialogRequested(certSel); });
+ connect(certSel, &CertificateLineEdit::certificateSelectionRequested,
+ this, [this, certSel]() { certificateSelectionRequested(certSel); });
return certSel;
}
void SignEncryptWidget::addRecipient(const Key &key)
{
CertificateLineEdit *certSel = addRecipientWidget();
if (!key.isNull()) {
certSel->setKey(key);
mAddedKeys << key;
}
}
void SignEncryptWidget::addRecipient(const KeyGroup &group)
{
CertificateLineEdit *certSel = addRecipientWidget();
if (!group.isNull()) {
certSel->setGroup(group);
mAddedGroups << group;
}
}
void SignEncryptWidget::dialogRequested(CertificateLineEdit *certificateLineEdit)
{
if (!certificateLineEdit->key().isNull()) {
auto cmd = new Commands::DetailsCommand(certificateLineEdit->key(), nullptr);
cmd->start();
return;
}
if (!certificateLineEdit->group().isNull()) {
auto dlg = new GroupDetailsDialog;
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setGroup(certificateLineEdit->group());
dlg->show();
return;
}
- auto const dlg = new CertificateSelectionDialog(this);
+ certificateSelectionRequested(certificateLineEdit);
+}
+
+void SignEncryptWidget::certificateSelectionRequested(CertificateLineEdit *certificateLineEdit)
+{
+ CertificateSelectionDialog dlg{this};
- dlg->setOptions(CertificateSelectionDialog::Options(
+ dlg.setOptions(CertificateSelectionDialog::Options(
CertificateSelectionDialog::MultiSelection |
CertificateSelectionDialog::EncryptOnly |
CertificateSelectionDialog::optionsFromProtocol(mCurrentProto) |
CertificateSelectionDialog::IncludeGroups));
- if (dlg->exec()) {
- const std::vector<Key> keys = dlg->selectedCertificates();
- const std::vector<KeyGroup> groups = dlg->selectedGroups();
+ if (dlg.exec()) {
+ const std::vector<Key> keys = dlg.selectedCertificates();
+ const std::vector<KeyGroup> groups = dlg.selectedGroups();
if (keys.size() == 0 && groups.size() == 0) {
return;
}
bool isFirstItem = true;
for (const Key &key : keys) {
if (isFirstItem) {
certificateLineEdit->setKey(key);
isFirstItem = false;
} else {
addRecipient(key);
}
}
for (const KeyGroup &group : groups) {
if (isFirstItem) {
certificateLineEdit->setGroup(group);
isFirstItem = false;
} else {
addRecipient(group);
}
}
}
- delete dlg;
+
recipientsChanged();
}
void SignEncryptWidget::clearAddedRecipients()
{
for (auto w: std::as_const(mUnknownWidgets)) {
mRecpLayout->removeWidget(w);
delete w;
}
for (auto &key: std::as_const(mAddedKeys)) {
removeRecipient(key);
}
for (auto &group: std::as_const(mAddedGroups)) {
removeRecipient(group);
}
}
void SignEncryptWidget::addUnknownRecipient(const char *keyID)
{
auto unknownWidget = new UnknownRecipientWidget(keyID);
mUnknownWidgets << unknownWidget;
if (mRecpLayout->count() > 0) {
auto lastWidget = mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget();
setTabOrder(lastWidget, unknownWidget);
}
mRecpLayout->addWidget(unknownWidget);
connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged,
this, [this] () {
// Check if any unknown recipient can now be found.
for (auto w: 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();
mRecpLayout->removeWidget(w);
mUnknownWidgets.removeAll(w);
delete w;
addRecipient(key);
}
});
}
void SignEncryptWidget::recipientsChanged()
{
const bool hasEmptyRecpWidget =
std::any_of(std::cbegin(mRecpWidgets), std::cend(mRecpWidgets),
[](auto w) { return w->isEmpty(); });
if (!hasEmptyRecpWidget) {
addRecipientWidget();
}
updateOp();
}
Key SignEncryptWidget::signKey() const
{
if (mSigSelect->isEnabled()) {
return mSigSelect->currentKey();
}
return Key();
}
Key SignEncryptWidget::selfKey() const
{
if (mSelfSelect->isEnabled()) {
return mSelfSelect->currentKey();
}
return Key();
}
std::vector<Key> SignEncryptWidget::recipients() const
{
std::vector<Key> ret;
for (const CertificateLineEdit *w : std::as_const(mRecpWidgets)) {
if (!w->isEnabled()) {
// If one is disabled, all are disabled.
break;
}
const Key k = w->key();
const KeyGroup g = w->group();
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));
}
}
const Key k = selfKey();
if (!k.isNull()) {
ret.push_back(k);
}
return ret;
}
bool SignEncryptWidget::isDeVsAndValid() const
{
if (!signKey().isNull()
&& (!IS_DE_VS(signKey()) || keyValidity(signKey()) < GpgME::UserID::Validity::Full)) {
return false;
}
if (!selfKey().isNull()
&& (!IS_DE_VS(selfKey()) || keyValidity(selfKey()) < GpgME::UserID::Validity::Full)) {
return false;
}
for (const auto &key: recipients()) {
if (!IS_DE_VS(key) || keyValidity(key) < GpgME::UserID::Validity::Full) {
return false;
}
}
return true;
}
void SignEncryptWidget::updateOp()
{
const Key sigKey = signKey();
const std::vector<Key> recp = recipients();
QString newOp;
if (!sigKey.isNull() && (!recp.empty() || encryptSymmetric())) {
newOp = i18nc("@action", "Sign / Encrypt");
} else if (!recp.empty() || encryptSymmetric()) {
newOp = i18nc("@action", "Encrypt");
} else if (!sigKey.isNull()) {
newOp = i18nc("@action", "Sign");
} else {
newOp = QString();
}
mOp = newOp;
Q_EMIT operationChanged(mOp);
Q_EMIT keysChanged();
}
QString SignEncryptWidget::currentOp() const
{
return mOp;
}
void SignEncryptWidget::recpRemovalRequested(CertificateLineEdit *w)
{
if (!w) {
return;
}
const int emptyEdits =
std::count_if(std::cbegin(mRecpWidgets), std::cend(mRecpWidgets),
[](auto w) { return w->isEmpty(); });
if (emptyEdits > 1) {
if (w->hasFocus()) {
const int index = mRecpLayout->indexOf(w);
const auto focusWidget = (index < mRecpLayout->count() - 1) ?
mRecpLayout->itemAt(index + 1)->widget() :
mRecpLayout->itemAt(mRecpLayout->count() - 2)->widget();
focusWidget->setFocus();
}
mRecpLayout->removeWidget(w);
mRecpWidgets.removeAll(w);
w->deleteLater();
}
}
void SignEncryptWidget::removeRecipient(const GpgME::Key &key)
{
for (CertificateLineEdit *edit: std::as_const(mRecpWidgets)) {
const auto editKey = edit->key();
if (key.isNull() && editKey.isNull()) {
recpRemovalRequested(edit);
return;
}
if (editKey.primaryFingerprint() &&
key.primaryFingerprint() &&
!strcmp(editKey.primaryFingerprint(), key.primaryFingerprint())) {
recpRemovalRequested(edit);
return;
}
}
}
void SignEncryptWidget::removeRecipient(const KeyGroup &group)
{
for (CertificateLineEdit *edit: std::as_const(mRecpWidgets)) {
const auto editGroup = edit->group();
if (group.isNull() && editGroup.isNull()) {
recpRemovalRequested(edit);
return;
}
if (editGroup.name() == group.name()) {
recpRemovalRequested(edit);
return;
}
}
}
bool SignEncryptWidget::encryptSymmetric() const
{
return mSymmetric->isChecked();
}
void SignEncryptWidget::loadKeys()
{
KConfigGroup keys(KSharedConfig::openConfig(), "SignEncryptKeys");
auto cache = KeyCache::instance();
mSigSelect->setDefaultKey(keys.readEntry("SigningKey", QString()));
mSelfSelect->setDefaultKey(keys.readEntry("EncryptKey", QString()));
}
void SignEncryptWidget::saveOwnKeys() const
{
KConfigGroup keys(KSharedConfig::openConfig(), "SignEncryptKeys");
auto sigKey = mSigSelect->currentKey();
auto encKey = mSelfSelect->currentKey();
if (!sigKey.isNull()) {
keys.writeEntry("SigningKey", sigKey.primaryFingerprint());
}
if (!encKey.isNull()) {
keys.writeEntry("EncryptKey", encKey.primaryFingerprint());
}
}
void SignEncryptWidget::setSigningChecked(bool value)
{
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 = FileOperationsPreferences().symmetricEncryptionOnly();
mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly);
mEncOtherChk->setChecked(havePublicKeys && !symmetricOnly);
mSymmetric->setChecked(symmetricOnly || !havePublicKeys);
} else {
mEncSelfChk->setChecked(false);
mEncOtherChk->setChecked(false);
mSymmetric->setChecked(false);
}
}
void SignEncryptWidget::setProtocol(GpgME::Protocol proto)
{
if (mCurrentProto == proto) {
return;
}
mCurrentProto = proto;
onProtocolChanged();
}
void Kleo::SignEncryptWidget::onProtocolChanged()
{
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 (CertificateLineEdit *edit : std::as_const(mRecpWidgets)) {
edit->setKeyFilter(encFilter);
}
if (mIsExclusive) {
mSymmetric->setDisabled(mCurrentProto == GpgME::CMS);
if (mSymmetric->isChecked() && mCurrentProto == GpgME::CMS) {
mSymmetric->setChecked(false);
}
if (mSigChk->isChecked() && mCurrentProto == GpgME::CMS &&
(mEncSelfChk->isChecked() || mEncOtherChk->isChecked())) {
mSigChk->setChecked(false);
}
}
}
bool SignEncryptWidget::validate()
{
QStringList unresolvedRecipients;
for (const auto edit: std::as_const(mRecpWidgets)) {
if (edit->isEnabled() && !edit->isEmpty() && edit->key().isNull() && edit->group().isNull()) {
unresolvedRecipients.push_back(edit->text().toHtmlEscaped());
}
}
if (!unresolvedRecipients.isEmpty()) {
KMessageBox::errorList(this,
i18n("Could not find a key for the following recipients:"),
unresolvedRecipients,
i18n("Failed to find some keys"));
}
return unresolvedRecipients.isEmpty();
}
void SignEncryptWidget::updateCheckBoxes()
{
const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
const bool havePublicKeys = !KeyCache::instance()->keys().empty();
const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly();
mSigChk->setEnabled(haveSecretKeys);
mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly);
mEncOtherChk->setEnabled(havePublicKeys && !symmetricOnly);
if (symmetricOnly) {
mEncSelfChk->setChecked(false);
mEncOtherChk->setChecked(false);
mSymmetric->setChecked(true);
}
}
diff --git a/src/crypto/gui/signencryptwidget.h b/src/crypto/gui/signencryptwidget.h
index 2a9bcb883..ac52d4ffe 100644
--- a/src/crypto/gui/signencryptwidget.h
+++ b/src/crypto/gui/signencryptwidget.h
@@ -1,141 +1,142 @@
/* crypto/gui/signencryptwidget.h
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
*/
#pragma once
#include <QWidget>
#include <QVector>
#include <Libkleo/KeyGroup>
#include <gpgme++/key.h>
class QCheckBox;
class QVBoxLayout;
namespace Kleo
{
class CertificateLineEdit;
class KeySelectionCombo;
class AbstractKeyListModel;
class UnknownRecipientWidget;
class SignEncryptWidget: public QWidget
{
Q_OBJECT
public:
/** If cmsSigEncExclusive is true CMS operations can be
* done only either as sign or as encrypt */
explicit SignEncryptWidget(QWidget *parent = nullptr, bool cmsSigEncExclusive = false);
/** Overwrite default text with custom text, e.g. with a character marked
* as shortcut key. */
void setSignAsText(const QString &text);
void setEncryptForMeText(const QString &text);
void setEncryptForOthersText(const QString &text);
void setEncryptWithPasswordText(const QString &text);
/** Returns the list of recipients selected in the dialog
* or an empty list if encryption is disabled */
std::vector<GpgME::Key> recipients() const;
/** Returns the selected signing key or a null key if signing
* is disabled. */
GpgME::Key signKey() const;
/** Returns the selected encrypt to self key or a null key if
* encrypt to self is disabled. */
GpgME::Key selfKey() const;
/** Returns the operation based on the current selection or
* a null string if nothing would happen. */
QString currentOp() const;
/** Whether or not symmetric encryption should also be used. */
bool encryptSymmetric() const;
/** Save the currently selected signing and encrypt to self keys. */
void saveOwnKeys() const;
/** Return whether or not all keys involved in the operation are
compliant with CO_DE_VS, and all keys are valid (i.e. all
userIDs have Validity >= Full). */
bool isDeVsAndValid() const;
/** Set whether or not signing group should be checked */
void setSigningChecked(bool value);
/** Set whether or not encryption group should be checked */
void setEncryptionChecked(bool value);
/** Filter for a specific protocol. Use UnknownProtocol for both
* S/MIME and OpenPGP */
void setProtocol(GpgME::Protocol protocol);
/** Add a recipient with the key key */
void addRecipient(const GpgME::Key &key);
/** Add a group of recipients */
void addRecipient(const Kleo::KeyGroup &group);
/** Add a placehoder for an unknown key */
void addUnknownRecipient(const char *keyId);
/** Remove all Recipients added by keyId or by key. */
void clearAddedRecipients();
/** Remove a Recipient key */
void removeRecipient(const GpgME::Key &key);
/** Remove a recipient group */
void removeRecipient(const Kleo::KeyGroup &group);
/** Validate that each line edit with content has a key. */
bool validate();
protected Q_SLOTS:
void updateOp();
void recipientsChanged();
void recpRemovalRequested(CertificateLineEdit *w);
void dialogRequested(CertificateLineEdit *w);
+ void certificateSelectionRequested(CertificateLineEdit *w);
protected:
void loadKeys();
Q_SIGNALS:
/* Emitted when the certificate selection changed the operation
* with that selection. e.g. "Sign" or "Sign/Encrypt".
* If no crypto operation is selected this returns a null string. */
void operationChanged(const QString &op);
/* Emitted when the certificate selection might be changed. */
void keysChanged();
private:
CertificateLineEdit* addRecipientWidget();
void onProtocolChanged();
void updateCheckBoxes();
private:
KeySelectionCombo *mSigSelect = nullptr;
KeySelectionCombo *mSelfSelect = nullptr;
QVector<CertificateLineEdit *> mRecpWidgets;
QVector<UnknownRecipientWidget *> mUnknownWidgets;
QVector<GpgME::Key> mAddedKeys;
QVector<KeyGroup> mAddedGroups;
QVBoxLayout *mRecpLayout = nullptr;
QString mOp;
AbstractKeyListModel *mModel = nullptr;
QCheckBox *mSymmetric = nullptr;
QCheckBox *mSigChk = nullptr;
QCheckBox *mEncOtherChk = nullptr;
QCheckBox *mEncSelfChk = nullptr;
GpgME::Protocol mCurrentProto = GpgME::UnknownProtocol;
const bool mIsExclusive;
};
} // namespace Kleo

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 5, 9:48 PM (8 h, 19 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
17/aa/0de902853b4a4e3a1e55b5f6b6bf

Event Timeline