Page MenuHome GnuPG

No OneTemporary

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

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

Event Timeline