doc/
authorMarcus Brinkmann <mb@g10code.com>
Thu, 24 Apr 2003 17:16:28 +0000 (17:16 +0000)
committerMarcus Brinkmann <mb@g10code.com>
Thu, 24 Apr 2003 17:16:28 +0000 (17:16 +0000)
2003-04-24  Marcus Brinkmann  <marcus@g10code.de>

* gpgme.texi (Generating Keys): Document changed gpgme_op_genkey
and new gpgme_op_genkey_result function.  Document
GpgmeGenKeyResult data type.

gpgme/
2003-04-24  Marcus Brinkmann  <marcus@g10code.de>

* gpgme.h (struct _gpgme_op_genkey_result): New structure.
(GpgmeGenKeyResult): New type.
(gpgme_op_genkey): Drop last argument.
(gpgme_op_genkey_result): New function.
* genkey.c: Do not include "util.h", but "gpgme.h".
(struct genkey_result): Replace with ...
(op_data_t): ... this new type.
(release_genkey_result): Replace with ...
(release_op_data): ... this new function.
(gpgme_op_genkey_result): New function.
(genkey_status_handler): Rewritten using new op_data_t type.
(get_key_parameter): New function.
(_gpgme_op_genkey_start): Renamed to
(genkey_start): ... this and rewritten.
(gpgme_op_genkey_start): Use genkey_start instead
_gpgme_op_genkey_start.
(gpgme_op_genkey): Rewritten.  Remove FPR argument.

tests/
2003-04-24  Marcus Brinkmann  <marcus@g10code.de>

* gpg/t-genkey.c: Rewritten to match new semantics.

NEWS
doc/ChangeLog
doc/gpgme.texi
gpgme/ChangeLog
gpgme/genkey.c
gpgme/gpgme.h
tests/ChangeLog
tests/gpg/t-genkey.c

diff --git a/NEWS b/NEWS
index 926739a..f985f1e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -46,6 +46,12 @@ Noteworthy changes in version 0.4.1 (unreleased)
    GPGME_No_Passphrase have been renamed to GPGME_No_UserID,
    GPGME_Invalid_UserID and GPGME_Bad_Passphrase resp.
 
+ * The FPR argument to gpgme_op_genkey was removed.  Instead, use the
+   gpgme_op_genkey_result function to retrieve a GpgmeGenKeyResult
+   pointer to a structure which contains the fingerprint.  This also
+   works with gpgme_op_genkey.  The structure also provides other
+   information about the generated keys.
+
  * Interface changes relative to the 0.4.0 release:
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 GpgmeIOCb                      CHANGED: Return type from void to GpgmeError.
@@ -61,9 +67,12 @@ gpgme_get_engine_info                CHANGED: Return info structure instead XML.
 gpgme_get_protocol_name                NEW
 GpgmePassphraseCb              CHANGED: Return error value, new argument.
 gpgme_cancel                   REMOVED: Return error in callback directly.
-GPGME_No_Recipients            CHANGED: GPGME_No_UserID
-GPGME_Invalid_Recipient                CHANGED: GPGME_Invalid_UserID
-GPGME_No_Passphrase            CHANGED: GPGME_Bad_Passphrase
+GPGME_No_Recipients            CHANGED: Now GPGME_No_UserID.
+GPGME_Invalid_Recipient                CHANGED: Now GPGME_Invalid_UserID.
+GPGME_No_Passphrase            CHANGED: Now GPGME_Bad_Passphrase.
+gpgme_op_genkey                        CHANGED: FPR argument dropped.
+gpgme_op_genkey_result         NEW
+GpgmeGenKeyResult              NEW
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Noteworthy changes in version 0.4.0 (2002-12-23)
index e9e1d1f..a387471 100644 (file)
@@ -1,5 +1,9 @@
 2003-04-24  Marcus Brinkmann  <marcus@g10code.de>
 
+       * gpgme.texi (Generating Keys): Document changed gpgme_op_genkey
+       and new gpgme_op_genkey_result function.  Document
+       GpgmeGenKeyResult data type.
+
        * gpgme.texi (Error Values): Rename GPGME_No_Passphrase to
        GPGME_Bad_Passphrase.
        * gpgme.texi (Decrypt): Likewise.
index 5edf7b8..105e031 100644 (file)
@@ -2115,7 +2115,7 @@ The function @code{gpgme_key_release} is an alias for
 @cindex key, creation
 @cindex key ring, add
 
