diff --git a/lang/cpp/tests/Makefile.am b/lang/cpp/tests/Makefile.am index 0b275955..9e67dff3 100644 --- a/lang/cpp/tests/Makefile.am +++ b/lang/cpp/tests/Makefile.am @@ -32,5 +32,14 @@ AM_CPPFLAGS = -I$(top_srcdir)/lang/cpp/src -I$(top_builddir)/src \ run_getkey_SOURCES = run-getkey.cpp run_keylist_SOURCES = run-keylist.cpp run_verify_SOURCES = run-verify.cpp +if !HAVE_W32_SYSTEM +run_wkdlookup_SOURCES = run-wkdlookup.cpp +endif -noinst_PROGRAMS = run-getkey run-keylist run-verify +if HAVE_W32_SYSTEM +programs_unix = +else +programs_unix = run-wkdlookup +endif + +noinst_PROGRAMS = run-getkey run-keylist run-verify $(programs_unix) diff --git a/lang/cpp/tests/run-wkdlookup.cpp b/lang/cpp/tests/run-wkdlookup.cpp new file mode 100644 index 00000000..71c4e704 --- /dev/null +++ b/lang/cpp/tests/run-wkdlookup.cpp @@ -0,0 +1,156 @@ +/* + run-wkdlookup.cpp + + This file is part of GpgMEpp's test suite. + Copyright (c) 2021 g10 Code GmbH + Software engineering by Ingo Klöcker + + GPGME++ is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + GPGME++ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with GPGME++; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "context.h" +#include "data.h" +#include "defaultassuantransaction.h" +#include "key.h" + +#include +#include +#include +#include + +using namespace GpgME; + +static int +show_usage (int ex) +{ + fputs ("usage: run-wkdlookup \n\n" + , stderr); + exit (ex); +} + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + if (argc) { + argc--; argv++; + } + + while (argc && last_argc != argc ) { + last_argc = argc; + if (!strcmp (*argv, "--")) { + argc--; argv++; + break; + } else if (!strcmp (*argv, "--help")) { + show_usage (0); + } else if (!strncmp (*argv, "--", 2)) { + show_usage (1); + } + } + + if (argc != 1) { + show_usage (1); + } + + const std::string email{*argv}; + + GpgME::initializeLibrary(); + Error err; + auto ctx = std::unique_ptr{Context::createForEngine(AssuanEngine, &err)}; + if (!ctx) { + std::cerr << "Failed to get context (Error: " << err.asString() << ")\n"; + return -1; + } + + const std::string dirmngrSocket = GpgME::dirInfo("dirmngr-socket"); + if ((err = ctx->setEngineFileName(dirmngrSocket.c_str()))) { + std::cerr << "Failed to set engine file name (Error: " << err.asString() << ")\n"; + return -1; + } + if ((err = ctx->setEngineHomeDirectory(""))) { + std::cerr << "Failed to set engine home directory (Error: " << err.asString() << ")\n"; + return -1; + } + + // try do connect to dirmngr + err = ctx->assuanTransact("GETINFO version"); + if (err && err.code() != GPG_ERR_ASS_CONNECT_FAILED) { + std::cerr << "Failed to start assuan transaction (Error: " << err.asString() << ")\n"; + return -1; + } + if (err.code() == GPG_ERR_ASS_CONNECT_FAILED) { + std::cerr << "Starting dirmngr ...\n"; + auto spawnCtx = std::unique_ptr{Context::createForEngine(SpawnEngine, &err)}; + if (!spawnCtx) { + std::cerr << "Failed to get context for spawn engine (Error: " << err.asString() << ")\n"; + return -1; + } + + const auto dirmngrProgram = GpgME::dirInfo("dirmngr-name"); + const auto homedir = GpgME::dirInfo("homedir"); + const char *argv[] = { + dirmngrProgram, + "--homedir", + homedir, + "--daemon", + NULL + }; + auto ignoreIO = Data{Data::null}; + err = spawnCtx->spawnAsync(dirmngrProgram, argv, + ignoreIO, ignoreIO, ignoreIO, + Context::SpawnDetached); + if (err) { + std::cerr << "Failed to start dirmngr (Error: " << err.asString() << ")\n"; + return -1; + } + + // wait for socket to become available + int cnt = 0; + do { + ++cnt; + std::cerr << "Waiting for dirmngr to start ...\n"; + std::this_thread::sleep_for(std::chrono::milliseconds{250 * cnt}); + err = ctx->assuanTransact("GETINFO version"); + } while (err.code() == GPG_ERR_ASS_CONNECT_FAILED && cnt < 5); + } + + const auto cmd = std::string{"WKD_GET "} + email; + err = ctx->assuanTransact(cmd.c_str()); + if (err && err.code() != GPG_ERR_NO_NAME && err.code() != GPG_ERR_NO_DATA) { + std::cerr << "Error: WKD_GET returned " << err.asString() << "\n"; + return -1; + } + + const auto transaction = std::unique_ptr(dynamic_cast(ctx->takeLastAssuanTransaction().release())); + const auto source = transaction->firstStatusLine("SOURCE"); + const auto rawData = transaction->data(); + if (rawData.size() == 0) { + std::cout << "No key found for " << email << "\n"; + } else { + const auto data = GpgME::Data{rawData.c_str(), rawData.size()}; + const auto keys = data.toKeys(GpgME::OpenPGP); + for (const auto &key : keys) { + std::cout << "Found key for " << email << " at " << source << ":\n" << key << "\n"; + } + } + + return 0; +}