From 46943d1dce4ecfac477f1cf9fcc6cd6e379b455e Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 16 Jun 2009 11:42:21 +0000 Subject: [PATCH] Add new functions to import and export keys specified by gpgme_key_t. Allow exporting keys to a keyserver. --- trunk/NEWS | 14 +- trunk/TODO | 18 +- trunk/assuan/ChangeLog | 5 + trunk/assuan/assuan-pipe-connect.c | 4 +- trunk/doc/ChangeLog | 5 + trunk/doc/gpgme.texi | 107 ++++++++++- trunk/src/ChangeLog | 31 ++++ trunk/src/engine-backend.h | 7 +- trunk/src/engine-gpg.c | 90 ++++++--- trunk/src/engine-gpgsm.c | 40 ++-- trunk/src/engine.c | 9 +- trunk/src/engine.h | 7 +- trunk/src/export.c | 148 ++++++++++++--- trunk/src/gpgme.def | 5 + trunk/src/gpgme.h.in | 31 +++- trunk/src/import.c | 80 +++++++- trunk/src/libgpgme.vers | 5 + trunk/tests/ChangeLog | 7 +- trunk/tests/gpg/Makefile.am | 3 +- trunk/tests/gpg/pgp-export.c | 168 +++++++++++++++++ trunk/tests/gpg/pgp-keylist.c | 284 +++++++++++++++++++++++++++++ trunk/tests/gpg/t-export.c | 27 +++ 22 files changed, 1004 insertions(+), 91 deletions(-) create mode 100644 trunk/tests/gpg/pgp-export.c create mode 100644 trunk/tests/gpg/pgp-keylist.c diff --git a/trunk/NEWS b/trunk/NEWS index 8629d31..232ff53 100644 --- a/trunk/NEWS +++ b/trunk/NEWS @@ -1,4 +1,4 @@ -Noteworthy changes in version 1.1.9 +Noteworthy changes in version 1.2.0 ------------------------------------------------ * New encryption flag GPGME_ENCRYPT_NO_ENCRYPT_TO to disable default @@ -11,7 +11,6 @@ Noteworthy changes in version 1.1.9 * New functions gpgme_io_read and gpgme_io_write for use with gpgme_passphrase_cb_t and gpgme_edit_cb_t functions. - * Interface changes relative to the 1.1.7 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GPGME_KEYLIST_MODE_EPHEMERAL NEW. @@ -22,12 +21,23 @@ Noteworthy changes in version 1.1.9 gpgme_op_assuan_transact_start NEW. gpgme_op_assuan_transact NEW. gpgme_op_assuan_result NEW. + gpgme_op_import_keys NEW. + gpgme_op_import_keys_start NEW. gpgme_subkey_t EXTENDED: New fields is_cardkey, card_number. GPGME_ENCRYPT_NO_ENCRYPT_TO NEW. gpgme_check_version CHANGED: Is now a macro. gpgme_new EXTENDED: More failure codes. gpgme_io_read NEW. gpgme_io_write NEW. + gpgme_result_ref NEW. + gpgme_result_unref NEW. + gpgme_export_mode_t NEW. + gpgme_export_ext_start EXTENDED: Arg RESERVED is now a MODE flag. + gpgme_op_export EXTENDED: Arg RESERVED is now a MODE flag. + gpgme_op_export_ext_start EXTENDED: Arg RESERVED is now a MODE flag. + gpgme_op_export_ext EXTENDED: Arg RESERVED is now a MODE flag. + gpgme_op_export_keys_start NEW. + gpgme_op_export_keys NEW. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/trunk/TODO b/trunk/TODO index 9aeeb26..d84b295 100644 --- a/trunk/TODO +++ b/trunk/TODO @@ -1,4 +1,4 @@ -Hey Emacs, this is -*- outline -*- mode! +Hey Emacs, this is -*- org -*- mode! * IMPORTANT ** When using descriptor passing, we need to set the fd to blocking before @@ -24,7 +24,7 @@ Hey Emacs, this is -*- outline -*- mode! (see edit.c::command_handler). ** I/O and User Data could be made extensible. But this can be done without breaking the ABI hopefully. -* All enums that should be enums need to have a maximum value to ensure +** All enums that should be enums need to have a maximum value to ensure a certain minimum width for extensibility. ** Compatibility interfaces that can be removed in future versions: *** gpgme_data_new_from_filepart @@ -68,9 +68,15 @@ Hey Emacs, this is -*- outline -*- mode! application can then do whatever is required. There are other usages too. This notfication system should be independent of any contextes of course. + + Not sure whether this is still required. GPGME_PROTOCOL_ASSUAN is + sufficient for this. + ** --learn-code support This might be integrated with import. we still need to work out how - to learn a card when gpg and gpgsm have support for smartcards. + to learn a card when gpg and gpgsm have support for smartcards. In + GPA we currently invoke gpg directly. + ** Might need a stat() for data objects and use it for length param to gpg. ** Implement support for photo ids. ** Allow selection of subkeys @@ -82,6 +88,7 @@ Hey Emacs, this is -*- outline -*- mode! *** Allow to export secret keys. Rejected because this is conceptually flawed. Secret keys on a smart card can not be exported, for example. + May eventually e supproted with a keywrapping system. *** Selecting the key ring, setting the version or comment in output. Rejected because the naive implementation is engine specific, the configuration is part of the engine's configuration or readily @@ -110,6 +117,11 @@ Hey Emacs, this is -*- outline -*- mode! (it's an internal error, as select_protocol checks already). ** When server mode is implemented properly, more care has to be taken to release all resources on error (for example to free assuan_cmd). +** op_import_keys and op_export_keys have a limit ion the number of keys. + This is because we pass them in gpg via the command line and gpgsm + via an assuan control line. We should pipe them instead and maybe + change gpg/gpgsm to not put them in memory. + * GPG breakage: ** gpg 1.4.2 lacks error reporting if sign/encrypt with revoked key. ** gpg 1.4.2 does crappy error reporting (namely none at all) when diff --git a/trunk/assuan/ChangeLog b/trunk/assuan/ChangeLog index 8dd2ebb..617cb00 100644 --- a/trunk/assuan/ChangeLog +++ b/trunk/assuan/ChangeLog @@ -1,3 +1,8 @@ +2009-06-12 Werner Koch + + * assuan-pipe-connect.c (_gpgme_io_spawn): Change prototype. + (pipe_connect_gpgme): Pass a flags arg. + 2009-04-08 Marcus Brinkmann * assuan.h (_gpgme_io_socket): New prototype. diff --git a/trunk/assuan/assuan-pipe-connect.c b/trunk/assuan/assuan-pipe-connect.c index 7fdfe74..fcede19 100644 --- a/trunk/assuan/assuan-pipe-connect.c +++ b/trunk/assuan/assuan-pipe-connect.c @@ -51,7 +51,7 @@ struct spawn_fd_item_s int _gpgme_io_pipe (int filedes[2], int inherit_idx); -int _gpgme_io_spawn (const char *path, char *const argv[], +int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, struct spawn_fd_item_s *fd_list, pid_t *r_pid); #endif @@ -659,7 +659,7 @@ pipe_connect_gpgme (assuan_context_t *ctx, child_fds[nr].dup_to = -1; /* Start the process. */ - res = _gpgme_io_spawn (name, (char *const *) argv, child_fds, NULL); + res = _gpgme_io_spawn (name, (char *const *) argv, 0, child_fds, NULL); if (res == -1) { _assuan_log_printf ("CreateProcess failed: %s\n", strerror (errno)); diff --git a/trunk/doc/ChangeLog b/trunk/doc/ChangeLog index e1bb4a5..5138a57 100644 --- a/trunk/doc/ChangeLog +++ b/trunk/doc/ChangeLog @@ -1,3 +1,8 @@ +2009-06-16 Werner Koch + + * gpgme.texi (Exporting Keys): Document gpgme_op_export_keys. + (Importing Keys): Document gpgme_op_import_keys. + 2009-05-28 Marcus Brinkmann * gpgme.texi (Library Version Check): Document selftest error. diff --git a/trunk/doc/gpgme.texi b/trunk/doc/gpgme.texi index 871575a..a6f15d1 100644 --- a/trunk/doc/gpgme.texi +++ b/trunk/doc/gpgme.texi @@ -3318,7 +3318,25 @@ operation is started on the context. @cindex key, export @cindex key ring, export from -@deftypefun gpgme_error_t gpgme_op_export (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{pattern}}, @w{unsigned int @var{reserved}}, @w{gpgme_data_t @var{keydata}}) +Exporting keys means the same as running @command{gpg} with the command +@option{--export}. However, a mode flag can be used to change the way +the export works. The available mode flags are described below, they +may be or-ed together. + +@table @code + +@item GPGME_EXPORT_MODE_EXTERN +If this bit is set, the output is send directly to the default +keyserver. This is currently only allowed for OpenPGP keys. It is good +practise to not send more than a few dozens key to a keyserver at one +time. Using this flag requires that the @var{keydata} argument of the +export function is set to @code{NULL}. + +@end table + + + +@deftypefun gpgme_error_t gpgme_op_export (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{pattern}}, @w{gpgme_export_mode_t @var{mode}}, @w{gpgme_data_t @var{keydata}}) The function @code{gpgme_op_export} extracts public keys and returns them in the data buffer @var{keydata}. The output format of the key data returned is determined by the @acronym{ASCII} armor attribute set @@ -3329,7 +3347,7 @@ If @var{pattern} is @code{NULL}, all available keys are returned. Otherwise, @var{pattern} contains an engine specific expression that is used to limit the list to all keys matching the pattern. -@var{reserved} is reserved for future use and must be @code{0}. +@var{mode} is usually 0; other values are described above. The function returns the error code @code{GPG_ERR_NO_ERROR} if the operation completed successfully, @code{GPG_ERR_INV_VALUE} if @@ -3337,7 +3355,7 @@ operation completed successfully, @code{GPG_ERR_INV_VALUE} if errors that are reported by the crypto engine support routines. @end deftypefun -@deftypefun gpgme_error_t gpgme_op_export_start (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{pattern}}, @w{unsigned int @var{reserved}}, @w{gpgme_data_t @var{keydata}}) +@deftypefun gpgme_error_t gpgme_op_export_start (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{pattern}}, @w{gpgme_export_mode_t @var{mode}}, @w{gpgme_data_t @var{keydata}}) The function @code{gpgme_op_export_start} initiates a @code{gpgme_op_export} operation. It can be completed by calling @code{gpgme_wait} on the context. @xref{Waiting For Completion}. @@ -3347,7 +3365,7 @@ operation could be started successfully, and @code{GPG_ERR_INV_VALUE} if @var{keydata} is not a valid empty data buffer. @end deftypefun -@deftypefun gpgme_error_t gpgme_op_export_ext (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{pattern}[]}, @w{unsigned int @var{reserved}}, @w{gpgme_data_t @var{keydata}}) +@deftypefun gpgme_error_t gpgme_op_export_ext (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{pattern}[]}, @w{gpgme_export_mode_t @var{mode}}, @w{gpgme_data_t @var{keydata}}) The function @code{gpgme_op_export} extracts public keys and returns them in the data buffer @var{keydata}. The output format of the key data returned is determined by the @acronym{ASCII} armor attribute set @@ -3359,7 +3377,7 @@ are returned. Otherwise, @var{pattern} is a @code{NULL} terminated array of strings that are used to limit the list to all keys matching at least one of the patterns verbatim. -@var{reserved} is reserved for future use and must be @code{0}. +@var{mode} is usually 0; other values are described above. The function returns the error code @code{GPG_ERR_NO_ERROR} if the operation completed successfully, @code{GPG_ERR_INV_VALUE} if @@ -3367,7 +3385,7 @@ operation completed successfully, @code{GPG_ERR_INV_VALUE} if errors that are reported by the crypto engine support routines. @end deftypefun -@deftypefun gpgme_error_t gpgme_op_export_ext_start (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{pattern}[]}, @w{unsigned int @var{reserved}}, @w{gpgme_data_t @var{keydata}}) +@deftypefun gpgme_error_t gpgme_op_export_ext_start (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{pattern}[]}, @w{gpgme_export_mode_t @var{mode}}, @w{gpgme_data_t @var{keydata}}) The function @code{gpgme_op_export_ext_start} initiates a @code{gpgme_op_export_ext} operation. It can be completed by calling @code{gpgme_wait} on the context. @xref{Waiting For Completion}. @@ -3378,11 +3396,50 @@ if @var{keydata} is not a valid empty data buffer. @end deftypefun +@deftypefun gpgme_error_t gpgme_op_export_keys (@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_key_t keys[]}, @w{gpgme_export_mode_t @var{mode}}, @w{gpgme_data_t @var{keydata}}) +The function @code{gpgme_op_export_keys} extracts public keys and returns +them in the data buffer @var{keydata}. The output format of the key +data returned is determined by the @acronym{ASCII} armor attribute set +for the context @var{ctx}, or, if that is not set, by the encoding +specified for @var{keydata}. + +The keys to export are taken form the @code{NULL} terminated array +@var{keys}. Only keys of the the currently selected protocol of +@var{ctx} which do have a fingerprint set are considered for export. +Other keys specified by the @var{keys} are ignored. In particular +OpenPGP keys retrieved via an external key listing are not included. + +@var{mode} is usually 0; other values are described above. + +The function returns the error code @code{GPG_ERR_NO_ERROR} if the +operation completed successfully, @code{GPG_ERR_INV_VALUE} if +@var{keydata} is not a valid empty data buffer, @code{GPG_ERR_NO_DATA} +if no useful keys are in @var{keys} and passes through any errors that +are reported by the crypto engine support routines. +@end deftypefun + +@deftypefun gpgme_error_t gpgme_op_export_keys_start (@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_key_t @var{keys}[]}, @w{gpgme_export_mode_t @var{mode}}, @w{gpgme_data_t @var{keydata}}) +The function @code{gpgme_op_export_keys_start} initiates a +@code{gpgme_op_export_ext} 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 +operation could be started successfully, and @code{GPG_ERR_INV_VALUE} +if @var{keydata} is not a valid empty data buffer, @code{GPG_ERR_NO_DATA} +if no useful keys are in @var{keys} and passes through any errors that +are reported by the crypto engine support routines. +@end deftypefun + + @node Importing Keys @subsection Importing Keys @cindex key, import @cindex key ring, import to +Importing keys means the same as running @command{gpg} with the command +@option{--import}. + + @deftypefun gpgme_error_t gpgme_op_import (@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_data_t @var{keydata}}) The function @code{gpgme_op_import} adds the keys in the data buffer @var{keydata} to the key ring of the crypto engine used by @var{ctx}. @@ -3409,6 +3466,44 @@ import could be started successfully, @code{GPG_ERR_INV_VALUE} if and @code{GPG_ERR_NO_DATA} if @var{keydata} is an empty data buffer. @end deftypefun +@deftypefun gpgme_error_t gpgme_op_import_keys (@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_key_t *@var{keys}}) +The function @code{gpgme_op_import_keys} adds the keys described by the +@code{NULL} terminated array @var{keys} to the key ring of the crypto +engine used by @var{ctx}. This function is the general interface to +move a key from one crypto engine to another as long as they are +compatible. In particular it is used to actually import and make keys +permanent which have been retrieved from an external source (i.e. using +@code{GPGME_KEYLIST_MODE_EXTERN}). @footnote{Thus it is a replacement +for the usual workaround of exporting and then importing a key to make +an X.509 key permanent.} + +Only keys of the the currently selected protocol of @var{ctx} are +considered for import. Other keys specified by the @var{keys} are +ignored. As of now all considered keys must have been retrieved using +the same method, that is the used key listing mode must be identical. + +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{keydata} if @var{ctx} or @var{keydata} is not a valid pointer, +@code{GPG_ERR_CONFLICT} if the key listing mode does not match, and +@code{GPG_ERR_NO_DATA} if no keys are considered for export. +@end deftypefun + +@deftypefun gpgme_error_t gpgme_op_import_keys_start (@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_key_t *@var{keys}}) +The function @code{gpgme_op_import_keys_start} initiates a +@code{gpgme_op_import_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 completed successfully, @code{GPG_ERR_INV_VALUE} if +@var{keydata} if @var{ctx} or @var{keydata} is not a valid pointer, +@code{GPG_ERR_CONFLICT} if the key listing mode does not match, and +@code{GPG_ERR_NO_DATA} if no keys are considered for export. +@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/trunk/src/ChangeLog b/trunk/src/ChangeLog index 35622ee..dc1e116 100644 --- a/trunk/src/ChangeLog +++ b/trunk/src/ChangeLog @@ -1,3 +1,34 @@ +2009-06-16 Werner Koch + + * gpgme.h.in (gpgme_op_export_keys_start, gpgme_op_export_keys): New. + * gpgme.def, libgpgme.vers: Add them. + * export.c (gpgme_op_export_keys_start, gpgme_op_export_keys): New. + (export_keys_start): New. + + * gpgme.h.in (gpgme_export_mode_t, GPGME_EXPORT_MODE_EXTERN): New. + (gpgme_op_export_start, gpgme_op_export, gpgme_op_export_ext_start) + (gpgme_op_export_ext): Change arg RESERVED to MODE of new + compatible type. + * export.c (gpgme_export_ext_start, gpgme_op_export) + (gpgme_op_export_ext_start, gpgme_op_export_ext): Ditto. + (export_start): Ditto. + * engine.c (_gpgme_engine_op_export): Ditto. + * engine-backend.h (struct engine_ops): Ditto. + * engine-gpgsm.c (gpgsm_export, gpgsm_export_ext): Ditto. + * engine-gpg.c (gpg_export, gpg_export_ext): Ditto. Implement + mode EXTERN. + (gpg_export, gpg_export_ext): Factor common code out to .. + (export_common): .. this. + + * gpgme.h.in (gpgme_op_import_keys_start, gpgme_op_import_keys): New. + * gpgme.def, libgpgme.vers: Add them. + * import.c (gpgme_op_import_keys_start, gpgme_op_import_keys): New. + (_gpgme_op_import_keys_start): New. + * engine.c (_gpgme_engine_op_import): Add arg KEYARRAY. + * engine-backend.h (struct engine_ops): Ditto. + * engine-gpgsm.c (gpgsm_import): Ditto. Not functional. + * engine-gpg.c (gpg_import): Ditto. Implement it. + 2009-06-15 Marcus Brinkmann * gpgme.h.in (gpgme_result_ref, gpgme_result_unref): Add diff --git a/trunk/src/engine-backend.h b/trunk/src/engine-backend.h index d656d9d..1fe600d 100644 --- a/trunk/src/engine-backend.h +++ b/trunk/src/engine-backend.h @@ -77,14 +77,15 @@ struct engine_ops gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t (*export) (void *engine, const char *pattern, - unsigned int reserved, gpgme_data_t keydata, + gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor); gpgme_error_t (*export_ext) (void *engine, const char *pattern[], - unsigned int reserved, gpgme_data_t keydata, + gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor); gpgme_error_t (*genkey) (void *engine, gpgme_data_t help_data, int use_armor, gpgme_data_t pubkey, gpgme_data_t seckey); - gpgme_error_t (*import) (void *engine, gpgme_data_t keydata); + gpgme_error_t (*import) (void *engine, gpgme_data_t keydata, + gpgme_key_t *keyarray); gpgme_error_t (*keylist) (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode); gpgme_error_t (*keylist_ext) (void *engine, const char *pattern[], diff --git a/trunk/src/engine-gpg.c b/trunk/src/engine-gpg.c index eec3fa6..3c06edf 100644 --- a/trunk/src/engine-gpg.c +++ b/trunk/src/engine-gpg.c @@ -678,7 +678,7 @@ command_handler (void *opaque, int fd) /* The Fnc will be called to get a value for one of the commands with - a key KEY. If the Code pssed to FNC is 0, the function may release + a key KEY. If the Code passed to FNC is 0, the function may release resources associated with the returned value from another call. To match such a second call to a first call, the returned value from the first call is passed as keyword. */ @@ -1704,23 +1704,42 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], static gpgme_error_t -gpg_export (void *engine, const char *pattern, unsigned int reserved, - gpgme_data_t keydata, int use_armor) +export_common (engine_gpg_t gpg, gpgme_export_mode_t mode, + gpgme_data_t keydata, int use_armor) { - engine_gpg_t gpg = engine; gpgme_error_t err; - if (reserved) - return gpg_error (GPG_ERR_INV_VALUE); + if ((mode & ~GPGME_EXPORT_MODE_EXTERN)) + return gpg_error (GPG_ERR_NOT_SUPPORTED); - err = add_arg (gpg, "--export"); - if (!err && use_armor) - err = add_arg (gpg, "--armor"); - if (!err) - err = add_data (gpg, keydata, 1, 1); + if ((mode & GPGME_EXPORT_MODE_EXTERN)) + { + err = add_arg (gpg, "--send-keys"); + } + else + { + err = add_arg (gpg, "--export"); + if (!err && use_armor) + err = add_arg (gpg, "--armor"); + if (!err) + err = add_data (gpg, keydata, 1, 1); + } if (!err) err = add_arg (gpg, "--"); + return err; +} + + +static gpgme_error_t +gpg_export (void *engine, const char *pattern, gpgme_export_mode_t mode, + gpgme_data_t keydata, int use_armor) +{ + engine_gpg_t gpg = engine; + gpgme_error_t err; + + err = export_common (gpg, mode, keydata, use_armor); + if (!err && pattern && *pattern) err = add_arg (gpg, pattern); @@ -1732,22 +1751,13 @@ gpg_export (void *engine, const char *pattern, unsigned int reserved, static gpgme_error_t -gpg_export_ext (void *engine, const char *pattern[], unsigned int reserved, +gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err; - if (reserved) - return gpg_error (GPG_ERR_INV_VALUE); - - err = add_arg (gpg, "--export"); - if (!err && use_armor) - err = add_arg (gpg, "--armor"); - if (!err) - err = add_data (gpg, keydata, 1, 1); - if (!err) - err = add_arg (gpg, "--"); + err = export_common (gpg, mode, keydata, use_armor); if (pattern) { @@ -1795,16 +1805,40 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor, static gpgme_error_t -gpg_import (void *engine, gpgme_data_t keydata) +gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { engine_gpg_t gpg = engine; gpgme_error_t err; + int idx; - err = add_arg (gpg, "--import"); - if (!err) - err = add_arg (gpg, "--"); - if (!err) - err = add_data (gpg, keydata, -1, 0); + if (keydata && keyarray) + gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */ + + if (keyarray) + { + err = add_arg (gpg, "--recv-keys"); + if (!err) + err = add_arg (gpg, "--"); + for (idx=0; !err && keyarray[idx]; idx++) + { + if (keyarray[idx]->protocol != GPGME_PROTOCOL_OpenPGP) + ; + else if (!keyarray[idx]->subkeys) + ; + else if (keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) + err = add_arg (gpg, keyarray[idx]->subkeys->fpr); + else if (*keyarray[idx]->subkeys->keyid) + err = add_arg (gpg, keyarray[idx]->subkeys->keyid); + } + } + else + { + err = add_arg (gpg, "--import"); + if (!err) + err = add_arg (gpg, "--"); + if (!err) + err = add_data (gpg, keydata, -1, 0); + } if (!err) err = start (gpg); diff --git a/trunk/src/engine-gpgsm.c b/trunk/src/engine-gpgsm.c index 2d92732..71ef221 100644 --- a/trunk/src/engine-gpgsm.c +++ b/trunk/src/engine-gpgsm.c @@ -1379,15 +1379,18 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, static gpgme_error_t -gpgsm_export (void *engine, const char *pattern, unsigned int reserved, +gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; char *cmd; - if (!gpgsm || reserved) + if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); + + if (mode) + return gpg_error (GPG_ERR_NOT_SUPPORTED); if (!pattern) pattern = ""; @@ -1414,7 +1417,7 @@ gpgsm_export (void *engine, const char *pattern, unsigned int reserved, static gpgme_error_t -gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved, +gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpgsm_t gpgsm = engine; @@ -1424,9 +1427,12 @@ gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved, int length = 7 + 1; char *linep; - if (!gpgsm || reserved) + if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); + if (mode) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + if (pattern && *pattern) { const char **pat = pattern; @@ -1534,7 +1540,7 @@ gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor, static gpgme_error_t -gpgsm_import (void *engine, gpgme_data_t keydata) +gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; @@ -1542,13 +1548,23 @@ gpgsm_import (void *engine, gpgme_data_t keydata) if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); - gpgsm->input_cb.data = keydata; - err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); - if (err) - return err; - gpgsm_clear_fd (gpgsm, OUTPUT_FD); - gpgsm_clear_fd (gpgsm, MESSAGE_FD); - gpgsm->inline_data = NULL; + if (keydata && keyarray) + gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */ + + if (keyarray) + { + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + else + { + gpgsm->input_cb.data = keydata; + err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); + if (err) + return err; + gpgsm_clear_fd (gpgsm, OUTPUT_FD); + gpgsm_clear_fd (gpgsm, MESSAGE_FD); + gpgsm->inline_data = NULL; + } err = start (gpgsm, "IMPORT"); return err; diff --git a/trunk/src/engine.c b/trunk/src/engine.c index 87d3939..c8ff012 100644 --- a/trunk/src/engine.c +++ b/trunk/src/engine.c @@ -620,7 +620,7 @@ _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[], gpgme_error_t _gpgme_engine_op_export (engine_t engine, const char *pattern, - unsigned int reserved, gpgme_data_t keydata, + gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { if (!engine) @@ -629,7 +629,7 @@ _gpgme_engine_op_export (engine_t engine, const char *pattern, if (!engine->ops->export) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); - return (*engine->ops->export) (engine->engine, pattern, reserved, + return (*engine->ops->export) (engine->engine, pattern, mode, keydata, use_armor); } @@ -667,7 +667,8 @@ _gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data, gpgme_error_t -_gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata) +_gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata, + gpgme_key_t *keyarray) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); @@ -675,7 +676,7 @@ _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); + return (*engine->ops->import) (engine->engine, keydata, keyarray); } diff --git a/trunk/src/engine.h b/trunk/src/engine.h index a043b3e..ca746c8 100644 --- a/trunk/src/engine.h +++ b/trunk/src/engine.h @@ -93,11 +93,11 @@ gpgme_error_t _gpgme_engine_op_encrypt_sign (engine_t engine, int use_armor, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t _gpgme_engine_op_export (engine_t engine, const char *pattern, - unsigned int reserved, + gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor); gpgme_error_t _gpgme_engine_op_export_ext (engine_t engine, const char *pattern[], - unsigned int reserved, + gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor); gpgme_error_t _gpgme_engine_op_genkey (engine_t engine, @@ -105,7 +105,8 @@ gpgme_error_t _gpgme_engine_op_genkey (engine_t engine, int use_armor, gpgme_data_t pubkey, gpgme_data_t seckey); gpgme_error_t _gpgme_engine_op_import (engine_t engine, - gpgme_data_t keydata); + gpgme_data_t keydata, + gpgme_key_t *keyarray); gpgme_error_t _gpgme_engine_op_keylist (engine_t engine, const char *pattern, int secret_only, diff --git a/trunk/src/export.c b/trunk/src/export.c index 079a7e0..1e29439 100644 --- a/trunk/src/export.c +++ b/trunk/src/export.c @@ -22,6 +22,8 @@ #if HAVE_CONFIG_H #include #endif +#include +#include #include "gpgme.h" #include "context.h" @@ -37,12 +39,24 @@ export_status_handler (void *priv, gpgme_status_code_t code, char *args) static gpgme_error_t export_start (gpgme_ctx_t ctx, int synchronous, const char *pattern, - unsigned int reserved, gpgme_data_t keydata) + gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; - if (!keydata) - return gpg_error (GPG_ERR_INV_VALUE); + if ((mode & ~(GPGME_EXPORT_MODE_EXTERN))) + return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */ + + + if ((mode & GPGME_EXPORT_MODE_EXTERN)) + { + if (keydata) + return gpg_error (GPG_ERR_INV_VALUE); + } + else + { + if (!keydata) + return gpg_error (GPG_ERR_INV_VALUE); + } err = _gpgme_op_reset (ctx, synchronous); if (err) @@ -50,26 +64,26 @@ export_start (gpgme_ctx_t ctx, int synchronous, const char *pattern, _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx); - return _gpgme_engine_op_export (ctx->engine, pattern, reserved, keydata, + return _gpgme_engine_op_export (ctx->engine, pattern, mode, keydata, ctx->use_armor); } -/* Export the keys listed in RECP into KEYDATA. */ +/* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern, - unsigned int reserved, gpgme_data_t keydata) + gpgme_export_mode_t mode, gpgme_data_t keydata) { - return export_start (ctx, 0, pattern, reserved, keydata); + return export_start (ctx, 0, pattern, mode, keydata); } -/* Export the keys listed in RECP into KEYDATA. */ +/* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t -gpgme_op_export (gpgme_ctx_t ctx, const char *pattern, unsigned int reserved, - gpgme_data_t keydata) +gpgme_op_export (gpgme_ctx_t ctx, const char *pattern, + gpgme_export_mode_t mode, gpgme_data_t keydata) { - gpgme_error_t err = export_start (ctx, 1, pattern, reserved, keydata); + gpgme_error_t err = export_start (ctx, 1, pattern, mode, keydata); if (!err) err = _gpgme_wait_one (ctx); return err; @@ -78,12 +92,23 @@ gpgme_op_export (gpgme_ctx_t ctx, const char *pattern, unsigned int reserved, static gpgme_error_t export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[], - unsigned int reserved, gpgme_data_t keydata) + gpgme_export_mode_t mode, gpgme_data_t keydata) { gpgme_error_t err; - if (!keydata) - return gpg_error (GPG_ERR_INV_VALUE); + if ((mode & ~(GPGME_EXPORT_MODE_EXTERN))) + return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */ + + if ((mode & GPGME_EXPORT_MODE_EXTERN)) + { + if (keydata) + return gpg_error (GPG_ERR_INV_VALUE); + } + else + { + if (!keydata) + return gpg_error (GPG_ERR_INV_VALUE); + } err = _gpgme_op_reset (ctx, synchronous); if (err) @@ -91,27 +116,108 @@ export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[], _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx); - return _gpgme_engine_op_export_ext (ctx->engine, pattern, reserved, keydata, + return _gpgme_engine_op_export_ext (ctx->engine, pattern, mode, keydata, ctx->use_armor); } -/* Export the keys listed in RECP into KEYDATA. */ +/* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_ext_start (gpgme_ctx_t ctx, const char *pattern[], - unsigned int reserved, gpgme_data_t keydata) + gpgme_export_mode_t mode, gpgme_data_t keydata) { - return export_ext_start (ctx, 0, pattern, reserved, keydata); + return export_ext_start (ctx, 0, pattern, mode, keydata); } -/* Export the keys listed in RECP into KEYDATA. */ +/* Export the keys listed in PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[], - unsigned int reserved, gpgme_data_t keydata) + gpgme_export_mode_t mode, gpgme_data_t keydata) +{ + gpgme_error_t err = export_ext_start (ctx, 1, pattern, mode, keydata); + if (!err) + err = _gpgme_wait_one (ctx); + return err; +} + + + + + +static gpgme_error_t +export_keys_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t keys[], + gpgme_export_mode_t mode, gpgme_data_t keydata) +{ + gpgme_error_t err; + int nkeys, idx; + char **pattern; + + if (!keys) + return gpg_error (GPG_ERR_INV_VALUE); + + /* Create a list of pattern from the keys. */ + for (idx=nkeys=0; keys[idx]; idx++) + if (keys[idx]->protocol == ctx->protocol) + nkeys++; + if (!nkeys) + return gpg_error (GPG_ERR_NO_DATA); + + pattern = calloc (nkeys+1, sizeof *pattern); + if (!pattern) + return gpg_error_from_syserror (); + + for (idx=nkeys=0; keys[idx]; idx++) + if (keys[idx]->protocol == ctx->protocol + && keys[idx]->subkeys + && keys[idx]->subkeys->fpr + && *keys[idx]->subkeys->fpr) + { + pattern[nkeys] = strdup (keys[idx]->subkeys->fpr); + if (!pattern[nkeys]) + { + err = gpg_error_from_syserror (); + goto leave; + } + nkeys++; + } + + + /* Pass on to the regular function. */ + err = export_ext_start (ctx, synchronous, (const char**)pattern, + mode, keydata); + + leave: + for (idx=0; pattern[idx]; idx++) + free (pattern[idx]); + free (pattern); + + return err; +} + + +/* Export the keys from the array KEYS into KEYDATA. Only keys of the + current protocol are exported and only those which have a + fingerprint set; that is keys received with some external search + methods are silently skipped. */ +gpgme_error_t +gpgme_op_export_keys_start (gpgme_ctx_t ctx, + gpgme_key_t keys[], + gpgme_export_mode_t mode, + gpgme_data_t keydata) { - gpgme_error_t err = export_ext_start (ctx, 1, pattern, reserved, keydata); + return export_keys_start (ctx, 0, keys, mode, keydata); +} + +gpgme_error_t +gpgme_op_export_keys (gpgme_ctx_t ctx, + gpgme_key_t keys[], + gpgme_export_mode_t mode, + gpgme_data_t keydata) +{ + gpgme_error_t err = export_keys_start (ctx, 1, keys, mode, keydata); if (!err) err = _gpgme_wait_one (ctx); return err; } + diff --git a/trunk/src/gpgme.def b/trunk/src/gpgme.def index 9bc95d0..ac0fe9a 100644 --- a/trunk/src/gpgme.def +++ b/trunk/src/gpgme.def @@ -180,5 +180,10 @@ EXPORTS gpgme_release_ref @138 gpgme_release_unref @139 + gpgme_op_import_keys @140 + gpgme_op_import_keys_start @141 + gpgme_op_export_keys @142 + gpgme_op_export_keys_start @143 + ; END diff --git a/trunk/src/gpgme.h.in b/trunk/src/gpgme.h.in index 8e224a4..ff9bba0 100644 --- a/trunk/src/gpgme.h.in +++ b/trunk/src/gpgme.h.in @@ -328,6 +328,12 @@ gpgme_protocol_t; typedef unsigned int gpgme_keylist_mode_t; + +/* The available export mode flags. */ +#define GPGME_EXPORT_MODE_EXTERN 2 + +typedef unsigned int gpgme_export_mode_t; + /* Flags for the audit log functions. */ #define GPGME_AUDITLOG_HTML 1 @@ -1497,22 +1503,39 @@ gpgme_error_t gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata); gpgme_error_t gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr) _GPGME_DEPRECATED; +/* Import the keys from the array KEYS into the keyring. */ +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[]); + + /* Export the keys found by PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern, - unsigned int reserved, + gpgme_export_mode_t mode, gpgme_data_t keydata); gpgme_error_t gpgme_op_export (gpgme_ctx_t ctx, const char *pattern, - unsigned int reserved, gpgme_data_t keydata); + gpgme_export_mode_t mode, + gpgme_data_t keydata); gpgme_error_t gpgme_op_export_ext_start (gpgme_ctx_t ctx, const char *pattern[], - unsigned int reserved, + gpgme_export_mode_t mode, gpgme_data_t keydata); gpgme_error_t gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[], - unsigned int reserved, + gpgme_export_mode_t mode, gpgme_data_t keydata); +/* Export the keys from the array KEYS into KEYDATA. */ +gpgme_error_t gpgme_op_export_keys_start (gpgme_ctx_t ctx, + gpgme_key_t keys[], + gpgme_export_mode_t mode, + gpgme_data_t keydata); +gpgme_error_t gpgme_op_export_keys (gpgme_ctx_t ctx, + gpgme_key_t keys[], + gpgme_export_mode_t mode, + gpgme_data_t keydata); + + /* Key generation. */ struct _gpgme_op_genkey_result diff --git a/trunk/src/import.c b/trunk/src/import.c index ad6b776..8212a25 100644 --- a/trunk/src/import.c +++ b/trunk/src/import.c @@ -238,7 +238,7 @@ _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); + return _gpgme_engine_op_import (ctx->engine, keydata, NULL); } @@ -260,6 +260,84 @@ gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata) } + +static gpgme_error_t +_gpgme_op_import_keys_start (gpgme_ctx_t ctx, int synchronous, + gpgme_key_t *keys) +{ + gpgme_error_t err; + void *hook; + op_data_t opd; + int idx, firstidx, nkeys; + + 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 (!keys) + return gpg_error (GPG_ERR_NO_DATA); + + for (idx=nkeys=0, firstidx=-1; keys[idx]; idx++) + { + /* We only consider keys of the current protocol. */ + if (keys[idx]->protocol != ctx->protocol) + continue; + if (firstidx == -1) + firstidx = idx; + /* If a key has been found using a different key listing mode, + we bail out. This makes the processing easier. Fixme: To + allow a mix of keys we would need to sort them by key listing + mode and start two import operations one after the other. */ + if (keys[idx]->keylist_mode != keys[firstidx]->keylist_mode) + return gpg_error (GPG_ERR_CONFLICT); + nkeys++; + } + if (!nkeys) + 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, keys); +} + + +/* Asynchronous version of gpgme_op_import_key. */ +gpgme_error_t +gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys) +{ + return _gpgme_op_import_keys_start (ctx, 0, keys); +} + + +/* Import the keys from the array KEYS into the keyring. This + function allows to move a key from one engine to another as long as + they are compatible. In particular it is used to actually import + keys retrieved from an external source (i.e. using + GPGME_KEYLIST_MODE_EXTERN). It replaces the old workaround of + exporting and then importing a key as used to make an X.509 key + permanent. This function automagically does the right thing. + + KEYS is a NULL terminated array of gpgme key objects. The result + is the usual import result structure. Only keys matching the + current protocol are imported; other keys are ignored. */ +gpgme_error_t +gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys) +{ + gpgme_error_t err = _gpgme_op_import_keys_start (ctx, 1, keys); + if (!err) + err = _gpgme_wait_one (ctx); + return err; +} + + +/* Deprecated interface. */ gpgme_error_t gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr) { diff --git a/trunk/src/libgpgme.vers b/trunk/src/libgpgme.vers index 44f6176..41fa1be 100644 --- a/trunk/src/libgpgme.vers +++ b/trunk/src/libgpgme.vers @@ -59,6 +59,11 @@ GPGME_1.1 { gpgme_result_ref; gpgme_result_unref; + + gpgme_op_import_keys; + gpgme_op_import_keys_start; + gpgme_op_export_keys; + gpgme_op_export_keys_start; }; diff --git a/trunk/tests/ChangeLog b/trunk/tests/ChangeLog index a727a89..c9ab133 100644 --- a/trunk/tests/ChangeLog +++ b/trunk/tests/ChangeLog @@ -1,3 +1,7 @@ +2009-06-16 Werner Koch + + * gpg/pgp-export.c, gpg/pgp-keylist.c: New. + 2009-06-09 Werner Koch * gpg/Makefile.am (./pubring.gpg): Ignore errors in case of @@ -16,7 +20,8 @@ * gpg/mkdemodirs: Renamed to ... * gpg/mkdemodirs.in: ... here. - * gpg/mkdemodirs.in (GPG): Derive value from @GPG@ instead of hard-coding "gpg". + * gpg/mkdemodirs.in (GPG): Derive value from @GPG@ instead of + hard-coding "gpg". 2009-02-24 Werner Koch diff --git a/trunk/tests/gpg/Makefile.am b/trunk/tests/gpg/Makefile.am index 5da0bbb..fdb023c 100644 --- a/trunk/tests/gpg/Makefile.am +++ b/trunk/tests/gpg/Makefile.am @@ -50,7 +50,8 @@ LDADD = ../../src/libgpgme.la t_thread1_LDADD = ../../src/libgpgme-pthread.la # We don't run t-genkey in the test suite, because it takes too long -noinst_PROGRAMS = $(TESTS) t-genkey +# The other programs are used for debugging. +noinst_PROGRAMS = $(TESTS) t-genkey pgp-keylist pgp-export mkdemodirs: mkdemodirs.in Makefile sed -e 's,[@]GPG[@],$(GPG),g' < $(srcdir)/mkdemodirs.in > mkdemodirs diff --git a/trunk/tests/gpg/pgp-export.c b/trunk/tests/gpg/pgp-export.c new file mode 100644 index 0000000..9911c18 --- /dev/null +++ b/trunk/tests/gpg/pgp-export.c @@ -0,0 +1,168 @@ +/* pgp-export.c - Helper to run an export command + 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 . +*/ + +/* 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 + +#define PGM "pgp-export" + +#include "t-support.h" + + +static int verbose; + + +static const char * +nonnull (const char *s) +{ + return s? s :"[none]"; +} + + +static int +show_usage (int ex) +{ + fputs ("usage: " PGM " [options] USERIDS\n\n" + "Options:\n" + " --verbose run in verbose mode\n" + " --extern send keys to the keyserver (TAKE CARE!)\n" + , stderr); + exit (ex); +} + +int +main (int argc, char **argv) +{ + int last_argc = -1; + gpgme_error_t err; + gpgme_ctx_t ctx; + gpgme_key_t key; + gpgme_keylist_result_t result; + gpgme_key_t keyarray[100]; + int keyidx = 0; + gpgme_data_t out; + gpgme_export_mode_t mode = 0; + + 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 (!strcmp (*argv, "--extern")) + { + mode |= GPGME_KEYLIST_MODE_EXTERN; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + show_usage (1); + + } + + if (!argc) + show_usage (1); + + init_gpgme (GPGME_PROTOCOL_OpenPGP); + + err = gpgme_new (&ctx); + fail_if_err (err); + gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP); + + /* Lookup the keys. */ + err = gpgme_op_keylist_ext_start (ctx, (const char**)argv, 0, 0); + fail_if_err (err); + + while (!(err = gpgme_op_keylist_next (ctx, &key))) + { + printf ("keyid: %s (fpr: %s)\n", + key->subkeys?nonnull (key->subkeys->keyid):"?", + key->subkeys?nonnull (key->subkeys->fpr):"?"); + + if (keyidx < DIM (keyarray)-1) + keyarray[keyidx++] = key; + else + { + fprintf (stderr, PGM": too many keys" + "- skipping this key\n"); + gpgme_key_unref (key); + } + } + if (gpg_err_code (err) != GPG_ERR_EOF) + fail_if_err (err); + err = gpgme_op_keylist_end (ctx); + fail_if_err (err); + keyarray[keyidx] = NULL; + + result = gpgme_op_keylist_result (ctx); + if (result->truncated) + { + fprintf (stderr, PGM ": key listing unexpectedly truncated\n"); + exit (1); + } + + /* Now for the actual export. */ + if ((mode & GPGME_KEYLIST_MODE_EXTERN)) + printf ("sending keys to keyserver\n"); + + err = gpgme_data_new (&out); + fail_if_err (err); + + gpgme_set_armor (ctx, 1); + err = gpgme_op_export_keys (ctx, keyarray, mode, + (mode & GPGME_KEYLIST_MODE_EXTERN)? NULL:out); + fail_if_err (err); + + fflush (NULL); + if (!(mode & GPGME_KEYLIST_MODE_EXTERN)) + { + fputs ("Begin Result:\n", stdout); + print_data (out); + fputs ("End Result.\n", stdout); + } + + /* Cleanup. */ + gpgme_data_release (out); + + for (keyidx=0; keyarray[keyidx]; keyidx++) + gpgme_key_unref (keyarray[keyidx]); + + gpgme_release (ctx); + return 0; +} diff --git a/trunk/tests/gpg/pgp-keylist.c b/trunk/tests/gpg/pgp-keylist.c new file mode 100644 index 0000000..2645f48 --- /dev/null +++ b/trunk/tests/gpg/pgp-keylist.c @@ -0,0 +1,284 @@ +/* pgp-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 . +*/ + +/* 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 + +#define PGM "pgp-keylist" + +#include "t-support.h" + + +static int verbose; + + +static const char * +nonnull (const char *s) +{ + return s? s :"[none]"; +} + + +static void +print_import_result (gpgme_import_result_t r) +{ + gpgme_import_status_t st; + + printf ("key import results:\n" + " considered: %d\n" + " no user id: %d\n" + " imported: %d\n" + " imported_rsa: %d\n" + " unchanged: %d\n" + " new user ids: %d\n" + " new subkeys: %d\n" + " new signatures: %d\n" + " new revocations: %d\n" + " secret read: %d\n" + " secret imported: %d\n" + " secret unchanged: %d\n" + " skipped new keys: %d\n" + " not imported: %d\n", + r->considered, + r->no_user_id, + r->imported, + r->imported_rsa, + r->unchanged, + r->new_user_ids, + r->new_sub_keys, + r->new_signatures, + r->new_revocations, + r->secret_read, + r->secret_imported, + r->secret_unchanged, + r->skipped_new_keys, + r->not_imported); + + for (st=r->imports; st; st = st->next) + { + printf (" fpr: %s err: %d (%s) status:", nonnull (st->fpr), + st->result, gpg_strerror (st->result)); + if (st->status & GPGME_IMPORT_NEW) + fputs (" new", stdout); + if (st->status & GPGME_IMPORT_UID) + fputs (" uid", stdout); + if (st->status & GPGME_IMPORT_SIG) + fputs (" sig", stdout); + if (st->status & GPGME_IMPORT_SUBKEY) + fputs (" subkey", stdout); + if (st->status & GPGME_IMPORT_SECRET) + fputs (" secret", stdout); + putchar ('\n'); + } +} + + +static int +show_usage (int ex) +{ + fputs ("usage: " PGM " [options] [USERID]\n\n" + "Options:\n" + " --verbose run in verbose mode\n" + " --local use GPGME_KEYLIST_MODE_LOCAL\n" + " --extern use GPGME_KEYLIST_MODE_EXTERN\n" + " --sigs use GPGME_KEYLIST_MODE_SIGS\n" + " --sig-notations use GPGME_KEYLIST_MODE_SIG_NOTATIONS\n" + " --ephemeral use GPGME_KEYLIST_MODE_EPHEMERAL\n" + " --validate use GPGME_KEYLIST_MODE_VALIDATE\n" + " --import import all keys\n" + , stderr); + exit (ex); +} + +int +main (int argc, char **argv) +{ + int last_argc = -1; + gpgme_error_t err; + gpgme_ctx_t ctx; + gpgme_keylist_mode_t mode = 0; + gpgme_key_t key; + gpgme_keylist_result_t result; + int import = 0; + gpgme_key_t keyarray[100]; + int keyidx = 0; + + 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 (!strcmp (*argv, "--local")) + { + mode |= GPGME_KEYLIST_MODE_LOCAL; + argc--; argv++; + } + else if (!strcmp (*argv, "--extern")) + { + mode |= GPGME_KEYLIST_MODE_EXTERN; + argc--; argv++; + } + else if (!strcmp (*argv, "--sigs")) + { + mode |= GPGME_KEYLIST_MODE_SIGS; + argc--; argv++; + } + else if (!strcmp (*argv, "--sig-notations")) + { + mode |= GPGME_KEYLIST_MODE_SIG_NOTATIONS; + argc--; argv++; + } + else if (!strcmp (*argv, "--ephemeral")) + { + mode |= GPGME_KEYLIST_MODE_EPHEMERAL; + argc--; argv++; + } + else if (!strcmp (*argv, "--validate")) + { + mode |= GPGME_KEYLIST_MODE_VALIDATE; + argc--; argv++; + } + else if (!strcmp (*argv, "--import")) + { + import = 1; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + show_usage (1); + + } + + if (argc > 1) + show_usage (1); + + init_gpgme (GPGME_PROTOCOL_OpenPGP); + + err = gpgme_new (&ctx); + fail_if_err (err); + gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP); + + gpgme_set_keylist_mode (ctx, mode); + + err = gpgme_op_keylist_start (ctx, argc? argv[0]:NULL, 0); + fail_if_err (err); + + while (!(err = gpgme_op_keylist_next (ctx, &key))) + { + gpgme_user_id_t uid; + int nuids; + + + printf ("keyid : %s\n", key->subkeys?nonnull (key->subkeys->keyid):"?"); + printf ("fpr : %s\n", key->subkeys?nonnull (key->subkeys->fpr):"?"); + printf ("caps : %s%s%s%s\n", + key->can_encrypt? "e":"", + key->can_sign? "s":"", + key->can_certify? "c":"", + key->can_authenticate? "a":""); + printf ("flags :%s%s%s%s%s%s\n", + key->secret? " secret":"", + key->revoked? " revoked":"", + key->expired? " expired":"", + key->disabled? " disabled":"", + key->invalid? " invalid":"", + key->is_qualified? " qualifid":""); + for (nuids=0, uid=key->uids; uid; uid = uid->next, nuids++) + { + printf ("userid %d: %s\n", nuids, nonnull(uid->uid)); + printf ("valid %d: %s\n", nuids, + uid->validity == GPGME_VALIDITY_UNKNOWN? "unknown": + uid->validity == GPGME_VALIDITY_UNDEFINED? "undefined": + uid->validity == GPGME_VALIDITY_NEVER? "never": + uid->validity == GPGME_VALIDITY_MARGINAL? "marginal": + uid->validity == GPGME_VALIDITY_FULL? "full": + uid->validity == GPGME_VALIDITY_ULTIMATE? "ultimate": "[?]"); + } + + putchar ('\n'); + + if (import) + { + if (keyidx < DIM (keyarray)-1) + keyarray[keyidx++] = key; + else + { + fprintf (stderr, PGM": too many keys in import mode" + "- skipping this key\n"); + gpgme_key_unref (key); + } + } + else + gpgme_key_unref (key); + } + if (gpg_err_code (err) != GPG_ERR_EOF) + fail_if_err (err); + err = gpgme_op_keylist_end (ctx); + fail_if_err (err); + keyarray[keyidx] = NULL; + + result = gpgme_op_keylist_result (ctx); + if (result->truncated) + { + fprintf (stderr, PGM ": key listing unexpectedly truncated\n"); + exit (1); + } + + if (import) + { + gpgme_import_result_t impres; + + err = gpgme_op_import_keys (ctx, keyarray); + 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); + } + + for (keyidx=0; keyarray[keyidx]; keyidx++) + gpgme_key_unref (keyarray[keyidx]); + + gpgme_release (ctx); + return 0; +} diff --git a/trunk/tests/gpg/t-export.c b/trunk/tests/gpg/t-export.c index f469843..85cff23 100644 --- a/trunk/tests/gpg/t-export.c +++ b/trunk/tests/gpg/t-export.c @@ -42,6 +42,7 @@ main (int argc, char **argv) gpgme_error_t err; gpgme_data_t out; const char *pattern[] = { "Alpha", "Bob", NULL }; + gpgme_key_t keyarray[3]; init_gpgme (GPGME_PROTOCOL_OpenPGP); @@ -61,6 +62,32 @@ main (int argc, char **argv) fputs ("End Result.\n", stdout); gpgme_data_release (out); + + /* Again. Now using a key array. */ + err = gpgme_data_new (&out); + fail_if_err (err); + + err = gpgme_get_key (ctx, "0x68697734" /* Alpha */, keyarray+0, 0); + fail_if_err (err); + err = gpgme_get_key (ctx, "0xA9E3B0B2" /* Bob */, keyarray+1, 0); + fail_if_err (err); + keyarray[2] = NULL; + + gpgme_set_armor (ctx, 1); + err = gpgme_op_export_keys (ctx, keyarray, 0, out); + fail_if_err (err); + + gpgme_key_unref (keyarray[0]); + gpgme_key_unref (keyarray[1]); + + fflush (NULL); + fputs ("Begin Result:\n", stdout); + print_data (out); + fputs ("End Result.\n", stdout); + + gpgme_data_release (out); + + gpgme_release (ctx); return 0; -- 2.26.2