-@deftypefun GpgmeError gpgme_op_genkey (@w{GpgmeCtx @var{ctx}}, @w{const char *@var{parms}}, @w{GpgmeData @var{pubkey}}, @w{GpgmeData @var{seckey}}, @w{char **@var{fpr}})
+@deftypefun GpgmeError gpgme_op_genkey (@w{GpgmeCtx @var{ctx}}, @w{const char *@var{parms}}, @w{GpgmeData @var{pubkey}}, @w{GpgmeData @var{seckey}})
 The function @code{gpgme_op_genkey} generates a new key pair in the
 context @var{ctx} and puts it into the standard key ring if both
 @var{pubkey} and @var{seckey} are @code{NULL}.  In this case the
@@ -2130,8 +2130,8 @@ the data object will contain the secret key.
 Note that not all crypto engines support this interface equally.
 GnuPG does not support @var{pubkey} and @var{subkey}, they should be
 both @code{NULL}, and the key pair will be added to the standard key
-ring.  GpgSM does only support @var{pubkey}, the secret key will be
-stored by @command{gpg-agent}.  GpgSM expects @var{pubkey} being not
+ring.  GpgSM only supports @var{pubkey}, the secret key will be stored
+by @command{gpg-agent}.  GpgSM expects @var{pubkey} being not
 @code{NULL}.
 
 The argument @var{parms} specifies parameters for the key in an XML
@@ -2168,12 +2168,8 @@ for now is ``internal''.  The content of the @code{GnupgKeyParms}
 container is passed verbatim to GnuPG.  Control statements are not
 allowed.
 
-If @var{fpr} is not a null pointer, the function succeeds, and the
-crypto engine supports it, *@var{fpr} will contain a string with the
-fingerprint of the key, allocated with @code{malloc}.  If both a
-primary and a sub key was generated, the fingerprint of the primary
-key will be returned.  If the crypto engine does not provide the
-fingerprint, *@var{fpr} will be a null pointer.
+After the operation completed successfully, the result can be
+retrieved with @code{gpgme_op_genkey_result}.
 
 The function returns @code{GPGME_No_Error} if the operation could be
 started successfully, @code{GPGME_Invalid_Value} if @var{parms} is not
@@ -2193,6 +2189,39 @@ a valid XML string, and @code{GPGME_Not_Supported} if @var{pubkey} or
 @var{seckey} is not @code{NULL}.
 @end deftypefun
 
+@deftp {Data type} {GpgmeGenKeyResult}
+This is a pointer to a structure used to store the result of a
+@code{gpgme_op_genkey} operation.  After successfully generating a
+key, you can retrieve the pointer to the result with
+@code{gpgme_op_genkey_result}.  The structure contains the following
+members:
+
+@table @code
+@item unsigned int primary : 1
+This is a flag that is set to 1 if a primary key was created and to 0
+if not.
+
+@item unsigned int sub : 1
+This is a flag that is set to 1 if a subkey was created and to 0
+if not.
+
+@item char *fpr
+This is the fingerprint of the key that was created.  If both a
+primary and a sub key were generated, the fingerprint of the primary
+key will be returned.  If the crypto engine does not provide the
+fingerprint, @code{fpr} will be a null pointer.
+@end table
+@end deftp
+
+@deftypefun GpgmeGenKeyResult gpgme_op_genkey_result (@w{GpgmeCtx @var{ctx}})
+The function @code{gpgme_op_genkey_result} returns a
+@code{GpgmeGenKeyResult} pointer to a structure holding the result of
+a @code{gpgme_op_genkey} operation.  The pointer is only valid if the
+last operation on the context was a @code{gpgme_op_genkey} or
+@code{gpgme_op_genkey_start} operation, and if this operation finished
+successfully.  The returned pointer is only valid until the next
+operation is started on the context.
+@end deftypefun
 
 @node Exporting Keys
 @subsection Exporting Keys
index af28e5a..0a7c44d 100644 (file)
@@ -1,5 +1,23 @@
 2003-04-24  Marcus Brinkmann  <marcus@g10code.de>
        
