Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34113601
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
88 KB
Subscribers
None
View Options
diff --git a/src/ui/auditlogviewer.cpp b/src/ui/auditlogviewer.cpp
index 7aabe944..45128e59 100644
--- a/src/ui/auditlogviewer.cpp
+++ b/src/ui/auditlogviewer.cpp
@@ -1,187 +1,187 @@
/*
SPDX-FileCopyrightText: 2015-2021 Laurent Montel <montel@kde.org>
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "auditlogviewer.h"
#include <libkleo/auditlogentry.h>
#include <libkleo/formatting.h>
#include <KConfigGroup>
#include <KGuiItem>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <KStandardGuiItem>
#ifdef HAVE_PIMTEXTEDIT
#include <TextCustomEditor/RichTextEditor>
#else
#include <QTextEdit>
#endif
#include <QDebug>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QPushButton>
#include <QSaveFile>
#include <QStyle>
#include <QTextStream>
#include <QVBoxLayout>
#include <gpgme++/error.h>
using namespace Kleo;
AuditLogViewer::AuditLogViewer(const QString &log, QWidget *parent)
: QDialog(parent)
, m_log(/* sic */)
,
#ifdef HAVE_PIMTEXTEDIT
m_textEdit(new TextCustomEditor::RichTextEditorWidget(this))
#else
m_textEdit(new QTextEdit(this))
#endif
{
setWindowTitle(i18nc("@title:window", "View GnuPG Audit Log"));
QDialogButtonBox *buttonBox = new QDialogButtonBox{};
auto copyClipBtn = buttonBox->addButton(i18n("&Copy to Clipboard"), QDialogButtonBox::ActionRole);
- copyClipBtn->setObjectName(QStringLiteral("copyClipBtn"));
+ copyClipBtn->setObjectName(QLatin1StringView("copyClipBtn"));
copyClipBtn->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
connect(copyClipBtn, &QPushButton::clicked, this, &AuditLogViewer::slotCopyClip);
auto saveAsBtn = buttonBox->addButton(i18n("&Save to Disk..."), QDialogButtonBox::ActionRole);
- saveAsBtn->setObjectName(QStringLiteral("saveAsBtn"));
+ saveAsBtn->setObjectName(QLatin1StringView("saveAsBtn"));
saveAsBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
connect(saveAsBtn, &QPushButton::clicked, this, &AuditLogViewer::slotSaveAs);
auto closeBtn = buttonBox->addButton(QString{}, QDialogButtonBox::AcceptRole);
- closeBtn->setObjectName(QStringLiteral("Close"));
+ closeBtn->setObjectName(QLatin1StringView("Close"));
KGuiItem::assign(closeBtn, KStandardGuiItem::close());
- m_textEdit->setObjectName(QStringLiteral("m_textEdit"));
+ m_textEdit->setObjectName(QLatin1StringView("m_textEdit"));
m_textEdit->setReadOnly(true);
auto mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(m_textEdit);
mainLayout->addWidget(buttonBox);
#if 0
qDebug() << "buttonBox->style()->styleHint(QStyle::SH_DialogButtonLayout, ...):" << buttonBox->style()->styleHint(QStyle::SH_DialogButtonLayout, nullptr, buttonBox);
qDebug() << __func__ << "buttonBox->focusProxy():" << buttonBox->focusProxy();
qDebug() << __func__ << "copyClipBtn->nextInFocusChain():" << copyClipBtn->nextInFocusChain();
qDebug() << __func__ << "saveAsBtn->nextInFocusChain():" << saveAsBtn->nextInFocusChain();
qDebug() << __func__ << "closeBtn->nextInFocusChain():" << closeBtn->nextInFocusChain();
#endif
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
setAuditLog(log);
readConfig();
}
AuditLogViewer::~AuditLogViewer()
{
writeConfig();
}
// static
void AuditLogViewer::showAuditLog(QWidget *parent, const AuditLogEntry &auditLog, const QString &title)
{
const GpgME::Error err = auditLog.error();
if (err.code() == GPG_ERR_NOT_IMPLEMENTED) {
KMessageBox::information(parent, i18n("Your system does not have support for GnuPG Audit Logs"), i18n("System Error"));
return;
}
if (err && err.code() != GPG_ERR_NO_DATA) {
KMessageBox::information(parent,
i18n("An error occurred while trying to retrieve the GnuPG Audit Log:\n%1", Formatting::errorAsString(err)),
i18n("GnuPG Audit Log Error"));
return;
}
if (auditLog.text().isEmpty()) {
KMessageBox::information(parent, i18n("No GnuPG Audit Log available for this operation."), i18n("No GnuPG Audit Log"));
return;
}
const auto alv = new AuditLogViewer{auditLog.text(), parent};
alv->setAttribute(Qt::WA_DeleteOnClose);
alv->setWindowTitle(title.isEmpty() ? i18n("GnuPG Audit Log Viewer") : title);
alv->show();
}
void AuditLogViewer::setAuditLog(const QString &log)
{
if (log == m_log) {
return;
}
m_log = log;
m_textEdit->setHtml(QLatin1String("<qt>") + log + QLatin1String("</qt>"));
}
void AuditLogViewer::slotSaveAs()
{
const QString fileName = QFileDialog::getSaveFileName(this, i18n("Choose File to Save GnuPG Audit Log to"));
if (fileName.isEmpty()) {
return;
}
QSaveFile file(fileName);
if (file.open(QIODevice::WriteOnly)) {
QTextStream s(&file);
s << "<html><head>";
if (!windowTitle().isEmpty()) {
s << "\n<title>" << windowTitle().toHtmlEscaped() << "</title>\n";
}
s << "</head><body>\n" << m_log << "\n</body></html>\n";
s.flush();
file.commit();
}
if (const int err = file.error()) {
KMessageBox::error(this, i18n("Could not save to file \"%1\": %2", file.fileName(), QString::fromLocal8Bit(strerror(err))), i18n("File Save Error"));
}
}
void AuditLogViewer::slotCopyClip()
{
#ifdef HAVE_PIMTEXTEDIT
m_textEdit->editor()->selectAll();
m_textEdit->editor()->copy();
m_textEdit->editor()->textCursor().clearSelection();
#else
m_textEdit->selectAll();
m_textEdit->copy();
m_textEdit->textCursor().clearSelection();
#endif
}
void AuditLogViewer::readConfig()
{
KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("AuditLogViewer"));
const QSize size = group.readEntry("Size", QSize());
if (size.isValid()) {
resize(size);
} else {
resize(600, 400);
}
}
void AuditLogViewer::writeConfig()
{
KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("AuditLogViewer"));
group.writeEntry("Size", size());
group.sync();
}
#include "moc_auditlogviewer.cpp"
diff --git a/src/ui/filenamerequester.cpp b/src/ui/filenamerequester.cpp
index bb1ec200..d694ee31 100644
--- a/src/ui/filenamerequester.cpp
+++ b/src/ui/filenamerequester.cpp
@@ -1,207 +1,207 @@
/* -*- mode: c++; c-basic-offset:4 -*-
ui/filenamerequester.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "filenamerequester.h"
#include <KLocalizedString>
#include <QCompleter>
#include <QEvent>
#include <QFileDialog>
#include <QFileSystemModel>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QString>
#include <QToolButton>
using namespace Kleo;
class Q_DECL_HIDDEN FileNameRequester::FileNameRequesterPrivate
{
friend class ::Kleo::FileNameRequester;
FileNameRequester *const q;
public:
explicit FileNameRequesterPrivate(FileNameRequester *qq);
~FileNameRequesterPrivate();
private:
void slotButtonClicked();
private:
#ifndef QT_NO_DIRMODEL
QFileSystemModel dirmodel;
QCompleter completer;
#else
QDir::Filters filter;
#endif
QLineEdit lineedit;
QToolButton button;
QHBoxLayout hlay;
QString nameFilter;
bool existingOnly;
};
FileNameRequester::FileNameRequesterPrivate::FileNameRequesterPrivate(FileNameRequester *qq)
: q(qq)
,
#ifndef QT_NO_DIRMODEL
dirmodel()
, completer(&dirmodel)
,
#else
filter()
,
#endif
lineedit(q)
, button(q)
, hlay(q)
, nameFilter()
, existingOnly(true)
{
#ifndef QT_NO_DIRMODEL
- dirmodel.setObjectName(QStringLiteral("dirmodel"));
- completer.setObjectName(QStringLiteral("completer"));
+ dirmodel.setObjectName(QLatin1StringView("dirmodel"));
+ completer.setObjectName(QLatin1StringView("completer"));
#endif
- lineedit.setObjectName(QStringLiteral("lineedit"));
- button.setObjectName(QStringLiteral("button"));
- hlay.setObjectName(QStringLiteral("hlay"));
+ lineedit.setObjectName(QLatin1StringView("lineedit"));
+ button.setObjectName(QLatin1StringView("button"));
+ hlay.setObjectName(QLatin1StringView("hlay"));
button.setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
button.setToolTip(i18n("Open file dialog"));
button.setAccessibleName(i18n("Open file dialog"));
#ifndef QT_NO_DIRMODEL
lineedit.setCompleter(&completer);
#endif
lineedit.setClearButtonEnabled(true);
hlay.setContentsMargins(0, 0, 0, 0);
hlay.addWidget(&lineedit);
hlay.addWidget(&button);
q->setFocusPolicy(lineedit.focusPolicy());
q->setFocusProxy(&lineedit);
connect(&button, &QToolButton::clicked, q, [this]() {
slotButtonClicked();
});
connect(&lineedit, &QLineEdit::textChanged, q, &FileNameRequester::fileNameChanged);
}
FileNameRequester::FileNameRequesterPrivate::~FileNameRequesterPrivate()
{
}
FileNameRequester::FileNameRequester(QWidget *p)
: QWidget(p)
, d(new FileNameRequesterPrivate(this))
{
}
FileNameRequester::FileNameRequester(QDir::Filters f, QWidget *p)
: QWidget(p)
, d(new FileNameRequesterPrivate(this))
{
#ifndef QT_NO_DIRMODEL
d->dirmodel.setFilter(f);
#else
d->filter = f;
#endif
}
FileNameRequester::~FileNameRequester() = default;
void FileNameRequester::setFileName(const QString &file)
{
d->lineedit.setText(file);
}
QString FileNameRequester::fileName() const
{
return d->lineedit.text();
}
void FileNameRequester::setExistingOnly(bool on)
{
d->existingOnly = on;
}
bool FileNameRequester::existingOnly() const
{
return d->existingOnly;
}
void FileNameRequester::setFilter(QDir::Filters f)
{
#ifndef QT_NO_DIRMODEL
d->dirmodel.setFilter(f);
#else
d->filter = f;
#endif
}
QDir::Filters FileNameRequester::filter() const
{
#ifndef QT_NO_DIRMODEL
return d->dirmodel.filter();
#else
return d->filter;
#endif
}
void FileNameRequester::setNameFilter(const QString &nameFilter)
{
d->nameFilter = nameFilter;
}
QString FileNameRequester::nameFilter() const
{
return d->nameFilter;
}
void FileNameRequester::setAccessibleNameOfLineEdit(const QString &name)
{
d->lineedit.setAccessibleName(name);
}
void FileNameRequester::FileNameRequesterPrivate::slotButtonClicked()
{
const QString fileName = q->requestFileName();
if (!fileName.isEmpty()) {
q->setFileName(fileName);
}
}
bool FileNameRequester::event(QEvent *e)
{
if (e->type() == QEvent::ToolTipChange) {
d->lineedit.setToolTip(toolTip());
}
return QWidget::event(e);
}
QString FileNameRequester::requestFileName()
{
#ifndef QT_NO_FILEDIALOG
const QDir::Filters filters = filter();
if ((filters & QDir::Dirs) && !(filters & QDir::Files)) {
return QFileDialog::getExistingDirectory(this);
} else if (d->existingOnly) {
return QFileDialog::getOpenFileName(this, QString(), QString(), d->nameFilter);
} else {
return QFileDialog::getSaveFileName(this, QString(), fileName(), d->nameFilter);
}
#else
return QString();
#endif
}
#include "moc_filenamerequester.cpp"
diff --git a/src/ui/keyselectiondialog.cpp b/src/ui/keyselectiondialog.cpp
index 997a7204..92d56c64 100644
--- a/src/ui/keyselectiondialog.cpp
+++ b/src/ui/keyselectiondialog.cpp
@@ -1,1022 +1,1022 @@
/* -*- c++ -*-
keyselectiondialog.cpp
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
Based on kpgpui.cpp
SPDX-FileCopyrightText: 2001, 2002 the KPGP authors
See file libkdenetwork/AUTHORS.kpgp for details
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "keyselectiondialog.h"
#include "keylistview.h"
#include "progressdialog.h"
#include <libkleo/compat.h>
#include <libkleo/compliance.h>
#include <libkleo/dn.h>
#include <libkleo/formatting.h>
#include <kleo_ui_debug.h>
#include <KConfig>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <QGpgME/KeyListJob>
#include <QApplication>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QFrame>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QProcess>
#include <QPushButton>
#include <QRegExp>
#include <QScrollBar>
#include <QTimer>
#include <QVBoxLayout>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include <algorithm>
#include <iterator>
#include <string.h>
using namespace Kleo;
static bool checkKeyUsage(const GpgME::Key &key, unsigned int keyUsage, QString *statusString = nullptr)
{
auto setStatusString = [statusString](const QString &status) {
if (statusString) {
*statusString = status;
}
};
if (keyUsage & KeySelectionDialog::ValidKeys) {
if (key.isInvalid()) {
if (key.keyListMode() & GpgME::Validate) {
qCDebug(KLEO_UI_LOG) << "key is invalid";
setStatusString(i18n("The key is not valid."));
return false;
} else {
qCDebug(KLEO_UI_LOG) << "key is invalid - ignoring";
}
}
if (key.isExpired()) {
qCDebug(KLEO_UI_LOG) << "key is expired";
setStatusString(i18n("The key is expired."));
return false;
} else if (key.isRevoked()) {
qCDebug(KLEO_UI_LOG) << "key is revoked";
setStatusString(i18n("The key is revoked."));
return false;
} else if (key.isDisabled()) {
qCDebug(KLEO_UI_LOG) << "key is disabled";
setStatusString(i18n("The key is disabled."));
return false;
}
}
if (keyUsage & KeySelectionDialog::EncryptionKeys && !Kleo::keyHasEncrypt(key)) {
qCDebug(KLEO_UI_LOG) << "key can't encrypt";
setStatusString(i18n("The key is not designated for encryption."));
return false;
}
if (keyUsage & KeySelectionDialog::SigningKeys && !Kleo::keyHasSign(key)) {
qCDebug(KLEO_UI_LOG) << "key can't sign";
setStatusString(i18n("The key is not designated for signing."));
return false;
}
if (keyUsage & KeySelectionDialog::CertificationKeys && !Kleo::keyHasCertify(key)) {
qCDebug(KLEO_UI_LOG) << "key can't certify";
setStatusString(i18n("The key is not designated for certifying."));
return false;
}
if (keyUsage & KeySelectionDialog::AuthenticationKeys && !Kleo::keyHasAuthenticate(key)) {
qCDebug(KLEO_UI_LOG) << "key can't authenticate";
setStatusString(i18n("The key is not designated for authentication."));
return false;
}
if (keyUsage & KeySelectionDialog::SecretKeys && !(keyUsage & KeySelectionDialog::PublicKeys) && !key.hasSecret()) {
qCDebug(KLEO_UI_LOG) << "key isn't secret";
setStatusString(i18n("The key is not secret."));
return false;
}
if (keyUsage & KeySelectionDialog::TrustedKeys && key.protocol() == GpgME::OpenPGP &&
// only check this for secret keys for now.
// Seems validity isn't checked for secret keylistings...
!key.hasSecret()) {
std::vector<GpgME::UserID> uids = key.userIDs();
for (std::vector<GpgME::UserID>::const_iterator it = uids.begin(); it != uids.end(); ++it) {
if (!it->isRevoked() && it->validity() >= GpgME::UserID::Marginal) {
setStatusString(i18n("The key can be used."));
return true;
}
}
qCDebug(KLEO_UI_LOG) << "key has no UIDs with validity >= Marginal";
setStatusString(i18n("The key is not trusted enough."));
return false;
}
// X.509 keys are always trusted, else they won't be the keybox.
// PENDING(marc) check that this ^ is correct
setStatusString(i18n("The key can be used."));
return true;
}
static bool checkKeyUsage(const std::vector<GpgME::Key> &keys, unsigned int keyUsage)
{
for (auto it = keys.begin(); it != keys.end(); ++it) {
if (!checkKeyUsage(*it, keyUsage)) {
return false;
}
}
return true;
}
namespace
{
class ColumnStrategy : public KeyListView::ColumnStrategy
{
public:
ColumnStrategy(unsigned int keyUsage);
QString title(int col) const override;
int width(int col, const QFontMetrics &fm) const override;
QString text(const GpgME::Key &key, int col) const override;
QString accessibleText(const GpgME::Key &key, int column) const override;
QString toolTip(const GpgME::Key &key, int col) const override;
QIcon icon(const GpgME::Key &key, int col) const override;
private:
const QIcon mKeyGoodPix, mKeyBadPix, mKeyUnknownPix, mKeyValidPix;
const unsigned int mKeyUsage;
};
static QString iconPath(const QString &name)
{
return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("libkleopatra/pics/") + name + QStringLiteral(".png"));
}
ColumnStrategy::ColumnStrategy(unsigned int keyUsage)
: KeyListView::ColumnStrategy()
, mKeyGoodPix(iconPath(QStringLiteral("key_ok")))
, mKeyBadPix(iconPath(QStringLiteral("key_bad")))
, mKeyUnknownPix(iconPath(QStringLiteral("key_unknown")))
, mKeyValidPix(iconPath(QStringLiteral("key")))
, mKeyUsage(keyUsage)
{
if (keyUsage == 0) {
qCWarning(KLEO_UI_LOG) << "KeySelectionDialog: keyUsage == 0. You want to use AllKeys instead.";
}
}
QString ColumnStrategy::title(int col) const
{
switch (col) {
case 0:
return i18n("Key ID");
case 1:
return i18n("User ID");
default:
return QString();
}
}
int ColumnStrategy::width(int col, const QFontMetrics &fm) const
{
if (col == 0) {
static const char hexchars[] = "0123456789ABCDEF";
int maxWidth = 0;
for (unsigned int i = 0; i < 16; ++i) {
maxWidth = qMax(fm.boundingRect(QLatin1Char(hexchars[i])).width(), maxWidth);
}
return 8 * maxWidth + 2 * 16 /* KIconLoader::SizeSmall */;
}
return KeyListView::ColumnStrategy::width(col, fm);
}
QString ColumnStrategy::text(const GpgME::Key &key, int col) const
{
switch (col) {
case 0: {
if (key.shortKeyID()) {
return Formatting::prettyID(key.shortKeyID());
} else {
return xi18n("<placeholder>unknown</placeholder>");
}
}
case 1: {
const char *uid = key.userID(0).id();
if (key.protocol() == GpgME::OpenPGP) {
return uid && *uid ? QString::fromUtf8(uid) : QString();
} else { // CMS
return DN(uid).prettyDN();
}
}
default:
return QString();
}
}
QString ColumnStrategy::accessibleText(const GpgME::Key &key, int col) const
{
switch (col) {
case 0: {
if (key.shortKeyID()) {
return Formatting::accessibleHexID(key.shortKeyID());
}
[[fallthrough]];
}
default:
return {};
}
}
QString ColumnStrategy::toolTip(const GpgME::Key &key, int) const
{
const char *uid = key.userID(0).id();
const char *fpr = key.primaryFingerprint();
const char *issuer = key.issuerName();
const GpgME::Subkey subkey = key.subkey(0);
const QString expiry = Formatting::expirationDateString(subkey);
const QString creation = Formatting::creationDateString(subkey);
QString keyStatusString;
if (!checkKeyUsage(key, mKeyUsage, &keyStatusString)) {
// Show the status in bold if there is a problem
keyStatusString = QLatin1String("<b>") % keyStatusString % QLatin1String("</b>");
}
QString html = QStringLiteral("<qt><p style=\"style='white-space:pre'\">");
if (key.protocol() == GpgME::OpenPGP) {
html += i18n("OpenPGP key for <b>%1</b>", uid ? QString::fromUtf8(uid) : i18n("unknown"));
} else {
html += i18n("S/MIME key for <b>%1</b>", uid ? DN(uid).prettyDN() : i18n("unknown"));
}
html += QStringLiteral("</p><table>");
const auto addRow = [&html](const QString &name, const QString &value) {
html += QStringLiteral("<tr><td align=\"right\"><b>%1: </b></td><td>%2</td></tr>").arg(name, value);
};
addRow(i18n("Valid from"), creation);
addRow(i18n("Valid until"), expiry);
addRow(i18nc("Key fingerprint", "Fingerprint"), fpr ? QString::fromLatin1(fpr) : i18n("unknown"));
if (key.protocol() != GpgME::OpenPGP) {
addRow(i18nc("Key issuer", "Issuer"), issuer ? DN(issuer).prettyDN() : i18n("unknown"));
}
addRow(i18nc("Key status", "Status"), keyStatusString);
if (DeVSCompliance::isActive()) {
addRow(i18nc("Compliance of key", "Compliance"), DeVSCompliance::name(key.isDeVs()));
}
html += QStringLiteral("</table></qt>");
return html;
}
QIcon ColumnStrategy::icon(const GpgME::Key &key, int col) const
{
if (col != 0) {
return QIcon();
}
// this key did not undergo a validating keylisting yet:
if (!(key.keyListMode() & GpgME::Validate)) {
return mKeyUnknownPix;
}
if (!checkKeyUsage(key, mKeyUsage)) {
return mKeyBadPix;
}
if (key.protocol() == GpgME::CMS) {
return mKeyGoodPix;
}
switch (key.userID(0).validity()) {
default:
case GpgME::UserID::Unknown:
case GpgME::UserID::Undefined:
return mKeyUnknownPix;
case GpgME::UserID::Never:
return mKeyValidPix;
case GpgME::UserID::Marginal:
case GpgME::UserID::Full:
case GpgME::UserID::Ultimate: {
if (DeVSCompliance::isActive() && !key.isDeVs()) {
return mKeyValidPix;
}
return mKeyGoodPix;
}
}
}
}
static const int sCheckSelectionDelay = 250;
KeySelectionDialog::KeySelectionDialog(QWidget *parent, Options options)
: QDialog(parent)
, mOpenPGPBackend(QGpgME::openpgp())
, mSMIMEBackend(QGpgME::smime())
, mKeyUsage(AllKeys)
{
qCDebug(KLEO_UI_LOG) << "mTruncated:" << mTruncated << "mSavedOffsetY:" << mSavedOffsetY;
setUpUI(options, QString());
}
KeySelectionDialog::KeySelectionDialog(const QString &title,
const QString &text,
const std::vector<GpgME::Key> &selectedKeys,
unsigned int keyUsage,
bool extendedSelection,
bool rememberChoice,
QWidget *parent,
bool modal)
: QDialog(parent)
, mSelectedKeys(selectedKeys)
, mKeyUsage(keyUsage)
{
setWindowTitle(title);
setModal(modal);
init(rememberChoice, extendedSelection, text, QString());
}
KeySelectionDialog::KeySelectionDialog(const QString &title,
const QString &text,
const QString &initialQuery,
const std::vector<GpgME::Key> &selectedKeys,
unsigned int keyUsage,
bool extendedSelection,
bool rememberChoice,
QWidget *parent,
bool modal)
: QDialog(parent)
, mSelectedKeys(selectedKeys)
, mKeyUsage(keyUsage)
, mSearchText(initialQuery)
, mInitialQuery(initialQuery)
{
setWindowTitle(title);
setModal(modal);
init(rememberChoice, extendedSelection, text, initialQuery);
}
KeySelectionDialog::KeySelectionDialog(const QString &title,
const QString &text,
const QString &initialQuery,
unsigned int keyUsage,
bool extendedSelection,
bool rememberChoice,
QWidget *parent,
bool modal)
: QDialog(parent)
, mKeyUsage(keyUsage)
, mSearchText(initialQuery)
, mInitialQuery(initialQuery)
{
setWindowTitle(title);
setModal(modal);
init(rememberChoice, extendedSelection, text, initialQuery);
}
void KeySelectionDialog::setUpUI(Options options, const QString &initialQuery)
{
auto mainLayout = new QVBoxLayout(this);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
mOkButton = buttonBox->button(QDialogButtonBox::Ok);
mOkButton->setDefault(true);
mOkButton->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Return));
mCheckSelectionTimer = new QTimer(this);
mStartSearchTimer = new QTimer(this);
QFrame *page = new QFrame(this);
mainLayout->addWidget(page);
mainLayout->addWidget(buttonBox);
mTopLayout = new QVBoxLayout(page);
mTopLayout->setContentsMargins(0, 0, 0, 0);
mTextLabel = new QLabel(page);
mTextLabel->setWordWrap(true);
// Setting the size policy is necessary as a workaround for https://issues.kolab.org/issue4429
// and http://bugreports.qt.nokia.com/browse/QTBUG-8740
mTextLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
connect(mTextLabel, &QLabel::linkActivated, this, &KeySelectionDialog::slotStartCertificateManager);
mTopLayout->addWidget(mTextLabel);
mTextLabel->hide();
QPushButton *const searchExternalPB = new QPushButton(i18n("Search for &External Certificates"), page);
mTopLayout->addWidget(searchExternalPB, 0, Qt::AlignLeft);
connect(searchExternalPB, &QAbstractButton::clicked, this, &KeySelectionDialog::slotStartSearchForExternalCertificates);
if (initialQuery.isEmpty()) {
searchExternalPB->hide();
}
auto hlay = new QHBoxLayout();
mTopLayout->addLayout(hlay);
auto le = new QLineEdit(page);
le->setClearButtonEnabled(true);
le->setText(initialQuery);
QLabel *lbSearchFor = new QLabel(i18n("&Search for:"), page);
lbSearchFor->setBuddy(le);
hlay->addWidget(lbSearchFor);
hlay->addWidget(le, 1);
le->setFocus();
connect(le, &QLineEdit::textChanged, this, [this](const QString &s) {
slotSearch(s);
});
connect(mStartSearchTimer, &QTimer::timeout, this, &KeySelectionDialog::slotFilter);
mKeyListView = new KeyListView(new ColumnStrategy(mKeyUsage), nullptr, page);
- mKeyListView->setObjectName(QStringLiteral("mKeyListView"));
+ mKeyListView->setObjectName(QLatin1StringView("mKeyListView"));
mKeyListView->header()->stretchLastSection();
mKeyListView->setRootIsDecorated(true);
mKeyListView->setSortingEnabled(true);
mKeyListView->header()->setSortIndicatorShown(true);
mKeyListView->header()->setSortIndicator(1, Qt::AscendingOrder); // sort by User ID
if (options & ExtendedSelection) {
mKeyListView->setSelectionMode(QAbstractItemView::ExtendedSelection);
}
mTopLayout->addWidget(mKeyListView, 10);
if (options & RememberChoice) {
mRememberCB = new QCheckBox(i18n("&Remember choice"), page);
mTopLayout->addWidget(mRememberCB);
mRememberCB->setWhatsThis(
i18n("<qt><p>If you check this box your choice will "
"be stored and you will not be asked again."
"</p></qt>"));
}
connect(mCheckSelectionTimer, &QTimer::timeout, this, [this]() {
slotCheckSelection();
});
connectSignals();
connect(mKeyListView, &KeyListView::doubleClicked, this, &KeySelectionDialog::slotTryOk);
connect(mKeyListView, &KeyListView::contextMenu, this, &KeySelectionDialog::slotRMB);
if (options & RereadKeys) {
QPushButton *button = new QPushButton(i18n("&Reread Keys"));
buttonBox->addButton(button, QDialogButtonBox::ActionRole);
connect(button, &QPushButton::clicked, this, &KeySelectionDialog::slotRereadKeys);
}
if (options & ExternalCertificateManager) {
QPushButton *button = new QPushButton(i18n("&Start Certificate Manager"));
buttonBox->addButton(button, QDialogButtonBox::ActionRole);
connect(button, &QPushButton::clicked, this, [this]() {
slotStartCertificateManager();
});
}
connect(mOkButton, &QPushButton::clicked, this, &KeySelectionDialog::slotOk);
connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &KeySelectionDialog::slotCancel);
mTopLayout->activate();
if (qApp) {
QSize dialogSize(sizeHint());
KConfigGroup dialogConfig(KSharedConfig::openStateConfig(), QStringLiteral("Key Selection Dialog"));
dialogSize = dialogConfig.readEntry("Dialog size", dialogSize);
const QByteArray headerState = dialogConfig.readEntry("header", QByteArray());
if (!headerState.isEmpty()) {
mKeyListView->header()->restoreState(headerState);
}
resize(dialogSize);
}
}
void KeySelectionDialog::init(bool rememberChoice, bool extendedSelection, const QString &text, const QString &initialQuery)
{
Options options = {RereadKeys, ExternalCertificateManager};
options.setFlag(ExtendedSelection, extendedSelection);
options.setFlag(RememberChoice, rememberChoice);
setUpUI(options, initialQuery);
setText(text);
if (mKeyUsage & OpenPGPKeys) {
mOpenPGPBackend = QGpgME::openpgp();
}
if (mKeyUsage & SMIMEKeys) {
mSMIMEBackend = QGpgME::smime();
}
slotRereadKeys();
}
KeySelectionDialog::~KeySelectionDialog()
{
disconnectSignals();
KConfigGroup dialogConfig(KSharedConfig::openStateConfig(), QStringLiteral("Key Selection Dialog"));
dialogConfig.writeEntry("Dialog size", size());
dialogConfig.writeEntry("header", mKeyListView->header()->saveState());
dialogConfig.sync();
}
void KeySelectionDialog::setText(const QString &text)
{
mTextLabel->setText(text);
mTextLabel->setVisible(!text.isEmpty());
}
void KeySelectionDialog::setKeys(const std::vector<GpgME::Key> &keys)
{
for (const GpgME::Key &key : keys) {
mKeyListView->slotAddKey(key);
}
}
void KeySelectionDialog::connectSignals()
{
if (mKeyListView->isMultiSelection()) {
connect(mKeyListView, &QTreeWidget::itemSelectionChanged, this, &KeySelectionDialog::slotSelectionChanged);
} else {
connect(mKeyListView,
qOverload<KeyListViewItem *>(&KeyListView::selectionChanged),
this,
qOverload<KeyListViewItem *>(&KeySelectionDialog::slotCheckSelection));
}
}
void KeySelectionDialog::disconnectSignals()
{
if (mKeyListView->isMultiSelection()) {
disconnect(mKeyListView, &QTreeWidget::itemSelectionChanged, this, &KeySelectionDialog::slotSelectionChanged);
} else {
disconnect(mKeyListView,
qOverload<KeyListViewItem *>(&KeyListView::selectionChanged),
this,
qOverload<KeyListViewItem *>(&KeySelectionDialog::slotCheckSelection));
}
}
const GpgME::Key &KeySelectionDialog::selectedKey() const
{
static const GpgME::Key null = GpgME::Key::null;
if (mKeyListView->isMultiSelection() || !mKeyListView->selectedItem()) {
return null;
}
return mKeyListView->selectedItem()->key();
}
QString KeySelectionDialog::fingerprint() const
{
return QLatin1String(selectedKey().primaryFingerprint());
}
QStringList KeySelectionDialog::fingerprints() const
{
QStringList result;
for (auto it = mSelectedKeys.begin(); it != mSelectedKeys.end(); ++it) {
if (const char *fpr = it->primaryFingerprint()) {
result.push_back(QLatin1String(fpr));
}
}
return result;
}
QStringList KeySelectionDialog::pgpKeyFingerprints() const
{
QStringList result;
for (auto it = mSelectedKeys.begin(); it != mSelectedKeys.end(); ++it) {
if (it->protocol() == GpgME::OpenPGP) {
if (const char *fpr = it->primaryFingerprint()) {
result.push_back(QLatin1String(fpr));
}
}
}
return result;
}
QStringList KeySelectionDialog::smimeFingerprints() const
{
QStringList result;
for (auto it = mSelectedKeys.begin(); it != mSelectedKeys.end(); ++it) {
if (it->protocol() == GpgME::CMS) {
if (const char *fpr = it->primaryFingerprint()) {
result.push_back(QLatin1String(fpr));
}
}
}
return result;
}
void KeySelectionDialog::slotRereadKeys()
{
mKeyListView->clear();
mListJobCount = 0;
mTruncated = 0;
mSavedOffsetY = mKeyListView->verticalScrollBar()->value();
disconnectSignals();
mKeyListView->setEnabled(false);
// FIXME: save current selection
if (mOpenPGPBackend) {
startKeyListJobForBackend(mOpenPGPBackend, std::vector<GpgME::Key>(), false /*non-validating*/);
}
if (mSMIMEBackend) {
startKeyListJobForBackend(mSMIMEBackend, std::vector<GpgME::Key>(), false /*non-validating*/);
}
if (mListJobCount == 0) {
mKeyListView->setEnabled(true);
KMessageBox::information(this,
i18n("No backends found for listing keys. "
"Check your installation."),
i18n("Key Listing Failed"));
connectSignals();
}
}
void KeySelectionDialog::slotStartCertificateManager(const QString &query)
{
QStringList args;
if (!query.isEmpty()) {
args << QStringLiteral("--search") << query;
}
const QString exec = QStandardPaths::findExecutable(QStringLiteral("kleopatra"));
if (exec.isEmpty()) {
qCWarning(KLEO_UI_LOG) << "Could not find kleopatra executable in PATH";
KMessageBox::error(this,
i18n("Could not start certificate manager; "
"please check your installation."),
i18n("Certificate Manager Error"));
} else {
QProcess::startDetached(QStringLiteral("kleopatra"), args);
qCDebug(KLEO_UI_LOG) << "\nslotStartCertManager(): certificate manager started.";
}
}
#ifndef __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
#define __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
static void showKeyListError(QWidget *parent, const GpgME::Error &err)
{
Q_ASSERT(err);
const QString msg = i18n(
"<qt><p>An error occurred while fetching "
"the keys from the backend:</p>"
"<p><b>%1</b></p></qt>",
Formatting::errorAsString(err));
KMessageBox::error(parent, msg, i18n("Key Listing Failed"));
}
#endif // __KLEO_UI_SHOW_KEY_LIST_ERROR_H__
namespace
{
struct ExtractFingerprint {
QString operator()(const GpgME::Key &key)
{
return QLatin1String(key.primaryFingerprint());
}
};
}
void KeySelectionDialog::startKeyListJobForBackend(const QGpgME::Protocol *backend, const std::vector<GpgME::Key> &keys, bool validate)
{
Q_ASSERT(backend);
QGpgME::KeyListJob *job = backend->keyListJob(false, false, validate); // local, w/o sigs, validation as given
if (!job) {
return;
}
connect(job, &QGpgME::KeyListJob::result, this, &KeySelectionDialog::slotKeyListResult);
if (validate) {
connect(job, &QGpgME::KeyListJob::nextKey, mKeyListView, &KeyListView::slotRefreshKey);
} else {
connect(job, &QGpgME::KeyListJob::nextKey, mKeyListView, &KeyListView::slotAddKey);
}
QStringList fprs;
std::transform(keys.begin(), keys.end(), std::back_inserter(fprs), ExtractFingerprint());
const GpgME::Error err = job->start(fprs, mKeyUsage & SecretKeys && !(mKeyUsage & PublicKeys));
if (err) {
return showKeyListError(this, err);
}
#ifndef LIBKLEO_NO_PROGRESSDIALOG
// FIXME: create a MultiProgressDialog:
(void)new ProgressDialog(job, validate ? i18n("Checking selected keys...") : i18n("Fetching keys..."), this);
#endif
++mListJobCount;
}
static void selectKeys(KeyListView *klv, const std::vector<GpgME::Key> &selectedKeys)
{
klv->clearSelection();
if (selectedKeys.empty()) {
return;
}
for (auto it = selectedKeys.begin(); it != selectedKeys.end(); ++it) {
if (KeyListViewItem *item = klv->itemByFingerprint(it->primaryFingerprint())) {
item->setSelected(true);
}
}
}
void KeySelectionDialog::slotKeyListResult(const GpgME::KeyListResult &res)
{
if (res.error()) {
showKeyListError(this, res.error());
} else if (res.isTruncated()) {
++mTruncated;
}
if (--mListJobCount > 0) {
return; // not yet finished...
}
if (mTruncated > 0) {
KMessageBox::information(this,
i18np("<qt>One backend returned truncated output.<p>"
"Not all available keys are shown</p></qt>",
"<qt>%1 backends returned truncated output.<p>"
"Not all available keys are shown</p></qt>",
mTruncated),
i18n("Key List Result"));
}
mKeyListView->flushKeys();
mKeyListView->setEnabled(true);
mListJobCount = mTruncated = 0;
mKeysToCheck.clear();
selectKeys(mKeyListView, mSelectedKeys);
slotFilter();
connectSignals();
slotSelectionChanged();
// restore the saved position of the contents
mKeyListView->verticalScrollBar()->setValue(mSavedOffsetY);
mSavedOffsetY = 0;
}
void KeySelectionDialog::slotSelectionChanged()
{
qCDebug(KLEO_UI_LOG) << "KeySelectionDialog::slotSelectionChanged()";
// (re)start the check selection timer. Checking the selection is delayed
// because else drag-selection doesn't work very good (checking key trust
// is slow).
mCheckSelectionTimer->start(sCheckSelectionDelay);
}
namespace
{
struct AlreadyChecked {
bool operator()(const GpgME::Key &key) const
{
return key.keyListMode() & GpgME::Validate;
}
};
}
void KeySelectionDialog::slotCheckSelection(KeyListViewItem *item)
{
qCDebug(KLEO_UI_LOG) << "KeySelectionDialog::slotCheckSelection()";
mCheckSelectionTimer->stop();
mSelectedKeys.clear();
if (!mKeyListView->isMultiSelection()) {
if (item) {
mSelectedKeys.push_back(item->key());
}
}
for (KeyListViewItem *it = mKeyListView->firstChild(); it; it = it->nextSibling()) {
if (it->isSelected()) {
mSelectedKeys.push_back(it->key());
}
}
mKeysToCheck.clear();
std::remove_copy_if(mSelectedKeys.begin(), mSelectedKeys.end(), std::back_inserter(mKeysToCheck), AlreadyChecked());
if (mKeysToCheck.empty()) {
mOkButton->setEnabled(!mSelectedKeys.empty() && checkKeyUsage(mSelectedKeys, mKeyUsage));
return;
}
// performed all fast checks - now for validating key listing:
startValidatingKeyListing();
}
void KeySelectionDialog::startValidatingKeyListing()
{
if (mKeysToCheck.empty()) {
return;
}
mListJobCount = 0;
mTruncated = 0;
mSavedOffsetY = mKeyListView->verticalScrollBar()->value();
disconnectSignals();
mKeyListView->setEnabled(false);
std::vector<GpgME::Key> smime;
std::vector<GpgME::Key> openpgp;
for (std::vector<GpgME::Key>::const_iterator it = mKeysToCheck.begin(); it != mKeysToCheck.end(); ++it) {
if (it->protocol() == GpgME::OpenPGP) {
openpgp.push_back(*it);
} else {
smime.push_back(*it);
}
}
if (!openpgp.empty()) {
Q_ASSERT(mOpenPGPBackend);
startKeyListJobForBackend(mOpenPGPBackend, openpgp, true /*validate*/);
}
if (!smime.empty()) {
Q_ASSERT(mSMIMEBackend);
startKeyListJobForBackend(mSMIMEBackend, smime, true /*validate*/);
}
Q_ASSERT(mListJobCount > 0);
}
bool KeySelectionDialog::rememberSelection() const
{
return mRememberCB && mRememberCB->isChecked();
}
void KeySelectionDialog::slotRMB(KeyListViewItem *item, const QPoint &p)
{
if (!item) {
return;
}
mCurrentContextMenuItem = item;
QMenu menu;
menu.addAction(i18n("Recheck Key"), this, &KeySelectionDialog::slotRecheckKey);
menu.exec(p);
}
void KeySelectionDialog::slotRecheckKey()
{
if (!mCurrentContextMenuItem || mCurrentContextMenuItem->key().isNull()) {
return;
}
mKeysToCheck.clear();
mKeysToCheck.push_back(mCurrentContextMenuItem->key());
}
void KeySelectionDialog::slotTryOk()
{
if (!mSelectedKeys.empty() && checkKeyUsage(mSelectedKeys, mKeyUsage)) {
slotOk();
}
}
void KeySelectionDialog::slotOk()
{
if (mCheckSelectionTimer->isActive()) {
slotCheckSelection();
}
#if 0 // Laurent I don't understand why we returns here.
// button could be disabled again after checking the selected key1
if (!mSelectedKeys.empty() && checkKeyUsage(mSelectedKeys, mKeyUsage)) {
return;
}
#endif
mStartSearchTimer->stop();
accept();
}
void KeySelectionDialog::slotCancel()
{
mCheckSelectionTimer->stop();
mStartSearchTimer->stop();
reject();
}
void KeySelectionDialog::slotSearch(const QString &text)
{
mSearchText = text.trimmed().toUpper();
slotSearch();
}
void KeySelectionDialog::slotSearch()
{
mStartSearchTimer->setSingleShot(true);
mStartSearchTimer->start(sCheckSelectionDelay);
}
void KeySelectionDialog::slotFilter()
{
if (mSearchText.isEmpty()) {
showAllItems();
return;
}
// OK, so we need to filter:
QRegExp keyIdRegExp(QLatin1String("(?:0x)?[A-F0-9]{1,8}"), Qt::CaseInsensitive);
if (keyIdRegExp.exactMatch(mSearchText)) {
if (mSearchText.startsWith(QLatin1String("0X")))
// search for keyID only:
{
filterByKeyID(mSearchText.mid(2));
} else
// search for UID and keyID:
{
filterByKeyIDOrUID(mSearchText);
}
} else {
// search in UID:
filterByUID(mSearchText);
}
}
void KeySelectionDialog::filterByKeyID(const QString &keyID)
{
Q_ASSERT(keyID.length() <= 8);
Q_ASSERT(!keyID.isEmpty()); // regexp in slotFilter should prevent these
if (keyID.isEmpty()) {
showAllItems();
} else {
for (KeyListViewItem *item = mKeyListView->firstChild(); item; item = item->nextSibling()) {
item->setHidden(!item->text(0).toUpper().startsWith(keyID));
}
}
}
static bool anyUIDMatches(const KeyListViewItem *item, QRegExp &rx)
{
if (!item) {
return false;
}
const std::vector<GpgME::UserID> uids = item->key().userIDs();
for (auto it = uids.begin(); it != uids.end(); ++it) {
if (it->id() && rx.indexIn(QString::fromUtf8(it->id())) >= 0) {
return true;
}
}
return false;
}
void KeySelectionDialog::filterByKeyIDOrUID(const QString &str)
{
Q_ASSERT(!str.isEmpty());
// match beginnings of words:
QRegExp rx(QLatin1String("\\b") + QRegExp::escape(str), Qt::CaseInsensitive);
for (KeyListViewItem *item = mKeyListView->firstChild(); item; item = item->nextSibling()) {
item->setHidden(!item->text(0).toUpper().startsWith(str) && !anyUIDMatches(item, rx));
}
}
void KeySelectionDialog::filterByUID(const QString &str)
{
Q_ASSERT(!str.isEmpty());
// match beginnings of words:
QRegExp rx(QLatin1String("\\b") + QRegExp::escape(str), Qt::CaseInsensitive);
for (KeyListViewItem *item = mKeyListView->firstChild(); item; item = item->nextSibling()) {
item->setHidden(!anyUIDMatches(item, rx));
}
}
void KeySelectionDialog::showAllItems()
{
for (KeyListViewItem *item = mKeyListView->firstChild(); item; item = item->nextSibling()) {
item->setHidden(false);
}
}
#include "moc_keyselectiondialog.cpp"
diff --git a/src/ui/newkeyapprovaldialog.cpp b/src/ui/newkeyapprovaldialog.cpp
index 17a2b096..4e5a41f5 100644
--- a/src/ui/newkeyapprovaldialog.cpp
+++ b/src/ui/newkeyapprovaldialog.cpp
@@ -1,934 +1,934 @@
/* -*- c++ -*-
newkeyapprovaldialog.cpp
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2018 Intevation GmbH
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "newkeyapprovaldialog.h"
#include "keyselectioncombo.h"
#include "progressdialog.h"
#include <libkleo/algorithm.h>
#include <libkleo/compliance.h>
#include <libkleo/debug.h>
#include <libkleo/defaultkeyfilter.h>
#include <libkleo/formatting.h>
#include <libkleo/gnupg.h>
#include <libkleo/keyhelpers.h>
#include <libkleo/systeminfo.h>
#include <libkleo_debug.h>
#include <KColorScheme>
#include <KLocalizedString>
#include <KMessageBox>
#include <QGpgME/Protocol>
#include <QGpgME/QuickJob>
#include <QButtonGroup>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QMap>
#include <QPushButton>
#include <QRadioButton>
#include <QScreen>
#include <QScrollArea>
#include <QToolTip>
#include <QVBoxLayout>
#include <gpgme++/context.h>
#include <gpgme++/key.h>
#include <gpgme++/keygenerationresult.h>
using namespace Kleo;
using namespace GpgME;
namespace
{
class EncryptFilter : public DefaultKeyFilter
{
public:
EncryptFilter()
: DefaultKeyFilter()
{
setHasEncrypt(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_encryptFilter = std::shared_ptr<KeyFilter>(new EncryptFilter);
class OpenPGPFilter : public DefaultKeyFilter
{
public:
OpenPGPFilter()
: DefaultKeyFilter()
{
setIsOpenPGP(DefaultKeyFilter::Set);
setHasEncrypt(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_pgpEncryptFilter = std::shared_ptr<KeyFilter>(new OpenPGPFilter);
class OpenPGPSignFilter : public DefaultKeyFilter
{
public:
OpenPGPSignFilter()
: DefaultKeyFilter()
{
/* Also list unusable keys to make it transparent why they are unusable */
setDisabled(DefaultKeyFilter::NotSet);
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setCanSign(DefaultKeyFilter::Set);
setHasSecret(DefaultKeyFilter::Set);
setIsOpenPGP(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_pgpSignFilter = std::shared_ptr<KeyFilter>(new OpenPGPSignFilter);
class SMIMEFilter : public DefaultKeyFilter
{
public:
SMIMEFilter()
: DefaultKeyFilter()
{
setIsOpenPGP(DefaultKeyFilter::NotSet);
setHasEncrypt(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_smimeEncryptFilter = std::shared_ptr<KeyFilter>(new SMIMEFilter);
class SMIMESignFilter : public DefaultKeyFilter
{
public:
SMIMESignFilter()
: DefaultKeyFilter()
{
setDisabled(DefaultKeyFilter::NotSet);
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setCanSign(DefaultKeyFilter::Set);
setIsOpenPGP(DefaultKeyFilter::NotSet);
setHasSecret(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_smimeSignFilter = std::shared_ptr<KeyFilter>(new SMIMESignFilter);
/* Some decoration and a button to remove the filter for a keyselectioncombo */
class ComboWidget : public QWidget
{
Q_OBJECT
public:
explicit ComboWidget(KeySelectionCombo *combo)
: mCombo(combo)
, mFilterBtn(new QPushButton)
{
auto hLay = new QHBoxLayout(this);
auto infoBtn = new QPushButton;
infoBtn->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual")));
infoBtn->setIconSize(QSize(22, 22));
infoBtn->setFlat(true);
infoBtn->setAccessibleName(i18nc("@action:button", "Show Details"));
hLay->addWidget(infoBtn);
hLay->addWidget(combo, 1);
hLay->addWidget(mFilterBtn, 0);
connect(infoBtn, &QPushButton::clicked, this, [this, infoBtn]() {
QToolTip::showText(infoBtn->mapToGlobal(QPoint()) + QPoint(infoBtn->width(), 0),
mCombo->currentData(Qt::ToolTipRole).toString(),
infoBtn,
QRect(),
30000);
});
// FIXME: This is ugly to enforce but otherwise the
// icon is broken.
combo->setMinimumHeight(22);
mFilterBtn->setMinimumHeight(23);
updateFilterButton();
connect(mFilterBtn, &QPushButton::clicked, this, [this]() {
const QString curFilter = mCombo->idFilter();
if (curFilter.isEmpty()) {
setIdFilter(mLastIdFilter);
mLastIdFilter = QString();
} else {
setIdFilter(QString());
mLastIdFilter = curFilter;
}
});
}
void setIdFilter(const QString &id)
{
mCombo->setIdFilter(id);
updateFilterButton();
}
void updateFilterButton()
{
if (mCombo->idFilter().isEmpty()) {
mFilterBtn->setIcon(QIcon::fromTheme(QStringLiteral("kt-add-filters")));
mFilterBtn->setAccessibleName(i18nc("@action:button", "Show Matching Keys"));
mFilterBtn->setToolTip(i18n("Show keys matching the email address"));
} else {
mFilterBtn->setIcon(QIcon::fromTheme(QStringLiteral("kt-remove-filters")));
mFilterBtn->setAccessibleName(i18nc("@action:button short for 'Show all keys'", "Show All"));
mFilterBtn->setToolTip(i18n("Show all keys"));
}
}
KeySelectionCombo *combo()
{
return mCombo;
}
GpgME::Protocol fixedProtocol() const
{
return mFixedProtocol;
}
void setFixedProtocol(GpgME::Protocol proto)
{
mFixedProtocol = proto;
}
private:
KeySelectionCombo *mCombo;
QPushButton *mFilterBtn;
QString mLastIdFilter;
GpgME::Protocol mFixedProtocol = GpgME::UnknownProtocol;
};
static bool key_has_addr(const GpgME::Key &key, const QString &addr)
{
for (const auto &uid : key.userIDs()) {
if (QString::fromStdString(uid.addrSpec()).toLower() == addr.toLower()) {
return true;
}
}
return false;
}
Key findfirstKeyOfType(const std::vector<Key> &keys, GpgME::Protocol protocol)
{
const auto it = std::find_if(std::begin(keys), std::end(keys), [protocol](const auto &key) {
return key.protocol() == protocol;
});
return it != std::end(keys) ? *it : Key();
}
} // namespace
class NewKeyApprovalDialog::Private
{
private:
enum Action {
Unset,
GenerateKey,
IgnoreKey,
};
public:
enum {
OpenPGPButtonId = 1,
SMIMEButtonId = 2,
};
Private(NewKeyApprovalDialog *qq,
bool encrypt,
bool sign,
GpgME::Protocol forcedProtocol,
GpgME::Protocol presetProtocol,
const QString &sender,
bool allowMixed)
: mForcedProtocol{forcedProtocol}
, mSender{sender}
, mSign{sign}
, mEncrypt{encrypt}
, mAllowMixed{allowMixed}
, q{qq}
{
Q_ASSERT(forcedProtocol == GpgME::UnknownProtocol || presetProtocol == GpgME::UnknownProtocol || presetProtocol == forcedProtocol);
Q_ASSERT(!allowMixed || (allowMixed && forcedProtocol == GpgME::UnknownProtocol));
Q_ASSERT(!(!allowMixed && presetProtocol == GpgME::UnknownProtocol));
// We do the translation here to avoid having the same string multiple times.
mGenerateTooltip = i18nc(
"@info:tooltip for a 'Generate new key pair' action "
"in a combobox when a user does not yet have an OpenPGP or S/MIME key.",
"Generate a new key using your E-Mail address.<br/><br/>"
"The key is necessary to decrypt and sign E-Mails. "
"You will be asked for a passphrase to protect this key and the protected key "
"will be stored in your home directory.");
mMainLay = new QVBoxLayout;
QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
mOkButton = btnBox->button(QDialogButtonBox::Ok);
#ifndef NDEBUG
- mOkButton->setObjectName(QStringLiteral("ok button"));
+ mOkButton->setObjectName(QLatin1StringView("ok button"));
#endif
QObject::connect(btnBox, &QDialogButtonBox::accepted, q, [this]() {
accepted();
});
QObject::connect(btnBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
mScrollArea = new QScrollArea;
mScrollArea->setWidget(new QWidget);
mScrollLayout = new QVBoxLayout;
mScrollArea->widget()->setLayout(mScrollLayout);
mScrollArea->setWidgetResizable(true);
mScrollArea->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
mScrollArea->setFrameStyle(QFrame::NoFrame);
mScrollLayout->setContentsMargins(0, 0, 0, 0);
q->setWindowTitle(i18nc("@title:window", "Security approval"));
auto fmtLayout = new QHBoxLayout;
mFormatBtns = new QButtonGroup(qq);
QAbstractButton *pgpBtn;
QAbstractButton *smimeBtn;
if (mAllowMixed) {
pgpBtn = new QCheckBox(i18n("OpenPGP"));
smimeBtn = new QCheckBox(i18n("S/MIME"));
} else {
pgpBtn = new QRadioButton(i18n("OpenPGP"));
smimeBtn = new QRadioButton(i18n("S/MIME"));
}
#ifndef NDEBUG
- pgpBtn->setObjectName(QStringLiteral("openpgp button"));
- smimeBtn->setObjectName(QStringLiteral("smime button"));
+ pgpBtn->setObjectName(QLatin1StringView("openpgp button"));
+ smimeBtn->setObjectName(QLatin1StringView("smime button"));
#endif
mFormatBtns->addButton(pgpBtn, OpenPGPButtonId);
mFormatBtns->addButton(smimeBtn, SMIMEButtonId);
mFormatBtns->setExclusive(!mAllowMixed);
connect(mFormatBtns, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), q, [this]() {
updateOkButton();
});
fmtLayout->addStretch(-1);
fmtLayout->addWidget(pgpBtn);
fmtLayout->addWidget(smimeBtn);
mMainLay->addLayout(fmtLayout);
if (mForcedProtocol != GpgME::UnknownProtocol) {
pgpBtn->setChecked(mForcedProtocol == GpgME::OpenPGP);
smimeBtn->setChecked(mForcedProtocol == GpgME::CMS);
pgpBtn->setVisible(false);
smimeBtn->setVisible(false);
} else {
pgpBtn->setChecked(presetProtocol == GpgME::OpenPGP || presetProtocol == GpgME::UnknownProtocol);
smimeBtn->setChecked(presetProtocol == GpgME::CMS || presetProtocol == GpgME::UnknownProtocol);
}
QObject::connect(mFormatBtns, &QButtonGroup::idClicked, q, [this](int buttonId) {
// ensure that at least one protocol button is checked
if (mAllowMixed && !mFormatBtns->button(OpenPGPButtonId)->isChecked() && !mFormatBtns->button(SMIMEButtonId)->isChecked()) {
mFormatBtns->button(buttonId == OpenPGPButtonId ? SMIMEButtonId : OpenPGPButtonId)->setChecked(true);
}
updateWidgets();
});
mMainLay->addWidget(mScrollArea);
mComplianceLbl = new QLabel;
mComplianceLbl->setVisible(false);
#ifndef NDEBUG
- mComplianceLbl->setObjectName(QStringLiteral("compliance label"));
+ mComplianceLbl->setObjectName(QLatin1StringView("compliance label"));
#endif
auto btnLayout = new QHBoxLayout;
btnLayout->addWidget(mComplianceLbl);
btnLayout->addWidget(btnBox);
mMainLay->addLayout(btnLayout);
q->setLayout(mMainLay);
}
~Private() = default;
GpgME::Protocol currentProtocol()
{
const bool openPGPButtonChecked = mFormatBtns->button(OpenPGPButtonId)->isChecked();
const bool smimeButtonChecked = mFormatBtns->button(SMIMEButtonId)->isChecked();
if (mAllowMixed) {
if (openPGPButtonChecked && !smimeButtonChecked) {
return GpgME::OpenPGP;
}
if (!openPGPButtonChecked && smimeButtonChecked) {
return GpgME::CMS;
}
} else if (openPGPButtonChecked) {
return GpgME::OpenPGP;
} else if (smimeButtonChecked) {
return GpgME::CMS;
}
return GpgME::UnknownProtocol;
}
auto findVisibleKeySelectionComboWithGenerateKey()
{
const auto it = std::find_if(std::begin(mAllCombos), std::end(mAllCombos), [](auto combo) {
return combo->isVisible() && combo->currentData(Qt::UserRole).toInt() == GenerateKey;
});
return it != std::end(mAllCombos) ? *it : nullptr;
}
void generateKey(KeySelectionCombo *combo)
{
if (!mRunningJobs.empty()) {
return;
}
const auto &addr = combo->property("address").toString();
auto job = QGpgME::openpgp()->quickJob();
auto progress =
new Kleo::ProgressDialog(job, i18n("Generating key for '%1'...", addr) + QStringLiteral("\n\n") + i18n("This can take several minutes."), q);
progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowContextHelpButtonHint);
progress->setWindowTitle(i18nc("@title:window", "Key generation"));
progress->setModal(true);
progress->setAutoClose(true);
progress->setMinimumDuration(0);
progress->setValue(0);
mRunningJobs << job;
if (!connect(job, &QGpgME::QuickJob::result, q, [this, job, combo]() {
handleKeyGenResult(QGpgME::Job::context(job)->keyGenerationResult(), job, combo);
})) {
qCWarning(LIBKLEO_LOG) << "new-style connect failed; connecting to QGpgME::QuickJob::result the old way";
connect(job, SIGNAL(result(const GpgME::Error &)), q, SLOT(handleKeyGenResult()));
}
job->startCreate(addr, nullptr);
return;
}
void handleKeyGenResult(const GpgME::KeyGenerationResult &result, QGpgME::Job *job, KeySelectionCombo *combo)
{
mLastError = result.error();
if (!mLastError) {
connect(combo, &KeySelectionCombo::keyListingFinished, q, [this, job]() {
mRunningJobs.removeAll(job);
});
// update all combos showing the GenerateKey item
for (auto c : std::as_const(mAllCombos)) {
if (c->currentData(Qt::UserRole).toInt() == GenerateKey) {
c->setDefaultKey(QString::fromLatin1(result.fingerprint()), GpgME::OpenPGP);
c->refreshKeys();
}
}
} else {
mRunningJobs.removeAll(job);
}
}
void checkAccepted()
{
if (mLastError) {
KMessageBox::error(q, Formatting::errorAsString(mLastError), i18n("Operation Failed"));
mRunningJobs.clear();
return;
}
if (!mRunningJobs.empty()) {
return;
}
/* Save the keys */
mAcceptedResult.protocol = currentProtocol();
for (const auto combo : std::as_const(mEncCombos)) {
const auto addr = combo->property("address").toString();
const auto key = combo->currentKey();
if (!combo->isVisible() || key.isNull()) {
continue;
}
mAcceptedResult.encryptionKeys[addr].push_back(key);
}
for (const auto combo : std::as_const(mSigningCombos)) {
const auto key = combo->currentKey();
if (!combo->isVisible() || key.isNull()) {
continue;
}
mAcceptedResult.signingKeys.push_back(key);
}
q->accept();
}
void accepted()
{
// We can assume everything was validly resolved, otherwise
// the OK button would have been disabled.
// Handle custom items now.
if (auto combo = findVisibleKeySelectionComboWithGenerateKey()) {
generateKey(combo);
return;
}
checkAccepted();
}
auto encryptionKeyFilter(GpgME::Protocol protocol)
{
switch (protocol) {
case OpenPGP:
return s_pgpEncryptFilter;
case CMS:
return s_smimeEncryptFilter;
default:
return s_encryptFilter;
}
}
void updateWidgets()
{
const GpgME::Protocol protocol = currentProtocol();
const auto encryptionFilter = encryptionKeyFilter(protocol);
for (auto combo : std::as_const(mSigningCombos)) {
auto widget = qobject_cast<ComboWidget *>(combo->parentWidget());
if (!widget) {
qCDebug(LIBKLEO_LOG) << "Failed to find signature combo widget";
continue;
}
widget->setVisible(protocol == GpgME::UnknownProtocol || widget->fixedProtocol() == GpgME::UnknownProtocol || widget->fixedProtocol() == protocol);
}
for (auto combo : std::as_const(mEncCombos)) {
auto widget = qobject_cast<ComboWidget *>(combo->parentWidget());
if (!widget) {
qCDebug(LIBKLEO_LOG) << "Failed to find combo widget";
continue;
}
widget->setVisible(protocol == GpgME::UnknownProtocol || widget->fixedProtocol() == GpgME::UnknownProtocol || widget->fixedProtocol() == protocol);
if (widget->isVisible() && combo->property("address") != mSender) {
combo->setKeyFilter(encryptionFilter);
}
}
// hide the labels indicating the protocol of the sender's keys if only a single protocol is active
const auto protocolLabels = q->findChildren<QLabel *>(QStringLiteral("protocol label"));
for (auto label : protocolLabels) {
label->setVisible(protocol == GpgME::UnknownProtocol);
}
}
auto createProtocolLabel(GpgME::Protocol protocol)
{
auto label = new QLabel(Formatting::displayName(protocol));
- label->setObjectName(QStringLiteral("protocol label"));
+ label->setObjectName(QLatin1StringView("protocol label"));
return label;
}
ComboWidget *createSigningCombo(const QString &addr, const GpgME::Key &key, GpgME::Protocol protocol = GpgME::UnknownProtocol)
{
Q_ASSERT(!key.isNull() || protocol != UnknownProtocol);
protocol = !key.isNull() ? key.protocol() : protocol;
auto combo = new KeySelectionCombo{true, KeyUsage::Sign};
auto comboWidget = new ComboWidget(combo);
#ifndef NDEBUG
- combo->setObjectName(QStringLiteral("signing key"));
+ combo->setObjectName(QLatin1StringView("signing key"));
#endif
if (protocol == GpgME::OpenPGP) {
combo->setKeyFilter(s_pgpSignFilter);
} else if (protocol == GpgME::CMS) {
combo->setKeyFilter(s_smimeSignFilter);
}
if (key.isNull() || key_has_addr(key, mSender)) {
comboWidget->setIdFilter(mSender);
}
comboWidget->setFixedProtocol(protocol);
if (!key.isNull()) {
combo->setDefaultKey(QString::fromLatin1(key.primaryFingerprint()), protocol);
}
if (key.isNull() && protocol == OpenPGP) {
combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Generate a new key pair"), GenerateKey, mGenerateTooltip);
}
combo->appendCustomItem(Formatting::unavailableIcon(),
i18n("Don't confirm identity and integrity"),
IgnoreKey,
i18nc("@info:tooltip for not selecting a key for signing.", "The E-Mail will not be cryptographically signed."));
mSigningCombos << combo;
mAllCombos << combo;
combo->setProperty("address", addr);
connect(combo, &KeySelectionCombo::currentKeyChanged, q, [this]() {
updateOkButton();
});
connect(combo, qOverload<int>(&QComboBox::currentIndexChanged), q, [this]() {
updateOkButton();
});
return comboWidget;
}
void setSigningKeys(const std::vector<GpgME::Key> &preferredKeys, const std::vector<GpgME::Key> &alternativeKeys)
{
auto group = new QGroupBox(i18nc("Caption for signing key selection", "Confirm identity '%1' as:", mSender));
group->setAlignment(Qt::AlignLeft);
auto sigLayout = new QVBoxLayout(group);
const bool mayNeedOpenPGP = mForcedProtocol != CMS;
const bool mayNeedCMS = mForcedProtocol != OpenPGP;
if (mayNeedOpenPGP) {
if (mAllowMixed) {
sigLayout->addWidget(createProtocolLabel(OpenPGP));
}
const Key preferredKey = findfirstKeyOfType(preferredKeys, OpenPGP);
const Key alternativeKey = findfirstKeyOfType(alternativeKeys, OpenPGP);
if (!preferredKey.isNull()) {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << preferredKey;
auto comboWidget = createSigningCombo(mSender, preferredKey);
sigLayout->addWidget(comboWidget);
} else if (!alternativeKey.isNull()) {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << alternativeKey;
auto comboWidget = createSigningCombo(mSender, alternativeKey);
sigLayout->addWidget(comboWidget);
} else {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for OpenPGP key";
auto comboWidget = createSigningCombo(mSender, Key(), OpenPGP);
sigLayout->addWidget(comboWidget);
}
}
if (mayNeedCMS) {
if (mAllowMixed) {
sigLayout->addWidget(createProtocolLabel(CMS));
}
const Key preferredKey = findfirstKeyOfType(preferredKeys, CMS);
const Key alternativeKey = findfirstKeyOfType(alternativeKeys, CMS);
if (!preferredKey.isNull()) {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << preferredKey;
auto comboWidget = createSigningCombo(mSender, preferredKey);
sigLayout->addWidget(comboWidget);
} else if (!alternativeKey.isNull()) {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << alternativeKey;
auto comboWidget = createSigningCombo(mSender, alternativeKey);
sigLayout->addWidget(comboWidget);
} else {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for S/MIME key";
auto comboWidget = createSigningCombo(mSender, Key(), CMS);
sigLayout->addWidget(comboWidget);
}
}
mScrollLayout->addWidget(group);
}
ComboWidget *createEncryptionCombo(const QString &addr, const GpgME::Key &key, GpgME::Protocol fixedProtocol)
{
auto combo = new KeySelectionCombo{false, KeyUsage::Encrypt};
auto comboWidget = new ComboWidget(combo);
#ifndef NDEBUG
- combo->setObjectName(QStringLiteral("encryption key"));
+ combo->setObjectName(QLatin1StringView("encryption key"));
#endif
if (fixedProtocol == GpgME::OpenPGP) {
combo->setKeyFilter(s_pgpEncryptFilter);
} else if (fixedProtocol == GpgME::CMS) {
combo->setKeyFilter(s_smimeEncryptFilter);
} else {
combo->setKeyFilter(s_encryptFilter);
}
if (key.isNull() || key_has_addr(key, addr)) {
comboWidget->setIdFilter(addr);
}
comboWidget->setFixedProtocol(fixedProtocol);
if (!key.isNull()) {
combo->setDefaultKey(QString::fromLatin1(key.primaryFingerprint()), fixedProtocol);
}
if (addr == mSender && key.isNull() && fixedProtocol == OpenPGP) {
combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Generate a new key pair"), GenerateKey, mGenerateTooltip);
}
combo->appendCustomItem(Formatting::unavailableIcon(),
i18n("No key. Recipient will be unable to decrypt."),
IgnoreKey,
i18nc("@info:tooltip for No Key selected for a specific recipient.",
"Do not select a key for this recipient.<br/><br/>"
"The recipient will receive the encrypted E-Mail, but it can only "
"be decrypted with the other keys selected in this dialog."));
connect(combo, &KeySelectionCombo::currentKeyChanged, q, [this]() {
updateOkButton();
});
connect(combo, qOverload<int>(&QComboBox::currentIndexChanged), q, [this]() {
updateOkButton();
});
mEncCombos << combo;
mAllCombos << combo;
combo->setProperty("address", addr);
return comboWidget;
}
void addEncryptionAddr(const QString &addr,
GpgME::Protocol preferredKeysProtocol,
const std::vector<GpgME::Key> &preferredKeys,
GpgME::Protocol alternativeKeysProtocol,
const std::vector<GpgME::Key> &alternativeKeys,
QGridLayout *encGrid)
{
if (addr == mSender) {
const bool mayNeedOpenPGP = mForcedProtocol != CMS;
const bool mayNeedCMS = mForcedProtocol != OpenPGP;
if (mayNeedOpenPGP) {
if (mAllowMixed) {
encGrid->addWidget(createProtocolLabel(OpenPGP), encGrid->rowCount(), 0);
}
for (const auto &key : preferredKeys) {
if (key.protocol() == OpenPGP) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, OpenPGP);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
for (const auto &key : alternativeKeys) {
if (key.protocol() == OpenPGP) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, OpenPGP);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
if (!anyKeyHasProtocol(preferredKeys, OpenPGP) && !anyKeyHasProtocol(alternativeKeys, OpenPGP)) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for OpenPGP key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), OpenPGP);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
if (mayNeedCMS) {
if (mAllowMixed) {
encGrid->addWidget(createProtocolLabel(CMS), encGrid->rowCount(), 0);
}
for (const auto &key : preferredKeys) {
if (key.protocol() == CMS) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, CMS);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
for (const auto &key : alternativeKeys) {
if (key.protocol() == CMS) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, CMS);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
if (!anyKeyHasProtocol(preferredKeys, CMS) && !anyKeyHasProtocol(alternativeKeys, CMS)) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for S/MIME key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), CMS);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
} else {
encGrid->addWidget(new QLabel(addr), encGrid->rowCount(), 0);
for (const auto &key : preferredKeys) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, preferredKeysProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
for (const auto &key : alternativeKeys) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, alternativeKeysProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
if (!mAllowMixed) {
if (preferredKeys.empty()) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << Formatting::displayName(preferredKeysProtocol)
<< "key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), preferredKeysProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
if (alternativeKeys.empty() && alternativeKeysProtocol != UnknownProtocol) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for"
<< Formatting::displayName(alternativeKeysProtocol) << "key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), alternativeKeysProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
} else {
if (preferredKeys.empty() && alternativeKeys.empty()) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for any key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), UnknownProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
}
}
void setEncryptionKeys(GpgME::Protocol preferredKeysProtocol,
const QMap<QString, std::vector<GpgME::Key>> &preferredKeys,
GpgME::Protocol alternativeKeysProtocol,
const QMap<QString, std::vector<GpgME::Key>> &alternativeKeys)
{
{
auto group = new QGroupBox(i18nc("Encrypt to self (email address):", "Encrypt to self (%1):", mSender));
#ifndef NDEBUG
- group->setObjectName(QStringLiteral("encrypt-to-self box"));
+ group->setObjectName(QLatin1StringView("encrypt-to-self box"));
#endif
group->setAlignment(Qt::AlignLeft);
auto encGrid = new QGridLayout(group);
addEncryptionAddr(mSender, preferredKeysProtocol, preferredKeys.value(mSender), alternativeKeysProtocol, alternativeKeys.value(mSender), encGrid);
encGrid->setColumnStretch(1, -1);
mScrollLayout->addWidget(group);
}
const bool hasOtherRecipients = std::any_of(preferredKeys.keyBegin(), preferredKeys.keyEnd(), [this](const auto &recipient) {
return recipient != mSender;
});
if (hasOtherRecipients) {
auto group = new QGroupBox(i18n("Encrypt to others:"));
#ifndef NDEBUG
- group->setObjectName(QStringLiteral("encrypt-to-others box"));
+ group->setObjectName(QLatin1StringView("encrypt-to-others box"));
#endif
group->setAlignment(Qt::AlignLeft);
auto encGrid = new QGridLayout{group};
for (auto it = std::begin(preferredKeys); it != std::end(preferredKeys); ++it) {
const auto &address = it.key();
const auto &keys = it.value();
if (address != mSender) {
addEncryptionAddr(address, preferredKeysProtocol, keys, alternativeKeysProtocol, alternativeKeys.value(address), encGrid);
}
}
encGrid->setColumnStretch(1, -1);
mScrollLayout->addWidget(group);
}
mScrollLayout->addStretch(-1);
}
void updateOkButton()
{
static QString origOkText = mOkButton->text();
const bool isGenerate = bool(findVisibleKeySelectionComboWithGenerateKey());
const bool allVisibleEncryptionKeysAreIgnored = Kleo::all_of(mEncCombos, [](auto combo) {
return !combo->isVisible() || combo->currentData(Qt::UserRole).toInt() == IgnoreKey;
});
const bool allVisibleEncryptionKeysAreUsable = Kleo::all_of(mEncCombos, [](auto combo) {
return !combo->isVisible() || combo->currentKey().isNull() || Kleo::canBeUsedForEncryption(combo->currentKey());
});
// If we don't encrypt, then the OK button is always enabled. Likewise,
// if the "generate key" option is selected. Otherwise,
// we only enable it if we encrypt to at least one recipient.
mOkButton->setEnabled(isGenerate || !mEncrypt || (!allVisibleEncryptionKeysAreIgnored && allVisibleEncryptionKeysAreUsable));
mOkButton->setText(isGenerate ? i18n("Generate") : origOkText);
if (!DeVSCompliance::isActive()) {
return;
}
// Handle compliance
bool de_vs = DeVSCompliance::isCompliant();
if (de_vs) {
const GpgME::Protocol protocol = currentProtocol();
for (const auto combo : std::as_const(mAllCombos)) {
if (!combo->isVisible()) {
continue;
}
const auto key = combo->currentKey();
if (key.isNull()) {
continue;
}
if (protocol != GpgME::UnknownProtocol && key.protocol() != protocol) {
continue;
}
if (!DeVSCompliance::keyIsCompliant(key)) {
de_vs = false;
break;
}
}
}
DeVSCompliance::decorate(mOkButton, de_vs);
mComplianceLbl->setText(DeVSCompliance::name(de_vs));
mComplianceLbl->setVisible(true);
}
GpgME::Protocol mForcedProtocol;
QList<KeySelectionCombo *> mSigningCombos;
QList<KeySelectionCombo *> mEncCombos;
QList<KeySelectionCombo *> mAllCombos;
QScrollArea *mScrollArea;
QVBoxLayout *mScrollLayout;
QPushButton *mOkButton;
QVBoxLayout *mMainLay;
QButtonGroup *mFormatBtns;
QString mSender;
bool mSign;
bool mEncrypt;
bool mAllowMixed;
NewKeyApprovalDialog *q;
QList<QGpgME::Job *> mRunningJobs;
GpgME::Error mLastError;
QLabel *mComplianceLbl;
KeyResolver::Solution mAcceptedResult;
QString mGenerateTooltip;
};
NewKeyApprovalDialog::NewKeyApprovalDialog(bool encrypt,
bool sign,
const QString &sender,
KeyResolver::Solution preferredSolution,
KeyResolver::Solution alternativeSolution,
bool allowMixed,
GpgME::Protocol forcedProtocol,
QWidget *parent,
Qt::WindowFlags f)
: QDialog(parent, f)
, d{std::make_unique<Private>(this, encrypt, sign, forcedProtocol, preferredSolution.protocol, sender, allowMixed)}
{
if (sign) {
d->setSigningKeys(std::move(preferredSolution.signingKeys), std::move(alternativeSolution.signingKeys));
}
if (encrypt) {
d->setEncryptionKeys(allowMixed ? UnknownProtocol : preferredSolution.protocol,
std::move(preferredSolution.encryptionKeys),
allowMixed ? UnknownProtocol : alternativeSolution.protocol,
std::move(alternativeSolution.encryptionKeys));
}
d->updateWidgets();
d->updateOkButton();
const auto size = sizeHint();
const auto desk = screen()->size();
resize(QSize(desk.width() / 3, qMin(size.height(), desk.height() / 2)));
}
Kleo::NewKeyApprovalDialog::~NewKeyApprovalDialog() = default;
KeyResolver::Solution NewKeyApprovalDialog::result()
{
return d->mAcceptedResult;
}
void NewKeyApprovalDialog::handleKeyGenResult()
{
if (d->mRunningJobs.empty()) {
qCWarning(LIBKLEO_LOG) << __func__ << "No running job";
}
const auto job = d->mRunningJobs.front();
const auto result = QGpgME::Job::context(job)->keyGenerationResult();
const auto combo = d->findVisibleKeySelectionComboWithGenerateKey();
d->handleKeyGenResult(result, job, combo);
}
#include "newkeyapprovaldialog.moc"
#include "moc_newkeyapprovaldialog.cpp"
diff --git a/tests/test_keygen.cpp b/tests/test_keygen.cpp
index 718818d5..80ec4d42 100644
--- a/tests/test_keygen.cpp
+++ b/tests/test_keygen.cpp
@@ -1,174 +1,174 @@
/*
test_keygen.cpp
This file is part of libkleopatra's test suite.
SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-only
*/
#include "test_keygen.h"
#include <libkleo/formatting.h>
#include <libkleo/progressdialog.h>
#include <qgpgme/keygenerationjob.h>
#include <qgpgme/keylistjob.h>
#include <qgpgme/protocol.h>
#include <gpgme++/keygenerationresult.h>
#include <KAboutData>
#include <KMessageBox>
#include <QDebug>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <KGuiItem>
#include <KLocalizedString>
#include <QApplication>
#include <QCommandLineParser>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
static const char *const keyParams[] = {
"Key-Type",
"Key-Length",
"Subkey-Type",
"Subkey-Length",
"Name-Real",
"Name-Comment",
"Name-Email",
"Name-DN",
"Expire-Date",
"Passphrase",
};
static const int numKeyParams = sizeof keyParams / sizeof *keyParams;
static const char *protocol = nullptr;
KeyGenerator::KeyGenerator(QWidget *parent)
: QDialog(parent)
{
setModal(true);
setWindowTitle(QStringLiteral("KeyGenerationJob test"));
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
QWidget *mainWidget = new QWidget(this);
auto mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(mainWidget);
auto user1Button = new QPushButton;
buttonBox->addButton(user1Button, QDialogButtonBox::ActionRole);
connect(buttonBox, &QDialogButtonBox::rejected, this, &KeyGenerator::reject);
user1Button->setDefault(true);
KGuiItem::assign(user1Button, KGuiItem(QStringLiteral("Create")));
QWidget *w = new QWidget(this);
mainLayout->addWidget(w);
mainLayout->addWidget(buttonBox);
auto glay = new QGridLayout(w);
int row = -1;
++row;
glay->addWidget(new QLabel(QStringLiteral("<GnupgKeyParms format=\"internal\">"), w), row, 0, 1, 2);
for (int i = 0; i < numKeyParams; ++i) {
++row;
glay->addWidget(new QLabel(QString::fromLatin1(keyParams[i]), w), row, 0);
glay->addWidget(mLineEdits[i] = new QLineEdit(w), row, 1);
}
++row;
glay->addWidget(new QLabel(QStringLiteral("</GnupgKeyParms>"), w), row, 0, 1, 2);
++row;
glay->setRowStretch(row, 1);
glay->setColumnStretch(1, 1);
connect(user1Button, &QPushButton::clicked, this, &KeyGenerator::slotStartKeyGeneration);
}
KeyGenerator::~KeyGenerator()
{
}
void KeyGenerator::slotStartKeyGeneration()
{
QString params = QStringLiteral("<GnupgKeyParms format=\"internal\">\n");
for (int i = 0; i < numKeyParams; ++i) {
if (mLineEdits[i] && !mLineEdits[i]->text().trimmed().isEmpty()) {
params += QString::fromLatin1(keyParams[i]) + (QStringLiteral(": ") + mLineEdits[i]->text().trimmed()) + QLatin1Char('\n');
}
}
params += QStringLiteral("</GnupgKeyParms>\n");
const QGpgME::Protocol *proto = nullptr;
if (protocol) {
proto = !strcmp(protocol, "openpgp") ? QGpgME::openpgp() : QGpgME::smime();
}
if (!proto) {
proto = QGpgME::smime();
}
Q_ASSERT(proto);
qDebug() << "Using protocol" << proto->name();
QGpgME::KeyGenerationJob *job = proto->keyGenerationJob();
Q_ASSERT(job);
connect(job, &QGpgME::KeyGenerationJob::result, this, &KeyGenerator::slotResult);
const GpgME::Error err = job->start(params);
if (err) {
showError(err);
}
#ifndef LIBKLEO_NO_PROGRESSDIALOG
else {
(void)new Kleo::ProgressDialog(job, QStringLiteral("Generating key"), this);
}
#endif
}
void KeyGenerator::showError(const GpgME::Error &err)
{
KMessageBox::error(this,
QStringLiteral("Could not start key generation: %1").arg(Kleo::Formatting::errorAsString(err)),
QStringLiteral("Key Generation Error"));
}
void KeyGenerator::slotResult(const GpgME::KeyGenerationResult &res, const QByteArray &keyData)
{
if (res.error()) {
showError(res.error());
} else {
KMessageBox::information(this,
QStringLiteral("Key generated successfully, %1 bytes long").arg(keyData.size()),
QStringLiteral("Key Generation Finished"));
}
}
int main(int argc, char **argv)
{
if (argc == 2) {
protocol = argv[1];
argc = 1; // hide from KDE
}
QApplication app(argc, argv);
KAboutData aboutData(QStringLiteral("test_keygen"), i18n("KeyGenerationJob Test"), QStringLiteral("0.1"));
QCommandLineParser parser;
KAboutData::setApplicationData(aboutData);
aboutData.setupCommandLine(&parser);
parser.process(app);
aboutData.processCommandLine(&parser);
auto keygen = new KeyGenerator(nullptr);
- keygen->setObjectName(QStringLiteral("KeyGenerator top-level"));
+ keygen->setObjectName(QLatin1StringView("KeyGenerator top-level"));
keygen->show();
return app.exec();
}
#include "moc_test_keygen.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Dec 5, 6:23 PM (1 d, 5 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
b2/77/e0aec3241b33d91528cbecaeb4ca
Attached To
rLIBKLEO Libkleo
Event Timeline
Log In to Comment