From 3f297387bf401475385c458e4d1d95b3eefaf3d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Kl=C3=B6cker?= Date: Wed, 23 Aug 2023 12:00:26 +0200 Subject: [PATCH] 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 --- lang/qt/src/qgpgmewkdrefreshjob.cpp | 40 ++++++++++++++-------- lang/qt/src/wkdrefreshjob.cpp | 7 ++++ lang/qt/src/wkdrefreshjob.h | 15 +++++++-- lang/qt/src/wkdrefreshjob_p.h | 1 + lang/qt/tests/run-wkdrefreshjob.cpp | 52 +++++++++++++++++++++++------ 5 files changed, 88 insertions(+), 27 deletions(-) diff --git a/lang/qt/src/qgpgmewkdrefreshjob.cpp b/lang/qt/src/qgpgmewkdrefreshjob.cpp index 8bad1ef1..f590ed4b 100644 --- a/lang/qt/src/qgpgmewkdrefreshjob.cpp +++ b/lang/qt/src/qgpgmewkdrefreshjob.cpp @@ -88,6 +88,21 @@ static QStringList toEmailAddressesOriginatingFromWKD(const std::vector &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) @@ -99,12 +114,11 @@ QGpgMEWKDRefreshJob::QGpgMEWKDRefreshJob(Context *context) QGpgMEWKDRefreshJob::~QGpgMEWKDRefreshJob() = default; -static ImportResult locate_external_keys(Context *ctx, const std::vector &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; if (emails.empty()) { - return ImportResult{}; + return std::make_tuple(ImportResult{}, QString{}, Error{}); } Context::KeyListModeSaver saver{ctx}; @@ -121,24 +135,22 @@ static ImportResult locate_external_keys(Context *ctx, const std::vector &k qCDebug(QGPGME_LOG) << __func__ << "result:" << toLogString(result).c_str(); job.release(); - return result; -} - -static QGpgMEWKDRefreshJob::result_type refresh_keys(Context *ctx, const std::vector &keys) -{ - const auto result = locate_external_keys(ctx, keys); - return std::make_tuple(result, QString{}, Error{}); } GpgME::Error QGpgMEWKDRefreshJobPrivate::startIt() { - if (m_keys.empty()) { - return Error::fromCode(GPG_ERR_INV_VALUE); + QStringList emails; + 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) { - return refresh_keys(ctx, m_keys); + q->run([emails](Context *ctx) { + return locate_external_keys(ctx, emails); }); return {}; diff --git a/lang/qt/src/wkdrefreshjob.cpp b/lang/qt/src/wkdrefreshjob.cpp index 37b990c8..f79df2c5 100644 --- a/lang/qt/src/wkdrefreshjob.cpp +++ b/lang/qt/src/wkdrefreshjob.cpp @@ -54,4 +54,11 @@ GpgME::Error WKDRefreshJob::start(const std::vector &keys) return d->startIt(); } +GpgME::Error WKDRefreshJob::start(const std::vector &userIDs) +{ + auto d = jobPrivate(this); + d->m_userIds = userIDs; + return d->startIt(); +} + #include "wkdrefreshjob.moc" diff --git a/lang/qt/src/wkdrefreshjob.h b/lang/qt/src/wkdrefreshjob.h index c3aec65f..cbaaf6b0 100644 --- a/lang/qt/src/wkdrefreshjob.h +++ b/lang/qt/src/wkdrefreshjob.h @@ -43,14 +43,14 @@ namespace GpgME { class Error; class Key; +class UserID; } namespace QGpgME { /** - * This job refreshes OpenPGP keys via WKD. Only user IDs that have WKD set as - * origin are used for the WKD lookup. Revoked user IDs are ignored. + * This job refreshes OpenPGP keys via WKD. */ class QGPGME_EXPORT WKDRefreshJob : public AbstractImportJob { @@ -61,9 +61,18 @@ public: ~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 &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 &userIDs); }; } diff --git a/lang/qt/src/wkdrefreshjob_p.h b/lang/qt/src/wkdrefreshjob_p.h index 377350f3..60fdfedd 100644 --- a/lang/qt/src/wkdrefreshjob_p.h +++ b/lang/qt/src/wkdrefreshjob_p.h @@ -44,6 +44,7 @@ namespace QGpgME struct WKDRefreshJobPrivate : public JobPrivate { std::vector m_keys; + std::vector m_userIds; }; } diff --git a/lang/qt/tests/run-wkdrefreshjob.cpp b/lang/qt/tests/run-wkdrefreshjob.cpp index 757ee899..c75718e6 100644 --- a/lang/qt/tests/run-wkdrefreshjob.cpp +++ b/lang/qt/tests/run-wkdrefreshjob.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -53,6 +54,36 @@ std::ostream &operator<<(std::ostream &os, const QString &s) 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 key; @@ -74,22 +105,19 @@ int main(int argc, char **argv) { GpgME::initializeLibrary(); - if (argc != 2) { - std::cerr << "Usage: " << argv[0] << " KEYID" << std::endl; - return 1; - } - QCoreApplication app{argc, argv}; - const auto keyId = qApp->arguments().last(); + app.setApplicationName("run-wkdrefreshjob"); + + const auto options = parseCommandLine(app.arguments()); Error err; - const auto key = getOpenPGPKey(keyId, err); + const auto key = getOpenPGPKey(options.keyId, err); 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; } 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; } if (err) { @@ -111,7 +139,11 @@ int main(int argc, char **argv) } qApp->quit(); }); - err = job->start({key}); + if (options.allUserIds) { + err = job->start(key.userIDs()); + } else { + err = job->start({key}); + } if (err) { std::cerr << "Error: " << err.asString() << std::endl; return 1;