+       * gpgme.h (struct _gpgme_op_genkey_result): New structure.
+       (GpgmeGenKeyResult): New type.
+       (gpgme_op_genkey): Drop last argument.
+       (gpgme_op_genkey_result): New function.
+       * genkey.c: Do not include "util.h", but "gpgme.h".
+       (struct genkey_result): Replace with ...
+       (op_data_t): ... this new type.
+       (release_genkey_result): Replace with ...
+       (release_op_data): ... this new function.
+       (gpgme_op_genkey_result): New function.
+       (genkey_status_handler): Rewritten using new op_data_t type.
+       (get_key_parameter): New function.
+       (_gpgme_op_genkey_start): Renamed to
+       (genkey_start): ... this and rewritten.
+       (gpgme_op_genkey_start): Use genkey_start instead
+       _gpgme_op_genkey_start.
+       (gpgme_op_genkey): Rewritten.  Remove FPR argument.
+
        * context.h (struct gpgme_context_s): Remove member verbosity.
        * gpgme.c (gpgme_new): Do not set member verbosity.
        * engine.h (_gpgme_engine_set_verbosity): Remove prototype.
index caca6cb..59f277e 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
-#include "util.h"
+#include "gpgme.h"
 #include "context.h"
 #include "ops.h"
 
-
-struct genkey_result
+\f
+typedef struct
 {
-  int created_primary : 1;
-  int created_sub : 1;
-  char *fpr;
-};
-typedef struct genkey_result *GenKeyResult;
+  struct _gpgme_op_genkey_result result;
+
+  /* The key parameters passed to the crypto engine.  */
+  GpgmeData key_parameter;
+} *op_data_t;
+
 
 static void
-release_genkey_result (void *hook)
+release_op_data (void *hook)
 {
-  GenKeyResult result = (GenKeyResult) hook;
+  op_data_t opd = (op_data_t) hook;
   
-  if (result->fpr)
-    free (result->fpr);
+  if (opd->result.fpr)
+    free (opd->result.fpr);
+  if (opd->key_parameter)
+    gpgme_data_release (opd->key_parameter);
 }
 
 
+GpgmeGenKeyResult
+gpgme_op_genkey_result (GpgmeCtx ctx)
+{
+  op_data_t opd;
+  GpgmeError err;
+
+  err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, (void **) &opd, -1, NULL);
+  if (err || !opd)
+    return NULL;
+
+  return &opd->result;
+}
+
+\f
 static GpgmeError
-genkey_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
+genkey_status_handler (void *priv, GpgmeStatusCode code, char *args)
 {
-  GenKeyResult result;
-  GpgmeError err = _gpgme_progress_status_handler (ctx, code, args);
+  GpgmeCtx ctx = (GpgmeCtx) priv;
+  GpgmeError err;
+  op_data_t opd;
+
+  /* Pipe the status code through the progress status handler.  */
+  err = _gpgme_progress_status_handler (ctx, code, args);
   if (err)
     return err;
 
-  err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, (void **) &result,
-                              sizeof (*result), release_genkey_result);
+  err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, (void **) &opd,
+                              -1, NULL);
   if (err)
     return err;
 
@@ -66,15 +87,15 @@ genkey_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
       if (args && *args)
        {
          if (*args == 'B' || *args == 'P')
-           result->created_primary = 1;
+           opd->result.primary = 1;
          if (*args == 'B' || *args == 'S')
-           result->created_sub = 1;
+           opd->result.sub = 1;
          if (args[1] == ' ')
            {
-             if (result->fpr)
-               free (result->fpr);
-             result->fpr = strdup (&args[2]);
-             if (!result->fpr)
+             if (opd->result.fpr)
+               free (opd->result.fpr);
+             opd->result.fpr = strdup (&args[2]);
+             if (!opd->result.fpr)
                return GPGME_Out_Of_Core;
            }
        }
@@ -82,8 +103,7 @@ genkey_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
 
     case GPGME_STATUS_EOF:
       /* FIXME: Should return some more useful error value.  */
-      if (!result->created_primary
-         && !result->created_sub)
+      if (!opd->result.primary && !opd->result.sub)
        return GPGME_General_Error;
       break;
 
@@ -95,139 +115,84 @@ genkey_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
 
 
 static GpgmeError
-_gpgme_op_genkey_start (GpgmeCtx ctx, int synchronous, const char *parms,
-                       GpgmeData pubkey, GpgmeData seckey)
+get_key_parameter (const char *parms, GpgmeData *key_parameter)
 {
-  int err = 0;
-  const char *s, *s2, *sx;
+  const char *content;
+  const char *attrib;
+  const char *endtag;
+
+  /* Extract the key parameter from the XML structure.  */
+  parms = strstr (parms, "<GnupgKeyParms ");
+  if (!parms)
+    return GPGME_Invalid_Value;
+
+  content = strchr (parms, '>');
+  if (!content)
+    return GPGME_Invalid_Value;
+  content++;
+
+  attrib = strstr (parms, "format=\"internal\"");
+  if (!attrib || attrib >= content)
+    return GPGME_Invalid_Value;
+
+  endtag = strstr (content, "</GnupgKeyParms>");
+  /* FIXME: Check that there are no control statements inside.  */
+  while (*content == '\n')
+    content++;
+
+  return gpgme_data_new_from_mem (key_parameter, content,
+                                 endtag - content, 0);
+}
+
 
