qt: Allow specifying user IDs to use when refreshing keys via WKD

* lang/qt/src/wkdrefreshjob.h, lang/qt/src/wkdrefreshjob.cpp
(WKDRefreshJob::start): New overload.
* lang/qt/src/wkdrefreshjob_p.h (WKDRefreshJobPrivate): Add field
m_userIds.
* lang/qt/src/qgpgmewkdrefreshjob.cpp (toEmailAddresses): New.
(locate_external_keys): Change return type and arguments.
(refresh_keys): Remove.
(QGpgMEWKDRefreshJobPrivate::startIt): Get emails from keys or user IDs.
Remove duplicates. Call locate_external_keys instead of refresh_keys.

* lang/qt/tests/run-wkdrefreshjob.cpp (CommandLineOptions,
parseCommandLine): New.
(main): Support new option --all-userids.
--

The new start() overload allows to specify the user IDs to use for the
WKD lookup explicitly. This allows updating user IDs via WKD which were
originally not retrieved via WKD.

GnuPG-bug-id: 6672
This commit is contained in:
Ingo Klöcker 2023-08-23 12:00:26 +02:00
parent aee18a2ab2
commit 3f297387bf
No known key found for this signature in database
GPG Key ID: F5A5D1692277A1E9
5 changed files with 88 additions and 27 deletions

View File

@ -88,6 +88,21 @@ static QStringList toEmailAddressesOriginatingFromWKD(const std::vector<GpgME::K
return emails; return emails;
} }
static QStringList toEmailAddresses(const std::vector<GpgME::UserID> &userIds)
{
const QStringList emails = std::accumulate(
std::begin(userIds),
std::end(userIds),
QStringList{},
[](QStringList &emails, const UserID &userId) {
if (!userId.isRevoked() && !userId.addrSpec().empty()) {
emails.push_back(QString::fromStdString(userId.addrSpec()));
}
return emails;
});
return emails;
}
} }
QGpgMEWKDRefreshJob::QGpgMEWKDRefreshJob(Context *context) QGpgMEWKDRefreshJob::QGpgMEWKDRefreshJob(Context *context)
@ -99,12 +114,11 @@ QGpgMEWKDRefreshJob::QGpgMEWKDRefreshJob(Context *context)
QGpgMEWKDRefreshJob::~QGpgMEWKDRefreshJob() = default; QGpgMEWKDRefreshJob::~QGpgMEWKDRefreshJob() = default;
static ImportResult locate_external_keys(Context *ctx, const std::vector<Key> &keys) static QGpgMEWKDRefreshJob::result_type locate_external_keys(Context *ctx, const QStringList &emails)
{ {
const auto emails = toEmailAddressesOriginatingFromWKD(keys);
qCDebug(QGPGME_LOG) << __func__ << "locating external keys for" << emails; qCDebug(QGPGME_LOG) << __func__ << "locating external keys for" << emails;
if (emails.empty()) { if (emails.empty()) {
return ImportResult{}; return std::make_tuple(ImportResult{}, QString{}, Error{});
} }
Context::KeyListModeSaver saver{ctx}; Context::KeyListModeSaver saver{ctx};
@ -121,24 +135,22 @@ static ImportResult locate_external_keys(Context *ctx, const std::vector<Key> &k
qCDebug(QGPGME_LOG) << __func__ << "result:" << toLogString(result).c_str(); qCDebug(QGPGME_LOG) << __func__ << "result:" << toLogString(result).c_str();
job.release(); job.release();
return result;
}
static QGpgMEWKDRefreshJob::result_type refresh_keys(Context *ctx, const std::vector<Key> &keys)
{
const auto result = locate_external_keys(ctx, keys);
return std::make_tuple(result, QString{}, Error{}); return std::make_tuple(result, QString{}, Error{});
} }
GpgME::Error QGpgMEWKDRefreshJobPrivate::startIt() GpgME::Error QGpgMEWKDRefreshJobPrivate::startIt()
{ {
if (m_keys.empty()) { QStringList emails;
return Error::fromCode(GPG_ERR_INV_VALUE); if (!m_keys.empty()) {
emails = toEmailAddressesOriginatingFromWKD(m_keys);
} else {
emails = toEmailAddresses(m_userIds);
} }
std::sort(emails.begin(), emails.end());
emails.erase(std::unique(emails.begin(), emails.end()), emails.end());
q->run([=](Context *ctx) { q->run([emails](Context *ctx) {
return refresh_keys(ctx, m_keys); return locate_external_keys(ctx, emails);
}); });
return {}; return {};

View File

@ -54,4 +54,11 @@ GpgME::Error WKDRefreshJob::start(const std::vector<GpgME::Key> &keys)
return d->startIt(); return d->startIt();
} }
GpgME::Error WKDRefreshJob::start(const std::vector<GpgME::UserID> &userIDs)
{
auto d = jobPrivate<WKDRefreshJobPrivate>(this);
d->m_userIds = userIDs;
return d->startIt();
}
#include "wkdrefreshjob.moc" #include "wkdrefreshjob.moc"

View File

@ -43,14 +43,14 @@ namespace GpgME
{ {
class Error; class Error;
class Key; class Key;
class UserID;
} }
namespace QGpgME namespace QGpgME
{ {
/** /**
* This job refreshes OpenPGP keys via WKD. Only user IDs that have WKD set as * This job refreshes OpenPGP keys via WKD.
* origin are used for the WKD lookup. Revoked user IDs are ignored.
*/ */
class QGPGME_EXPORT WKDRefreshJob : public AbstractImportJob class QGPGME_EXPORT WKDRefreshJob : public AbstractImportJob
{ {
@ -61,9 +61,18 @@ public:
~WKDRefreshJob() override; ~WKDRefreshJob() override;
/** /**
* Starts a refresh of the \a keys. * Starts a refresh of the \a keys. Only user IDs that have WKD set as
* origin are used for the WKD lookup. Revoked user IDs are ignored.
*
* Use the other start overload to use all user IDs for the WKD lookup.
*/ */
GpgME::Error start(const std::vector<GpgME::Key> &keys); GpgME::Error start(const std::vector<GpgME::Key> &keys);
/**
* Starts a refresh of the keys belonging to the user IDs \a userIDs.
* All user IDs are used for the WKD lookup. Revoked user IDs are ignored.
*/
GpgME::Error start(const std::vector<GpgME::UserID> &userIDs);
}; };
} }

View File

@ -44,6 +44,7 @@ namespace QGpgME
struct WKDRefreshJobPrivate : public JobPrivate struct WKDRefreshJobPrivate : public JobPrivate
{ {
std::vector<GpgME::Key> m_keys; std::vector<GpgME::Key> m_keys;
std::vector<GpgME::UserID> m_userIds;
}; };
} }

