Add new functions to import and export keys specified by gpgme_key_t.
authorWerner Koch <wk@gnupg.org>
Tue, 16 Jun 2009 11:42:21 +0000 (11:42 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 16 Jun 2009 11:42:21 +0000 (11:42 +0000)
Allow  exporting keys to a keyserver.

22 files changed:
NEWS
TODO
assuan/ChangeLog
assuan/assuan-pipe-connect.c
doc/ChangeLog
doc/gpgme.texi
src/ChangeLog
src/engine-backend.h
src/engine-gpg.c
src/engine-gpgsm.c
src/engine.c
src/engine.h
src/export.c
src/gpgme.def
src/gpgme.h.in
src/import.c
src/libgpgme.vers
tests/ChangeLog
tests/gpg/Makefile.am
tests/gpg/pgp-export.c [new file with mode: 0644]
tests/gpg/pgp-keylist.c [new file with mode: 0644]
tests/gpg/t-export.c

diff --git a/NEWS b/NEWS
index 8629d31..232ff53 100644 (file)
--- a/NEWS
+++ b/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
 ------------------------------------------------
 
  * 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.
 
  * 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.
  * 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_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_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/TODO b/TODO
index 9aeeb26..d84b295 100644 (file)
--- a/TODO
+++ b/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
 
 * 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.
    (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
    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.
    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
 ** --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
 ** 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.
 *** 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
 *** 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).
    (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
 * 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
index 8dd2ebb..617cb00 100644 (file)
@@ -1,3 +1,8 @@
+2009-06-12  Werner Koch  <wk@g10code.com>
+
+       * assuan-pipe-connect.c (_gpgme_io_spawn): Change prototype.
+       (pipe_connect_gpgme): Pass a flags arg.
+
 2009-04-08  Marcus Brinkmann  <marcus@g10code.de>
 
        * assuan.h (_gpgme_io_socket): New prototype.
 2009-04-08  Marcus Brinkmann  <marcus@g10code.de>
 
        * assuan.h (_gpgme_io_socket): New prototype.
index 7fdfe74..fcede19 100644 (file)
@@ -51,7 +51,7 @@ struct spawn_fd_item_s
 
 
 int _gpgme_io_pipe (int filedes[2], int inherit_idx);
 
 
 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
 
                     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.  */
   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));
   if (res == -1)
     {
       _assuan_log_printf ("CreateProcess failed: %s\n", strerror (errno));
index e1bb4a5..5138a57 100644 (file)
@@ -1,3 +1,8 @@
+2009-06-16  Werner Koch  <wk@g10code.com>
+
+       * gpgme.texi (Exporting Keys): Document gpgme_op_export_keys.
+       (Importing Keys): Document gpgme_op_import_keys.
+
 2009-05-28  Marcus Brinkmann  <marcus@g10code.de>
 
        * gpgme.texi (Library Version Check): Document selftest error.
 2009-05-28  Marcus Brinkmann  <marcus@g10code.de>
 
        * gpgme.texi (Library Version Check): Document selftest error.
index 871575a..a6f15d1 100644 (file)
@@ -3318,7 +3318,25 @@ operation is started on the context.
 @cindex key, export
 @cindex key ring, export from
 
 @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
 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.
 
 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
 
 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
 
 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}.
 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
 
 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
 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.
 
 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
 
 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
 
 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}.
 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
 
 
 @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
 
 @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}.
 @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
 
 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
 @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
index 35622ee..dc1e116 100644 (file)
@@ -1,3 +1,34 @@
+2009-06-16  Werner Koch  <wk@g10code.com>
+
+       * 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  <marcus@g10code.de>
 
        * gpgme.h.in (gpgme_result_ref, gpgme_result_unref): Add
 2009-06-15  Marcus Brinkmann  <marcus@g10code.de>
 
        * gpgme.h.in (gpgme_result_ref, gpgme_result_unref): Add
index d656d9d..1fe600d 100644 (file)
@@ -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,
                                 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[],
                           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);
                               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[],
   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[],
index eec3fa6..3c06edf 100644 (file)
@@ -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
 
 
 /* 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.  */
    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
 
 
 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;
 
   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, "--");
 
   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);
 
   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
 
 
 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;
 
                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)
     {
 
   if (pattern)
     {
@@ -1795,16 +1805,40 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
 
 
 static gpgme_error_t
 
 
 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;
 {
   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);
 
   if (!err)
     err = start (gpg);
index 2d92732..71ef221 100644 (file)
@@ -1379,15 +1379,18 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
 
 
 static gpgme_error_t
 
 
 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;
 
              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);
     return gpg_error (GPG_ERR_INV_VALUE);
+  
+  if (mode)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
 
   if (!pattern)
     pattern = "";
 
   if (!pattern)
     pattern = "";
@@ -1414,7 +1417,7 @@ gpgsm_export (void *engine, const char *pattern, unsigned int reserved,
 
 
 static gpgme_error_t
 
 
 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;
                  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;
 
   int length = 7 + 1;
   char *linep;
 
-  if (!gpgsm || reserved)
+  if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
     return gpg_error (GPG_ERR_INV_VALUE);
 
+  if (mode)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
   if (pattern && *pattern)
     {
       const char **pat = pattern;
   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
 
 
 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;
 {
   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);
 
   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;
 
   err = start (gpgsm, "IMPORT");
   return err;
index 87d3939..c8ff012 100644 (file)
@@ -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,
 
 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)
                         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);
 
   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);
 }
 
                                 keydata, use_armor);
 }
 