+static GpgmeError
+genkey_start (GpgmeCtx ctx, int synchronous, const char *parms,
+             GpgmeData pubkey, GpgmeData seckey)
+{
+  GpgmeError err;
+  op_data_t opd;
   err = _gpgme_op_reset (ctx, synchronous);
   if (err)
-    goto leave;
-
-  gpgme_data_release (ctx->help_data_1);
-  ctx->help_data_1 = NULL;
-
-  if ((parms = strstr (parms, "<GnupgKeyParms ")) 
-      && (s = strchr (parms, '>'))
-      && (sx = strstr (parms, "format=\"internal\""))
-      && sx < s
-      && (s2 = strstr (s+1, "</GnupgKeyParms>")))
-    {
-      /* FIXME: Check that there are no control statements inside.  */
-      s++;  /* Skip '>'.  */
-      while (*s == '\n')
-       s++;
-      err = gpgme_data_new_from_mem (&ctx->help_data_1, s, s2-s, 1);
-    }
-  else 
-    err = GPGME_Invalid_Value;
+    return err;
+  
+  err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, (void **) &opd,
+                              sizeof (*opd), release_op_data);
+  if (err)
+    return err;
 
+  err = get_key_parameter (parms, &opd->key_parameter);
   if (err)
-    goto leave;
+    return err;
 
   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
 
-  err = _gpgme_engine_op_genkey (ctx->engine, ctx->help_data_1, ctx->use_armor,
-                                pubkey, seckey);
-
- leave:
-  if (err)
-    {
-      _gpgme_engine_release (ctx->engine);
-      ctx->engine = NULL;
-    }
-  return err;
+  return _gpgme_engine_op_genkey (ctx->engine, opd->key_parameter,
+                                 ctx->use_armor, pubkey, seckey);
 }
 
 
-/**
- * gpgme_op_genkey:
- * @c: the context
- * @parms: XML string with the key parameters
- * @pubkey: Returns the public key
- * @seckey: Returns the secret key
- * 
- * Generate a new key and store the key in the default keyrings if
- * both @pubkey and @seckey are NULL.  If @pubkey and @seckey are
- * given, the newly created key will be returned in these data
- * objects.  This function just starts the gheneration and does not
- * wait for completion.
- *
- * Here is an example on how @parms should be formatted; for deatils
- * see the file doc/DETAILS from the GnuPG distribution.
- *
- * <literal>
- * <![CDATA[
- * <GnupgKeyParms format="internal">
- * Key-Type: DSA
- * Key-Length: 1024
- * Subkey-Type: ELG-E
- * Subkey-Length: 1024
- * Name-Real: Joe Tester
- * Name-Comment: with stupid passphrase
- * Name-Email: joe@foo.bar
- * Expire-Date: 0
- * Passphrase: abc
- * </GnupgKeyParms>
- * ]]>
- * </literal> 
- *
- * Strings should be given in UTF-8 encoding.  The format we support
- * for now is only "internal".  The content of the
- * &lt;GnupgKeyParms&gt; container is passed verbatim to GnuPG.
- * Control statements are not allowed.
- * 
- * Return value: 0 for success or an error code
- **/
+/* Generate a new keypair and add it to the keyring.  PUBKEY and
+   SECKEY should be null for now.  PARMS specifies what keys should be
+   generated.  */
 GpgmeError
 gpgme_op_genkey_start (GpgmeCtx ctx, const char *parms,
                       GpgmeData pubkey, GpgmeData seckey)
 {
-  return _gpgme_op_genkey_start (ctx, 0, parms, pubkey, seckey);
+  return genkey_start (ctx, 0, parms, pubkey, seckey);
 }
 
 
-/**
- * gpgme_op_genkey:
- * @c: the context
- * @parms: XML string with the key parameters
- * @pubkey: Returns the public key
- * @seckey: Returns the secret key
- * @fpr: Returns the fingerprint of the key.
- *
- * Generate a new key and store the key in the default keyrings if both
- * @pubkey and @seckey are NULL.  If @pubkey and @seckey are given, the newly
- * created key will be returned in these data objects.
- * See gpgme_op_genkey_start() for a description of @parms.
- * 
- * Return value: 0 for success or an error code
- **/
+/* Generate a new keypair and add it to the keyring.  PUBKEY and
+   SECKEY should be null for now.  PARMS specifies what keys should be
+   generated.  */
 GpgmeError