View File

@ -38,6 +38,7 @@
#include <protocol.h> #include <protocol.h>
#include <wkdrefreshjob.h> #include <wkdrefreshjob.h>
#include <QCommandLineParser>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
@ -53,6 +54,36 @@ std::ostream &operator<<(std::ostream &os, const QString &s)
return os << s.toLocal8Bit().constData(); return os << s.toLocal8Bit().constData();
} }
struct CommandLineOptions {
bool allUserIds;
QString keyId;
};
CommandLineOptions parseCommandLine(const QStringList &arguments)
{
CommandLineOptions options;
QCommandLineParser parser;
parser.setApplicationDescription("Test program for WKDRefreshJob");
parser.addHelpOption();
parser.addOptions({
{"all-userids", "Query WKD for all user IDs."},
});
parser.addPositionalArgument("key ID", "Key to refresh");
parser.process(arguments);
const auto args = parser.positionalArguments();
if (args.size() != 1) {
parser.showHelp(1);
}
options.allUserIds = parser.isSet("all-userids");
options.keyId = args[0];
return options;
}
Key getOpenPGPKey(const QString &keyId, Error &err) Key getOpenPGPKey(const QString &keyId, Error &err)
{ {
Key key; Key key;
@ -74,22 +105,19 @@ int main(int argc, char **argv)
{ {
GpgME::initializeLibrary(); GpgME::initializeLibrary();
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " KEYID" << std::endl;
return 1;
}
QCoreApplication app{argc, argv}; QCoreApplication app{argc, argv};
const auto keyId = qApp->arguments().last(); app.setApplicationName("run-wkdrefreshjob");
const auto options = parseCommandLine(app.arguments());
Error err; Error err;
const auto key = getOpenPGPKey(keyId, err); const auto key = getOpenPGPKey(options.keyId, err);
if (err.code() == GPG_ERR_AMBIGUOUS_NAME) { if (err.code() == GPG_ERR_AMBIGUOUS_NAME) {
std::cerr << "Error: Multiple OpenPGP keys matching '" << keyId << "' found" << std::endl; std::cerr << "Error: Multiple OpenPGP keys matching '" << options.keyId << "' found" << std::endl;
return 1; return 1;
} }
if (key.isNull()) { if (key.isNull()) {
std::cerr << "Error: No OpenPGP key matching '" << keyId << "' found" << std::endl; std::cerr << "Error: No OpenPGP key matching '" << options.keyId << "' found" << std::endl;
return 1; return 1;
} }
if (err) { if (err) {
@ -111,7 +139,11 @@ int main(int argc, char **argv)
} }
qApp->quit(); qApp->quit();
}); });
if (options.allUserIds) {
err = job->start(key.userIDs());
} else {
err = job->start({key}); err = job->start({key});
}
if (err) { if (err) {
std::cerr << "Error: " << err.asString() << std::endl; std::cerr << "Error: " << err.asString() << std::endl;
return 1; return 1;