diff --git a/NEWS b/NEWS
index ffd7a7e9..c42c5fa2 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,9 @@ Noteworthy changes in version 1.16.1 (unreleased)
* Detect errors during the export of secret keys. [#5766]
+ * New function gpgme_op_receive_keys to import keys from a keyserver
+ without first running a key listing. [#5808]
+
* cpp,qt: Add support for export of secret keys and secret subkeys.
[#5757]
@@ -19,6 +22,8 @@ Noteworthy changes in version 1.16.1 (unreleased)
* Interface changes relative to the 1.16.0 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ gpgme_op_receive_keys NEW.
+ gpgme_op_receive_keys_start NEW.
qt: Protocol::secretSubkeyExportJob NEW.
cpp: Context::exportSecretSubkeys NEW.
cpp: Context::startSecretSubkeyExport NEW.
diff --git a/doc/gpgme.texi b/doc/gpgme.texi
index 012afac1..9590ce88 100644
--- a/doc/gpgme.texi
+++ b/doc/gpgme.texi
@@ -5108,6 +5108,36 @@ listing mode does not match, and @code{GPG_ERR_NO_DATA} if no keys were
considered for import.
@end deftypefun
+@deftypefun gpgme_error_t gpgme_op_receive_keys (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{keyids}[]})
+@since{1.17.0}
+
+The function @code{gpgme_op_receive_keys} adds the keys described by
+the @code{NULL} terminated array @var{keyids} to the key ring of the
+crypto engine used by @var{ctx}. It is used to retrieve and import keys
+from an external source. This function currently works only for OpenPGP.
+
+After the operation completed successfully, the result can be
+retrieved with @code{gpgme_op_import_result}.
+
+The function returns the error code @code{GPG_ERR_NO_ERROR} if the
+import was completed successfully, @code{GPG_ERR_INV_VALUE} if
+@var{ctx} is not a valid pointer, and @code{GPG_ERR_NO_DATA} if no keys
+were considered for import.
+@end deftypefun
+
+@deftypefun gpgme_error_t gpgme_op_receive_keys_start (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{keyids}[]})
+@since{1.17.0}
+
+The function @code{gpgme_op_receive_keys_start} initiates a
+@code{gpgme_op_receive_keys} operation. It can be completed by calling
+@code{gpgme_wait} on the context. @xref{Waiting For Completion}.
+
+The function returns the error code @code{GPG_ERR_NO_ERROR} if the
+import was started successfully, @code{GPG_ERR_INV_VALUE} if
+@var{ctx} is not a valid pointer, and @code{GPG_ERR_NO_DATA} if no keys
+were considered for import.
+@end deftypefun
+
@deftp {Data type} {gpgme_import_status_t}
This is a pointer to a structure used to store a part of the result of
a @code{gpgme_op_import} operation. For each considered key one
diff --git a/src/engine-backend.h b/src/engine-backend.h
index d5d44a57..9b755a32 100644
--- a/src/engine-backend.h
+++ b/src/engine-backend.h
@@ -96,6 +96,7 @@ struct engine_ops
gpgme_data_t pubkey, gpgme_data_t seckey);
gpgme_error_t (*import) (void *engine, gpgme_data_t keydata,
gpgme_key_t *keyarray,
+ const char *keyids[],
const char *import_filter,
const char *key_origin);
gpgme_error_t (*keylist) (void *engine, const char *pattern,
diff --git a/src/engine-gpg.c b/src/engine-gpg.c
index f619a646..88a248d2 100644
--- a/src/engine-gpg.c
+++ b/src/engine-gpg.c
@@ -2770,19 +2770,34 @@ string_from_data (gpgme_data_t data, int delim,
static gpgme_error_t
gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray,
- const char *import_filter, const char *key_origin)
+ const char *keyids[], const char *import_filter,
+ const char *key_origin)
{
engine_gpg_t gpg = engine;
gpgme_error_t err;
int idx;
gpgme_data_encoding_t dataenc;
- if (keydata && keyarray)
+ if ((keydata && keyarray) || (keydata && keyids) || (keyarray && keyids))
return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */
dataenc = gpgme_data_get_encoding (keydata);
- if (keyarray)
+ if (keyids)
+ {
+ err = add_arg (gpg, "--recv-keys");
+ if (!err && import_filter && have_gpg_version (gpg, "2.1.14"))
+ {
+ err = add_arg (gpg, "--import-filter");
+ if (!err)
+ err = add_arg (gpg, import_filter);
+ }
+ if (!err)
+ err = add_arg (gpg, "--");
+ while (!err && *keyids && **keyids)
+ err = add_arg (gpg, *(keyids++));
+ }
+ else if (keyarray)
{
err = add_arg (gpg, "--recv-keys");
if (!err && import_filter && have_gpg_version (gpg, "2.1.14"))
diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
index 182f6a39..9ab05551 100644
--- a/src/engine-gpgsm.c
+++ b/src/engine-gpgsm.c
@@ -1709,7 +1709,8 @@ gpgsm_genkey (void *engine,
static gpgme_error_t
gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray,
- const char *import_filter, const char *key_origin)
+ const char *keyids[], const char *import_filter,
+ const char *key_origin)
{
engine_gpgsm_t gpgsm = engine;
gpgme_error_t err;
@@ -1722,6 +1723,9 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray,
if (!gpgsm)
return gpg_error (GPG_ERR_INV_VALUE);
+ if (keyids)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
if (keydata && keyarray)
return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */
diff --git a/src/engine.c b/src/engine.c
index 0b90d5b4..db594cb8 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -850,8 +850,8 @@ _gpgme_engine_op_tofu_policy (engine_t engine,
gpgme_error_t
_gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata,
- gpgme_key_t *keyarray, const char *import_filter,
- const char *key_origin)
+ gpgme_key_t *keyarray, const char *keyids[],
+ const char *import_filter, const char *key_origin)
{
if (!engine)
return gpg_error (GPG_ERR_INV_VALUE);
@@ -859,8 +859,8 @@ _gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata,
if (!engine->ops->import)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
- return (*engine->ops->import) (engine->engine, keydata, keyarray, import_filter,
- key_origin);
+ return (*engine->ops->import) (engine->engine, keydata, keyarray, keyids,
+ import_filter, key_origin);
}
diff --git a/src/engine.h b/src/engine.h
index 087f3586..8b45e13a 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -142,6 +142,7 @@ gpgme_error_t _gpgme_engine_op_tofu_policy (engine_t engine,
gpgme_error_t _gpgme_engine_op_import (engine_t engine,
gpgme_data_t keydata,
gpgme_key_t *keyarray,
+ const char *keyids[],
const char *import_filter,
const char *key_origin);
gpgme_error_t _gpgme_engine_op_keylist (engine_t engine,
diff --git a/src/gpgme.def b/src/gpgme.def
index 6644caef..d8ccd4ca 100644
--- a/src/gpgme.def
+++ b/src/gpgme.def
@@ -280,5 +280,8 @@ EXPORTS
gpgme_op_revsig @207
gpgme_op_revsig_start @208
+ gpgme_op_receive_keys @209
+ gpgme_op_receive_keys_start @210
+
; END
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index 8a9cd259..0f7c3619 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -1738,6 +1738,12 @@ gpgme_error_t gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata);
gpgme_error_t gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t keys[]);
gpgme_error_t gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t keys[]);
+/* Import the keys given by the array KEYIDS from a keyserver into the
+ * keyring. */
+gpgme_error_t gpgme_op_receive_keys_start (gpgme_ctx_t ctx,
+ const char *keyids[]);
+gpgme_error_t gpgme_op_receive_keys (gpgme_ctx_t ctx, const char *keyids[]);
+
/* Export the keys found by PATTERN into KEYDATA. */
gpgme_error_t gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern,
diff --git a/src/import.c b/src/import.c
index ae7b972a..85c459b1 100644
--- a/src/import.c
+++ b/src/import.c
@@ -334,8 +334,8 @@ _gpgme_op_import_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t keydata)
_gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
- return _gpgme_engine_op_import (ctx->engine, keydata, NULL, ctx->import_filter,
- ctx->key_origin);
+ return _gpgme_engine_op_import (ctx->engine, keydata, NULL, NULL,
+ ctx->import_filter, ctx->key_origin);
}
@@ -418,8 +418,8 @@ _gpgme_op_import_keys_start (gpgme_ctx_t ctx, int synchronous,
_gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
- return _gpgme_engine_op_import (ctx->engine, NULL, keys, ctx->import_filter,
- ctx->key_origin);
+ return _gpgme_engine_op_import (ctx->engine, NULL, keys, NULL,
+ ctx->import_filter, ctx->key_origin);
}
@@ -492,6 +492,94 @@ gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys)
}
+static gpgme_error_t
+_gpgme_op_receive_keys_start (gpgme_ctx_t ctx, int synchronous, const char *keyids[])
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return err;
+ opd->lastp = &opd->result.imports;
+
+ if (!keyids || !*keyids)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
+
+ return _gpgme_engine_op_import (ctx->engine, NULL, NULL, keyids,
+ ctx->import_filter, ctx->key_origin);
+}
+
+
+/* Asynchronous version of gpgme_op_receive_keys. */
+gpgme_error_t
+gpgme_op_receive_keys_start (gpgme_ctx_t ctx, const char *keyids[])
+{
+ gpgme_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_receive_keys_start", ctx, "");
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (_gpgme_debug_trace () && keyids)
+ {
+ int i = 0;
+
+ while (keyids[i] && *keyids[i])
+ {
+ TRACE_LOG ("keyids[%i] = %s", i, keyids[i]);
+ i++;
+ }
+ }
+
+ err = _gpgme_op_receive_keys_start (ctx, 1, keyids);
+ return TRACE_ERR (err);
+}
+
+
+/* Retrieve the keys from the array KEYIDS from a keyserver and import
+ them into the keyring.
+
+ KEYIDS is a NULL terminated array of . The result
+ is the usual import result structure. */
+gpgme_error_t
+gpgme_op_receive_keys (gpgme_ctx_t ctx, const char *keyids[])
+{
+ gpgme_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_receive_keys", ctx, "");
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (_gpgme_debug_trace () && keyids)
+ {
+ int i = 0;
+
+ while (keyids[i] && *keyids[i])
+ {
+ TRACE_LOG ("keyids[%i] = %s", i, keyids[i]);
+ i++;
+ }
+ }
+
+ err = _gpgme_op_receive_keys_start (ctx, 1, keyids);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
+
+
/* Deprecated interface. */
gpgme_error_t
gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr)
diff --git a/src/libgpgme.vers b/src/libgpgme.vers
index 8e86e4e4..86d2a5df 100644
--- a/src/libgpgme.vers
+++ b/src/libgpgme.vers
@@ -279,6 +279,9 @@ GPGME_1.0 {
gpgme_op_revsig;
gpgme_op_revsig_start;
+ gpgme_op_receive_keys;
+ gpgme_op_receive_keys_start;
+
local:
*;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 20315675..99b6fb46 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -41,7 +41,8 @@ noinst_HEADERS = run-support.h
noinst_PROGRAMS = $(TESTS) run-keylist run-export run-import run-sign \
run-verify run-encrypt run-identify run-decrypt run-genkey \
- run-keysign run-tofu run-swdb run-threaded
+ run-keysign run-tofu run-swdb run-threaded \
+ run-receive-keys
run_threaded_CPPFLAGS = -I$(top_builddir)/src @GPG_ERROR_MT_CFLAGS@
run_threaded_LDADD = ../src/libgpgme.la \
diff --git a/tests/run-receive-keys.c b/tests/run-receive-keys.c
new file mode 100644
index 00000000..6b55b815
--- /dev/null
+++ b/tests/run-receive-keys.c
@@ -0,0 +1,129 @@
+/* run-keylist.c - Helper to show a key listing.
+ * Copyright (C) 2008, 2009 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see .
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+/* We need to include config.h so that we know whether we are building
+ with large file system (LFS) support. */
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+#include
+#include
+#include
+
+#include
+
+#define PGM "run-receive-keys"
+
+#include "run-support.h"
+
+
+static int verbose;
+
+
+static int
+show_usage (int ex)
+{
+ fputs ("usage: " PGM " [options] [KEYIDs_or_FINGERPRINTs]\n\n"
+ "Options:\n"
+ " --verbose run in verbose mode\n"
+ , stderr);
+ exit (ex);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+ gpgme_error_t err;
+ gpgme_ctx_t ctx;
+ const char *keyids[100];
+ const char **keyid = NULL;
+ gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
+ gpgme_import_result_t impres;
+
+
+ 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 (!strcmp (*argv, "--verbose"))
+ {
+ verbose = 1;
+ argc--; argv++;
+ }
+ else if (!strncmp (*argv, "--", 2))
+ show_usage (1);
+ }
+
+ if (!argc)
+ show_usage (1);
+ if (argc > 99) {
+ argc = 99;
+ }
+ for (keyid = keyids; argc; argc--, argv++, keyid++) {
+ *keyid = *argv;
+ }
+ *keyid = NULL;
+
+ init_gpgme (protocol);
+
+ err = gpgme_new (&ctx);
+ fail_if_err (err);
+ gpgme_set_protocol (ctx, protocol);
+
+ err = gpgme_op_receive_keys (ctx, keyids);
+ fail_if_err (err);
+ impres = gpgme_op_import_result (ctx);
+ if (!impres)
+ {
+ fprintf (stderr, PGM ": no import result returned\n");
+ exit (1);
+ }
+ print_import_result (impres);
+
+ if (verbose)
+ {
+ gpgme_data_t log;
+ char *buf;
+ size_t len;
+
+ gpgme_data_new (&log);
+ err = gpgme_op_getauditlog (ctx, log, GPGME_AUDITLOG_DIAG);
+ fail_if_err (err);
+ buf = gpgme_data_release_and_get_mem (log, &len);
+ printf ("\nDiagnostic output:\n%.*s\n", (int)len, buf);
+ free (buf);
+ }
+
+ gpgme_release (ctx);
+ return 0;
+}