Page MenuHome GnuPG

No OneTemporary

diff --git a/src/kleo/checksumdefinition.cpp b/src/kleo/checksumdefinition.cpp
index 6251a1fc..5a0fae0a 100644
--- a/src/kleo/checksumdefinition.cpp
+++ b/src/kleo/checksumdefinition.cpp
@@ -1,432 +1,432 @@
/* -*- mode: c++; c-basic-offset:4 -*-
checksumdefinition.cpp
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "checksumdefinition.h"
#include "kleoexception.h"
#include <libkleo_debug.h>
#include <KConfig>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <KShell>
#include <QByteArray>
#include <QCoreApplication>
#include <QDebug>
#include <QFileInfo>
#include <QMutex>
#include <QProcess>
#include <QRegularExpression>
#include <QStandardPaths>
#ifdef stdin
#undef stdin // pah..
#endif
using namespace Kleo;
static QMutex installPathMutex;
Q_GLOBAL_STATIC(QString, _installPath)
QString ChecksumDefinition::installPath()
{
const QMutexLocker locker(&installPathMutex);
QString *const ip = _installPath();
if (ip->isEmpty()) {
if (QCoreApplication::instance()) {
*ip = QCoreApplication::applicationDirPath();
} else {
qCWarning(LIBKLEO_LOG) << "checksumdefinition.cpp: installPath() called before QCoreApplication was constructed";
}
}
return *ip;
}
void ChecksumDefinition::setInstallPath(const QString &ip)
{
const QMutexLocker locker(&installPathMutex);
*_installPath() = ip;
}
// Checksum Definition #N groups
static const QLatin1String ID_ENTRY("id");
static const QLatin1String NAME_ENTRY("Name");
static const QLatin1String CREATE_COMMAND_ENTRY("create-command");
static const QLatin1String VERIFY_COMMAND_ENTRY("verify-command");
static const QLatin1String FILE_PATTERNS_ENTRY("file-patterns");
static const QLatin1String OUTPUT_FILE_ENTRY("output-file");
static const QLatin1String FILE_PLACEHOLDER("%f");
static const QLatin1String INSTALLPATH_PLACEHOLDER("%I");
static const QLatin1String NULL_SEPARATED_STDIN_INDICATOR("0|");
static const QLatin1Char NEWLINE_SEPARATED_STDIN_INDICATOR('|');
// ChecksumOperations group
static const QLatin1String CHECKSUM_DEFINITION_ID_ENTRY("checksum-definition-id");
namespace
{
class ChecksumDefinitionError : public Kleo::Exception
{
const QString m_id;
public:
ChecksumDefinitionError(const QString &id, const QString &message)
: Kleo::Exception(GPG_ERR_INV_PARAMETER, i18n("Error in checksum definition %1: %2", id, message), MessageOnly)
, m_id(id)
{
}
~ChecksumDefinitionError() throw() override
{
}
const QString &checksumDefinitionId() const
{
return m_id;
}
};
}
static QString try_extensions(const QString &path)
{
static const char exts[][4] = {
"",
"exe",
"bat",
"bin",
"cmd",
};
static const size_t numExts = sizeof exts / sizeof *exts;
for (unsigned int i = 0; i < numExts; ++i) {
const QFileInfo fi(path + QLatin1Char('.') + QLatin1String(exts[i]));
if (fi.exists()) {
return fi.filePath();
}
}
return QString();
}
static void parse_command(QString cmdline,
const QString &id,
const QString &whichCommand,
QString *command,
QStringList *prefix,
QStringList *suffix,
ChecksumDefinition::ArgumentPassingMethod *method)
{
Q_ASSERT(prefix);
Q_ASSERT(suffix);
Q_ASSERT(method);
KShell::Errors errors;
QStringList l;
if (cmdline.startsWith(NULL_SEPARATED_STDIN_INDICATOR)) {
*method = ChecksumDefinition::NullSeparatedInputFile;
cmdline.remove(0, 2);
} else if (cmdline.startsWith(NEWLINE_SEPARATED_STDIN_INDICATOR)) {
*method = ChecksumDefinition::NewlineSeparatedInputFile;
cmdline.remove(0, 1);
} else {
*method = ChecksumDefinition::CommandLine;
}
if (*method != ChecksumDefinition::CommandLine && cmdline.contains(FILE_PLACEHOLDER)) {
throw ChecksumDefinitionError(id, i18n("Cannot use both %f and | in '%1'", whichCommand));
}
cmdline
.replace(FILE_PLACEHOLDER, QLatin1String("__files_go_here__")) //
.replace(INSTALLPATH_PLACEHOLDER, QStringLiteral("__path_goes_here__"));
l = KShell::splitArgs(cmdline, KShell::AbortOnMeta | KShell::TildeExpand, &errors);
l = l.replaceInStrings(QStringLiteral("__files_go_here__"), FILE_PLACEHOLDER);
static const QRegularExpression regExpression(QLatin1String(".*__path_goes_here__.*"));
if (l.indexOf(regExpression) >= 0) {
l = l.replaceInStrings(QStringLiteral("__path_goes_here__"), ChecksumDefinition::installPath());
}
if (errors == KShell::BadQuoting) {
throw ChecksumDefinitionError(id, i18n("Quoting error in '%1' entry", whichCommand));
}
if (errors == KShell::FoundMeta) {
throw ChecksumDefinitionError(id, i18n("'%1' too complex (would need shell)", whichCommand));
}
qCDebug(LIBKLEO_LOG) << "ChecksumDefinition[" << id << ']' << l;
if (l.empty()) {
throw ChecksumDefinitionError(id, i18n("'%1' entry is empty/missing", whichCommand));
}
const QFileInfo fi1(l.front());
if (fi1.isAbsolute()) {
*command = try_extensions(l.front());
} else {
*command = QStandardPaths::findExecutable(fi1.fileName());
}
if (command->isEmpty()) {
throw ChecksumDefinitionError(id, i18n("'%1' empty or not found", whichCommand));
}
const int idx1 = l.indexOf(FILE_PLACEHOLDER);
if (idx1 < 0) {
// none -> append
*prefix = l.mid(1);
} else {
*prefix = l.mid(1, idx1 - 1);
*suffix = l.mid(idx1 + 1);
}
switch (*method) {
case ChecksumDefinition::CommandLine:
qCDebug(LIBKLEO_LOG) << "ChecksumDefinition[" << id << ']' << *command << *prefix << FILE_PLACEHOLDER << *suffix;
break;
case ChecksumDefinition::NewlineSeparatedInputFile:
qCDebug(LIBKLEO_LOG) << "ChecksumDefinition[" << id << ']' << "find | " << *command << *prefix;
break;
case ChecksumDefinition::NullSeparatedInputFile:
qCDebug(LIBKLEO_LOG) << "ChecksumDefinition[" << id << ']' << "find -print0 | " << *command << *prefix;
break;
case ChecksumDefinition::NumArgumentPassingMethods:
Q_ASSERT(!"Should not happen");
break;
}
}
namespace
{
class KConfigBasedChecksumDefinition : public ChecksumDefinition
{
public:
explicit KConfigBasedChecksumDefinition(const KConfigGroup &group)
: ChecksumDefinition(group.readEntryUntranslated(ID_ENTRY),
group.readEntry(NAME_ENTRY),
group.readEntry(OUTPUT_FILE_ENTRY),
group.readEntry(FILE_PATTERNS_ENTRY, QStringList()))
{
if (id().isEmpty()) {
throw ChecksumDefinitionError(group.name(), i18n("'id' entry is empty/missing"));
}
if (outputFileName().isEmpty()) {
throw ChecksumDefinitionError(id(), i18n("'output-file' entry is empty/missing"));
}
if (patterns().empty()) {
throw ChecksumDefinitionError(id(), i18n("'file-patterns' entry is empty/missing"));
}
// create-command
ArgumentPassingMethod method;
parse_command(group.readEntry(CREATE_COMMAND_ENTRY),
id(),
CREATE_COMMAND_ENTRY,
&m_createCommand,
&m_createPrefixArguments,
&m_createPostfixArguments,
&method);
setCreateCommandArgumentPassingMethod(method);
// verify-command
parse_command(group.readEntry(VERIFY_COMMAND_ENTRY),
id(),
VERIFY_COMMAND_ENTRY,
&m_verifyCommand,
&m_verifyPrefixArguments,
&m_verifyPostfixArguments,
&method);
setVerifyCommandArgumentPassingMethod(method);
}
private:
QString doGetCreateCommand() const override
{
return m_createCommand;
}
QStringList doGetCreateArguments(const QStringList &files) const override
{
return m_createPrefixArguments + files + m_createPostfixArguments;
}
QString doGetVerifyCommand() const override
{
return m_verifyCommand;
}
QStringList doGetVerifyArguments(const QStringList &files) const override
{
return m_verifyPrefixArguments + files + m_verifyPostfixArguments;
}
private:
QString m_createCommand, m_verifyCommand;
QStringList m_createPrefixArguments, m_createPostfixArguments;
QStringList m_verifyPrefixArguments, m_verifyPostfixArguments;
};
}
ChecksumDefinition::ChecksumDefinition(const QString &id, const QString &label, const QString &outputFileName, const QStringList &patterns)
: m_id(id)
, m_label(label.isEmpty() ? id : label)
, m_outputFileName(outputFileName)
, m_patterns(patterns)
, m_createMethod(CommandLine)
, m_verifyMethod(CommandLine)
{
}
ChecksumDefinition::~ChecksumDefinition()
{
}
QString ChecksumDefinition::createCommand() const
{
return doGetCreateCommand();
}
QString ChecksumDefinition::verifyCommand() const
{
return doGetVerifyCommand();
}
#if 0
QStringList ChecksumDefinition::createCommandArguments(const QStringList &files) const
{
return doGetCreateArguments(files);
}
QStringList ChecksumDefinition::verifyCommandArguments(const QStringList &files) const
{
return doGetVerifyArguments(files);
}
#endif
static QByteArray make_input(const QStringList &files, char sep)
{
QByteArray result;
for (const QString &file : files) {
result += QFile::encodeName(file);
result += sep;
}
return result;
}
static bool start_command(QProcess *p,
const char *functionName,
const QString &cmd,
const QStringList &args,
const QStringList &files,
ChecksumDefinition::ArgumentPassingMethod method)
{
if (!p) {
qCWarning(LIBKLEO_LOG) << functionName << ": process == NULL";
return false;
}
switch (method) {
case ChecksumDefinition::NumArgumentPassingMethods:
Q_ASSERT(!"Should not happen");
case ChecksumDefinition::CommandLine:
qCDebug(LIBKLEO_LOG) << "Starting: " << cmd << " " << args.join(QLatin1Char(' '));
p->start(cmd, args, QIODevice::ReadOnly);
return true;
case ChecksumDefinition::NewlineSeparatedInputFile:
case ChecksumDefinition::NullSeparatedInputFile:
qCDebug(LIBKLEO_LOG) << "Starting: " << cmd << " " << args.join(QLatin1Char(' '));
p->start(cmd, args, QIODevice::ReadWrite);
if (!p->waitForStarted()) {
return false;
}
const char sep = method == ChecksumDefinition::NewlineSeparatedInputFile ? '\n' : '\0';
const QByteArray stdin = make_input(files, sep);
if (p->write(stdin) != stdin.size()) {
return false;
}
p->closeWriteChannel();
return true;
}
return false; // make compiler happy
}
bool ChecksumDefinition::startCreateCommand(QProcess *p, const QStringList &files) const
{
return start_command(p,
Q_FUNC_INFO,
doGetCreateCommand(),
m_createMethod == CommandLine ? doGetCreateArguments(files) : doGetCreateArguments(QStringList()),
files,
m_createMethod);
}
bool ChecksumDefinition::startVerifyCommand(QProcess *p, const QStringList &files) const
{
return start_command(p,
Q_FUNC_INFO,
doGetVerifyCommand(),
m_verifyMethod == CommandLine ? doGetVerifyArguments(files) : doGetVerifyArguments(QStringList()),
files,
m_verifyMethod);
}
// static
std::vector<std::shared_ptr<ChecksumDefinition>> ChecksumDefinition::getChecksumDefinitions()
{
QStringList errors;
return getChecksumDefinitions(errors);
}
// static
std::vector<std::shared_ptr<ChecksumDefinition>> ChecksumDefinition::getChecksumDefinitions(QStringList &errors)
{
std::vector<std::shared_ptr<ChecksumDefinition>> result;
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"));
const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Checksum Definition #")));
result.reserve(groups.size());
for (const QString &group : groups) {
try {
const std::shared_ptr<ChecksumDefinition> ad(new KConfigBasedChecksumDefinition(KConfigGroup(config, group)));
result.push_back(ad);
} catch (const std::exception &e) {
qDebug() << e.what();
errors.push_back(QString::fromLocal8Bit(e.what()));
} catch (...) {
errors.push_back(i18n("Caught unknown exception in group %1", group));
}
}
return result;
}
// static
std::shared_ptr<ChecksumDefinition>
ChecksumDefinition::getDefaultChecksumDefinition(const std::vector<std::shared_ptr<ChecksumDefinition>> &checksumDefinitions)
{
- const KConfigGroup group(KSharedConfig::openConfig(), "ChecksumOperations");
+ const KConfigGroup group(KSharedConfig::openConfig(), QLatin1String("ChecksumOperations"));
const QString checksumDefinitionId = group.readEntry(CHECKSUM_DEFINITION_ID_ENTRY, QStringLiteral("sha256sum"));
if (!checksumDefinitionId.isEmpty()) {
for (const std::shared_ptr<ChecksumDefinition> &cd : checksumDefinitions) {
if (cd && cd->id() == checksumDefinitionId) {
return cd;
}
}
}
if (!checksumDefinitions.empty()) {
return checksumDefinitions.front();
} else {
return std::shared_ptr<ChecksumDefinition>();
}
}
// static
void ChecksumDefinition::setDefaultChecksumDefinition(const std::shared_ptr<ChecksumDefinition> &checksumDefinition)
{
if (!checksumDefinition) {
return;
}
- KConfigGroup group(KSharedConfig::openConfig(), "ChecksumOperations");
+ KConfigGroup group(KSharedConfig::openConfig(), QLatin1String("ChecksumOperations"));
group.writeEntry(CHECKSUM_DEFINITION_ID_ENTRY, checksumDefinition->id());
group.sync();
}
diff --git a/src/kleo/keygroupconfig.cpp b/src/kleo/keygroupconfig.cpp
index 158fa8df..674d188f 100644
--- a/src/kleo/keygroupconfig.cpp
+++ b/src/kleo/keygroupconfig.cpp
@@ -1,177 +1,177 @@
/*
kleo/keygroupconfig.cpp
This file is part of libkleopatra, the KDE keymanagement library
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 "keygroupconfig.h"
#include "debug.h"
#include "keygroup.h"
#include <libkleo/keycache.h>
#include <libkleo/keyhelpers.h>
#include <libkleo/qtstlhelpers.h>
#include <libkleo_debug.h>
#include <KConfigGroup>
#include <KSharedConfig>
#include <QString>
#include <gpgme++/key.h>
using namespace Kleo;
using namespace GpgME;
static const QString groupNamePrefix = QStringLiteral("Group-");
class KeyGroupConfig::Private
{
public:
explicit Private(const QString &filename);
std::vector<KeyGroup> readGroups() const;
KeyGroup writeGroup(const KeyGroup &group);
bool removeGroup(const KeyGroup &group);
private:
KeyGroup readGroup(const KSharedConfigPtr &groupsConfig, const QString &groupId) const;
private:
QString filename;
};
KeyGroupConfig::Private::Private(const QString &filename)
: filename{filename}
{
if (filename.isEmpty()) {
qCWarning(LIBKLEO_LOG) << __func__ << "Warning: name of configuration file is empty";
}
}
KeyGroup KeyGroupConfig::Private::readGroup(const KSharedConfigPtr &groupsConfig, const QString &groupId) const
{
const KConfigGroup configGroup = groupsConfig->group(groupNamePrefix + groupId);
const QString groupName = configGroup.readEntry("Name", QString());
const auto fingerprints = toStdStrings(configGroup.readEntry("Keys", QStringList()));
const std::vector<Key> groupKeys = KeyCache::instance()->findByFingerprint(fingerprints);
// treat group as immutable if any of its entries is immutable
const QStringList entries = configGroup.keyList();
const bool isImmutable = (configGroup.isImmutable() //
|| std::any_of(entries.begin(), entries.end(), [configGroup](const QString &entry) {
return configGroup.isEntryImmutable(entry);
}));
KeyGroup g(groupId, groupName, groupKeys, KeyGroup::ApplicationConfig);
g.setIsImmutable(isImmutable);
qCDebug(LIBKLEO_LOG) << "Read group" << g;
return g;
}
std::vector<KeyGroup> KeyGroupConfig::Private::readGroups() const
{
std::vector<KeyGroup> groups;
if (filename.isEmpty()) {
return groups;
}
const KSharedConfigPtr groupsConfig = KSharedConfig::openConfig(filename);
const QStringList configGroups = groupsConfig->groupList();
for (const QString &configGroupName : configGroups) {
qCDebug(LIBKLEO_LOG) << "Reading config group" << configGroupName;
if (configGroupName.startsWith(groupNamePrefix)) {
const QString keyGroupId = configGroupName.mid(groupNamePrefix.size());
if (keyGroupId.isEmpty()) {
qCWarning(LIBKLEO_LOG) << "Config group" << configGroupName << "has empty group id";
continue;
}
KeyGroup group = readGroup(groupsConfig, keyGroupId);
groups.push_back(group);
}
}
return groups;
}
KeyGroup KeyGroupConfig::Private::writeGroup(const KeyGroup &group)
{
if (filename.isEmpty()) {
return {};
}
if (group.isNull()) {
qCDebug(LIBKLEO_LOG) << __func__ << "Error: group is null";
return group;
}
KSharedConfigPtr groupsConfig = KSharedConfig::openConfig(filename);
KConfigGroup configGroup = groupsConfig->group(groupNamePrefix + group.id());
qCDebug(LIBKLEO_LOG) << __func__ << "Writing config group" << configGroup.name();
configGroup.writeEntry("Name", group.name());
configGroup.writeEntry("Keys", Kleo::getFingerprints(group.keys()));
// reread group to ensure that it reflects the saved group in case of immutable entries
return readGroup(groupsConfig, group.id());
}
bool KeyGroupConfig::Private::removeGroup(const KeyGroup &group)
{
if (filename.isEmpty()) {
return false;
}
if (group.isNull()) {
qCDebug(LIBKLEO_LOG) << __func__ << "Error: group is null";
return false;
}
KSharedConfigPtr groupsConfig = KSharedConfig::openConfig(filename);
KConfigGroup configGroup = groupsConfig->group(groupNamePrefix + group.id());
qCDebug(LIBKLEO_LOG) << __func__ << "Removing config group" << configGroup.name();
- configGroup.deleteGroup();
+ configGroup.deleteGroup(QLatin1String());
return true;
}
KeyGroupConfig::KeyGroupConfig(const QString &filename)
: d{std::make_unique<Private>(filename)}
{
}
KeyGroupConfig::~KeyGroupConfig() = default;
std::vector<KeyGroup> KeyGroupConfig::readGroups() const
{
return d->readGroups();
}
KeyGroup KeyGroupConfig::writeGroup(const KeyGroup &group)
{
return d->writeGroup(group);
}
void KeyGroupConfig::writeGroups(const std::vector<KeyGroup> &groups)
{
std::for_each(std::begin(groups), std::end(groups), [this](const auto &group) {
d->writeGroup(group);
});
}
bool KeyGroupConfig::removeGroup(const KeyGroup &group)
{
return d->removeGroup(group);
}
diff --git a/src/ui/auditlogviewer.cpp b/src/ui/auditlogviewer.cpp
index 37361273..844758af 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->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->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"));
KGuiItem::assign(closeBtn, KStandardGuiItem::close());
m_textEdit->setObjectName(QStringLiteral("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(), "AuditLogViewer");
+ KConfigGroup group(KSharedConfig::openConfig(), QLatin1String("AuditLogViewer"));
const QSize size = group.readEntry("Size", QSize());
if (size.isValid()) {
resize(size);
} else {
resize(600, 400);
}
}
void AuditLogViewer::writeConfig()
{
- KConfigGroup group(KSharedConfig::openConfig(), "AuditLogViewer");
+ KConfigGroup group(KSharedConfig::openConfig(), QLatin1String("AuditLogViewer"));
group.writeEntry("Size", size());
group.sync();
}
#include "moc_auditlogviewer.cpp"
diff --git a/src/ui/editdirectoryservicedialog.cpp b/src/ui/editdirectoryservicedialog.cpp
index 22aa6155..af823c14 100644
--- a/src/ui/editdirectoryservicedialog.cpp
+++ b/src/ui/editdirectoryservicedialog.cpp
@@ -1,410 +1,410 @@
/*
ui/editdirectoryservicedialog.cpp
This file is part of libkleopatra, the KDE keymanagement library
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 "editdirectoryservicedialog.h"
#include <libkleo/algorithm.h>
#include <libkleo/gnupg.h>
#include <libkleo/keyserverconfig.h>
#include <KCollapsibleGroupBox>
#include <KConfigGroup>
#include <KGuiItem>
#include <KLocalizedString>
#include <KPasswordLineEdit>
#include <KSharedConfig>
#include <KStandardGuiItem>
#include <QButtonGroup>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QRadioButton>
#include <QSpinBox>
#include <QVBoxLayout>
using namespace Kleo;
namespace
{
int defaultPort(KeyserverConnection connection)
{
return connection == KeyserverConnection::TunnelThroughTLS ? 636 : 389;
}
}
class EditDirectoryServiceDialog::Private
{
EditDirectoryServiceDialog *const q;
struct Ui {
QLineEdit *hostEdit = nullptr;
QSpinBox *portSpinBox = nullptr;
QCheckBox *useDefaultPortCheckBox = nullptr;
QButtonGroup *authenticationGroup = nullptr;
QLineEdit *userEdit = nullptr;
KPasswordLineEdit *passwordEdit = nullptr;
QButtonGroup *connectionGroup = nullptr;
KCollapsibleGroupBox *advancedSettings = nullptr;
QLineEdit *baseDnEdit = nullptr;
QLineEdit *additionalFlagsEdit = nullptr;
QDialogButtonBox *buttonBox = nullptr;
Ui(QWidget *parent)
: hostEdit{new QLineEdit{parent}}
, portSpinBox{new QSpinBox{parent}}
, useDefaultPortCheckBox{new QCheckBox{parent}}
, authenticationGroup{new QButtonGroup{parent}}
, userEdit{new QLineEdit{parent}}
, passwordEdit{new KPasswordLineEdit{parent}}
, connectionGroup{new QButtonGroup{parent}}
, advancedSettings{new KCollapsibleGroupBox{parent}}
, baseDnEdit{new QLineEdit{parent}}
, additionalFlagsEdit{new QLineEdit{parent}}
, buttonBox{new QDialogButtonBox{parent}}
{
#define SET_OBJECT_NAME(x) x->setObjectName(QStringLiteral(#x));
SET_OBJECT_NAME(hostEdit)
SET_OBJECT_NAME(portSpinBox)
SET_OBJECT_NAME(useDefaultPortCheckBox)
SET_OBJECT_NAME(authenticationGroup)
SET_OBJECT_NAME(userEdit)
SET_OBJECT_NAME(passwordEdit)
SET_OBJECT_NAME(connectionGroup)
SET_OBJECT_NAME(advancedSettings)
SET_OBJECT_NAME(baseDnEdit)
SET_OBJECT_NAME(additionalFlagsEdit)
SET_OBJECT_NAME(buttonBox)
#undef SET_OBJECT_NAME
auto mainLayout = new QVBoxLayout{parent};
auto serverWidget = new QWidget{parent};
{
auto layout = new QGridLayout{serverWidget};
layout->setColumnStretch(2, 1);
int row = 0;
layout->addWidget(new QLabel{i18n("Host:")}, row, 0);
hostEdit->setToolTip(i18nc("@info:tooltip", "Enter the name or IP address of the server hosting the directory service."));
hostEdit->setClearButtonEnabled(true);
layout->addWidget(hostEdit, row, 1, 1, -1);
++row;
layout->addWidget(new QLabel{i18n("Port:")}, row, 0);
portSpinBox->setRange(1, USHRT_MAX);
portSpinBox->setToolTip(i18nc("@info:tooltip",
"<b>(Optional, the default is fine in most cases)</b> "
"Pick the port number the directory service is listening on."));
layout->addWidget(portSpinBox, row, 1);
useDefaultPortCheckBox->setText(i18n("Use default"));
useDefaultPortCheckBox->setChecked(true);
layout->addWidget(useDefaultPortCheckBox, row, 2);
}
mainLayout->addWidget(serverWidget);
auto authenticationWidget = new QGroupBox{i18n("Authentication"), parent};
{
auto layout = new QVBoxLayout{authenticationWidget};
{
auto radioButton = new QRadioButton{i18n("Anonymous")};
radioButton->setToolTip(i18nc("@info:tooltip", "Use an anonymous LDAP server that does not require authentication."));
radioButton->setChecked(true);
authenticationGroup->addButton(radioButton, static_cast<int>(KeyserverAuthentication::Anonymous));
layout->addWidget(radioButton);
}
{
auto radioButton = new QRadioButton{i18n("Authenticate via Active Directory")};
if (!engineIsVersion(2, 2, 28, GpgME::GpgSMEngine)) {
radioButton->setText(i18n("Authenticate via Active Directory (requires GnuPG 2.2.28 or later)"));
}
radioButton->setToolTip(
i18nc("@info:tooltip", "On Windows, authenticate to the LDAP server using the Active Directory with the current user."));
authenticationGroup->addButton(radioButton, static_cast<int>(KeyserverAuthentication::ActiveDirectory));
layout->addWidget(radioButton);
}
{
auto radioButton = new QRadioButton{i18n("Authenticate with user and password")};
radioButton->setToolTip(i18nc("@info:tooltip", "Authenticate to the LDAP server with your LDAP credentials."));
authenticationGroup->addButton(radioButton, static_cast<int>(KeyserverAuthentication::Password));
layout->addWidget(radioButton);
}
auto credentialsWidget = new QWidget{parent};
{
auto layout = new QGridLayout{credentialsWidget};
layout->setColumnStretch(1, 1);
int row = 0;
layout->addWidget(new QLabel{i18n("User:")}, row, 0);
userEdit->setToolTip(i18nc("@info:tooltip", "Enter your LDAP user resp. Bind DN for authenticating to the LDAP server."));
userEdit->setClearButtonEnabled(true);
layout->addWidget(userEdit, row, 1);
++row;
layout->addWidget(new QLabel{i18n("Password:")}, row, 0);
passwordEdit->setToolTip(xi18nc("@info:tooltip",
"Enter your password for authenticating to the LDAP server.<nl/>"
"<warning>The password will be saved in the clear "
"in a configuration file in your home directory.</warning>"));
passwordEdit->setClearButtonEnabled(true);
layout->addWidget(passwordEdit, row, 1);
}
layout->addWidget(credentialsWidget);
}
mainLayout->addWidget(authenticationWidget);
auto securityWidget = new QGroupBox{i18n("Connection Security"), parent};
if (!engineIsVersion(2, 2, 28, GpgME::GpgSMEngine)) {
securityWidget->setTitle(i18n("Connection Security (requires GnuPG 2.2.28 or later)"));
}
{
auto layout = new QVBoxLayout{securityWidget};
{
auto radioButton = new QRadioButton{i18n("Use default connection (probably not TLS secured)")};
radioButton->setToolTip(i18nc("@info:tooltip",
"Use GnuPG's default to connect to the LDAP server. "
"By default, GnuPG 2.3 and earlier use a plain, not TLS secured connection. "
"<b>(Not recommended)</b>"));
radioButton->setChecked(true);
connectionGroup->addButton(radioButton, static_cast<int>(KeyserverConnection::Default));
layout->addWidget(radioButton);
}
{
auto radioButton = new QRadioButton{i18n("Do not use a TLS secured connection")};
radioButton->setToolTip(i18nc("@info:tooltip",
"Use a plain, not TLS secured connection to connect to the LDAP server. "
"<b>(Not recommended)</b>"));
connectionGroup->addButton(radioButton, static_cast<int>(KeyserverConnection::Plain));
layout->addWidget(radioButton);
}
{
auto radioButton = new QRadioButton{i18n("Use TLS secured connection")};
radioButton->setToolTip(i18nc("@info:tooltip",
"Use a standard TLS secured connection (initiated with STARTTLS) "
"to connect to the LDAP server. "
"<b>(Recommended)</b>"));
connectionGroup->addButton(radioButton, static_cast<int>(KeyserverConnection::UseSTARTTLS));
layout->addWidget(radioButton);
}
{
auto radioButton = new QRadioButton{i18n("Tunnel LDAP through a TLS connection")};
radioButton->setToolTip(i18nc("@info:tooltip",
"Use a TLS secured connection through which the connection to the "
"LDAP server is tunneled. "
"<b>(Not recommended)</b>"));
connectionGroup->addButton(radioButton, static_cast<int>(KeyserverConnection::TunnelThroughTLS));
layout->addWidget(radioButton);
}
}
mainLayout->addWidget(securityWidget);
advancedSettings->setTitle(i18n("Advanced Settings"));
{
auto layout = new QGridLayout{advancedSettings};
layout->setColumnStretch(1, 1);
int row = 0;
layout->addWidget(new QLabel{i18n("Base DN:")}, row, 0);
baseDnEdit->setToolTip(i18nc("@info:tooltip",
"<b>(Optional, can usually be left empty)</b> "
"Enter the base DN for this LDAP server to limit searches "
"to only that subtree of the directory."));
baseDnEdit->setClearButtonEnabled(true);
layout->addWidget(baseDnEdit, row, 1);
++row;
layout->addWidget(new QLabel{i18n("Additional flags:")}, row, 0);
additionalFlagsEdit->setToolTip(i18nc("@info:tooltip",
"Here you can enter additional flags that are not yet (or no longer) "
"supported by Kleopatra. For example, older versions of GnuPG use "
"<code>ldaps</code> to request a TLS secured connection."));
additionalFlagsEdit->setClearButtonEnabled(true);
layout->addWidget(additionalFlagsEdit, row, 1);
}
mainLayout->addWidget(advancedSettings);
mainLayout->addStretch(1);
buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
KGuiItem::assign(okButton, KStandardGuiItem::ok());
KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
mainLayout->addWidget(buttonBox);
};
} ui;
QString host() const
{
return ui.hostEdit->text().trimmed();
}
int port() const
{
return ui.useDefaultPortCheckBox->isChecked() ? -1 : ui.portSpinBox->value();
}
KeyserverAuthentication authentication() const
{
return KeyserverAuthentication{ui.authenticationGroup->checkedId()};
}
QString user() const
{
return ui.userEdit->text().trimmed();
}
QString password() const
{
return ui.passwordEdit->password(); // not trimmed
}
KeyserverConnection connection() const
{
return KeyserverConnection{ui.connectionGroup->checkedId()};
}
QString baseDn() const
{
return ui.baseDnEdit->text().trimmed();
}
QStringList additionalFlags() const
{
return transformInPlace(ui.additionalFlagsEdit->text().split(QLatin1Char{','}, Qt::SkipEmptyParts), [](const auto &flag) {
return flag.trimmed();
});
}
bool inputIsAcceptable() const
{
const bool hostIsSet = !host().isEmpty();
const bool requiredCredentialsAreSet = authentication() != KeyserverAuthentication::Password || (!user().isEmpty() && !password().isEmpty());
return hostIsSet && requiredCredentialsAreSet;
}
void updateWidgets()
{
ui.portSpinBox->setEnabled(!ui.useDefaultPortCheckBox->isChecked());
if (ui.useDefaultPortCheckBox->isChecked()) {
ui.portSpinBox->setValue(defaultPort(connection()));
}
ui.userEdit->setEnabled(authentication() == KeyserverAuthentication::Password);
ui.passwordEdit->setEnabled(authentication() == KeyserverAuthentication::Password);
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(inputIsAcceptable());
}
public:
Private(EditDirectoryServiceDialog *q)
: q{q}
, ui{q}
{
connect(ui.hostEdit, &QLineEdit::textEdited, q, [this]() {
updateWidgets();
});
connect(ui.useDefaultPortCheckBox, &QCheckBox::toggled, q, [this]() {
updateWidgets();
});
connect(ui.authenticationGroup, &QButtonGroup::idToggled, q, [this]() {
updateWidgets();
});
connect(ui.userEdit, &QLineEdit::textEdited, q, [this]() {
updateWidgets();
});
connect(ui.passwordEdit, &KPasswordLineEdit::passwordChanged, q, [this]() {
updateWidgets();
});
connect(ui.connectionGroup, &QButtonGroup::idToggled, q, [this]() {
updateWidgets();
});
connect(ui.buttonBox, &QDialogButtonBox::accepted, q, &EditDirectoryServiceDialog::accept);
connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &EditDirectoryServiceDialog::reject);
updateWidgets();
restoreLayout();
}
~Private()
{
saveLayout();
}
void setKeyserver(const KeyserverConfig &keyserver)
{
ui.hostEdit->setText(keyserver.host());
ui.useDefaultPortCheckBox->setChecked(keyserver.port() == -1);
ui.portSpinBox->setValue(keyserver.port() == -1 ? defaultPort(keyserver.connection()) : keyserver.port());
ui.authenticationGroup->button(static_cast<int>(keyserver.authentication()))->setChecked(true);
ui.userEdit->setText(keyserver.user());
ui.passwordEdit->setPassword(keyserver.password());
ui.connectionGroup->button(static_cast<int>(keyserver.connection()))->setChecked(true);
ui.baseDnEdit->setText(keyserver.ldapBaseDn());
ui.additionalFlagsEdit->setText(keyserver.additionalFlags().join(QLatin1Char{','}));
ui.advancedSettings->setExpanded(!keyserver.ldapBaseDn().isEmpty() || !keyserver.additionalFlags().empty());
updateWidgets();
}
KeyserverConfig keyserver() const
{
KeyserverConfig keyserver;
keyserver.setHost(host());
keyserver.setPort(port());
keyserver.setAuthentication(authentication());
keyserver.setUser(user());
keyserver.setPassword(password());
keyserver.setConnection(connection());
keyserver.setLdapBaseDn(baseDn());
keyserver.setAdditionalFlags(additionalFlags());
return keyserver;
}
private:
void saveLayout()
{
- KConfigGroup configGroup{KSharedConfig::openStateConfig(), "EditDirectoryServiceDialog"};
+ KConfigGroup configGroup{KSharedConfig::openStateConfig(), QLatin1String("EditDirectoryServiceDialog")};
configGroup.writeEntry("Size", q->size());
configGroup.sync();
}
void restoreLayout()
{
- const KConfigGroup configGroup{KSharedConfig::openStateConfig(), "EditDirectoryServiceDialog"};
+ const KConfigGroup configGroup{KSharedConfig::openStateConfig(), QLatin1String("EditDirectoryServiceDialog")};
const auto size = configGroup.readEntry("Size", QSize{});
if (size.isValid()) {
q->resize(size);
}
}
};
EditDirectoryServiceDialog::EditDirectoryServiceDialog(QWidget *parent, Qt::WindowFlags f)
: QDialog{parent, f}
, d{std::make_unique<Private>(this)}
{
setWindowTitle(i18nc("@title:window", "Edit Directory Service"));
}
EditDirectoryServiceDialog::~EditDirectoryServiceDialog() = default;
void EditDirectoryServiceDialog::setKeyserver(const KeyserverConfig &keyserver)
{
d->setKeyserver(keyserver);
}
KeyserverConfig EditDirectoryServiceDialog::keyserver() const
{
return d->keyserver();
}
#include "moc_editdirectoryservicedialog.cpp"
diff --git a/src/ui/keyselectiondialog.cpp b/src/ui/keyselectiondialog.cpp
index f0f22dc1..50f38597 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->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(), "Key Selection Dialog");
+ KConfigGroup dialogConfig(KSharedConfig::openStateConfig(), QLatin1String("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(), "Key Selection Dialog");
+ KConfigGroup dialogConfig(KSharedConfig::openStateConfig(), QLatin1String("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"

File Metadata

Mime Type
text/x-diff
Expires
Fri, Dec 5, 4:59 AM (16 h, 2 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
91/5c/26896419b12325623b5a2c47f15c

Event Timeline