@@ -667,7 +667,8 @@ _gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data,
 
 
 gpgme_error_t
 
 
 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);
 {
   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);
 
   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);
 }
 
 
 }
 
 
index a043b3e..ca746c8 100644 (file)
@@ -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,
                                             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[],
                                       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,
                                           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,
                                       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,
 gpgme_error_t _gpgme_engine_op_keylist (engine_t engine,
                                        const char *pattern,
                                        int secret_only,
index 079a7e0..1e29439 100644 (file)
@@ -22,6 +22,8 @@
 #if HAVE_CONFIG_H
 #include <config.h>
 #endif
 #if HAVE_CONFIG_H
 #include <config.h>
 #endif
+#include <stdlib.h>
+#include <string.h>
 
 #include "gpgme.h"
 #include "context.h"
 
 #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,
 
 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;
 
 {
   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)
 
   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);
 
 
   _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);
 }
 
 
                                  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,
 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_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;
   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,
 \f
 static gpgme_error_t
 export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[],
 \f
 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;
 
 {
   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)
 
   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);
 
 
   _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);
 }
 
 
                                      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[],
 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[],
 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;
+}
+
+
+
+\f
+
+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;
 }
   if (!err)
     err = _gpgme_wait_one (ctx);
   return err;
 }
+
index 9bc95d0..ac0fe9a 100644 (file)
@@ -180,5 +180,10 @@ EXPORTS
     gpgme_release_ref                     @138
     gpgme_release_unref                   @139
 
     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
 
 ; END
 
index 8e224a4..ff9bba0 100644 (file)
@@ -329,6 +329,12 @@ gpgme_protocol_t;
 typedef unsigned int gpgme_keylist_mode_t;
 
 \f
 typedef unsigned int gpgme_keylist_mode_t;
 
 \f
+/* The available export mode flags.  */
+#define GPGME_EXPORT_MODE_EXTERN                2
+
+typedef unsigned int gpgme_export_mode_t;
+
+\f
 /* Flags for the audit log functions.  */
 #define GPGME_AUDITLOG_HTML      1 
 #define GPGME_AUDITLOG_WITH_HELP 128
 /* Flags for the audit log functions.  */
 #define GPGME_AUDITLOG_HTML      1 
 #define GPGME_AUDITLOG_WITH_HELP 128
@@ -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;
 
 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[]);
+
+
 \f
 /* Export the keys found by PATTERN into KEYDATA.  */
 gpgme_error_t gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern,
 \f
 /* 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,
                                     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[],
 
 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[],
                                         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);
 
                                   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);
+
+
 \f
 /* Key generation.  */
 struct _gpgme_op_genkey_result
 \f
 /* Key generation.  */
 struct _gpgme_op_genkey_result
index ad6b776..8212a25 100644 (file)
@@ -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);
 
 
   _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)
 }
 
 
 }
 
 
+\f
+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)
 {
 gpgme_error_t
 gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr)
 {
index 44f6176..41fa1be 100644 (file)
@@ -59,6 +59,11 @@ GPGME_1.1 {
 
     gpgme_result_ref;
     gpgme_result_unref;
 
     gpgme_result_ref;
     gpgme_result_unref;
+
+    gpgme_op_import_keys;
+    gpgme_op_import_keys_start;
+    gpgme_op_export_keys;
+    gpgme_op_export_keys_start;
 };
 
 
 };
 
 
index a727a89..c9ab133 100644 (file)
@@ -1,3 +1,7 @@
+2009-06-16  Werner Koch  <wk@g10code.com>
+
+       * gpg/pgp-export.c, gpg/pgp-keylist.c: New.
+
 2009-06-09  Werner Koch  <wk@g10code.com>
 
        * gpg/Makefile.am (./pubring.gpg): Ignore errors in case of
 2009-06-09  Werner Koch  <wk@g10code.com>
 
        * gpg/Makefile.am (./pubring.gpg): Ignore errors in case of
@@ -16,7 +20,8 @@
 
        * gpg/mkdemodirs: Renamed to ...
        * gpg/mkdemodirs.in: ... here.
 
        * 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  <wk@g10code.com>
 
 
 2009-02-24  Werner Koch  <wk@g10code.com>
 
index 5da0bbb..fdb023c 100644 (file)
@@ -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
 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
 
 mkdemodirs: mkdemodirs.in Makefile
        sed -e 's,[@]GPG[@],$(GPG),g' < $(srcdir)/mkdemodirs.in > mkdemodirs
diff --git a/tests/gpg/pgp-export.c b/tests/gpg/pgp-export.c
new file mode 100644 (file)
index 0000000..9911c18
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/* 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 <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <gpgme.h>
+
+#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/tests/gpg/pgp-keylist.c b/tests/gpg/pgp-keylist.c
new file mode 100644 (file)
index 0000000..2645f48
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+/* 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 <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <gpgme.h>
+
+#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;
+}
index f469843..85cff23 100644 (file)
@@ -42,6 +42,7 @@ main (int argc, char **argv)
   gpgme_error_t err;
   gpgme_data_t  out;
   const char *pattern[] = { "Alpha", "Bob", NULL };
   gpgme_error_t err;
   gpgme_data_t  out;
   const char *pattern[] = { "Alpha", "Bob", NULL };
+  gpgme_key_t keyarray[3];
 
   init_gpgme (GPGME_PROTOCOL_OpenPGP);
 
 
   init_gpgme (GPGME_PROTOCOL_OpenPGP);
 
@@ -61,6 +62,32 @@ main (int argc, char **argv)
   fputs ("End Result.\n", stdout);
    
   gpgme_data_release (out);
   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;
   gpgme_release (ctx);
    
   return 0;