-gpgme_op_genkey (GpgmeCtx ctx, const char *parms,
-                 GpgmeData pubkey, GpgmeData seckey,
-                char **fpr)
+gpgme_op_genkey (GpgmeCtx ctx, const char *parms, GpgmeData pubkey,
+                GpgmeData seckey)
 {
-  GpgmeError err = _gpgme_op_genkey_start (ctx, 1, parms, pubkey, seckey);
+  GpgmeError err;
+
+  err = genkey_start (ctx, 1, parms, pubkey, seckey);
   if (!err)
     err = _gpgme_wait_one (ctx);
-  if (!err && fpr)
-    {
-      GenKeyResult result;
-
-      err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, (void **) &result,
-                                  -1, NULL);
-      if (err)
-       return err;
-
-      if (result && result->fpr)
-       {
-         *fpr = strdup (result->fpr);
-         if (!*fpr)
-           return GPGME_Out_Of_Core;
-       }
-      else
-       *fpr = NULL;
-    }
   return err;
 }
index a5f56ae..0ad0f23 100644 (file)
@@ -756,17 +756,36 @@ GpgmeError gpgme_op_export_start (GpgmeCtx ctx, GpgmeRecipients recp,
 GpgmeError gpgme_op_export (GpgmeCtx ctx, GpgmeRecipients recp,
                            GpgmeData keydata);
 
+\f
+/* Key generation.  */
+struct _gpgme_op_genkey_result
+{
+  /* A primary key was generated.  */
+  unsigned int primary : 1;
+
+  /* A sub key was generated.  */
+  unsigned int sub : 1;
+
+  /* Internal to GPGME, do not use.  */
+  unsigned int _unused : 30;
+
+  /* The fingerprint of the generated key.  */
+  char *fpr;
+};
+typedef struct _gpgme_op_genkey_result *GpgmeGenKeyResult;
+
 /* Generate a new keypair and add it to the keyring.  PUBKEY and
    SECKEY should be null for now.  PARMS specifies what keys should be
-   generated.  On success, if *FPR is non-null, it contains a
-   malloc()'ed string with the fingerprint of the generated key on
-   success.  */
+   generated.  */
 GpgmeError gpgme_op_genkey_start (GpgmeCtx ctx, const char *parms,
                                  GpgmeData pubkey, GpgmeData seckey);
 GpgmeError gpgme_op_genkey (GpgmeCtx ctx, const char *parms,
-                           GpgmeData pubkey, GpgmeData seckey,
-                           char **fpr);
+                           GpgmeData pubkey, GpgmeData seckey);
 
+/* Retrieve a pointer to the result of the genkey operation.  */
+GpgmeGenKeyResult gpgme_op_genkey_result (GpgmeCtx ctx);
+
+\f
 /* Delete KEY from the keyring.  If ALLOW_SECRET is non-zero, secret
    keys are also deleted.  */
 GpgmeError gpgme_op_delete_start (GpgmeCtx ctx, const GpgmeKey key,
index 37d6d11..407d0cf 100644 (file)
@@ -1,3 +1,7 @@
+2003-04-24  Marcus Brinkmann  <marcus@g10code.de>
+
+       * gpg/t-genkey.c: Rewritten to match new semantics.
+
 2003-02-06  Marcus Brinkmann  <marcus@g10code.de>
 
        * gpg/t-decrypt.c (passphrase_cb): Fix to new prototype.
index 4bb59b2..75ef421 100644 (file)
 /* t-genkey.c  - regression test
- *     Copyright (C) 2000 Werner Koch (dd9jn)
- *      Copyright (C) 2001 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 General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * GPGME is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
+   Copyright (C) 2000 Werner Koch (dd9jn)
+   Copyright (C) 2001, 2003 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 General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   GPGME is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+   You should have received a copy of the GNU General Public License
+   along with GPGME; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <assert.h>
 
 #include <gpgme.h>
 
-#define fail_if_err(a) do { if(a) {                                       \
-                               fprintf (stderr, "%s:%d: GpgmeError %s\n", \
-                                __FILE__, __LINE__, gpgme_strerror(a));   \
-                                exit (1); }                               \
-                             } while(0)
+#define fail_if_err(err)                                       \
+  do                                                           \
+    {                                                          \
+      if (err)                                                 \
+        {                                                      \
+          fprintf (stderr, "%s:%d: GpgmeError %s\n",           \
+                   __FILE__, __LINE__, gpgme_strerror (err));   \
+          exit (1);                                            \
+        }                                                      \
+    }                                                          \
+  while (0)
 
+/* True if progress function printed something on the screen.  */
+int progress_called;
 
 static void
-progress ( void *self, const char *what, int type, int current, int total)
+progress (void *self, const char *what, int type, int current, int total)
 {
-    fprintf (stderr, "progress `%s' %d %d %d\n", what, type, current, total);
+  if (!strcmp (what, "primegen") && !current && !total
+      && (type == '.' || type == '+' || type == '!'
+         || type == '^' || type == '<' || type == '>'))
+    {
+      printf ("%c", type);
+      fflush (stdout);
+      progress_called = 1;
+    }
+  else
+    {
+      fprintf (stderr, "unknown progress `%s' %d %d %d\n", what, type,
+              current, total);
+      exit (1);
+    }
 }
 
 
 int 
-main (int argc, char **argv )
+main (int argc, char **argv)
 {
-    GpgmeCtx ctx;
-    GpgmeError err;
-    const char *format;
-    char *parms;
-    int count = 0;
-
-  do {
-    err = gpgme_new (&ctx);
-    fail_if_err (err);
+  GpgmeCtx ctx;
+  GpgmeError err;
+  const char *parms = "<GnupgKeyParms format=\"internal\">\n"
+    "Key-Type: DSA\n"
+    "Key-Length: 1024\n"
+    "Subkey-Type: ELG-E\n"
+    "Subkey-Length: 1024\n"
+    "Name-Real: Joe Tester\n"
+    "Name-Comment: (pp=abc)\n"
+    "Name-Email: joe@foo.bar\n"
+    "Expire-Date: 0\n"
+    "Passphrase: abc\n"
+    "</GnupgKeyParms>\n";
+  GpgmeGenKeyResult result;
 
-    gpgme_set_progress_cb (ctx, progress, NULL);
+  err = gpgme_new (&ctx);
+  fail_if_err (err);
 
-    format = "<GnupgKeyParms format=\"internal\">\n"
-             "Key-Type: DSA\n"
-             "Key-Length: 1024\n"
-             "Subkey-Type: ELG-E\n"
-             "Subkey-Length: 1024\n"
-             "Name-Real: Joe Tester\n"
-             "Name-Comment: (pp=abc,try=%d)\n"
-             "Name-Email: joe@foo.bar\n"
-             "Expire-Date: 0\n"
-             "Passphrase: abc\n"
-             "</GnupgKeyParms>\n";
-    parms = malloc ( strlen (format) + 1 + 20 );
-    if (!parms)
-        exit (8);
-    sprintf (parms, format, ++count );
-    err = gpgme_op_genkey (ctx, parms, NULL, NULL, NULL);
-    fail_if_err (err);
-    free (parms);
-
-    gpgme_release (ctx);
-  } while ( argc > 1 && !strcmp( argv[1], "--loop" ) );
-   
-    return 0;
-}
+  gpgme_set_progress_cb (ctx, progress, NULL);
+  
+  err = gpgme_op_genkey (ctx, parms, NULL, NULL);
+  fail_if_err (err);
 
+  result = gpgme_op_genkey_result (ctx);
+  if (!result)
+    {
+      fprintf (stderr, "%s:%d: gpgme_op_genkey_result returns NULL\n",
+              __FILE__, __LINE__);
+      exit (1);
+    }
+  if (progress_called)
+    printf ("\n");
 
+  printf ("Generated key: %s (%s)\n", result->fpr ? result->fpr : "none",
+         result->primary ? (result->sub ? "primary, sub" : "primary")
+         : (result->sub ? "sub" : "none"));
 
+  if (strlen (result->fpr) != 40)
+    {
+      fprintf (stderr, "%s:%d: generated key has unexpected fingerprint\n",
+              __FILE__, __LINE__);
+      exit (1);
+    }
+  if (!result->primary)
+    {
+      fprintf (stderr, "%s:%d: primary key was not generated\n",
+              __FILE__, __LINE__);
+      exit (1);
+    }
+  if (!result->sub)
+    {
+      fprintf (stderr, "%s:%d: sub key was not generated\n",
+              __FILE__, __LINE__);
+      exit (1);
+    }
+  gpgme_release (ctx);
+  return 0;
+}