diff --git a/src/kleo/checksumdefinition.cpp b/src/kleo/checksumdefinition.cpp index e67234c0c..4185186b1 100644 --- a/src/kleo/checksumdefinition.cpp +++ b/src/kleo/checksumdefinition.cpp @@ -1,405 +1,405 @@ /* -*- 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 "checksumdefinition.h" #include "kleoexception.h" #include "libkleo_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #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() {} + ~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); if (l.indexOf(QRegExp(QLatin1String(".*__path_goes_here__.*"))) >= 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' : /* else */ '\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) : /* else */ 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) : /* else */ doGetVerifyArguments(QStringList()), files, m_verifyMethod); } // static std::vector< std::shared_ptr > ChecksumDefinition::getChecksumDefinitions() { QStringList errors; return getChecksumDefinitions(errors); } // static std::vector< std::shared_ptr > ChecksumDefinition::getChecksumDefinitions(QStringList &errors) { std::vector< std::shared_ptr > 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 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::getDefaultChecksumDefinition(const std::vector< std::shared_ptr > &checksumDefinitions) { const KConfigGroup group(KSharedConfig::openConfig(), "ChecksumOperations"); const QString checksumDefinitionId = group.readEntry(CHECKSUM_DEFINITION_ID_ENTRY, QStringLiteral("sha256sum")); if (!checksumDefinitionId.isEmpty()) { for (const std::shared_ptr &cd : checksumDefinitions) { if (cd && cd->id() == checksumDefinitionId) { return cd; } } } if (!checksumDefinitions.empty()) { return checksumDefinitions.front(); } else { return std::shared_ptr(); } } // static void ChecksumDefinition::setDefaultChecksumDefinition(const std::shared_ptr &checksumDefinition) { if (!checksumDefinition) { return; } KConfigGroup group(KSharedConfig::openConfig(), "ChecksumOperations"); group.writeEntry(CHECKSUM_DEFINITION_ID_ENTRY, checksumDefinition->id()); group.sync(); } diff --git a/src/kleo/keyfiltermanager.h b/src/kleo/keyfiltermanager.h index 4eca65247..00178058d 100644 --- a/src/kleo/keyfiltermanager.h +++ b/src/kleo/keyfiltermanager.h @@ -1,73 +1,73 @@ /* keyfiltermanager.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #include #include #include namespace GpgME { class Key; } class QAbstractItemModel; class QModelIndex; class QFont; class QColor; class QIcon; namespace Kleo { class KLEO_EXPORT KeyFilterManager : public QObject { Q_OBJECT public: enum ModelRoles { FilterIdRole = Qt::UserRole, FilterMatchContextsRole, }; protected: explicit KeyFilterManager(QObject *parent = nullptr); - ~KeyFilterManager(); + ~KeyFilterManager() override; public: static KeyFilterManager *instance(); const std::shared_ptr &filterMatching(const GpgME::Key &key, KeyFilter::MatchContexts contexts) const; std::vector> filtersMatching(const GpgME::Key &key, KeyFilter::MatchContexts contexts) const; QAbstractItemModel *model() const; const std::shared_ptr &keyFilterByID(const QString &id) const; const std::shared_ptr &fromModelIndex(const QModelIndex &mi) const; QModelIndex toModelIndex(const std::shared_ptr &kf) const; void reload(); QFont font(const GpgME::Key &key, const QFont &baseFont) const; QColor bgColor(const GpgME::Key &key) const; QColor fgColor(const GpgME::Key &key) const; QIcon icon(const GpgME::Key &key) const; class Private; private: std::unique_ptr d; static KeyFilterManager *mSelf; }; } diff --git a/src/kleo/kleoexception.h b/src/kleo/kleoexception.h index b6d11dad1..51ff354b5 100644 --- a/src/kleo/kleoexception.h +++ b/src/kleo/kleoexception.h @@ -1,57 +1,57 @@ /* -*- mode: c++; c-basic-offset:4 -*- exception.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #include #include namespace Kleo { class KLEO_EXPORT Exception : public GpgME::Exception { public: Exception(gpg_error_t e, const std::string &msg, Options opt = NoOptions) : GpgME::Exception(GpgME::Error(e), msg, opt) {} Exception(gpg_error_t e, const char *msg, Options opt = NoOptions) : GpgME::Exception(GpgME::Error(e), msg, opt) {} Exception(gpg_error_t e, const QString &msg, Options opt = NoOptions) : GpgME::Exception(GpgME::Error(e), msg.toLocal8Bit().constData(), opt) {} Exception(const GpgME::Error &e, const std::string &msg) : GpgME::Exception(e, msg) {} Exception(const GpgME::Error &e, const char *msg) : GpgME::Exception(e, msg) {} Exception(const GpgME::Error &e, const QString &msg) : GpgME::Exception(e, msg.toLocal8Bit().constData()) {} - ~Exception() throw (); + ~Exception() throw () override; const std::string &messageLocal8Bit() const { return GpgME::Exception::message(); } gpg_error_t error_code() const { return error().encodedError(); } QString message() const { return QString::fromLocal8Bit(GpgME::Exception::message().c_str()); } }; } diff --git a/src/models/keycache.h b/src/models/keycache.h index 736fb2b42..be706d6a4 100644 --- a/src/models/keycache.h +++ b/src/models/keycache.h @@ -1,203 +1,203 @@ /* -*- mode: c++; c-basic-offset:4 -*- models/keycache.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include #include #include #include #include namespace GpgME { class Key; class DecryptionResult; class VerificationResult; class KeyListResult; class Subkey; } namespace KMime { namespace Types { class Mailbox; } } namespace Kleo { class FileSystemWatcher; class KeyGroup; enum class KeyUsage : char; class KLEO_EXPORT KeyCache : public QObject { Q_OBJECT protected: explicit KeyCache(); public: static std::shared_ptr instance(); static std::shared_ptr mutableInstance(); - ~KeyCache(); + ~KeyCache() override; void setGroupsEnabled(bool enabled); void setGroupsConfig(const QString &filename); void insert(const GpgME::Key &key); void insert(const std::vector &keys); bool insert(const KeyGroup &group); void refresh(const std::vector &keys); bool update(const KeyGroup &group); void remove(const GpgME::Key &key); void remove(const std::vector &keys); bool remove(const KeyGroup &group); void addFileSystemWatcher(const std::shared_ptr &watcher); void enableFileSystemWatcher(bool enable); void setRefreshInterval(int hours); int refreshInterval() const; void enableRemarks(bool enable); bool remarksEnabled() const; const std::vector &keys() const; std::vector secretKeys() const; std::vector groups() const; std::vector configurableGroups() const; void saveConfigurableGroups(const std::vector &groups); const GpgME::Key &findByFingerprint(const char *fpr) const; const GpgME::Key &findByFingerprint(const std::string &fpr) const; std::vector findByEMailAddress(const char *email) const; std::vector findByEMailAddress(const std::string &email) const; /** Look through the cache and search for the best key for a mailbox. * * The best key is the key with a UID for the provided mailbox that * has the highest validity and a subkey that is capable for the given * usage. * If more then one key have a UID with the same validity * the most recently created key is taken. * * @returns the "best" key for the mailbox. */ GpgME::Key findBestByMailBox(const char *addr, GpgME::Protocol proto, KeyUsage usage) const; /** * Looks for a group named @a name which contains keys with protocol @a protocol * that are suitable for the usage @a usage. * * If @a protocol is GpgME::OpenPGP or GpgME::CMS, then only groups consisting of keys * matching this protocol are considered. Use @a protocol GpgME::UnknownProtocol to consider * any groups regardless of the protocol including mixed-protocol groups. * * If @a usage is not KeyUsage::AnyUsage, then only groups consisting of keys supporting this usage * are considered. * The validity of keys and the presence of a private key (necessary for signing, certification, and * authentication) is not taken into account. * * The first group that fulfills all conditions is returned. * * @returns a matching group or a null group if no matching group is found. */ KeyGroup findGroup(const QString &name, GpgME::Protocol protocol, KeyUsage usage) const; const GpgME::Key &findByShortKeyID(const char *id) const; const GpgME::Key &findByShortKeyID(const std::string &id) const; const GpgME::Key &findByKeyIDOrFingerprint(const char *id) const; const GpgME::Key &findByKeyIDOrFingerprint(const std::string &id) const; std::vector findByKeyIDOrFingerprint(const std::vector &ids) const; const GpgME::Subkey &findSubkeyByKeyGrip(const char *grip, GpgME::Protocol protocol = GpgME::UnknownProtocol) const; const GpgME::Subkey &findSubkeyByKeyGrip(const std::string &grip, GpgME::Protocol protocol = GpgME::UnknownProtocol) const; std::vector findSubkeysByKeyID(const std::vector &ids) const; std::vector findRecipients(const GpgME::DecryptionResult &result) const; std::vector findSigners(const GpgME::VerificationResult &result) const; std::vector findSigningKeysByMailbox(const QString &mb) const; std::vector findEncryptionKeysByMailbox(const QString &mb) const; /** Check for group keys. * * @returns A list of keys configured for groupName. Empty if no group cached.*/ std::vector getGroupKeys(const QString &groupName) const; enum Option { NoOption = 0, RecursiveSearch = 1, IncludeSubject = 2 }; Q_DECLARE_FLAGS(Options, Option) std::vector findSubjects(const GpgME::Key &key, Options option = RecursiveSearch) const; std::vector findSubjects(const std::vector &keys, Options options = RecursiveSearch) const; std::vector findSubjects(std::vector::const_iterator first, std::vector::const_iterator last, Options options = RecursiveSearch) const; std::vector findIssuers(const GpgME::Key &key, Options options = RecursiveSearch) const; std::vector findIssuers(const std::vector &keys, Options options = RecursiveSearch) const; std::vector findIssuers(std::vector::const_iterator first, std::vector::const_iterator last, Options options = RecursiveSearch) const; /** Check if at least one keylisting was finished. */ bool initialized() const; /** Check if all keys have OpenPGP Protocol. */ bool pgpOnly() const; /** Set the keys the cache shall contain. Marks cache as initialized. Use for tests only. */ void setKeys(const std::vector &keys); void setGroups(const std::vector &groups); public Q_SLOTS: void clear(); void startKeyListing(GpgME::Protocol proto = GpgME::UnknownProtocol) { reload(proto); } void reload(GpgME::Protocol proto = GpgME::UnknownProtocol); void cancelKeyListing(); Q_SIGNALS: //void changed( const GpgME::Key & key ); void aboutToRemove(const GpgME::Key &key); void added(const GpgME::Key &key); void keyListingDone(const GpgME::KeyListResult &result); void keysMayHaveChanged(); void groupAdded(const Kleo::KeyGroup &group); void groupUpdated(const Kleo::KeyGroup &group); void groupRemoved(const Kleo::KeyGroup &group); private: class RefreshKeysJob; class Private; QScopedPointer const d; }; } Q_DECLARE_OPERATORS_FOR_FLAGS(Kleo::KeyCache::Options) diff --git a/src/models/keycache_p.h b/src/models/keycache_p.h index 368c7552e..123db7931 100644 --- a/src/models/keycache_p.h +++ b/src/models/keycache_p.h @@ -1,44 +1,44 @@ /* -*- mode: c++; c-basic-offset:4 -*- models/keycache_p.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "keycache.h" namespace GpgME { class KeyListResult; } namespace Kleo { class KeyCache::RefreshKeysJob : public QObject { Q_OBJECT public: explicit RefreshKeysJob(KeyCache *cache, QObject *parent = nullptr); - ~RefreshKeysJob(); + ~RefreshKeysJob() override; void start(); void cancel(); Q_SIGNALS: void done(const GpgME::KeyListResult &); void canceled(); private: class Private; friend class Private; Private * const d; Q_PRIVATE_SLOT(d, void listAllKeysJobDone(GpgME::KeyListResult, std::vector)) }; } diff --git a/src/models/keylistmodel.cpp b/src/models/keylistmodel.cpp index c73a7559e..048680d23 100644 --- a/src/models/keylistmodel.cpp +++ b/src/models/keylistmodel.cpp @@ -1,1470 +1,1470 @@ /* -*- mode: c++; c-basic-offset:4 -*- models/keylistmodel.cpp This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "keylistmodel.h" #include "keycache.h" #include "kleo/keygroup.h" #include "kleo/predicates.h" #include "kleo/keyfiltermanager.h" #include "kleo/keyfilter.h" #include "utils/algorithm.h" #include "utils/formatting.h" #ifdef KLEO_MODEL_TEST # include #endif #include #include #include #include #include #include #include #include #include #ifndef Q_MOC_RUN // QTBUG-22829 #include #include #endif #include #include #include #include #include #if GPGMEPP_VERSION >= 0x10E00 // 1.14.0 # define GPGME_HAS_REMARKS #endif using namespace GpgME; using namespace Kleo; using namespace Kleo::KeyList; Q_DECLARE_METATYPE(GpgME::Key) Q_DECLARE_METATYPE(KeyGroup) class AbstractKeyListModel::Private { AbstractKeyListModel *const q; public: explicit Private(AbstractKeyListModel *qq); void updateFromKeyCache(); public: int m_toolTipOptions = Formatting::Validity; mutable QHash prettyEMailCache; mutable QHash remarksCache; bool m_useKeyCache = false; bool m_modelResetInProgress = false; KeyList::Options m_keyListOptions = AllKeys; std::vector m_remarkKeys; }; AbstractKeyListModel::Private::Private(Kleo::AbstractKeyListModel *qq) : q(qq) { } void AbstractKeyListModel::Private::updateFromKeyCache() { if (m_useKeyCache) { q->setKeys(m_keyListOptions == SecretKeysOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys()); if (m_keyListOptions == IncludeGroups) { q->setGroups(KeyCache::instance()->groups()); } } } AbstractKeyListModel::AbstractKeyListModel(QObject *p) : QAbstractItemModel(p), KeyListModelInterface(), d(new Private(this)) { connect(this, &QAbstractItemModel::modelAboutToBeReset, this, [this] () { d->m_modelResetInProgress = true; }); connect(this, &QAbstractItemModel::modelReset, this, [this] () { d->m_modelResetInProgress = false; }); } AbstractKeyListModel::~AbstractKeyListModel() {} void AbstractKeyListModel::setToolTipOptions(int opts) { d->m_toolTipOptions = opts; } int AbstractKeyListModel::toolTipOptions() const { return d->m_toolTipOptions; } void AbstractKeyListModel::setRemarkKeys(const std::vector &keys) { d->m_remarkKeys = keys; } std::vector AbstractKeyListModel::remarkKeys() const { return d->m_remarkKeys; } Key AbstractKeyListModel::key(const QModelIndex &idx) const { if (idx.isValid()) { return doMapToKey(idx); } else { return Key::null; } } std::vector AbstractKeyListModel::keys(const QList &indexes) const { std::vector result; result.reserve(indexes.size()); std::transform(indexes.begin(), indexes.end(), std::back_inserter(result), [this](const QModelIndex &idx) { return this->key(idx); }); result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&GpgME::Key::isNull)), result.end()); _detail::remove_duplicates_by_fpr(result); return result; } KeyGroup AbstractKeyListModel::group(const QModelIndex &idx) const { if (idx.isValid()) { return doMapToGroup(idx); } else { return KeyGroup(); } } QModelIndex AbstractKeyListModel::index(const Key &key) const { return index(key, 0); } QModelIndex AbstractKeyListModel::index(const Key &key, int col) const { if (key.isNull() || col < 0 || col >= NumColumns) { return {}; } else { return doMapFromKey(key, col); } } QList AbstractKeyListModel::indexes(const std::vector &keys) const { QList result; result.reserve(keys.size()); std::transform(keys.begin(), keys.end(), std::back_inserter(result), [this](const Key &key) { return this->index(key); }); return result; } QModelIndex AbstractKeyListModel::index(const KeyGroup &group) const { return index(group, 0); } QModelIndex AbstractKeyListModel::index(const KeyGroup &group, int col) const { if (group.isNull() || col < 0 || col >= NumColumns) { return {}; } else { return doMapFromGroup(group, col); } } void AbstractKeyListModel::setKeys(const std::vector &keys) { beginResetModel(); clear(Keys); addKeys(keys); endResetModel(); } QModelIndex AbstractKeyListModel::addKey(const Key &key) { const std::vector vec(1, key); const QList l = doAddKeys(vec); return l.empty() ? QModelIndex() : l.front(); } void AbstractKeyListModel::removeKey(const Key &key) { if (key.isNull()) { return; } doRemoveKey(key); d->prettyEMailCache.remove(key.primaryFingerprint()); d->remarksCache.remove(key.primaryFingerprint()); } QList AbstractKeyListModel::addKeys(const std::vector &keys) { std::vector sorted; sorted.reserve(keys.size()); std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), std::mem_fn(&Key::isNull)); std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint()); return doAddKeys(sorted); } void AbstractKeyListModel::setGroups(const std::vector &groups) { beginResetModel(); clear(Groups); doSetGroups(groups); endResetModel(); } QModelIndex AbstractKeyListModel::addGroup(const KeyGroup &group) { if (group.isNull()) { return QModelIndex(); } return doAddGroup(group); } bool AbstractKeyListModel::removeGroup(const KeyGroup &group) { if (group.isNull()) { return false; } return doRemoveGroup(group); } void AbstractKeyListModel::clear(ItemTypes types) { const bool inReset = modelResetInProgress(); if (!inReset) { beginResetModel(); } doClear(types); if (types & Keys) { d->prettyEMailCache.clear(); d->remarksCache.clear(); } if (!inReset) { endResetModel(); } } int AbstractKeyListModel::columnCount(const QModelIndex &) const { return NumColumns; } QVariant AbstractKeyListModel::headerData(int section, Qt::Orientation o, int role) const { if (o == Qt::Horizontal) { if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) { switch (section) { case PrettyName: return i18n("Name"); case PrettyEMail: return i18n("E-Mail"); case Validity: return i18n("User-IDs"); case ValidFrom: return i18n("Valid From"); case ValidUntil: return i18n("Valid Until"); case TechnicalDetails: return i18n("Protocol"); case ShortKeyID: return i18n("Key-ID"); case KeyID: return i18n("Key-ID"); case Fingerprint: return i18n("Fingerprint"); case Issuer: return i18n("Issuer"); case SerialNumber: return i18n("Serial Number"); case Origin: return i18n("Origin"); case LastUpdate: return i18n("Last Update"); case OwnerTrust: return i18n("Certification Trust"); case Remarks: return i18n("Tags"); case NumColumns:; } } } return QVariant(); } static QVariant returnIfValid(const QColor &t) { if (t.isValid()) { return t; } else { return QVariant(); } } static QVariant returnIfValid(const QIcon &t) { if (!t.isNull()) { return t; } else { return QVariant(); } } QVariant AbstractKeyListModel::data(const QModelIndex &index, int role) const { const Key key = this->key(index); if (!key.isNull()) { return data(key, index.column(), role); } const KeyGroup group = this->group(index); if (!group.isNull()) { return data(group, index.column(), role); } return QVariant(); } QVariant AbstractKeyListModel::data(const Key &key, int column, int role) const { if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (column) { case PrettyName: return Formatting::prettyName(key); case PrettyEMail: if (const char *const fpr = key.primaryFingerprint()) { const QHash::const_iterator it = d->prettyEMailCache.constFind(fpr); if (it != d->prettyEMailCache.constEnd()) { return *it; } else { return d->prettyEMailCache[fpr] = Formatting::prettyEMail(key); } } else { return QVariant(); } case Validity: return Formatting::complianceStringShort(key); case ValidFrom: if (role == Qt::EditRole) { return Formatting::creationDate(key); } else { return Formatting::creationDateString(key); } case ValidUntil: if (role == Qt::EditRole) { return Formatting::expirationDate(key); } else { return Formatting::expirationDateString(key); } case TechnicalDetails: return Formatting::type(key); case ShortKeyID: return QString::fromLatin1(key.shortKeyID()); case KeyID: return Formatting::prettyID(key.keyID()); case Summary: return Formatting::summaryLine(key); case Fingerprint: return Formatting::prettyID(key.primaryFingerprint()); case Issuer: return QString::fromUtf8(key.issuerName()); case Origin: return Formatting::origin(key.origin()); case LastUpdate: return Formatting::dateString(key.lastUpdate()); case SerialNumber: return QString::fromUtf8(key.issuerSerial()); case OwnerTrust: return Formatting::ownerTrustShort(key.ownerTrust()); case Remarks: #ifdef GPGME_HAS_REMARKS { const char *const fpr = key.primaryFingerprint(); if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() && d->m_remarkKeys.size()) { if (!(key.keyListMode() & GpgME::SignatureNotations)) { return i18n("Loading..."); } const QHash::const_iterator it = d->remarksCache.constFind(fpr); if (it != d->remarksCache.constEnd()) { return *it; } else { GpgME::Error err; const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err); if (remarks.size() == 1) { const auto remark = QString::fromStdString(remarks[0]); return d->remarksCache[fpr] = remark; } else { QStringList remarkList; remarkList.reserve(remarks.size()); for (const auto &rem: remarks) { remarkList << QString::fromStdString(rem); } const auto remark = remarkList.join(QStringLiteral("; ")); return d->remarksCache[fpr] = remark; } } } else { return QVariant(); } } #endif return QVariant(); case NumColumns: break; } } else if (role == Qt::ToolTipRole) { return Formatting::toolTip(key, toolTipOptions()); } else if (role == Qt::FontRole) { return KeyFilterManager::instance()->font(key, (column == ShortKeyID || column == KeyID || column == Fingerprint) ? QFont(QStringLiteral("monospace")) : QFont()); } else if (role == Qt::DecorationRole) { return column == Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) : QVariant(); } else if (role == Qt::BackgroundRole) { return returnIfValid(KeyFilterManager::instance()->bgColor(key)); } else if (role == Qt::ForegroundRole) { return returnIfValid(KeyFilterManager::instance()->fgColor(key)); } else if (role == FingerprintRole) { return QString::fromLatin1(key.primaryFingerprint()); } else if (role == KeyRole) { return QVariant::fromValue(key); } return QVariant(); } QVariant AbstractKeyListModel::data(const KeyGroup &group, int column, int role) const { if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (column) { case PrettyName: return group.name(); case PrettyEMail: return QVariant(); case Validity: return Formatting::complianceStringShort(group); case ValidFrom: return QString(); case ValidUntil: return QString(); case TechnicalDetails: return Formatting::type(group); case ShortKeyID: return QString(); case KeyID: return QString(); case Summary: return Formatting::summaryLine(group); // used for filtering case Fingerprint: return QString(); case Issuer: return QString(); case Origin: return QString(); case LastUpdate: return QString(); case SerialNumber: return QString(); case OwnerTrust: return QString(); case Remarks: return QVariant(); case NumColumns: break; } } else if (role == Qt::ToolTipRole) { return Formatting::toolTip(group, toolTipOptions()); } else if (role == Qt::FontRole) { return QFont(); } else if (role == Qt::DecorationRole) { return column == Icon ? QIcon::fromTheme(QStringLiteral("group")) : QVariant(); } else if (role == Qt::BackgroundRole) { } else if (role == Qt::ForegroundRole) { } else if (role == GroupRole) { return QVariant::fromValue(group); } return QVariant(); } bool AbstractKeyListModel::setData(const QModelIndex &index, const QVariant &value, int role) { Q_UNUSED(role) Q_ASSERT(value.canConvert()); if (value.canConvert()) { const KeyGroup group = value.value(); return doSetGroupData(index, group); } return false; } bool AbstractKeyListModel::modelResetInProgress() { return d->m_modelResetInProgress; } namespace { template class TableModelMixin : public Base { public: explicit TableModelMixin(QObject *p = nullptr) : Base(p) {} - ~TableModelMixin() {} + ~TableModelMixin() override {} using Base::index; QModelIndex index(int row, int column, const QModelIndex &pidx = QModelIndex()) const override { return this->hasIndex(row, column, pidx) ? this->createIndex(row, column, nullptr) : QModelIndex(); } private: QModelIndex parent(const QModelIndex &) const override { return QModelIndex(); } bool hasChildren(const QModelIndex &pidx) const override { return (pidx.model() == this || !pidx.isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0; } }; class FlatKeyListModel #ifndef Q_MOC_RUN : public TableModelMixin #else : public AbstractKeyListModel #endif { Q_OBJECT public: explicit FlatKeyListModel(QObject *parent = nullptr); ~FlatKeyListModel() override; int rowCount(const QModelIndex &pidx) const override { return pidx.isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size(); } private: Key doMapToKey(const QModelIndex &index) const override; QModelIndex doMapFromKey(const Key &key, int col) const override; QList doAddKeys(const std::vector &keys) override; void doRemoveKey(const Key &key) override; KeyGroup doMapToGroup(const QModelIndex &index) const override; QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override; void doSetGroups(const std::vector &groups) override; QModelIndex doAddGroup(const KeyGroup &group) override; bool doSetGroupData(const QModelIndex &index, const KeyGroup &group) override; bool doRemoveGroup(const KeyGroup &group) override; void doClear(ItemTypes types) override { if (types & Keys) { mKeysByFingerprint.clear(); } if (types & Groups) { mGroups.clear(); } } int firstGroupRow() const { return mKeysByFingerprint.size(); } int lastGroupRow() const { return mKeysByFingerprint.size() + mGroups.size() - 1; } int groupIndex(const QModelIndex &index) const { if (!index.isValid() || index.row() < firstGroupRow() || index.row() > lastGroupRow() || index.column() >= NumColumns) { return -1; } return index.row() - firstGroupRow(); } private: std::vector mKeysByFingerprint; std::vector mGroups; }; class HierarchicalKeyListModel : public AbstractKeyListModel { Q_OBJECT public: explicit HierarchicalKeyListModel(QObject *parent = nullptr); ~HierarchicalKeyListModel() override; int rowCount(const QModelIndex &pidx) const override; using AbstractKeyListModel::index; QModelIndex index(int row, int col, const QModelIndex &pidx) const override; QModelIndex parent(const QModelIndex &idx) const override; bool hasChildren(const QModelIndex &pidx) const override { return rowCount(pidx) > 0; } private: Key doMapToKey(const QModelIndex &index) const override; QModelIndex doMapFromKey(const Key &key, int col) const override; QList doAddKeys(const std::vector &keys) override; void doRemoveKey(const Key &key) override; KeyGroup doMapToGroup(const QModelIndex &index) const override; QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override; void doSetGroups(const std::vector &groups) override; QModelIndex doAddGroup(const KeyGroup &group) override; bool doSetGroupData(const QModelIndex &index, const KeyGroup &group) override; bool doRemoveGroup(const KeyGroup &group) override; void doClear(ItemTypes types) override { if (types & Keys) { mTopLevels.clear(); mKeysByFingerprint.clear(); mKeysByExistingParent.clear(); mKeysByNonExistingParent.clear(); } if (types & Groups) { mGroups.clear(); } } int firstGroupRow() const { return mTopLevels.size(); } int lastGroupRow() const { return mTopLevels.size() + mGroups.size() - 1; } int groupIndex(const QModelIndex &index) const { if (!index.isValid() || index.row() < firstGroupRow() || index.row() > lastGroupRow() || index.column() >= NumColumns) { return -1; } return index.row() - firstGroupRow(); } private: void addTopLevelKey(const Key &key); void addKeyWithParent(const char *issuer_fpr, const Key &key); void addKeyWithoutParent(const char *issuer_fpr, const Key &key); private: typedef std::map< std::string, std::vector > Map; std::vector mKeysByFingerprint; // all keys Map mKeysByExistingParent, mKeysByNonExistingParent; // parent->child map std::vector mTopLevels; // all roots + parent-less std::vector mGroups; }; static const char *cleanChainID(const Key &key) { if (key.isRoot()) { return ""; } if (const char *chid = key.chainID()) { return chid; } return ""; } } FlatKeyListModel::FlatKeyListModel(QObject *p) : TableModelMixin(p) { } FlatKeyListModel::~FlatKeyListModel() {} Key FlatKeyListModel::doMapToKey(const QModelIndex &idx) const { Q_ASSERT(idx.isValid()); if (static_cast(idx.row()) < mKeysByFingerprint.size() && idx.column() < NumColumns) { return mKeysByFingerprint[ idx.row() ]; } else { return Key::null; } } QModelIndex FlatKeyListModel::doMapFromKey(const Key &key, int col) const { Q_ASSERT(!key.isNull()); const std::vector::const_iterator it = std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint()); if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint()(*it, key)) { return {}; } else { return createIndex(it - mKeysByFingerprint.begin(), col); } } QList FlatKeyListModel::doAddKeys(const std::vector &keys) { Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint())); if (keys.empty()) { return QList(); } for (auto it = keys.begin(), end = keys.end(); it != end; ++it) { // find an insertion point: const std::vector::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint()); const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos); if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) { // key existed before - replace with new one: mKeysByFingerprint[idx - 1] = *it; if (!modelResetInProgress()) { Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1)); } } else { // new key - insert: if (!modelResetInProgress()) { beginInsertRows(QModelIndex(), idx, idx); } mKeysByFingerprint.insert(pos, *it); if (!modelResetInProgress()) { endInsertRows(); } } } return indexes(keys); } void FlatKeyListModel::doRemoveKey(const Key &key) { const std::vector::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint()); if (it == mKeysByFingerprint.end()) { return; } const unsigned int row = std::distance(mKeysByFingerprint.begin(), it); if (!modelResetInProgress()) { beginRemoveRows(QModelIndex(), row, row); } mKeysByFingerprint.erase(it); if (!modelResetInProgress()) { endRemoveRows(); } } KeyGroup FlatKeyListModel::doMapToGroup(const QModelIndex &idx) const { Q_ASSERT(idx.isValid()); if (static_cast(idx.row()) >= mKeysByFingerprint.size() && static_cast(idx.row()) < mKeysByFingerprint.size() + mGroups.size() && idx.column() < NumColumns) { return mGroups[ idx.row() - mKeysByFingerprint.size() ]; } else { return KeyGroup(); } } QModelIndex FlatKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const { Q_ASSERT(!group.isNull()); const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](const KeyGroup &g) { return g.source() == group.source() && g.id() == group.id(); }); if (it == mGroups.cend()) { return QModelIndex(); } else { return createIndex(it - mGroups.cbegin() + mKeysByFingerprint.size(), column); } } void FlatKeyListModel::doSetGroups(const std::vector &groups) { Q_ASSERT(mGroups.empty()); // ensure that groups have been cleared const int first = mKeysByFingerprint.size(); const int last = first + groups.size() - 1; if (!modelResetInProgress()) { beginInsertRows(QModelIndex(), first, last); } mGroups = groups; if (!modelResetInProgress()) { endInsertRows(); } } QModelIndex FlatKeyListModel::doAddGroup(const KeyGroup &group) { const int newRow = lastGroupRow() + 1; if (!modelResetInProgress()) { beginInsertRows(QModelIndex(), newRow, newRow); } mGroups.push_back(group); if (!modelResetInProgress()) { endInsertRows(); } return createIndex(newRow, 0); } bool FlatKeyListModel::doSetGroupData(const QModelIndex &index, const KeyGroup &group) { if (group.isNull()) { return false; } const int groupIndex = this->groupIndex(index); if (groupIndex == -1) { return false; } mGroups[groupIndex] = group; if (!modelResetInProgress()) { Q_EMIT dataChanged(createIndex(index.row(), 0), createIndex(index.row(), NumColumns - 1)); } return true; } bool FlatKeyListModel::doRemoveGroup(const KeyGroup &group) { const QModelIndex modelIndex = doMapFromGroup(group, 0); if (!modelIndex.isValid()) { return false; } const int groupIndex = this->groupIndex(modelIndex); Q_ASSERT(groupIndex != -1); if (groupIndex == -1) { return false; } if (!modelResetInProgress()) { beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row()); } mGroups.erase(mGroups.begin() + groupIndex); if (!modelResetInProgress()) { endRemoveRows(); } return true; } HierarchicalKeyListModel::HierarchicalKeyListModel(QObject *p) : AbstractKeyListModel(p), mKeysByFingerprint(), mKeysByExistingParent(), mKeysByNonExistingParent(), mTopLevels() { } HierarchicalKeyListModel::~HierarchicalKeyListModel() {} int HierarchicalKeyListModel::rowCount(const QModelIndex &pidx) const { // toplevel item: if (!pidx.isValid()) { return mTopLevels.size() + mGroups.size(); } if (pidx.column() != 0) { return 0; } // non-toplevel item - find the number of subjects for this issuer: const Key issuer = this->key(pidx); const char *const fpr = issuer.primaryFingerprint(); if (!fpr || !*fpr) { return 0; } const Map::const_iterator it = mKeysByExistingParent.find(fpr); if (it == mKeysByExistingParent.end()) { return 0; } return it->second.size(); } QModelIndex HierarchicalKeyListModel::index(int row, int col, const QModelIndex &pidx) const { if (row < 0 || col < 0 || col >= NumColumns) { return {}; } // toplevel item: if (!pidx.isValid()) { if (static_cast(row) < mTopLevels.size()) { return index(mTopLevels[row], col); } else if (static_cast(row) < mTopLevels.size() + mGroups.size()) { return index(mGroups[row - mTopLevels.size()], col); } else { return QModelIndex(); } } // non-toplevel item - find the row'th subject of this key: const Key issuer = this->key(pidx); const char *const fpr = issuer.primaryFingerprint(); if (!fpr || !*fpr) { return QModelIndex(); } const Map::const_iterator it = mKeysByExistingParent.find(fpr); if (it == mKeysByExistingParent.end() || static_cast(row) >= it->second.size()) { return QModelIndex(); } return index(it->second[row], col); } QModelIndex HierarchicalKeyListModel::parent(const QModelIndex &idx) const { const Key key = this->key(idx); if (key.isNull() || key.isRoot()) { return {}; } const std::vector::const_iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), cleanChainID(key), _detail::ByFingerprint()); return it != mKeysByFingerprint.end() ? index(*it) : QModelIndex(); } Key HierarchicalKeyListModel::doMapToKey(const QModelIndex &idx) const { if (!idx.isValid()) { return Key::null; } const char *const issuer_fpr = static_cast(idx.internalPointer()); if (!issuer_fpr || !*issuer_fpr) { // top-level: if (static_cast(idx.row()) >= mTopLevels.size()) { return Key::null; } else { return mTopLevels[idx.row()]; } } // non-toplevel: const Map::const_iterator it = mKeysByExistingParent.find(issuer_fpr); if (it == mKeysByExistingParent.end() || static_cast(idx.row()) >= it->second.size()) { return Key::null; } return it->second[idx.row()]; } QModelIndex HierarchicalKeyListModel::doMapFromKey(const Key &key, int col) const { if (key.isNull()) { return {}; } const char *issuer_fpr = cleanChainID(key); // we need to look in the toplevels list,... const std::vector *v = &mTopLevels; if (issuer_fpr && *issuer_fpr) { const std::map< std::string, std::vector >::const_iterator it = mKeysByExistingParent.find(issuer_fpr); // ...unless we find an existing parent: if (it != mKeysByExistingParent.end()) { v = &it->second; } else { issuer_fpr = nullptr; // force internalPointer to zero for toplevels } } const std::vector::const_iterator it = std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint()); if (it == v->end() || !_detail::ByFingerprint()(*it, key)) { return QModelIndex(); } const unsigned int row = std::distance(v->begin(), it); return createIndex(row, col, const_cast(issuer_fpr)); } void HierarchicalKeyListModel::addKeyWithParent(const char *issuer_fpr, const Key &key) { Q_ASSERT(issuer_fpr); Q_ASSERT(*issuer_fpr); Q_ASSERT(!key.isNull()); std::vector &subjects = mKeysByExistingParent[issuer_fpr]; // find insertion point: const std::vector::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint()); const int row = std::distance(subjects.begin(), it); if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) { // exists -> replace *it = key; if (!modelResetInProgress()) { Q_EMIT dataChanged(createIndex(row, 0, const_cast(issuer_fpr)), createIndex(row, NumColumns - 1, const_cast(issuer_fpr))); } } else { // doesn't exist -> insert const std::vector::const_iterator pos = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint()); Q_ASSERT(pos != mKeysByFingerprint.end()); if (!modelResetInProgress()) { beginInsertRows(index(*pos), row, row); } subjects.insert(it, key); if (!modelResetInProgress()) { endInsertRows(); } } } void HierarchicalKeyListModel::addKeyWithoutParent(const char *issuer_fpr, const Key &key) { Q_ASSERT(issuer_fpr); Q_ASSERT(*issuer_fpr); Q_ASSERT(!key.isNull()); std::vector &subjects = mKeysByNonExistingParent[issuer_fpr]; // find insertion point: const std::vector::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint()); if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) // exists -> replace { *it = key; } else // doesn't exist -> insert { subjects.insert(it, key); } addTopLevelKey(key); } void HierarchicalKeyListModel::addTopLevelKey(const Key &key) { // find insertion point: const std::vector::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint()); const int row = std::distance(mTopLevels.begin(), it); if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) { // exists -> replace *it = key; if (!modelResetInProgress()) { Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1)); } } else { // doesn't exist -> insert if (!modelResetInProgress()) { beginInsertRows(QModelIndex(), row, row); } mTopLevels.insert(it, key); if (!modelResetInProgress()) { endInsertRows(); } } } // sorts 'keys' such that parent always come before their children: static std::vector topological_sort(const std::vector &keys) { boost::adjacency_list<> graph(keys.size()); // add edges from children to parents: for (unsigned int i = 0, end = keys.size(); i != end; ++i) { const char *const issuer_fpr = cleanChainID(keys[i]); if (!issuer_fpr || !*issuer_fpr) { continue; } const std::vector::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint()); if (it == keys.end()) { continue; } add_edge(i, std::distance(keys.begin(), it), graph); } std::vector order; order.reserve(keys.size()); topological_sort(graph, std::back_inserter(order)); Q_ASSERT(order.size() == keys.size()); std::vector result; result.reserve(keys.size()); for (int i : std::as_const(order)) { result.push_back(keys[i]); } return result; } QList HierarchicalKeyListModel::doAddKeys(const std::vector &keys) { Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint())); if (keys.empty()) { return QList(); } const std::vector oldKeys = mKeysByFingerprint; std::vector merged; merged.reserve(keys.size() + mKeysByFingerprint.size()); std::set_union(keys.begin(), keys.end(), mKeysByFingerprint.begin(), mKeysByFingerprint.end(), std::back_inserter(merged), _detail::ByFingerprint()); mKeysByFingerprint = merged; std::set > changedParents; const auto topologicalSortedList = topological_sort(keys); for (const Key &key : topologicalSortedList) { // check to see whether this key is a parent for a previously parent-less group: const char *const fpr = key.primaryFingerprint(); if (!fpr || !*fpr) { continue; } const bool keyAlreadyExisted = std::binary_search(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint()); const Map::iterator it = mKeysByNonExistingParent.find(fpr); const std::vector children = it != mKeysByNonExistingParent.end() ? it->second : std::vector(); if (it != mKeysByNonExistingParent.end()) { mKeysByNonExistingParent.erase(it); } // Step 1: For new keys, remove children from toplevel: if (!keyAlreadyExisted) { auto last = mTopLevels.begin(); auto lastFP = mKeysByFingerprint.begin(); for (const Key &k : std::as_const(children)) { last = Kleo::binary_find(last, mTopLevels.end(), k, _detail::ByFingerprint()); Q_ASSERT(last != mTopLevels.end()); const int row = std::distance(mTopLevels.begin(), last); lastFP = Kleo::binary_find(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint()); Q_ASSERT(lastFP != mKeysByFingerprint.end()); Q_EMIT rowAboutToBeMoved(QModelIndex(), row); if (!modelResetInProgress()) { beginRemoveRows(QModelIndex(), row, row); } last = mTopLevels.erase(last); lastFP = mKeysByFingerprint.erase(lastFP); if (!modelResetInProgress()) { endRemoveRows(); } } } // Step 2: add/update key const char *const issuer_fpr = cleanChainID(key); if (!issuer_fpr || !*issuer_fpr) // root or something... { addTopLevelKey(key); } else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint())) // parent exists... { addKeyWithParent(issuer_fpr, key); } else // parent doesn't exist yet... { addKeyWithoutParent(issuer_fpr, key); } const QModelIndex key_idx = index(key); QModelIndex key_parent = key_idx.parent(); while (key_parent.isValid()) { changedParents.insert(doMapToKey(key_parent)); key_parent = key_parent.parent(); } // Step 3: Add children to new parent ( == key ) if (!keyAlreadyExisted && !children.empty()) { addKeys(children); const QModelIndex new_parent = index(key); // Q_EMIT the rowMoved() signals in reversed direction, so the // implementation can use a stack for mapping. for (int i = children.size() - 1; i >= 0; --i) { Q_EMIT rowMoved(new_parent, i); } } } //Q_EMIT dataChanged for all parents with new children. This triggers KeyListSortFilterProxyModel to //show a parent node if it just got children matching the proxy's filter if (!modelResetInProgress()) { for (const Key &i : std::as_const(changedParents)) { const QModelIndex idx = index(i); if (idx.isValid()) { Q_EMIT dataChanged(idx.sibling(idx.row(), 0), idx.sibling(idx.row(), NumColumns - 1)); } } } return indexes(keys); } void HierarchicalKeyListModel::doRemoveKey(const Key &key) { const QModelIndex idx = index(key); if (!idx.isValid()) { return; } const char *const fpr = key.primaryFingerprint(); if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) { //handle non-leave nodes: std::vector keys = mKeysByFingerprint; const std::vector::iterator it = Kleo::binary_find(keys.begin(), keys.end(), key, _detail::ByFingerprint()); if (it == keys.end()) { return; } keys.erase(it); // FIXME for simplicity, we just clear the model and re-add all keys minus the removed one. This is suboptimal, // but acceptable given that deletion of non-leave nodes is rather rare. clear(Keys); addKeys(keys); return; } //handle leave nodes: const std::vector::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint()); Q_ASSERT(it != mKeysByFingerprint.end()); Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end()); Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end()); if (!modelResetInProgress()) { beginRemoveRows(parent(idx), idx.row(), idx.row()); } mKeysByFingerprint.erase(it); const char *const issuer_fpr = cleanChainID(key); const std::vector::iterator tlIt = Kleo::binary_find(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint()); if (tlIt != mTopLevels.end()) { mTopLevels.erase(tlIt); } if (issuer_fpr && *issuer_fpr) { const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr); if (nexIt != mKeysByNonExistingParent.end()) { const std::vector::iterator eit = Kleo::binary_find(nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint()); if (eit != nexIt->second.end()) { nexIt->second.erase(eit); } if (nexIt->second.empty()) { mKeysByNonExistingParent.erase(nexIt); } } const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr); if (exIt != mKeysByExistingParent.end()) { const std::vector::iterator eit = Kleo::binary_find(exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint()); if (eit != exIt->second.end()) { exIt->second.erase(eit); } if (exIt->second.empty()) { mKeysByExistingParent.erase(exIt); } } } if (!modelResetInProgress()) { endRemoveRows(); } } KeyGroup HierarchicalKeyListModel::doMapToGroup(const QModelIndex &idx) const { Q_ASSERT(idx.isValid()); if (idx.parent().isValid()) { // groups are always top-level return KeyGroup(); } if (static_cast(idx.row()) >= mTopLevels.size() && static_cast(idx.row()) < mTopLevels.size() + mGroups.size() && idx.column() < NumColumns) { return mGroups[ idx.row() - mTopLevels.size() ]; } else { return KeyGroup(); } } QModelIndex HierarchicalKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const { Q_ASSERT(!group.isNull()); const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](const KeyGroup &g) { return g.source() == group.source() && g.id() == group.id(); }); if (it == mGroups.cend()) { return QModelIndex(); } else { return createIndex(it - mGroups.cbegin() + mTopLevels.size(), column); } } void HierarchicalKeyListModel::doSetGroups(const std::vector &groups) { Q_ASSERT(mGroups.empty()); // ensure that groups have been cleared const int first = mTopLevels.size(); const int last = first + groups.size() - 1; if (!modelResetInProgress()) { beginInsertRows(QModelIndex(), first, last); } mGroups = groups; if (!modelResetInProgress()) { endInsertRows(); } } QModelIndex HierarchicalKeyListModel::doAddGroup(const KeyGroup &group) { const int newRow = lastGroupRow() + 1; if (!modelResetInProgress()) { beginInsertRows(QModelIndex(), newRow, newRow); } mGroups.push_back(group); if (!modelResetInProgress()) { endInsertRows(); } return createIndex(newRow, 0); } bool HierarchicalKeyListModel::doSetGroupData(const QModelIndex &index, const KeyGroup &group) { if (group.isNull()) { return false; } const int groupIndex = this->groupIndex(index); if (groupIndex == -1) { return false; } mGroups[groupIndex] = group; if (!modelResetInProgress()) { Q_EMIT dataChanged(createIndex(index.row(), 0), createIndex(index.row(), NumColumns - 1)); } return true; } bool HierarchicalKeyListModel::doRemoveGroup(const KeyGroup &group) { const QModelIndex modelIndex = doMapFromGroup(group, 0); if (!modelIndex.isValid()) { return false; } const int groupIndex = this->groupIndex(modelIndex); Q_ASSERT(groupIndex != -1); if (groupIndex == -1) { return false; } if (!modelResetInProgress()) { beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row()); } mGroups.erase(mGroups.begin() + groupIndex); if (!modelResetInProgress()) { endRemoveRows(); } return true; } void AbstractKeyListModel::useKeyCache(bool value, KeyList::Options options) { d->m_keyListOptions = options; d->m_useKeyCache = value; if (!d->m_useKeyCache) { clear(All); } else { d->updateFromKeyCache(); } connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, [this] { d->updateFromKeyCache(); }); } // static AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(QObject *p) { AbstractKeyListModel *const m = new FlatKeyListModel(p); #ifdef KLEO_MODEL_TEST new QAbstractItemModelTester(m, p); #endif return m; } // static AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(QObject *p) { AbstractKeyListModel *const m = new HierarchicalKeyListModel(p); #ifdef KLEO_MODEL_TEST new QAbstractItemModelTester(m, p); #endif return m; } #include "keylistmodel.moc" /*! \fn AbstractKeyListModel::rowAboutToBeMoved( const QModelIndex & old_parent, int old_row ) Emitted before the removal of a row from that model. It will later be added to the model again, in response to which rowMoved() will be emitted. If multiple rows are moved in one go, multiple rowAboutToBeMoved() signals are emitted before the corresponding number of rowMoved() signals is emitted - in reverse order. This works around the absence of move semantics in QAbstractItemModel. Clients can maintain a stack to perform the QModelIndex-mapping themselves, or, e.g., to preserve the selection status of the row: \code std::vector mMovingRowWasSelected; // transient, used when rows are moved // ... void slotRowAboutToBeMoved( const QModelIndex & p, int row ) { mMovingRowWasSelected.push_back( selectionModel()->isSelected( model()->index( row, 0, p ) ) ); } void slotRowMoved( const QModelIndex & p, int row ) { const bool wasSelected = mMovingRowWasSelected.back(); mMovingRowWasSelected.pop_back(); if ( wasSelected ) selectionModel()->select( model()->index( row, 0, p ), Select|Rows ); } \endcode A similar mechanism could be used to preserve the current item during moves. */ /*! \fn AbstractKeyListModel::rowMoved( const QModelIndex & new_parent, int new_parent ) See rowAboutToBeMoved() */ diff --git a/src/tests/test_keygen.h b/src/tests/test_keygen.h index 97b957f9f..fbb294ad2 100644 --- a/src/tests/test_keygen.h +++ b/src/tests/test_keygen.h @@ -1,40 +1,40 @@ /* test_keygen.h This file is part of libkleopatra's test suite. SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-only */ #pragma once #include #include namespace GpgME { class Error; class KeyGenerationResult; } class QLineEdit; class KeyGenerator : public QDialog { Q_OBJECT public: KeyGenerator(QWidget *parent = nullptr); - ~KeyGenerator(); + ~KeyGenerator() override; public Q_SLOTS: void slotStartKeyGeneration(); void slotResult(const GpgME::KeyGenerationResult &res, const QByteArray &keyData); private: void showError(const GpgME::Error &err); private: QLineEdit *mLineEdits[20]; }; diff --git a/src/tests/test_keylister.cpp b/src/tests/test_keylister.cpp index 25c0c44b6..6ebdd2add 100644 --- a/src/tests/test_keylister.cpp +++ b/src/tests/test_keylister.cpp @@ -1,136 +1,136 @@ /* test_keylister.cpp This file is part of libkleopatra's test suite. SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-only */ #include "test_keylister.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::chrono_literals; namespace { class TestColumnStrategy : public Kleo::KeyListView::ColumnStrategy { public: - ~TestColumnStrategy() {} + ~TestColumnStrategy() override {} QString title(int col) const override; QString toolTip(const GpgME::Key &key, int col) const override; QString text(const GpgME::Key &key, int col) const override; }; QString TestColumnStrategy::title(int col) const { switch (col) { case 0: return QStringLiteral("Subject"); case 1: return QStringLiteral("EMail"); case 2: return QStringLiteral("Issuer"); case 3: return QStringLiteral("Serial"); case 4: return QStringLiteral("Protocol"); case 5: return QStringLiteral("Validity"); default: return QString(); } } QString TestColumnStrategy::toolTip(const GpgME::Key &key, int) const { return QStringLiteral("Fingerprint: ") + QString::fromUtf8(key.primaryFingerprint()); } QString TestColumnStrategy::text(const GpgME::Key &key, int col) const { if (key.isNull()) { return QStringLiteral(""); } switch (col) { case 0: return QString::fromUtf8(key.userID(0).id()); case 1: return QString::fromUtf8(key.userID(0).email()); case 2: return QString::fromUtf8(key.issuerName()); case 3: return QString::fromLatin1(key.issuerSerial()); case 4: return QString::fromLatin1(key.protocolAsString()); case 5: return QString(QLatin1Char(key.userID(0).validityAsString())); default: return QString(); } } } CertListView::CertListView(QWidget *parent, Qt::WindowFlags f) : Kleo::KeyListView(new TestColumnStrategy(), nullptr, parent, f) { setHierarchical(true); setRootIsDecorated(true); } CertListView::~CertListView() {} void CertListView::slotResult(const GpgME::KeyListResult &result) { qDebug() << "CertListView::slotResult()"; if (result.isNull()) { QMessageBox::information(this, QStringLiteral("Key Listing Result"), QStringLiteral("KeyListResult is null!")); } else if (result.error()) { QMessageBox::critical(this, QStringLiteral("Key Listing Result"), QStringLiteral("KeyListResult Error: %1").arg(QString::fromLatin1(result.error().asString()))); } else if (result.isTruncated()) { QMessageBox::information(this, QStringLiteral("Key Listing Result"), QStringLiteral("KeyListResult is truncated!")); } else { QMessageBox::information(this, QStringLiteral("Key Listing Result"), QStringLiteral("Key listing successful")); } } void CertListView::slotStart() { qDebug() << "CertListView::slotStart()"; QGpgME::KeyListJob *job = QGpgME::smime()->keyListJob(false); Q_ASSERT(job); QObject::connect(job, &QGpgME::KeyListJob::nextKey, this, &CertListView::slotAddKey); QObject::connect(job, &QGpgME::KeyListJob::result, this, &CertListView::slotResult); #if 0 QStringList l; l << "Marc"; job->start(l, false); #else job->start(QStringList(), false); #endif } int main(int argc, char **argv) { QApplication app(argc, argv); KAboutData aboutData(QStringLiteral("test_keylister"), i18n("KeyLister Test"), QStringLiteral("0.1")); QCommandLineParser parser; KAboutData::setApplicationData(aboutData); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); auto clv = new CertListView; clv->show(); QTimer::singleShot(5s, clv, &CertListView::slotStart); return app.exec(); } diff --git a/src/tests/test_keylister.h b/src/tests/test_keylister.h index 95abda91c..1bc7be05f 100644 --- a/src/tests/test_keylister.h +++ b/src/tests/test_keylister.h @@ -1,31 +1,31 @@ /* test_keylister.h This file is part of libkleopatra's test suite. SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-only */ #pragma once #include "ui/keylistview.h" namespace GpgME { class Key; class KeyListResult; } class CertListView : public Kleo::KeyListView { Q_OBJECT public: explicit CertListView(QWidget *parent = nullptr, Qt::WindowFlags f = {}); - ~CertListView(); + ~CertListView() override; public Q_SLOTS: void slotResult(const GpgME::KeyListResult &result); void slotStart(); }; diff --git a/src/ui/auditlogviewer.h b/src/ui/auditlogviewer.h index 842ff461c..9f768b5b3 100644 --- a/src/ui/auditlogviewer.h +++ b/src/ui/auditlogviewer.h @@ -1,57 +1,57 @@ /* SPDX-FileCopyrightText: 2015-2021 Laurent Montel SPDX-License-Identifier: LGPL-2.0-or-later */ #pragma once #include #include #ifdef HAVE_PIMTEXTEDIT #include namespace KPIMTextEdit { class RichTextEditorWidget; } #else class QTextEdit; #endif // HAVE_PIMTEXTEDIT namespace Kleo { namespace Private { class KLEO_EXPORT AuditLogViewer : public QDialog { Q_OBJECT public: explicit AuditLogViewer(const QString &log, QWidget *parent = nullptr); - ~AuditLogViewer(); + ~AuditLogViewer() override; void setAuditLog(const QString &log); private Q_SLOTS: void slotSaveAs(); void slotCopyClip(); private: void writeConfig(); void readConfig(); QString m_log; #ifdef HAVE_PIMTEXTEDIT KPIMTextEdit::RichTextEditorWidget *m_textEdit = nullptr; #else QTextEdit *m_textEdit = nullptr; #endif }; } } diff --git a/src/ui/dnattributeorderconfigwidget.h b/src/ui/dnattributeorderconfigwidget.h index b78e1e018..56afb507a 100644 --- a/src/ui/dnattributeorderconfigwidget.h +++ b/src/ui/dnattributeorderconfigwidget.h @@ -1,67 +1,67 @@ /* -*- c++ -*- dnattributeorderconfigwidget.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include namespace Kleo { class DNAttributeMapper; } class QTreeWidgetItem; namespace Kleo { class KLEO_EXPORT DNAttributeOrderConfigWidget : public QWidget { Q_OBJECT public: /*! Use Kleo::DNAttributeMapper::instance()->configWidget( parent, name ) instead. */ explicit DNAttributeOrderConfigWidget(DNAttributeMapper *mapper, QWidget *parent = nullptr, Qt::WindowFlags f = {}); - ~DNAttributeOrderConfigWidget(); + ~DNAttributeOrderConfigWidget() override; void load(); void save() const; void defaults(); Q_SIGNALS: void changed(); // // only boring stuff below... // private Q_SLOTS: void slotAvailableSelectionChanged(QTreeWidgetItem *); void slotCurrentOrderSelectionChanged(QTreeWidgetItem *); void slotDoubleUpButtonClicked(); void slotUpButtonClicked(); void slotDownButtonClicked(); void slotDoubleDownButtonClicked(); void slotLeftButtonClicked(); void slotRightButtonClicked(); private: void takePlaceHolderItem(); void enableDisableButtons(QTreeWidgetItem *); private: class DNAttributeOrderConfigWidgetPrivate; std::unique_ptr const d; protected: virtual void virtual_hook(int, void *); }; } diff --git a/src/ui/filenamerequester.h b/src/ui/filenamerequester.h index 096675a33..626017fc2 100644 --- a/src/ui/filenamerequester.h +++ b/src/ui/filenamerequester.h @@ -1,58 +1,58 @@ /* -*- mode: c++; c-basic-offset:4 -*- ui/filenamerequester.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #include namespace Kleo { class KLEO_EXPORT FileNameRequester : public QWidget { Q_OBJECT Q_PROPERTY(QString fileName READ fileName WRITE setFileName) Q_PROPERTY(bool existingOnly READ existingOnly WRITE setExistingOnly) public: explicit FileNameRequester(QWidget *parent = nullptr); explicit FileNameRequester(QDir::Filters filter, QWidget *parent = nullptr); - ~FileNameRequester(); + ~FileNameRequester() override; void setFileName(const QString &name); QString fileName() const; void setExistingOnly(bool on); bool existingOnly() const; void setFilter(QDir::Filters f); QDir::Filters filter() const; void setNameFilter(const QString &nameFilter); QString nameFilter() const; Q_SIGNALS: void fileNameChanged(const QString &filename); protected: bool event(QEvent *event) override; private: virtual QString requestFileName(); private: class FileNameRequesterPrivate; std::unique_ptr const d; }; } diff --git a/src/ui/keyapprovaldialog.h b/src/ui/keyapprovaldialog.h index 5721c2050..d271833ca 100644 --- a/src/ui/keyapprovaldialog.h +++ b/src/ui/keyapprovaldialog.h @@ -1,66 +1,66 @@ /* -*- c++ -*- keyapprovaldialog.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB Based on kpgpui.h SPDX-FileCopyrightText: 2001, 2002 the KPGP authors See file libkdenetwork/AUTHORS.kpgp for details SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include "libkleo/enum.h" #include #include #include namespace GpgME { class Key; } namespace Kleo { class KLEO_EXPORT KeyApprovalDialog : public QDialog { Q_OBJECT public: struct Item { Item() : pref(UnknownPreference) {} Item(const QString &a, const std::vector &k, EncryptionPreference p = UnknownPreference) : address(a), keys(k), pref(p) {} QString address; std::vector keys; EncryptionPreference pref; }; KeyApprovalDialog(const std::vector &recipients, const std::vector &sender, QWidget *parent = nullptr); - ~KeyApprovalDialog(); + ~KeyApprovalDialog() override; std::vector items() const; std::vector senderKeys() const; bool preferencesChanged() const; private Q_SLOTS: void slotPrefsChanged(); private: class KeyApprovalDialogPrivate; std::unique_ptr const d; }; } // namespace Kleo diff --git a/src/ui/keylistview.h b/src/ui/keylistview.h index d77b81b6d..154e05a7c 100644 --- a/src/ui/keylistview.h +++ b/src/ui/keylistview.h @@ -1,202 +1,202 @@ /* keylistview.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #include #include #include #include class QFont; class QColor; namespace Kleo { // work around moc parser bug... #define TEMPLATE_TYPENAME(T) template TEMPLATE_TYPENAME(T) inline T *lvi_cast(QTreeWidgetItem *item) { return item && (item->type() == T::RTTI) ? static_cast(item) : nullptr; } TEMPLATE_TYPENAME(T) inline const T *lvi_cast(const QTreeWidgetItem *item) { return item && (item->type() == T::RTTI) ? static_cast(item) : nullptr; } #undef TEMPLATE_TYPENAME class KeyListView; class KeyListViewItem : public QTreeWidgetItem { public: KeyListViewItem(KeyListView *parent, const GpgME::Key &key); KeyListViewItem(KeyListView *parent, KeyListViewItem *after, const GpgME::Key &key); KeyListViewItem(KeyListViewItem *parent, const GpgME::Key &key); KeyListViewItem(KeyListViewItem *parent, KeyListViewItem *after, const GpgME::Key &key); ~KeyListViewItem() override; void setKey(const GpgME::Key &key); const GpgME::Key &key() const { return mKey; } enum { RTTI = QTreeWidgetItem::UserType + 1 }; // // only boring stuff below: // virtual QString toolTip(int column) const; /*! \reimp for covariant return */ KeyListView *listView() const; /*! \reimp for covariant return */ KeyListViewItem *nextSibling() const; /*! \reimp */ bool operator<(const QTreeWidgetItem &other) const override; /*! \reimp */ void takeItem(QTreeWidgetItem *item); private: GpgME::Key mKey; }; class KLEO_EXPORT KeyListView : public QTreeWidget { Q_OBJECT friend class KeyListViewItem; public: class KLEO_EXPORT ColumnStrategy { public: virtual ~ColumnStrategy(); virtual QString title(int column) const = 0; virtual int width(int column, const QFontMetrics &fm) const; virtual QHeaderView::ResizeMode resizeMode(int) const { return QHeaderView::Interactive; } virtual QString text(const GpgME::Key &key, int column) const = 0; virtual QString toolTip(const GpgME::Key &key, int column) const; virtual QIcon icon(const GpgME::Key &, int) const { return QIcon(); } virtual int compare(const GpgME::Key &key1, const GpgME::Key &key2, const int column) const; }; class KLEO_EXPORT DisplayStrategy { public: virtual ~DisplayStrategy(); //font virtual QFont keyFont(const GpgME::Key &, const QFont &) const; //foreground virtual QColor keyForeground(const GpgME::Key &, const QColor &) const; //background virtual QColor keyBackground(const GpgME::Key &, const QColor &) const; }; explicit KeyListView(const ColumnStrategy *strategy, const DisplayStrategy *display = nullptr, QWidget *parent = nullptr, Qt::WindowFlags f = {}); - ~KeyListView(); + ~KeyListView() override; const ColumnStrategy *columnStrategy() const { return mColumnStrategy; } const DisplayStrategy *displayStrategy() const { return mDisplayStrategy; } bool hierarchical() const { return mHierarchical; } virtual void setHierarchical(bool hier); void flushKeys() { slotUpdateTimeout(); } bool isMultiSelection() const; KeyListViewItem *itemByFingerprint(const QByteArray &) const; public: using QTreeWidget::selectionChanged; // for below, but moc doesn't like it to be in the Q_SIGNALS: section Q_SIGNALS: void doubleClicked(Kleo::KeyListViewItem *, int); void returnPressed(Kleo::KeyListViewItem *); void selectionChanged(Kleo::KeyListViewItem *); void contextMenu(Kleo::KeyListViewItem *, const QPoint &); protected: void keyPressEvent(QKeyEvent *event) override; public Q_SLOTS: virtual void slotAddKey(const GpgME::Key &key); virtual void slotRefreshKey(const GpgME::Key &key); // // Only boring stuff below: // private Q_SLOTS: void slotEmitDoubleClicked(QTreeWidgetItem *, int); void slotEmitReturnPressed(QTreeWidgetItem *); void slotEmitSelectionChanged(); void slotEmitContextMenu(const QPoint &pos); void slotUpdateTimeout(); public: /*! \reimp for covariant return */ KeyListViewItem *selectedItem() const; /*! \reimp */ QList selectedItems() const; /*! \reimp for covariant return */ KeyListViewItem *firstChild() const; /*! \reimp */ void clear(); /*! \reimp */ void takeItem(QTreeWidgetItem *); private: void doHierarchicalInsert(const GpgME::Key &); void gatherScattered(); void scatterGathered(KeyListViewItem *); void registerItem(KeyListViewItem *); void deregisterItem(const KeyListViewItem *); private: const ColumnStrategy *mColumnStrategy = nullptr; const DisplayStrategy *mDisplayStrategy = nullptr; bool mHierarchical = false; class KeyListViewPrivate; std::unique_ptr const d; }; } diff --git a/src/ui/keyrequester.h b/src/ui/keyrequester.h index 8c10b84c1..6b780eeb2 100644 --- a/src/ui/keyrequester.h +++ b/src/ui/keyrequester.h @@ -1,201 +1,201 @@ /* -*- c++ -*- keyrequester.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB Based on kpgpui.h SPDX-FileCopyrightText: 2001, 2002 the KPGP authors See file libkdenetwork/AUTHORS.kpgp for details This file is part of KPGP, the KDE PGP/GnuPG support library. SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #include #include #include namespace GpgME { class Key; class KeyListResult; } #include class QString; class QPushButton; namespace Kleo { /// Base class for SigningKeyRequester and EncryptionKeyRequester class KLEO_EXPORT KeyRequester : public QWidget { Q_OBJECT public: explicit KeyRequester(unsigned int allowedKeys, bool multipleKeys = false, QWidget *parent = nullptr); // Constructor for Qt Designer explicit KeyRequester(QWidget *parent = nullptr); - ~KeyRequester(); + ~KeyRequester() override; const GpgME::Key &key() const; /** Preferred method to set a key for non-multi-KeyRequesters. Doesn't start a backend KeyListJob. */ void setKey(const GpgME::Key &key); const std::vector &keys() const; /** Preferred method to set a key for multi-KeyRequesters. Doesn't start a backend KeyListJob. */ void setKeys(const std::vector &keys); QString fingerprint() const; /** Set the key by fingerprint. Starts a background KeyListJob to retrieve the complete GpgME::Key object */ void setFingerprint(const QString &fingerprint); QStringList fingerprints() const; /** Set the keys by fingerprint. Starts a background KeyListJob to retrieve the complete GpgME::Key objects */ void setFingerprints(const QStringList &fingerprints); QPushButton *eraseButton(); QPushButton *dialogButton(); void setDialogCaption(const QString &caption); void setDialogMessage(const QString &message); bool isMultipleKeysEnabled() const; void setMultipleKeysEnabled(bool enable); unsigned int allowedKeys() const; void setAllowedKeys(unsigned int allowed); void setInitialQuery(const QString &s) { mInitialQuery = s; } const QString &initialQuery() const { return mInitialQuery; } Q_SIGNALS: void changed(); private: void init(); void startKeyListJob(const QStringList &fingerprints); void updateKeys(); private Q_SLOTS: void slotNextKey(const GpgME::Key &key); void slotKeyListResult(const GpgME::KeyListResult &result); void slotDialogButtonClicked(); void slotEraseButtonClicked(); private: const QGpgME::Protocol *mOpenPGPBackend = nullptr; const QGpgME::Protocol *mSMIMEBackend = nullptr; QLabel *mLabel = nullptr; QPushButton *mEraseButton = nullptr; QPushButton *mDialogButton = nullptr; QString mDialogCaption, mDialogMessage, mInitialQuery; bool mMulti; unsigned int mKeyUsage; int mJobs; std::vector mKeys; std::vector mTmpKeys; private: class Private; Private *const d; protected: virtual void virtual_hook(int, void *); }; class KLEO_EXPORT EncryptionKeyRequester : public KeyRequester { Q_OBJECT public: enum { OpenPGP = 1, SMIME = 2, AllProtocols = OpenPGP | SMIME }; /** * Preferred constructor */ explicit EncryptionKeyRequester(bool multipleKeys = false, unsigned int proto = AllProtocols, QWidget *parent = nullptr, bool onlyTrusted = true, bool onlyValid = true); /** * Constructor for Qt designer */ explicit EncryptionKeyRequester(QWidget *parent); - ~EncryptionKeyRequester(); + ~EncryptionKeyRequester() override; void setAllowedKeys(unsigned int proto, bool onlyTrusted = true, bool onlyValid = true); private: class Private; Private *const d; protected: void virtual_hook(int, void *) override; }; class KLEO_EXPORT SigningKeyRequester : public KeyRequester { Q_OBJECT public: enum { OpenPGP = 1, SMIME = 2, AllProtocols = OpenPGP | SMIME }; /** * Preferred constructor * @param multipleKeys whether multiple keys can be selected * * @param proto the allowed protocols, OpenPGP and/or SMIME * @param parent the parent widget * @param onlyTrusted only show trusted keys * @param onlyValid only show valid keys */ explicit SigningKeyRequester(bool multipleKeys = false, unsigned int proto = AllProtocols, QWidget *parent = nullptr, bool onlyTrusted = true, bool onlyValid = true); /** * Constructor for Qt designer */ explicit SigningKeyRequester(QWidget *parent); - ~SigningKeyRequester(); + ~SigningKeyRequester() override; /* * Those parameters affect the parameters given to the key selection dialog. * @param proto the allowed protocols, OpenPGP and/or SMIME * @param onlyTrusted only show trusted keys * @param onlyValid only show valid keys */ void setAllowedKeys(unsigned int proto, bool onlyTrusted = true, bool onlyValid = true); private: class Private; Private *const d; protected: void virtual_hook(int, void *) override; }; } diff --git a/src/ui/keyselectiondialog.h b/src/ui/keyselectiondialog.h index c2837fb3e..f20abe704 100644 --- a/src/ui/keyselectiondialog.h +++ b/src/ui/keyselectiondialog.h @@ -1,199 +1,199 @@ /* -*- c++ -*- keyselectiondialog.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB Based on kpgpui.h SPDX-FileCopyrightText: 2001, 2002 the KPGP authors See file libkdenetwork/AUTHORS.kpgp for details SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #include #include #include #include class QCheckBox; class QLabel; class QPoint; class QRegExp; class QTimer; class QVBoxLayout; namespace Kleo { class KeyListView; class KeyListViewItem; } namespace GpgME { class KeyListResult; } namespace Kleo { class KLEO_EXPORT KeySelectionDialog : public QDialog { Q_OBJECT public: enum Option { RereadKeys = 0x01, ExternalCertificateManager = 0x02, ExtendedSelection = 0x04, RememberChoice = 0x08 }; Q_DECLARE_FLAGS(Options, Option) enum KeyUsage { PublicKeys = 1, SecretKeys = 2, EncryptionKeys = 4, SigningKeys = 8, ValidKeys = 16, TrustedKeys = 32, CertificationKeys = 64, AuthenticationKeys = 128, OpenPGPKeys = 256, SMIMEKeys = 512, AllKeys = PublicKeys | SecretKeys | OpenPGPKeys | SMIMEKeys, ValidEncryptionKeys = AllKeys | EncryptionKeys | ValidKeys, ValidTrustedEncryptionKeys = AllKeys | EncryptionKeys | ValidKeys | TrustedKeys }; explicit KeySelectionDialog(QWidget *parent = nullptr, Options options = Options()); KeySelectionDialog(const QString &title, const QString &text, const std::vector &selectedKeys = std::vector(), unsigned int keyUsage = AllKeys, bool extendedSelection = false, bool rememberChoice = false, QWidget *parent = nullptr, bool modal = true); KeySelectionDialog(const QString &title, const QString &text, const QString &initialPattern, const std::vector &selectedKeys, unsigned int keyUsage = AllKeys, bool extendedSelection = false, bool rememberChoice = false, QWidget *parent = nullptr, bool modal = true); KeySelectionDialog(const QString &title, const QString &text, const QString &initialPattern, unsigned int keyUsage = AllKeys, bool extendedSelection = false, bool rememberChoice = false, QWidget *parent = nullptr, bool modal = true); - ~KeySelectionDialog(); + ~KeySelectionDialog() override; void setText(const QString &text); void setKeys(const std::vector &keys); /** Returns the key ID of the selected key in single selection mode. Otherwise it returns a null key. */ const GpgME::Key &selectedKey() const; QString fingerprint() const; /** Returns a list of selected key IDs. */ const std::vector &selectedKeys() const { return mSelectedKeys; } /// Return all the selected fingerprints QStringList fingerprints() const; /// Return the selected openpgp fingerprints QStringList pgpKeyFingerprints() const; /// Return the selected smime fingerprints QStringList smimeFingerprints() const; bool rememberSelection() const; // Could be used by derived classes to insert their own widget QVBoxLayout *topLayout() const { return mTopLayout; } private Q_SLOTS: void slotRereadKeys(); void slotStartCertificateManager(const QString &query = QString()); void slotStartSearchForExternalCertificates() { slotStartCertificateManager(mInitialQuery); } void slotKeyListResult(const GpgME::KeyListResult &); void slotSelectionChanged(); void slotCheckSelection() { slotCheckSelection(nullptr); } void slotCheckSelection(Kleo::KeyListViewItem *); void slotRMB(Kleo::KeyListViewItem *, const QPoint &); void slotRecheckKey(); void slotTryOk(); void slotOk(); void slotCancel(); void slotSearch(const QString &text); void slotSearch(); void slotFilter(); private: void filterByKeyID(const QString &keyID); void filterByKeyIDOrUID(const QString &keyID); void filterByUID(const QString &uid); void showAllItems(); void connectSignals(); void disconnectSignals(); void startKeyListJobForBackend(const QGpgME::Protocol *, const std::vector &, bool); void startValidatingKeyListing(); void setUpUI(Options options, const QString &); void init(bool, bool, const QString &, const QString &); private: QVBoxLayout *mTopLayout = nullptr; QLabel *mTextLabel = nullptr; Kleo::KeyListView *mKeyListView = nullptr; Kleo::KeyListViewItem *mCurrentContextMenuItem = nullptr; QCheckBox *mRememberCB = nullptr; QPushButton *mOkButton = nullptr; const QGpgME::Protocol *mOpenPGPBackend = nullptr; const QGpgME::Protocol *mSMIMEBackend = nullptr; std::vector mSelectedKeys, mKeysToCheck; unsigned int mKeyUsage; QTimer *mCheckSelectionTimer = nullptr; QTimer *mStartSearchTimer = nullptr; // cross-eventloop temporaries: QString mSearchText; const QString mInitialQuery; int mTruncated = 0, mListJobCount = 0, mSavedOffsetY = 0; }; } Q_DECLARE_OPERATORS_FOR_FLAGS(Kleo::KeySelectionDialog::Options) diff --git a/src/ui/progressdialog.h b/src/ui/progressdialog.h index 1e2819ffb..3ba4d5ab3 100644 --- a/src/ui/progressdialog.h +++ b/src/ui/progressdialog.h @@ -1,52 +1,52 @@ /* progressdialog.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #ifndef QT_NO_PROGRESSDIALOG #include #include namespace Kleo { /** @short A progress dialog for Kleo::Jobs */ class KLEO_EXPORT ProgressDialog : public QProgressDialog { Q_OBJECT public: ProgressDialog(QGpgME::Job *job, const QString &baseText, QWidget *widget = nullptr, Qt::WindowFlags f = {}); - ~ProgressDialog(); + ~ProgressDialog() override; public Q_SLOTS: /*! reimplementation */ void setMinimumDuration(int ms); private Q_SLOTS: void slotProgress(const QString &what, int current, int total); void slotDone(); private: QString mBaseText; }; } #else # ifndef LIBKLEO_NO_PROGRESSDIALOG # define LIBKLEO_NO_PROGRESSDIALOG # endif #endif // QT_NO_PROGRESSDIALOG diff --git a/src/utils/filesystemwatcher.h b/src/utils/filesystemwatcher.h index 804b87e0a..920290872 100644 --- a/src/utils/filesystemwatcher.h +++ b/src/utils/filesystemwatcher.h @@ -1,56 +1,56 @@ /* -*- mode: c++; c-basic-offset:4 -*- filesystemwatcher.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include class QString; #include namespace Kleo { class KLEO_EXPORT FileSystemWatcher : public QObject { Q_OBJECT public: explicit FileSystemWatcher(QObject *parent = nullptr); explicit FileSystemWatcher(const QStringList &paths, QObject *parent = nullptr); - ~FileSystemWatcher(); + ~FileSystemWatcher() override; void setDelay(int ms); int delay() const; void setEnabled(bool enable); bool isEnabled() const; void addPaths(const QStringList &paths); void addPath(const QString &path); void blacklistFiles(const QStringList &patterns); void whitelistFiles(const QStringList &patterns); QStringList files() const; void removePaths(const QStringList &path); void removePath(const QString &path); Q_SIGNALS: void directoryChanged(const QString &path); void fileChanged(const QString &path); void triggered(); private: class Private; QScopedPointer const d; }; }