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