diff --git a/lang/qt/src/qgpgmelistallkeysjob.cpp b/lang/qt/src/qgpgmelistallkeysjob.cpp index 82483f25..6f169ac9 100644 --- a/lang/qt/src/qgpgmelistallkeysjob.cpp +++ b/lang/qt/src/qgpgmelistallkeysjob.cpp @@ -40,7 +40,10 @@ #include "key.h" #include "context.h" +#include "engineinfo.h" +#include "global.h" #include "keylistresult.h" + #include #include @@ -61,7 +64,9 @@ QGpgMEListAllKeysJob::QGpgMEListAllKeysJob(Context *context) QGpgMEListAllKeysJob::~QGpgMEListAllKeysJob() {} -static KeyListResult do_list_keys(Context *ctx, std::vector &keys, bool secretOnly) +namespace { + +static KeyListResult do_list_keys_legacy(Context *ctx, std::vector &keys, bool secretOnly) { const char **pat = nullptr; @@ -81,9 +86,6 @@ static KeyListResult do_list_keys(Context *ctx, std::vector &keys, bool sec return result; } -namespace -{ - template ForwardIterator unique_by_merge(ForwardIterator first, ForwardIterator last, BinaryPredicate pred) { @@ -103,8 +105,6 @@ ForwardIterator unique_by_merge(ForwardIterator first, ForwardIterator last, Bin return ++dest; } -} - static void merge_keys(std::vector &merged, std::vector &pub, std::vector &sec) { merged.reserve(pub.size() + sec.size()); @@ -118,15 +118,15 @@ static void merge_keys(std::vector &merged, std::vector &pub, std::vec merged.end()); } -static QGpgMEListAllKeysJob::result_type list_keys(Context *ctx, bool mergeKeys) +static QGpgMEListAllKeysJob::result_type list_keys_legacy(Context *ctx, bool mergeKeys) { std::vector pub, sec, merged; KeyListResult r; - r.mergeWith(do_list_keys(ctx, pub, false)); + r.mergeWith(do_list_keys_legacy(ctx, pub, false)); std::sort(pub.begin(), pub.end(), ByFingerprint()); - r.mergeWith(do_list_keys(ctx, sec, true)); + r.mergeWith(do_list_keys_legacy(ctx, sec, true)); std::sort(sec.begin(), sec.end(), ByFingerprint()); if (mergeKeys) { @@ -137,6 +137,49 @@ static QGpgMEListAllKeysJob::result_type list_keys(Context *ctx, bool mergeKeys) return std::make_tuple(r, merged, sec, QString(), Error()); } +static KeyListResult do_list_keys(Context *ctx, std::vector &keys) +{ + const unsigned int keyListMode = ctx->keyListMode(); + ctx->addKeyListMode(KeyListMode::WithSecret); + + const char **pat = nullptr; + if (const Error err = ctx->startKeyListing(pat)) { + ctx->setKeyListMode(keyListMode); + return KeyListResult(nullptr, err); + } + + Error err; + do { + keys.push_back(ctx->nextKey(err)); + } while (!err); + + keys.pop_back(); + + const KeyListResult result = ctx->endKeyListing(); + ctx->setKeyListMode(keyListMode); + + ctx->cancelPendingOperation(); + return result; +} + +static QGpgMEListAllKeysJob::result_type list_keys(Context *ctx, bool mergeKeys) +{ + if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.0") { + return list_keys_legacy(ctx, mergeKeys); + } + + std::vector keys; + KeyListResult r = do_list_keys(ctx, keys); + std::sort(keys.begin(), keys.end(), ByFingerprint()); + + std::vector sec; + std::copy_if(keys.begin(), keys.end(), std::back_inserter(sec), [](const Key &key) { return key.hasSecret(); }); + + return std::make_tuple(r, keys, sec, QString(), Error()); +} + +} + Error QGpgMEListAllKeysJob::start(bool mergeKeys) { run(std::bind(&list_keys, std::placeholders::_1, mergeKeys)); diff --git a/lang/qt/tests/t-keylist.cpp b/lang/qt/tests/t-keylist.cpp index 21349c1c..5875dfb4 100644 --- a/lang/qt/tests/t-keylist.cpp +++ b/lang/qt/tests/t-keylist.cpp @@ -39,10 +39,12 @@ #include #include #include "keylistjob.h" +#include "listallkeysjob.h" #include "qgpgmebackend.h" #include "keylistresult.h" #include "context.h" +#include "engineinfo.h" #include @@ -138,6 +140,68 @@ private Q_SLOTS: QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } + + void testListAllKeysSync() + { + const auto accumulateFingerprints = [](std::vector &v, const Key &key) { v.push_back(std::string(key.primaryFingerprint())); return v; }; + + ListAllKeysJob *job = openpgp()->listAllKeysJob(/* includeSigs= */false, /* validate= */false); + std::vector pubKeys, secKeys; + GpgME::KeyListResult result = job->exec(pubKeys, secKeys, /* mergeKeys= */false); // mergeKeys is unused for GnuPG >= 2.1 + delete job; + QVERIFY(!result.error()); + + QCOMPARE(secKeys.size(), 2u); + std::vector secKeyFingerprints = std::accumulate(secKeys.begin(), secKeys.end(), std::vector(), accumulateFingerprints); + QCOMPARE(secKeyFingerprints, std::vector({ + "23FD347A419429BACCD5E72D6BC4778054ACD246", + "A0FF4590BB6122EDEF6E3C542D727CC768697734" + })); + QVERIFY(secKeys[0].hasSecret()); + if (!(GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.0")) { + QVERIFY(secKeys[0].subkeys()[0].keyGrip()); + } + + QCOMPARE(pubKeys.size(), 26u); + std::vector pubKeyFingerprints = std::accumulate(pubKeys.begin(), pubKeys.end(), std::vector(), accumulateFingerprints); + QCOMPARE(pubKeyFingerprints, std::vector({ + "045B2334ADD69FC221076841A5E67F7FA3AE3EA1", + "04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", + "0DBCAD3F08843B9557C6C4D4A94C0F75653244D6", + "1DDD28CEF714F5B03B8C246937CAB51FB79103F8", + "23FD347A419429BACCD5E72D6BC4778054ACD246", + "2686AA191A278013992C72EBBE794852BE5CF886", + "3531152DE293E26A07F504BC318C1FAEFAEF6D1B", + "38FBE1E4BF6A5E1242C8F6A13BDBEDB1777FBED3", + "3FD11083779196C2ECDD9594AD1B0FAD43C2D0C7", + "43929E89F8F79381678CAE515F6356BA6D9732AC", + "56D33268F7FE693FBB594762D4BF57F37372E243", + "5AB9D6D7BAA1C95B3BAA3D9425B00FD430CEC684", + "61EE841A2A27EB983B3B3C26413F4AF31AFDAB6C", + "6560C59C43D031C54D7C588EEBA9F240EB9DC9E6", + "6FAA9C201E5E26DCBAEC39FD5D15E01D3FF13206", + "9E91CBB11E4D4135583EF90513DB965534C6E3F1", + "A0FF4590BB6122EDEF6E3C542D727CC768697734", + "A7969DA1C3297AA96D49843F1C67EC133C661C84", + "C9C07DCC6621B9FB8D071B1D168410A48FC282E6", + "CD538D6CC9FB3D745ECDA5201FE8FC6F04259677", + "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", + "E8143C489C8D41124DC40D0B47AF4B6961F04784", + "E8D6C90B683B0982BD557A99DEF0F7B8EC67DBDE", + "ECAC774F4EEEB0620767044A58CB9A4C85A81F38", + "ED9B316F78644A58D042655A9EEF34CD4B11B25F", + "F8F1EDC73995AB739AD54B380C820C71D2699313" + })); + if (!(GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.0")) { + // with GnuPG >= 2.1 the job always lists keys with --with-keygrip and --with-secret, + // i.e. the key grips and information about secret keys are always available + QVERIFY(!pubKeys[0].hasSecret()); + QVERIFY(pubKeys[0].subkeys()[0].keyGrip()); + + QVERIFY(pubKeys[4].hasSecret()); + QVERIFY(pubKeys[4].subkeys()[0].keyGrip()); + } + } }; QTEST_MAIN(KeyListTest)