Verified Commit 9c65203c authored by Andre Heinecke's avatar Andre Heinecke
Browse files

Add remark support to keylistmodel and keycache

Support the new remark feature of GPGME 1.14.0.
This allows user ids to have optionally a remark
based on a signature annotation of a local certification
signature. This can be used to order / search for
keys.

Minor version bump for the new API.
parent 85983c18
cmake_minimum_required(VERSION 3.5)
set(PIM_VERSION "5.13.40")
set(PIM_VERSION "5.13.41")
project(libkleo VERSION ${PIM_VERSION})
......
......@@ -42,6 +42,7 @@
#include "utils/filesystemwatcher.h"
#include <gpgme++/error.h>
#include <gpgme++/context.h>
#include <gpgme++/key.h>
#include <gpgme++/decryptionresult.h>
#include <gpgme++/verificationresult.h>
......@@ -89,7 +90,7 @@ class KeyCache::Private
friend class ::Kleo::KeyCache;
KeyCache *const q;
public:
explicit Private(KeyCache *qq) : q(qq), m_refreshInterval(1), m_initalized(false), m_pgpOnly(true)
explicit Private(KeyCache *qq) : q(qq), m_refreshInterval(1), m_initalized(false), m_pgpOnly(true), m_remarks_enabled(false)
{
connect(&m_autoKeyListingTimer, &QTimer::timeout, q, [this]() { q->startKeyListing(); });
updateAutoKeyListingTimer();
......@@ -250,6 +251,7 @@ private:
} by;
bool m_initalized;
bool m_pgpOnly;
bool m_remarks_enabled;
QMap <QString, std::vector<Key> > m_groups;
};
......@@ -334,6 +336,30 @@ void KeyCache::addFileSystemWatcher(const std::shared_ptr<FileSystemWatcher> &wa
watcher->setEnabled(d->m_refreshJob.isNull());
}
void KeyCache::enableRemarks(bool value)
{
if (!d->m_remarks_enabled && value) {
d->m_remarks_enabled = value;
if (d->m_initalized && !d->m_refreshJob) {
qCDebug(LIBKLEO_LOG) << "Reloading keycache with remarks enabled";
reload();
} else {
connect(d->m_refreshJob.data(), &RefreshKeysJob::done,
this, [this](const GpgME::KeyListResult &) {
qCDebug(LIBKLEO_LOG) << "Reloading keycache with remarks enabled";
QTimer::singleShot(1000, this, [this](){ reload(); });
});
}
} else {
d->m_remarks_enabled = value;
}
}
bool KeyCache::remarksEnabled() const
{
return d->m_remarks_enabled;
}
void KeyCache::Private::refreshJobDone(const KeyListResult &result)
{
q->enableFileSystemWatcher(true);
......@@ -1157,6 +1183,16 @@ Error KeyCache::RefreshKeysJob::Private::startKeyListing(GpgME::Protocol proto)
connect(q, &RefreshKeysJob::canceled, job, &QGpgME::Job::slotCancel);
// Only do this for initialized keycaches to avoid huge waits for
// signature notations during initial keylisting.
if (proto == GpgME::OpenPGP && m_cache->remarksEnabled() && m_cache->initialized()) {
auto ctx = QGpgME::Job::context(job);
if (ctx) {
ctx->addKeyListMode(KeyListMode::Signatures |
KeyListMode::SignatureNotations);
}
}
const Error error = job->start(true);
if (!error && !error.isCanceled()) {
......
......@@ -91,6 +91,9 @@ public:
void setRefreshInterval(int hours);
int refreshInterval() const;
void enableRemarks(bool enable);
bool remarksEnabled() const;
const std::vector<GpgME::Key> &keys() const;
std::vector<GpgME::Key> secretKeys() const;
......
......@@ -64,6 +64,11 @@
#include <set>
#include <iterator>
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
# define GPGME_HAS_REMARKS
#endif
using namespace GpgME;
using namespace Kleo;
......@@ -78,8 +83,10 @@ public:
m_secretOnly(false) {}
int m_toolTipOptions;
mutable QHash<const char *, QVariant> prettyEMailCache;
mutable QHash<const char *, QVariant> remarksCache;
bool m_useKeyCache;
bool m_secretOnly;
std::vector<GpgME::Key> m_remarkKeys;
};
AbstractKeyListModel::AbstractKeyListModel(QObject *p)
: QAbstractItemModel(p), KeyListModelInterface(), d(new Private)
......@@ -99,6 +106,16 @@ int AbstractKeyListModel::toolTipOptions() const
return d->m_toolTipOptions;
}
void AbstractKeyListModel::setRemarkKeys(const std::vector<GpgME::Key> &keys)
{
d->m_remarkKeys = keys;
}
std::vector<GpgME::Key> AbstractKeyListModel::remarkKeys() const
{
return d->m_remarkKeys;
}
Key AbstractKeyListModel::key(const QModelIndex &idx) const
{
if (idx.isValid()) {
......@@ -162,6 +179,7 @@ void AbstractKeyListModel::removeKey(const Key &key)
}
doRemoveKey(key);
d->prettyEMailCache.remove(key.primaryFingerprint());
d->remarksCache.remove(key.primaryFingerprint());
}
QList<QModelIndex> AbstractKeyListModel::addKeys(const std::vector<Key> &keys)
......@@ -180,6 +198,7 @@ void AbstractKeyListModel::clear()
beginResetModel();
doClear();
d->prettyEMailCache.clear();
d->remarksCache.clear();
endResetModel();
}
......@@ -207,6 +226,7 @@ QVariant AbstractKeyListModel::headerData(int section, Qt::Orientation o, int ro
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();
......@@ -288,6 +308,39 @@ QVariant AbstractKeyListModel::data(const QModelIndex &index, int role) const
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 char *, QVariant>::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;
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;
}
......
......@@ -96,6 +96,14 @@ public:
void setToolTipOptions(int opts);
/**
* Set the keys to use for KeyListModelInterface::Remark column
* to obtain remarks from this keys signature notations.
* Needs at least GpgME 1.14 to work properly. Remarks are
* joined by a semicolon and a space. */
void setRemarkKeys(const std::vector<GpgME::Key> &remarkKeys);
std::vector<GpgME::Key> remarkKeys() const;
private:
virtual GpgME::Key doMapToKey(const QModelIndex &index) const = 0;
virtual QModelIndex doMapFromKey(const GpgME::Key &key, int column) const = 0;
......
......@@ -74,6 +74,7 @@ public:
Validity,
Summary, // Short summary line
Remarks, // Additional remark notations
NumColumns,
Icon = PrettyName // which column shall the icon be displayed in?
};
......
......@@ -40,6 +40,10 @@
#include <gpgme++/key.h>
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
# define GPGME_HAS_REMARKS
#endif
using namespace Kleo;
using namespace GpgME;
......@@ -203,6 +207,17 @@ bool KeyListSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelI
match = true;
break;
}
#ifdef GPGME_HAS_REMARKS
// Also match against remarks (search tags)
const auto alm = dynamic_cast<AbstractKeyListModel *>(sourceModel());
if (alm) {
const auto remarks = alm->data(alm->index(key, KeyListModelInterface::Remarks));
if (!remarks.isNull() && remarks.toString().contains(rx)) {
match = true;
break;
}
}
#endif
}
if (!match) {
return false;
......
......@@ -41,6 +41,13 @@
#include <QVariant>
#include <QIcon>
#include "keycache.h"
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
# define GPGME_HAS_REMARKS
#endif
using namespace GpgME;
using namespace Kleo;
......@@ -51,16 +58,27 @@ class UIDModelItem
// null values. (Not null but isNull)
//
public:
explicit UIDModelItem(const UserID::Signature &sig, UIDModelItem *parentItem)
explicit UIDModelItem(const UserID::Signature &sig, UIDModelItem *parentItem, bool showRemarks)
{
mItemData << QString::fromUtf8(sig.signerKeyID())
<< Formatting::prettyName(sig)
<< Formatting::prettyEMail(sig)
<< Formatting::creationDateString(sig)
<< Formatting::expirationDateString(sig)
<< Formatting::validityShort(sig);
<< Formatting::validityShort(sig)
<< (sig.isExportable() ? QStringLiteral("✓") : QString());
mSig = sig;
mParentItem = parentItem;
if (showRemarks && parentItem) {
QString lastNotation;
for (const auto &notation: sig.notations()) {
if (notation.name() && !strcmp(notation.name(), "rem@gnupg.org")) {
lastNotation = QString::fromUtf8(notation.value());
}
}
mItemData << lastNotation;
}
}
explicit UIDModelItem(const UserID &uid, UIDModelItem *parentItem)
......@@ -71,14 +89,19 @@ public:
}
// The root item
explicit UIDModelItem() : mParentItem(nullptr)
explicit UIDModelItem(bool showRemarks) : mParentItem(nullptr)
{
mItemData << i18n("ID")
<< i18n("Name")
<< i18n("E-Mail")
<< i18n("Valid From")
<< i18n("Valid Until")
<< i18n("Status");
<< i18n("Status")
<< i18n("Exportable");
if (showRemarks) {
mItemData << i18n("Tags");
}
}
~UIDModelItem()
......@@ -190,14 +213,14 @@ void UserIDListModel::setKey(const Key &key)
delete mRootItem;
mKey = key;
mRootItem = new UIDModelItem();
mRootItem = new UIDModelItem(mRemarksEnabled);
for (int i = 0, ids = key.numUserIDs(); i < ids; ++i) {
UserID uid = key.userID(i);
UIDModelItem *uidItem = new UIDModelItem(uid, mRootItem);
mRootItem->appendChild(uidItem);
for (int j = 0, sigs = uid.numSignatures(); j < sigs; ++j) {
UserID::Signature sig = uid.signature(j);
UIDModelItem *sigItem = new UIDModelItem(sig, uidItem);
UIDModelItem *sigItem = new UIDModelItem(sig, uidItem, mRemarksEnabled);
uidItem->appendChild(sigItem);
}
}
......@@ -332,3 +355,8 @@ QVector<UserID::Signature> UserIDListModel::signatures (const QModelIndexList &i
}
return ret;
}
void UserIDListModel::enableRemarks(bool value)
{
mRemarksEnabled = value;
}
......@@ -56,6 +56,7 @@ public:
public:
QVector<GpgME::UserID> userIDs(const QModelIndexList &indexs) const;
QVector<GpgME::UserID::Signature> signatures(const QModelIndexList &indexs) const;
void enableRemarks(bool value);
public Q_SLOTS:
void setKey(const GpgME::Key &key);
......@@ -71,6 +72,7 @@ public:
private:
GpgME::Key mKey;
bool mRemarksEnabled = false;
UIDModelItem *mRootItem = nullptr;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment