Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F35313537
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
47 KB
Subscribers
None
View Options
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
Details
Attached
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
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment