2002-12-04 Marcus Brinkmann <marcus@g10code.de>
authorMarcus Brinkmann <mb@g10code.com>
Wed, 4 Dec 2002 16:28:34 +0000 (16:28 +0000)
committerMarcus Brinkmann <mb@g10code.com>
Wed, 4 Dec 2002 16:28:34 +0000 (16:28 +0000)
* gpgme.h: Add prototype for gpgme_get_key.
* key.c (gpgme_get_key): New function.
* verify.c (gpgme_get_sig_key): Rewrite using gpgme_get_key.

* gpgme.h: Add prototypes for new interfaces
gpgme_key_sig_get_string_attr and gpgme_key_get_ulong_attr.
(enum GpgmeAttr): New attribute GPGME_ATTR_SIG_CLASS.
* gpgme.c (gpgme_set_keylist_mode): Allow GPGME_KEYLIST_MODE_SIGS.
* key.h (struct certsig_s): New members ALGO, NAME_PART,
EMAIL_PART, COMMENT_PART, NAME, SIG_STAT and SIG_CLASS.

* conversion.c (_gpgme_decode_c_string): Add new parameter LEN.
Use that to determine if allocation is desired or not.
* util.h: Adjust prototype of _gpgme_decode_c_string.
* keylist.c (keylist_colon_handler): Adjust caller of
_gpgme_decode_c_string.

* key.h (struct gpgme_key_s): New member last_uid.
* key.c (_gpgme_key_append_name): Rewritten using
_gpgme_decode_c_string and the last_uid pointer.
(my_isdigit): Macro removed.
(ALLOC_CHUNK): Likewise.
* keylist.c (set_userid_flags): Use last_uid member of KEY.

* context.h (struct user_id_s): New member last_certsig.
* key.h: Add prototype for _gpgme_key_add_certsig.
* key.c (_gpgme_key_add_certsig): New function.
(set_user_id_part): Move function before _gpgme_key_add_certsig.
(parse_user_id): Change first argument to SRC, add new arguments
NAME, EMAIL and COMMENT.  Change code to use these arguments
instead going through UID.  Move function before
_gpgme_add_certsig.
(parse_x509_user_id): Likewise.
(_gpgme_key_append_name): Adjust arguments to parse_x509_user_id
and parse_user_id invocation.
(one_certsig_as_xml): New function.
(one_uid_as_xml): Print signatures.
* context.h (struct gpgme_context_s): New member TMP_UID.
* keylist.c (keylist_colon_handler): Rewritten, implement "sig"
record entries.

* key.c (get_certsig): New function.
(gpgme_key_sig_get_string_attr): Likewise.
(gpgme_key_sig_get_ulong_attr): Likewise.

* keylist.c: Include <ctype.h>.
(my_isdigit): Macro removed.
(set_mainkey_trust_info): Use isdigit, not my_isdigit.
(set_userid_flags): Likewise.
(set_subkey_trust_info): Likewise.
(set_ownertrust): Likewise.
(finish_key): Move function up a bit and remove prototype.

* rungpg.c (gpg_keylist_ext): Correct precedence of signature
listing mode.
(gpg_keylist_ext): Implement signature listing mode.

12 files changed:
NEWS
gpgme/ChangeLog
gpgme/context.h
gpgme/conversion.c
gpgme/gpgme.c
gpgme/gpgme.h
gpgme/key.c
gpgme/key.h
gpgme/keylist.c
gpgme/rungpg.c
gpgme/util.h
gpgme/verify.c

diff --git a/NEWS b/NEWS
index 771a25c73505d6115eb32eb445a1ad997ec71877..06eb9606b122c06a8ccf6f9b16aedc4588c05a36 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,12 @@
 Noteworthy changes in version 0.4.0 (unreleased)
 ------------------------------------------------
 
+ * Supports signatures of user IDs in keys via the new
+   GPGME_KEYLIST_MODE_SIGS keylist mode and the
+   gpgme_key_sig_get_string_attr and gpgme_key_sig_get_ulong_attr
+   interfaces.  The XML info about a key also includes the signatures
+   if available.
+
  * New data object interface, which is more flexible and transparent.
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -23,6 +29,11 @@ gpgme_op_verify                      CHANGED: Take different data objects for
 gpgme_op_verify_start          CHANGED: See gpgme_op_verify.
 gpgme_check_engine             REMOVED: Deprecated since 0.3.0.
 gpgme_op_genkey                        CHANGED: New parameter FPR.
+GPGME_KEYLIST_MODE_SIGS                NEW
+gpgme_key_sig_get_string_attr  NEW
+gpgme_key_sig_get_ulong_attr   NEW
+gpgme_get_key                  NEW
+GPGME_ATTR_SIG_CLASS           NEW
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Noteworthy changes in version 0.3.13 (2002-11-20)
index a34cf953df257d5a980b7aa60ac7c09ea8b117b4..c9986a0d236517f2298d2d0b9dc8f41f174a6980 100644 (file)
@@ -1,3 +1,62 @@
+2002-12-04  Marcus Brinkmann  <marcus@g10code.de>
+
+       * gpgme.h: Add prototype for gpgme_get_key.
+       * key.c (gpgme_get_key): New function.
+       * verify.c (gpgme_get_sig_key): Rewrite using gpgme_get_key.
+
+       * gpgme.h: Add prototypes for new interfaces
+       gpgme_key_sig_get_string_attr and gpgme_key_get_ulong_attr.
+       (enum GpgmeAttr): New attribute GPGME_ATTR_SIG_CLASS.
+       * gpgme.c (gpgme_set_keylist_mode): Allow GPGME_KEYLIST_MODE_SIGS.
+       * key.h (struct certsig_s): New members ALGO, NAME_PART,
+       EMAIL_PART, COMMENT_PART, NAME, SIG_STAT and SIG_CLASS.
+
+       * conversion.c (_gpgme_decode_c_string): Add new parameter LEN.
+       Use that to determine if allocation is desired or not.
+       * util.h: Adjust prototype of _gpgme_decode_c_string.
+       * keylist.c (keylist_colon_handler): Adjust caller of
+       _gpgme_decode_c_string.
+
+       * key.h (struct gpgme_key_s): New member last_uid.
+       * key.c (_gpgme_key_append_name): Rewritten using
+       _gpgme_decode_c_string and the last_uid pointer.
+       (my_isdigit): Macro removed.
+       (ALLOC_CHUNK): Likewise.
+       * keylist.c (set_userid_flags): Use last_uid member of KEY.
+
+       * context.h (struct user_id_s): New member last_certsig.
+       * key.h: Add prototype for _gpgme_key_add_certsig.
+       * key.c (_gpgme_key_add_certsig): New function.
+       (set_user_id_part): Move function before _gpgme_key_add_certsig.
+       (parse_user_id): Change first argument to SRC, add new arguments
+       NAME, EMAIL and COMMENT.  Change code to use these arguments
+       instead going through UID.  Move function before
+       _gpgme_add_certsig.
+       (parse_x509_user_id): Likewise.
+       (_gpgme_key_append_name): Adjust arguments to parse_x509_user_id
+       and parse_user_id invocation.
+       (one_certsig_as_xml): New function.
+       (one_uid_as_xml): Print signatures.
+       * context.h (struct gpgme_context_s): New member TMP_UID.
+       * keylist.c (keylist_colon_handler): Rewritten, implement "sig"
+       record entries.
+
+       * key.c (get_certsig): New function.
+       (gpgme_key_sig_get_string_attr): Likewise.
+       (gpgme_key_sig_get_ulong_attr): Likewise.
+
+       * keylist.c: Include <ctype.h>.
+       (my_isdigit): Macro removed.
+       (set_mainkey_trust_info): Use isdigit, not my_isdigit.
+       (set_userid_flags): Likewise.
+       (set_subkey_trust_info): Likewise.
+       (set_ownertrust): Likewise.
+       (finish_key): Move function up a bit and remove prototype.
+
+       * rungpg.c (gpg_keylist_ext): Correct precedence of signature
+       listing mode.
+       (gpg_keylist_ext): Implement signature listing mode.
+
 2002-11-25  Marcus Brinkmann  <marcus@g10code.de>
 
        * rungpg.c (_gpgme_gpg_spawn): Do not set parent fds to -1.
index 4a588ce3e867769f262456b29840ea99ac320eaa..ced7a7933dff77d5ea402bb834f19c119c89288b 100644 (file)
@@ -1,23 +1,22 @@
 /* context.h
- *     Copyright (C) 2000 Werner Koch (dd9jn)
- *      Copyright (C) 2001, 2002 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, 2002 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.  */
 
 #ifndef CONTEXT_H
 #define CONTEXT_H
@@ -93,6 +92,7 @@ struct gpgme_context_s
 
   /* Used by keylist.c.  */
   GpgmeKey tmp_key;
+  struct user_id_s *tmp_uid;
   /* Something new is available.  */
   volatile int key_cond;
   struct key_queue_item_s *key_queue;
@@ -124,6 +124,7 @@ struct user_id_s
   unsigned int invalid : 1;
   GpgmeValidity validity; 
   struct certsig_s *certsigs;
+  struct certsig_s *last_certsig;
   const char *name_part;       /* All 3 point into strings behind name  */
   const char *email_part;      /* or to read-only strings.  */
   const char *comment_part;
index e0344ec7e5fe9a42fe67cfbeb19a0e32f88963cb..32fc954cb214fe3ea5b4204d0d1c93383c07ce41 100644 (file)
@@ -57,18 +57,29 @@ _gpgme_hextobyte (const byte *str)
 }
 
 
+/* Decode the C formatted string SRC and store the result in the
+   buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
+   large enough buffer is allocated with malloc and *DESTP is set to
+   the result.  Currently, LEN is only used to specify if allocation
+   is desired or not, the caller is expected to make sure that *DESTP
+   is large enough if LEN is not zero.  */
 GpgmeError
-_gpgme_decode_c_string (const char *src, char **destp)
+_gpgme_decode_c_string (const char *src, char **destp, int len)
 {
   char *dest;
 
-  /* We can malloc a buffer of the same length, because the converted
-     string will never be larger.  */
-  dest = malloc (strlen (src) + 1);
-  if (!dest)
-    return mk_error (Out_Of_Core);
+  if (len)
+    dest = *destp;
+  else
+    {
+      /* We can malloc a buffer of the same length, because the converted
+        string will never be larger.  */
+      dest = malloc (strlen (src) + 1);
+      if (!dest)
+       return mk_error (Out_Of_Core);
 
-  *destp = dest;
+      *destp = dest;
+    }
 
   while (*src)
     {
index 3e965b4018126f483f9200b40c18df246d774b93..aaa8e944482c594aedcc767132ea707070318858 100644 (file)
@@ -1,25 +1,26 @@
 /* gpgme.c -  GnuPG Made Easy
*     Copyright (C) 2000 Werner Koch (dd9jn)
*      Copyright (C) 2001, 2002 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, 2002 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.  */
+
+#if HAVE_CONFIG_H
 #include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -348,8 +349,9 @@ gpgme_set_keylist_mode (GpgmeCtx ctx, int mode)
     return mk_error (Invalid_Value);
 
   if (!((mode & GPGME_KEYLIST_MODE_LOCAL)
-       || (mode & GPGME_KEYLIST_MODE_EXTERN)))
-    return mk_error (Invalid_Value);
+       || (mode & GPGME_KEYLIST_MODE_EXTERN)
+       || (mode & GPGME_KEYLIST_MODE_SIGS)))
+     return mk_error (Invalid_Value);
 
   ctx->keylist_mode = mode;
   return 0;
index 6d9e4b7301ef24761642f2fc2060acdf564a3a57..198a06b0a54a8a9b17722d92876b44229dacdc18 100644 (file)
@@ -196,7 +196,8 @@ typedef enum
     GPGME_ATTR_CHAINID      = 28,
     GPGME_ATTR_SIG_STATUS   = 29,
     GPGME_ATTR_ERRTOK       = 30,
-    GPGME_ATTR_SIG_SUMMARY  = 31
+    GPGME_ATTR_SIG_SUMMARY  = 31,
+    GPGME_ATTR_SIG_CLASS    = 32
   }
 GpgmeAttr;
 
@@ -611,6 +612,13 @@ GpgmeError gpgme_data_rewind (GpgmeData dh);
 
 /* Key and trust functions.  */
 
+/* Get the key with the fingerprint FPR from the key cache or from the
+   crypto backend.  If FORCE_UPDATE is true, force a refresh of the
+   key from the crypto backend and replace the key in the cache, if
+   any.  If SECRET is true, get the secret key.  */
+GpgmeError gpgme_get_key (GpgmeCtx ctx, const char *fpr, GpgmeKey *r_key,
+                         int secret, int force_update);
+
 /* Acquire a reference to KEY.  */
 void gpgme_key_ref (GpgmeKey key);
 
@@ -635,6 +643,21 @@ const char *gpgme_key_get_string_attr (GpgmeKey key, GpgmeAttr what,
 unsigned long gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what,
                                        const void *reserved, int idx);
 
+/* Return the value of the attribute WHAT of a signature on user ID
+   UID_IDX in KEY, which has to be representable by a string.  IDX
+   specifies the signature.  */
+const char *gpgme_key_sig_get_string_attr (GpgmeKey key, int uid_idx,
+                                          GpgmeAttr what,
+                                          const void *reserved, int idx);
+
+/* Return the value of the attribute WHAT of a signature on user ID
+   UID_IDX in KEY, which has to be representable by an unsigned
+   integer string.  IDX specifies the signature.  */
+unsigned long gpgme_key_sig_get_ulong_attr (GpgmeKey key, int uid_idx,
+                                           GpgmeAttr what,
+                                           const void *reserved, int idx);
+
+
 /* Release the trust item ITEM.  */
 void gpgme_trust_item_release (GpgmeTrustItem item);
 
index ebb060d9dd87dbca6e2236db0a0b1ea702a1f0c7..d67d710d35a4750160f578e584d6e32214212242 100644 (file)
@@ -1,25 +1,26 @@
-/* key.c - Key and keyList objects
- *     Copyright (C) 2000 Werner Koch (dd9jn)
- *      Copyright (C) 2001, 2002 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
- */
+/* key.c - Key objects.
+   Copyright (C) 2000 Werner Koch (dd9jn)
+   Copyright (C) 2001, 2002 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.  */
 
+#if HAVE_CONFIG_H
 #include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "key.h"
 #include "sema.h"
 
-#define ALLOC_CHUNK 1024
-#define my_isdigit(a) ((a) >='0' && (a) <= '9')
-
 #if SIZEOF_UNSIGNED_INT < 4
 #error unsigned int too short to be used as a hash value
 #endif
 
+\f
 struct key_cache_item_s
 {
   struct key_cache_item_s *next;
@@ -82,6 +81,7 @@ hash_key (const char *fpr, unsigned int *rhash)
   return 0;
 }
 
+
 void
 _gpgme_key_cache_init (void)
 {
@@ -251,7 +251,7 @@ _gpgme_key_cache_get (const char *fpr)
   return NULL;
 }
 
-
+\f
 static const char *
 pkalgo_to_string (int algo)
 {
@@ -288,12 +288,14 @@ key_new (GpgmeKey *r_key, int secret)
   return 0;
 }
 
+
 GpgmeError
 _gpgme_key_new (GpgmeKey *r_key)
 {
   return key_new (r_key, 0);
 }
 
+
 GpgmeError
 _gpgme_key_new_secret (GpgmeKey *r_key)
 {
@@ -317,7 +319,7 @@ gpgme_key_ref (GpgmeKey key)
   UNLOCK (key_ref_lock);
 }
 
-
+\f
 static struct subkey_s *
 add_subkey (GpgmeKey key, int secret)
 {
@@ -327,7 +329,7 @@ add_subkey (GpgmeKey key, int secret)
   if (!k)
     return NULL;
 
-  if(!(kk = key->keys.next))
+  if (!(kk = key->keys.next))
     key->keys.next = k;
   else
     {
@@ -354,7 +356,173 @@ _gpgme_key_add_secret_subkey (GpgmeKey key)
   return add_subkey (key, 1);
 }
 
+\f
+static char *
+set_user_id_part (char *tail, const char *buf, size_t len)
+{
+  while (len && (buf[len - 1] == ' ' || buf[len - 1] == '\t')) 
+    len--;
+  for (; len; len--)
+    *tail++ = *buf++;
+  *tail++ = 0;
+  return tail;
+}
+
+
+static void
+parse_user_id (const char *src, const char **name, const char **email,
+                   const char **comment, char *tail)
+{
+  const char *start = NULL;
+  int in_name = 0;
+  int in_email = 0;
+  int in_comment = 0;
 
+  while (*src)
+    {
+      if (in_email)
+       {
+         if (*src == '<')
+           /* Not legal but anyway.  */
+           in_email++;
+         else if (*src == '>')
+           {
+             if (!--in_email && !*email)
+               {
+                 *email = tail;
+                 tail = set_user_id_part (tail, start, src - start);
+               }
+           }
+       }
+      else if (in_comment)
+       {
+         if (*src == '(')
+           in_comment++;
+         else if (*src == ')')
+           {
+             if (!--in_comment && !*comment)
+               {
+                 *comment = tail;
+                 tail = set_user_id_part (tail, start, src - start);
+               }
+           }
+       }
+      else if (*src == '<')
+       {
+         if (in_name)
+           {
+             if (!*name)
+               {
+                 *name = tail;
+                 tail = set_user_id_part (tail, start, src - start);
+               }
+             in_name = 0;
+           }
+         in_email = 1;
+         start = src + 1;
+       }
+      else if (*src == '(')
+       {
+         if (in_name)
+           {
+             if (!*name)
+               {
+                 *name = tail;
+                 tail = set_user_id_part (tail, start, src - start);
+               }
+             in_name = 0;
+           }
+         in_comment = 1;
+         start = src + 1;
+       }
+      else if (!in_name && *src != ' ' && *src != '\t')
+       {
+         in_name = 1;
+         start = src;
+       }    
+      src++;
+    }
+  if (in_name)
+    {
+      if (!*name)
+       {
+         *name = tail;
+         tail = set_user_id_part (tail, start, src - start);
+       }
+    }
+  /* Let unused parts point to an EOS.  */
+  tail--;
+  if (!*name)
+    *name = tail;
+  if (!*email)
+    *email = tail;
+  if (!*comment)
+    *comment = tail;
+}
+
+
+static void
+parse_x509_user_id (const char *src, const char **name, const char **email,
+                   const char **comment, char *tail)
+{
+  if (*src == '<' && src[strlen (src) - 1] == '>')
+    *email = src;
+  
+  /* Let unused parts point to an EOS.  */
+  tail--;
+  if (!*name)
+    *name = tail;
+  if (!*email)
+    *email = tail;
+  if (!*comment)
+    *comment = tail;
+}
+
+\f
+struct certsig_s *
+_gpgme_key_add_certsig (GpgmeKey key, char *src)
+{
+  int src_len = src ? strlen (src) : 0;
+  struct user_id_s *uid;
+  struct certsig_s *certsig;
+
+  assert (key);        /* XXX */
+
+  uid = key->last_uid;
+  assert (uid);        /* XXX */
+
+  /* We can malloc a buffer of the same length, because the converted
+     string will never be larger. Actually we allocate it twice the
+     size, so that we are able to store the parsed stuff there too.  */
+  certsig = calloc (1, sizeof (*certsig) + 2 * src_len + 3);
+  if (!certsig)
+    return NULL;
+
+  if (src)
+    {
+      char *dst = certsig->name;
+      _gpgme_decode_c_string (src, &dst, src_len + 1);
+      dst += src_len + 1;
+      if (key->x509)
+       parse_x509_user_id (src, &certsig->name_part, &certsig->email_part,
+                           &certsig->comment_part, dst);
+      else
+       parse_user_id (src, &certsig->name_part, &certsig->email_part,
+                      &certsig->comment_part, dst);
+    }
+
+  if (!uid->certsigs)
+    uid->certsigs = certsig;
+  if (uid->last_certsig)
+    uid->last_certsig->next = certsig;
+  uid->last_certsig = certsig;
+
+  return certsig;
+}
+
+\f
 /**
  * gpgme_key_release:
  * @key: Key Object or NULL
@@ -406,6 +574,7 @@ gpgme_key_release (GpgmeKey key)
   free (key);
 }
 
+
 /**
  * gpgme_key_unref:
  * @key: Key Object
@@ -418,237 +587,41 @@ gpgme_key_unref (GpgmeKey key)
   gpgme_key_release (key);
 }
 
-
-static char *
-set_user_id_part (char *tail, const char *buf, size_t len)
-{
-  while (len && (buf[len-1] == ' ' || buf[len-1] == '\t')) 
-    len--;
-  for (; len; len--)
-    *tail++ = *buf++;
-  *tail++ = 0;
-  return tail;
-}
-
-
-static void
-parse_user_id (struct user_id_s *uid, char *tail)
-{
-  const char *s, *start=NULL;
-  int in_name = 0;
-  int in_email = 0;
-  int in_comment = 0;
-
-    for (s = uid->name; *s; s++)
-      {
-        if (in_email)
-         {
-            if (*s == '<')
-             /* Not legal but anyway.  */
-             in_email++;
-            else if (*s == '>')
-             {
-                if (!--in_email)
-                 {
-                    if (!uid->email_part)
-                     {
-                        uid->email_part = tail;
-                        tail = set_user_id_part ( tail, start, s-start );
-                     }
-                 }
-             }
-         }
-        else if (in_comment)
-         {
-            if (*s == '(')
-             in_comment++;
-            else if (*s== ')')
-             {
-                if (!--in_comment)
-                 {
-                    if (!uid->comment_part)
-                     {
-                        uid->comment_part = tail;
-                        tail = set_user_id_part ( tail, start, s-start );
-                     }
-                 }
-             }
-         }
-        else if (*s == '<')
-         {
-            if (in_name)
-             {
-                if (!uid->name_part)
-                 {
-                    uid->name_part = tail;
-                    tail = set_user_id_part (tail, start, s-start);
-                 }
-                in_name = 0;
-             }
-            in_email = 1;
-            start = s+1;
-         }
-        else if (*s == '(')
-         {
-            if (in_name)
-             {
-                if (!uid->name_part)
-                 {
-                    uid->name_part = tail;
-                    tail = set_user_id_part (tail, start, s-start );
-                 }
-                in_name = 0;
-             }
-            in_comment = 1;
-            start = s+1;
-         }
-        else if (!in_name && *s != ' ' && *s != '\t')
-         {
-            in_name = 1;
-            start = s;
-         }    
-      }
-    if (in_name)
-      {
-        if (!uid->name_part)
-         {
-            uid->name_part = tail;
-            tail = set_user_id_part (tail, start, s-start);
-         }
-      }
-    /* Let unused parts point to an EOS.  */
-    tail--;
-    if (!uid->name_part)
-      uid->name_part = tail;
-    if (!uid->email_part)
-      uid->email_part = tail;
-    if (!uid->comment_part)
-      uid->comment_part = tail;
-}
-
-static void
-parse_x509_user_id (struct user_id_s *uid, char *tail)
-{
-  const char *s;
-
-  s=uid->name; 
-  if (*s == '<' && s[strlen (s) - 1] == '>')
-    uid->email_part = s;
-  
-  /* Let unused parts point to an EOS.  */
-  tail--;
-  if (!uid->name_part)
-    uid->name_part = tail;
-  if (!uid->email_part)
-    uid->email_part = tail;
-  if (!uid->comment_part)
-    uid->comment_part = tail;
-}
-
-/* 
- * Take a name from the --with-colon listing, remove certain escape sequences
- * sequences and put it into the list of UIDs
- */
+\f
+/* Take a name from the --with-colon listing, remove certain escape
+   sequences sequences and put it into the list of UIDs.  */
 GpgmeError
-_gpgme_key_append_name (GpgmeKey key, const char *s)
+_gpgme_key_append_name (GpgmeKey key, const char *src)
 {
   struct user_id_s *uid;
-  char *d;
+  char *dst;
+  int src_len = strlen (src);
 
   assert (key);
   /* We can malloc a buffer of the same length, because the converted
      string will never be larger. Actually we allocate it twice the
      size, so that we are able to store the parsed stuff there too.  */
-  uid = malloc (sizeof *uid + 2*strlen (s)+3);
+  uid = malloc (sizeof (*uid) + 2 * src_len + 3);
   if (!uid)
     return mk_error (Out_Of_Core);
   memset (uid, 0, sizeof *uid);
-  d = uid->name;
 
-  while (*s)
-    {
-      if (*s != '\\')
-       *d++ = *s++;
-      else if (s[1] == '\\')
-       {
-         s++;
-         *d++ = *s++; 
-        }
-      else if (s[1] == 'n')
-       {
-         s += 2;
-         *d++ = '\n'; 
-        }
-      else if (s[1] == 'r')
-       {
-         s += 2;
-         *d++ = '\r'; 
-        }
-      else if (s[1] == 'v')
-       {
-         s += 2;
-         *d++ = '\v'; 
-        }
-      else if (s[1] == 'b')
-       {
-         s += 2;
-         *d++ = '\b'; 
-        }
-      else if (s[1] == '0')
-       {
-         /* Hmmm: no way to express this */
-         s += 2;
-         *d++ = '\\';
-         *d++ = '\0'; 
-        }
-      else if (s[1] == 'x' && isxdigit (s[2]) && isxdigit (s[3]))
-       {
-         int val = _gpgme_hextobyte (&s[2]);
-         if (val == -1)
-           {
-             /* Should not happen.  */
-             *d++ = *s++;
-             *d++ = *s++;
-             *d++ = *s++;
-             *d++ = *s++;
-           }
-         else
-           {
-             if (!val)
-               {
-                 *d++ = '\\';
-                 *d++ = '\0'; 
-               }
-             else 
-               *(byte*)d++ = val;
-             s += 4;
-           }
-        }
-      else
-       {
-         /* should not happen */
-         s++;
-         *d++ = '\\'; 
-         *d++ = *s++;
-        } 
-    }
-  *d++ = 0;
+  dst = uid->name;
+  _gpgme_decode_c_string (src, &dst, src_len + 1);
+
+  dst += src_len + 1;
   if (key->x509)
-    parse_x509_user_id (uid, d);
+    parse_x509_user_id (src, &uid->name_part, &uid->email_part,
+                       &uid->comment_part, dst);
   else
-    parse_user_id (uid, d);
+    parse_user_id (src, &uid->name_part, &uid->email_part,
+                  &uid->comment_part, dst);
 
-  if (key->uids)
-    {
-      struct user_id_s *u = key->uids;
-      while (u->next)
-       u = u->next;
-      u->next = uid;
-    }
-  else
+  if (!key->uids)
     key->uids = uid;
+  if (key->last_uid)
+    key->last_uid->next = uid;
+  key->last_uid = uid;
 
   return 0;
 }
@@ -662,6 +635,7 @@ add_otag (GpgmeData d, const char *tag)
   _gpgme_data_append_string (d, ">");
 }
 
+
 static void
 add_ctag (GpgmeData d, const char *tag)
 {
@@ -670,6 +644,7 @@ add_ctag (GpgmeData d, const char *tag)
   _gpgme_data_append_string (d, ">\n");
 }
 
+
 static void
 add_tag_and_string (GpgmeData d, const char *tag, const char *string)
 {
@@ -678,6 +653,7 @@ add_tag_and_string (GpgmeData d, const char *tag, const char *string)
   add_ctag (d, tag); 
 }
 
+
 static void
 add_tag_and_uint (GpgmeData d, const char *tag, unsigned int val)
 {
@@ -686,6 +662,7 @@ add_tag_and_uint (GpgmeData d, const char *tag, unsigned int val)
   add_tag_and_string (d, tag, buf);
 }
 
+
 static void
 add_tag_and_time (GpgmeData d, const char *tag, time_t val)
 {
@@ -697,22 +674,55 @@ add_tag_and_time (GpgmeData d, const char *tag, time_t val)
   add_tag_and_string (d, tag, buf);
 }
 
+
 static void
-one_uid_as_xml (GpgmeData d, struct user_id_s *u)
+one_certsig_as_xml (GpgmeData data, struct certsig_s *certsig)
 {
-  _gpgme_data_append_string (d, "  <userid>\n");
-  if (u->invalid)
-    _gpgme_data_append_string (d, "    <invalid/>\n");
-  if (u->revoked)
-    _gpgme_data_append_string (d, "    <revoked/>\n");
-  add_tag_and_string (d, "raw", u->name);
-  if (*u->name_part)
-    add_tag_and_string (d, "name", u->name_part);
-  if (*u->email_part)
-    add_tag_and_string (d, "email", u->email_part);
-  if (*u->comment_part)
-    add_tag_and_string (d, "comment", u->comment_part);
-  _gpgme_data_append_string (d, "  </userid>\n");
+  _gpgme_data_append_string (data, "    <signature>\n");
+  if (certsig->flags.invalid)
+    _gpgme_data_append_string (data, "      <invalid/>\n");
+  if (certsig->flags.revoked)
+    _gpgme_data_append_string (data, "      <revoked/>\n");
+  if (certsig->flags.expired)
+    _gpgme_data_append_string (data, "      <expired/>\n");
+  add_tag_and_string (data, "keyid", certsig->keyid);
+  add_tag_and_uint (data, "algo", certsig->algo);
+  add_tag_and_time (data, "created", certsig->timestamp);
+  add_tag_and_time (data, "expire", certsig->expires_at);
+  if (*certsig->name)
+    add_tag_and_string (data, "raw", certsig->name);
+  if (*certsig->name_part)
+    add_tag_and_string (data, "name", certsig->name_part);
+  if (*certsig->email_part)
+    add_tag_and_string (data, "email", certsig->email_part);
+  if (*certsig->comment_part)
+    add_tag_and_string (data, "comment", certsig->comment_part);
+  _gpgme_data_append_string (data, "    </signature>\n");
+}
+
+
+static void
+one_uid_as_xml (GpgmeData data, struct user_id_s *uid)
+{
+  struct certsig_s *certsig;
+
+  _gpgme_data_append_string (data, "  <userid>\n");
+  if (uid->invalid)
+    _gpgme_data_append_string (data, "    <invalid/>\n");
+  if (uid->revoked)
+    _gpgme_data_append_string (data, "    <revoked/>\n");
+  add_tag_and_string (data, "raw", uid->name);
+  if (*uid->name_part)
+    add_tag_and_string (data, "name", uid->name_part);
+  if (*uid->email_part)
+    add_tag_and_string (data, "email", uid->email_part);
+  if (*uid->comment_part)
+    add_tag_and_string (data, "comment", uid->comment_part);
+
+  /* Now the signatures.  */
+  for (certsig = uid->certsigs; certsig; certsig = certsig->next)
+    one_certsig_as_xml (data, certsig);
+  _gpgme_data_append_string (data, "  </userid>\n");
 }
 
 
@@ -817,8 +827,8 @@ capabilities_to_string (struct subkey_s *k)
       "esc"
     };
   return strings[(!!k->flags.can_encrypt << 2)
-                | (!!k->flags.can_sign    << 1)
-                | (!!k->flags.can_certify     )];
+                | (!!k->flags.can_sign << 1)
+                | (!!k->flags.can_certify)];
 }
 
 
@@ -972,6 +982,7 @@ gpgme_key_get_string_attr (GpgmeKey key, GpgmeAttr what,
     case GPGME_ATTR_SIG_STATUS:
     case GPGME_ATTR_SIG_SUMMARY:
     case GPGME_ATTR_ERRTOK:
+    case GPGME_ATTR_SIG_CLASS:
       /* Not of any use here.  */
       break;
     }
@@ -1102,3 +1113,154 @@ gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what,
   return val;
 }
 
+
+static struct certsig_s *
+get_certsig (GpgmeKey key, int uid_idx, int idx)
+{
+  struct user_id_s *uid;
+  struct certsig_s *certsig;
+
+  if (!key || uid_idx < 0 || idx < 0)
+    return NULL;
+
+  uid = key->uids;
+  while (uid && uid_idx > 0)
+    {
+      uid = uid->next;
+      uid_idx--;
+    }
+  if (!uid)
+    return NULL;
+
+  certsig = uid->certsigs;
+  while (certsig && idx > 0)
+    {
+      certsig = certsig->next;
+      idx--;
+    }
+  return certsig;
+}
+
+
+const char *
+gpgme_key_sig_get_string_attr (GpgmeKey key, int uid_idx, GpgmeAttr what,
+                              const void *reserved, int idx)
+{
+  struct certsig_s *certsig = get_certsig (key, uid_idx, idx);
+
+  if (!certsig || reserved)
+    return NULL;
+
+  switch (what)
+    {
+    case GPGME_ATTR_KEYID:
+      return certsig->keyid;
+
+    case GPGME_ATTR_ALGO:    
+      return pkalgo_to_string (certsig->algo);
+
+    case GPGME_ATTR_USERID:  
+      return certsig->name;
+
+    case GPGME_ATTR_NAME:   
+      return certsig->name_part;
+
+    case GPGME_ATTR_EMAIL:
+      return certsig->email_part;
+
+    case GPGME_ATTR_COMMENT:
+      return certsig->comment_part;
+   
+    default:
+      return NULL;
+    }
+}
+
+
+unsigned long
+gpgme_key_sig_get_ulong_attr (GpgmeKey key, int uid_idx, GpgmeAttr what,
+                             const void *reserved, int idx)
+{
+  struct certsig_s *certsig = get_certsig (key, uid_idx, idx);
+
+  if (!certsig || reserved)
+    return 0;
+
+  switch (what)
+    {
+    case GPGME_ATTR_ALGO:    
+      return (unsigned long) certsig->algo;
+
+    case GPGME_ATTR_CREATED: 
+      return certsig->timestamp < 0 ? 0L : (unsigned long) certsig->timestamp;
+
+    case GPGME_ATTR_EXPIRE: 
+      return certsig->expires_at < 0 ? 0L : (unsigned long) certsig->expires_at;
+
+    case GPGME_ATTR_KEY_REVOKED:
+      return certsig->flags.revoked;
+
+    case GPGME_ATTR_KEY_INVALID:
+      return certsig->flags.invalid;
+
+    case GPGME_ATTR_KEY_EXPIRED:
+      return certsig->flags.expired;
+
+    case GPGME_ATTR_SIG_CLASS:
+      return certsig->sig_class;
+
+    case GPGME_ATTR_SIG_STATUS:
+      return certsig->sig_stat;
+
+    default:
+      return 0;
+    }
+}
+
+
+/* Get the key with the fingerprint FPR from the key cache or from the
+   crypto backend.  If FORCE_UPDATE is true, force a refresh of the
+   key from the crypto backend and replace the key in the cache, if
+   any.  If SECRET is true, get the secret key.  */
+GpgmeError
+gpgme_get_key (GpgmeCtx ctx, const char *fpr, GpgmeKey *r_key,
+              int secret, int force_update)
+{
+  GpgmeCtx listctx;
+  GpgmeError err;
+
+  if (!ctx || !r_key)
+    return mk_error (Invalid_Value);
+  if (ctx->pending)
+    return mk_error (Busy);
+  
+  if (strlen (fpr) < 16)       /* We have at least a key ID.  */
+    return mk_error (Invalid_Key);
+
+  if (!force_update)
+    {
+      *r_key = _gpgme_key_cache_get (fpr);
+      if (*r_key)
+       {
+         /* If the primary UID (if available) has no signatures, and
+            we are in the signature listing keylist mode, then try to
+            update the key below before returning.  */
+         if (!((ctx->keylist_mode & GPGME_KEYLIST_MODE_SIGS)
+               && (*r_key)->uids && !(*r_key)->uids->certsigs))
+           return 0;
+       }
+    }
+
+  /* Fixme: This can be optimized by keeping an internal context
+     used for such key listings.  */
+  err = gpgme_new (&listctx);
+  if (err)
+    return err;
+  gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
+  gpgme_set_keylist_mode (listctx, ctx->keylist_mode);
+  err = gpgme_op_keylist_start (listctx, fpr, secret);
+  if (!err)
+    err = gpgme_op_keylist_next (listctx, r_key);
+  gpgme_release (listctx);
+  return err;
+}
index 2c1114e06fd70f491670ced46433be1e592ce50a..f2c4025a701f1fd7ff47922caca6c7a8cb6f2d00 100644 (file)
@@ -1,23 +1,22 @@
-/* key.h 
- *     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
- */
+/* key.h - Key handling interface.
+   Copyright (C) 2000 Werner Koch (dd9jn)
+   Copyright (C) 2001, 2002 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.  */
 
 #ifndef KEY_H
 #define KEY_H
 #include "types.h"
 #include "context.h"
 
-struct certsig_s {
+\f
+struct certsig_s
+{
   struct certsig_s *next;
-  struct {
-    unsigned int revoked:1 ;
-    unsigned int expired:1 ;
-    unsigned int invalid:1 ;
+  struct
+  {
+    unsigned int revoked : 1;
+    unsigned int expired : 1;
+    unsigned int invalid : 1;
+    unsigned int exportable : 1;
   } flags;
-  char keyid[16+1]; 
-  time_t timestamp;  /* -1 for invalid, 0 for not available */
-  time_t expires_at; /* 0 for does not expires */
+  unsigned int algo;
+  char keyid[16 + 1]; 
+  time_t timestamp;            /* -1 for invalid, 0 for not available.  */
+  time_t expires_at;           /* 0 for no expiration.  */
+  GpgmeSigStat sig_stat;
+  unsigned int sig_class;
+  const char *name_part;       /* All 3 point into strings behind name  */
+  const char *email_part;      /* or to read-only strings.  */
+  const char *comment_part;
+  char name[1];
 };
 
-struct subkey_s {
+
+struct subkey_s
+{
   struct subkey_s *next;
   unsigned int secret:1;
-  struct {
-    unsigned int revoked:1 ;
-    unsigned int expired:1 ;
-    unsigned int disabled:1 ;
-    unsigned int invalid:1 ;
-    unsigned int can_encrypt:1;
-    unsigned int can_sign:1;
-    unsigned int can_certify:1;
+  struct
+  {
+    unsigned int revoked : 1;
+    unsigned int expired : 1;
+    unsigned int disabled : 1;
+    unsigned int invalid : 1;
+    unsigned int can_encrypt : 1;
+    unsigned int can_sign : 1;
+    unsigned int can_certify : 1;
   } flags;
   unsigned int key_algo;
   unsigned int key_len;
-  char keyid[16+1]; 
-  char *fingerprint; /* malloced hex digits */
-  time_t timestamp;  /* -1 for invalid, 0 for not available */
-  time_t expires_at; /* 0 for does not expires */
+  char keyid[16 + 1];
+  char *fingerprint;   /* Malloced hex digits.  */
+  time_t timestamp;    /* -1 for invalid, 0 for not available.  */
+  time_t expires_at;   /* 0 for does not expires.  */
 };
 
-struct gpgme_key_s {
-  struct {
-    unsigned int revoked:1 ;
-    unsigned int expired:1 ;
-    unsigned int disabled:1 ;
-    unsigned int invalid:1 ;
-    unsigned int can_encrypt:1;
-    unsigned int can_sign:1;
-    unsigned int can_certify:1;
-  } gloflags; 
+
+struct gpgme_key_s
+{
+  struct
+  {
+    unsigned int revoked : 1;
+    unsigned int expired : 1;
+    unsigned int disabled : 1;
+    unsigned int invalid : 1;
+    unsigned int can_encrypt : 1;
+    unsigned int can_sign : 1;
+    unsigned int can_certify : 1;
+  } gloflags;
   unsigned int ref_count;
-  unsigned int secret:1;
-  unsigned int x509:1;
-  char *issuer_serial; /* malloced string used only with X.509 */
-  char *issuer_name;   /* ditto */
-  char *chain_id;      /* ditto */
-  GpgmeValidity otrust; /* only used with OpenPGP */
-  struct subkey_s   keys; 
+  unsigned int secret : 1;
+  unsigned int x509 : 1;
+  char *issuer_serial; /* Malloced string used only with X.509.  */
+  char *issuer_name;   /* Ditto.  */
+  char *chain_id;      /* Ditto.  */
+  GpgmeValidity otrust;        /* Only used with OpenPGP.  */
+  struct subkey_s keys;
   struct user_id_s *uids;
+  struct user_id_s *last_uid;
 };
 
+\f
 void _gpgme_key_cache_init (void);
 void _gpgme_key_cache_add (GpgmeKey key);
 GpgmeKey _gpgme_key_cache_get (const char *fpr);
 
 
+struct certsig_s *_gpgme_key_add_certsig (GpgmeKey key, char *src);
 struct subkey_s *_gpgme_key_add_subkey (GpgmeKey key);
 struct subkey_s *_gpgme_key_add_secret_subkey (GpgmeKey key);
-GpgmeError _gpgme_key_append_name ( GpgmeKey key, const char *s );
-
-
+GpgmeError _gpgme_key_append_name (GpgmeKey key, const char *str);
 
-#endif /* KEY_H */
+#endif /* KEY_H */
index 8c78a5f192fbfd8fa806971016f9bb090113fe22..09cb89f09e19e961ed22a92400cc31f6e49448f2 100644 (file)
@@ -1,46 +1,45 @@
-/* keylist.c -  key listing
*     Copyright (C) 2000 Werner Koch (dd9jn)
*      Copyright (C) 2001, 2002 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
- */
-
+/* keylist.c - Listing keys.
+   Copyright (C) 2000 Werner Koch (dd9jn)
+   Copyright (C) 2001, 2002 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.  */
+
+#if HAVE_CONFIG_H
 #include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <assert.h>
+#include <ctype.h>
 
 #include "util.h"
 #include "context.h"
 #include "ops.h"
 #include "key.h"
 
-#define my_isdigit(a) ( (a) >='0' && (a) <= '9' )
-
+\f
 struct keylist_result_s
 {
   int truncated;
   GpgmeData xmlinfo;
 };
 
-static void finish_key ( GpgmeCtx ctx );
-
 
 void
 _gpgme_release_keylist_result (KeylistResult result)
@@ -50,6 +49,7 @@ _gpgme_release_keylist_result (KeylistResult result)
   free (result);
 }
 
+
 /* Append some XML info.  args is currently ignore but we might want
    to add more information in the future (like source of the
    keylisting.  With args of NULL the XML structure is closed.  */
@@ -78,15 +78,11 @@ append_xml_keylistinfo (GpgmeData *rdh, char *args)
       return;
     }
 
-  _gpgme_data_append_string (dh,
-                             "  <keylisting>\n"
-                             "    <truncated/>\n"
-                            );
+  _gpgme_data_append_string (dh, "  <keylisting>\n    <truncated/>\n");
     
 }
 
 
-
 static void
 keylist_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
 {
@@ -117,414 +113,577 @@ keylist_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
     }
 }
 
-
+\f
 static time_t
-parse_timestamp (char *p)
+parse_timestamp (char *timestamp)
 {
-  if (!*p)
+  if (!*timestamp)
     return 0;
 
-  return (time_t)strtoul (p, NULL, 10);
+  return (time_t) strtoul (timestamp, NULL, 10);
 }
 
 
 static void
-set_mainkey_trust_info (GpgmeKey key, const char *s)
+set_mainkey_trust_info (GpgmeKey key, const char *src)
 {
   /* Look at letters and stop at the first digit.  */
-  for (; *s && !my_isdigit (*s); s++)
+  while (*src && !isdigit (*src))
     {
-      switch (*s)
+      switch (*src)
        {
-       case 'e': key->keys.flags.expired = 1; break;
-       case 'r': key->keys.flags.revoked = 1; break;
-       case 'd': key->keys.flags.disabled = 1; break;
-       case 'i': key->keys.flags.invalid = 1; break;
+       case 'e':
+         key->keys.flags.expired = 1;
+         break;
+
+       case 'r':
+         key->keys.flags.revoked = 1;
+         break;
+
+       case 'd':
+         key->keys.flags.disabled = 1;
+         break;
+
+       case 'i':
+         key->keys.flags.invalid = 1;
+         break;
         }
+      src++;
     }
 }
 
 
 static void
-set_userid_flags (GpgmeKey key, const char *s)
+set_userid_flags (GpgmeKey key, const char *src)
 {
-  struct user_id_s *u = key->uids;
-
-  assert (u);
-  while (u->next)
-    u = u->next;
+  struct user_id_s *uid = key->last_uid;
 
+  assert (uid);
   /* Look at letters and stop at the first digit.  */
-  for (; *s && !my_isdigit (*s); s++)
+  while (*src && !isdigit (*src))
     {
-      switch (*s)
+      switch (*src)
        {
-       case 'r': u->revoked  = 1; break;
-       case 'i': u->invalid  = 1; break;
-
-       case 'n': u->validity = GPGME_VALIDITY_NEVER; break;
-       case 'm': u->validity = GPGME_VALIDITY_MARGINAL; break;
-       case 'f': u->validity = GPGME_VALIDITY_FULL; break;
-       case 'u': u->validity = GPGME_VALIDITY_ULTIMATE; break;
+       case 'r':
+         uid->revoked = 1;
+         break;
+         
+       case 'i':
+         uid->invalid = 1;
+         break;
+
+       case 'n':
+         uid->validity = GPGME_VALIDITY_NEVER;
+         break;
+
+       case 'm':
+         uid->validity = GPGME_VALIDITY_MARGINAL;
+         break;
+
+       case 'f':
+         uid->validity = GPGME_VALIDITY_FULL;
+         break;
+
+       case 'u':
+         uid->validity = GPGME_VALIDITY_ULTIMATE;
+         break;
         }
+      src++;
     }
 }
 
 
 static void
-set_subkey_trust_info (struct subkey_s *k, const char *s)
+set_subkey_trust_info (struct subkey_s *subkey, const char *src)
 {
   /* Look at letters and stop at the first digit.  */
-  for (; *s && !my_isdigit (*s); s++)
+  while (*src && !isdigit (*src))
     {
-      switch (*s)
+      switch (*src)
        {
-       case 'e': k->flags.expired = 1; break;
-       case 'r': k->flags.revoked = 1; break;
-       case 'd': k->flags.disabled = 1; break;
-       case 'i': k->flags.invalid = 1; break;
+       case 'e':
+         subkey->flags.expired = 1;
+         break;
+
+       case 'r':
+         subkey->flags.revoked = 1;
+         break;
+
+       case 'd':
+         subkey->flags.disabled = 1;
+         break;
+
+       case 'i':
+         subkey->flags.invalid = 1;
+         break;
         }
+      src++;
     }
 }
 
 
 static void
-set_mainkey_capability (GpgmeKey key, const char *s)
+set_mainkey_capability (GpgmeKey key, const char *src)
 {
-  for (; *s ; s++)
+  while (*src)
     {
-      switch (*s)
+      switch (*src)
        {
-       case 'e': key->keys.flags.can_encrypt = 1; break;
-       case 's': key->keys.flags.can_sign = 1; break;
-       case 'c': key->keys.flags.can_certify = 1; break;
-       case 'E': key->gloflags.can_encrypt = 1; break;
-       case 'S': key->gloflags.can_sign = 1; break;
-       case 'C': key->gloflags.can_certify = 1; break;
+       case 'e':
+         key->keys.flags.can_encrypt = 1;
+         break;
+
+       case 's':
+         key->keys.flags.can_sign = 1;
+         break;
+
+       case 'c':
+         key->keys.flags.can_certify = 1;
+         break;
+
+       case 'E':
+         key->gloflags.can_encrypt = 1;
+         break;
+
+       case 'S':
+         key->gloflags.can_sign = 1;
+         break;
+
+       case 'C':
+         key->gloflags.can_certify = 1;
+         break;
         }
+      src++;
     }
 }
 
 
 static void
-set_subkey_capability ( struct subkey_s *k, const char *s)
+set_subkey_capability (struct subkey_s *subkey, const char *src)
 {
-  for (; *s; s++)
+  while (*src)
     {
-      switch (*s)
+      switch (*src)
        {
-       case 'e': k->flags.can_encrypt = 1; break;
-       case 's': k->flags.can_sign = 1; break;
-       case 'c': k->flags.can_certify = 1; break;
+       case 'e':
+         subkey->flags.can_encrypt = 1;
+         break;
+
+       case 's':
+         subkey->flags.can_sign = 1;
+         break;
+
+       case 'c':
+         subkey->flags.can_certify = 1;
+         break;
         }
+      src++;
     }
 }
 
 static void
-set_ownertrust (GpgmeKey key, const char *s)
+set_ownertrust (GpgmeKey key, const char *src)
 {
   /* Look at letters and stop at the first digit.  */
-  for (; *s && !my_isdigit (*s); s++)
+  while (*src && !isdigit (*src))
     {
-      switch (*s)
+      switch (*src)
        {
-       case 'n': key->otrust = GPGME_VALIDITY_NEVER; break;
-       case 'm': key->otrust = GPGME_VALIDITY_MARGINAL; break;
-       case 'f': key->otrust = GPGME_VALIDITY_FULL; break;
-       case 'u': key->otrust = GPGME_VALIDITY_ULTIMATE; break;
-        default : key->otrust = GPGME_VALIDITY_UNKNOWN; break;
+       case 'n':
+         key->otrust = GPGME_VALIDITY_NEVER;
+         break;
+
+       case 'm':
+         key->otrust = GPGME_VALIDITY_MARGINAL;
+         break;
+
+       case 'f':
+         key->otrust = GPGME_VALIDITY_FULL;
+         break;
+
+       case 'u':
+         key->otrust = GPGME_VALIDITY_ULTIMATE;
+         break;
+
+        default:
+         key->otrust = GPGME_VALIDITY_UNKNOWN;
+         break;
         }
+      src++;
     }
 }
 
 
+/* We have read an entire key into ctx->tmp_key and should now finish
+   it.  It is assumed that this releases ctx->tmp_key.  */
+static void
+finish_key (GpgmeCtx ctx)
+{
+  GpgmeKey key = ctx->tmp_key;
+
+  ctx->tmp_key = NULL;
+
+  if (key)
+    _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key);
+}
+
+
 /* Note: We are allowed to modify LINE.  */
 static void
 keylist_colon_handler (GpgmeCtx ctx, char *line)
 {
-  char *p, *pend;
-  int field = 0;
   enum
     {
       RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC,
-      RT_CRT, RT_CRS
+      RT_CRT, RT_CRS, RT_REV
     }
   rectype = RT_NONE;
+#define NR_FIELDS 13
+  char *field[NR_FIELDS];
+  int fields = 0;
   GpgmeKey key = ctx->tmp_key;
-  int i;
-  const char *trust_info = NULL;
-  struct subkey_s *sk = NULL;
+  struct subkey_s *subkey = NULL;
+  struct certsig_s *certsig = NULL;
+
+  DEBUG3 ("keylist_colon_handler ctx = %p, key = %p, line = %s\n",
+         ctx, key, line ? line : "(null)");
 
-  DEBUG3 ("keylist_colon_handler ctx=%p, key=%p, line=%s\n", ctx, key,
-          line? line: "(null)");
   if (ctx->error)
     return;
+
   if (!line)
     {
-      /* EOF */
+      /* End Of File.  */
       finish_key (ctx);
       return; 
     }
-  
-  for (p = line; p; p = pend)
+
+  while (line && fields < NR_FIELDS)
     {
-      field++;
-      pend = strchr (p, ':');
-      if (pend) 
-       *pend++ = 0;
+      field[fields++] = line;
+      line = strchr (line, ':');
+      if (line)
+       *(line++) = '\0';
+    }
 
-      if (field == 1)
+  if (!strcmp (field[0], "sig"))
+    rectype = RT_SIG;
+  else if (!strcmp (field[0], "rev"))
+    rectype = RT_REV;
+  else if (!strcmp (field[0], "uid") && key)
+    rectype = RT_UID;
+  else if (!strcmp (field[0], "sub") && key)
+    {
+      /* Start a new subkey.  */
+      rectype = RT_SUB; 
+      if (!(subkey = _gpgme_key_add_subkey (key)))
        {
-         if (!strcmp (p, "sig"))
-           rectype = RT_SIG;
-         else if (!strcmp (p, "uid") && key)
-           {
-             rectype = RT_UID;
-             key = ctx->tmp_key;
-            }
-         else if (!strcmp (p, "sub") && key)
-           {
-             /* Start a new subkey.  */
-             rectype = RT_SUB; 
-             if (!(sk = _gpgme_key_add_subkey (key)))
-               {
-                 ctx->error = mk_error (Out_Of_Core);
-                 return;
-                }
-            }
-         else if (!strcmp (p, "ssb") && key)
-           {
-             /* Start a new secret subkey.  */
-             rectype = RT_SSB;
-             if (!(sk = _gpgme_key_add_secret_subkey (key)))
-               {
-                 ctx->error = mk_error (Out_Of_Core);
-                 return;
-                }
-            }
-         else if (!strcmp (p, "pub"))
-           {
-             /* Start a new keyblock.  */
-             if (_gpgme_key_new (&key))
-               {
-                 /* The only kind of error we can get.  */
-                 ctx->error = mk_error (Out_Of_Core);
-                 return;
-                }
-             rectype = RT_PUB;
-             finish_key (ctx);
-             assert (!ctx->tmp_key);
-             ctx->tmp_key = key;
-            }
-         else if (!strcmp (p, "sec"))
-           {
-             /* Start a new keyblock,  */
-             if (_gpgme_key_new_secret (&key))
-               {
-                 /* The only kind of error we can get.  */
-                 ctx->error = mk_error (Out_Of_Core);
-                 return;
-               }
-             rectype = RT_SEC;
-             finish_key (ctx);
-             assert (!ctx->tmp_key);
-             ctx->tmp_key = key;
-            }
-         else if (!strcmp (p, "crt"))
-           {
-             /* Start a new certificate. */
-             if (_gpgme_key_new (&key))
-               {
-                 /* The only kind of error we can get.  */
-                 ctx->error = mk_error (Out_Of_Core);
-                 return;
-               }
-             key->x509 = 1;
-             rectype = RT_CRT;
-             finish_key (ctx);
-             assert (!ctx->tmp_key);
-             ctx->tmp_key = key;
-            }
-         else if (!strcmp (p, "crs"))
-           {
-             /* Start a new certificate.  */
-             if (_gpgme_key_new_secret (&key))
-               {
-                 /* The only kind of error we can get.  */
-                 ctx->error = mk_error (Out_Of_Core);
-                 return;
-                }
-             key->x509 = 1;
-             rectype = RT_CRS;
-             finish_key (ctx);
-             assert (!ctx->tmp_key);
-             ctx->tmp_key = key;
-            }
-         else if (!strcmp (p, "fpr") && key) 
-           rectype = RT_FPR;
-         else 
-           rectype = RT_NONE;
-        }
-      else if (rectype == RT_PUB || rectype == RT_SEC
-              || rectype == RT_CRT || rectype == RT_CRS)
+         ctx->error = mk_error (Out_Of_Core);
+         return;
+       }
+    }
+  else if (!strcmp (field[0], "ssb") && key)
+    {
+      /* Start a new secret subkey.  */
+      rectype = RT_SSB;
+      if (!(subkey = _gpgme_key_add_secret_subkey (key)))
        {
-         switch (field)
-           {
-           case 2: /* trust info */
-             trust_info = p; 
-             set_mainkey_trust_info (key, trust_info);
-             break;
-           case 3: /* key length */
-             i = atoi (p); 
-             if (i > 1) /* ignore invalid values */
-               key->keys.key_len = i; 
-                break;
-           case 4: /* pubkey algo */
-             i = atoi (p);
-             if (i >= 1 && i < 128)
-               key->keys.key_algo = i;
-             break;
-              case 5: /* long keyid */
-                if (strlen (p) == DIM(key->keys.keyid) - 1)
-                 strcpy (key->keys.keyid, p);
-                break;
-           case 6: /* timestamp (seconds) */
-             key->keys.timestamp = parse_timestamp (p);
-             break;
-           case 7: /* expiration time (seconds) */
-             key->keys.expires_at = parse_timestamp (p);
-             break;
-           case 8: /* X.509 serial number */
-              if (rectype == RT_CRT || rectype == RT_CRS)
-                {
-                  key->issuer_serial = strdup (p);
-                 if (!key->issuer_serial)
-                   ctx->error = mk_error (Out_Of_Core);
-                }
-             break;
-           case 9: /* ownertrust */
-              set_ownertrust (key, p);
-             break;
-           case 10:
-             /* Not used for gpg due to --fixed-list-mode option but
-                GPGSM stores the issuer name.  */
-              if (rectype == RT_CRT || rectype == RT_CRS)
-               if (_gpgme_decode_c_string (p, &key->issuer_name))
-                 ctx->error = mk_error (Out_Of_Core);
-             break;
-           case 11: /* signature class  */
-             break;
-           case 12: /* capabilities */
-             set_mainkey_capability (key, p);
-             break;
-           case 13:
-             pend = NULL;  /* we can stop here */
-             break;
-            }
-          }
-        else if ((rectype == RT_SUB || rectype== RT_SSB) && sk)
-         {
-            switch (field)
-             {
-              case 2: /* trust info */
-                set_subkey_trust_info (sk, p);
-                break;
-              case 3: /* key length */
-                i = atoi (p); 
-                if (i > 1) /* ignore invalid values */
-                 sk->key_len = i; 
-                break;
-              case 4: /* pubkey algo */
-                i = atoi (p);
-                if (i >= 1 && i < 128)
-                 sk->key_algo = i;
-                break;
-              case 5: /* long keyid */
-                if (strlen (p) == DIM(sk->keyid) - 1)
-                 strcpy (sk->keyid, p);
-                break;
-              case 6: /* timestamp (seconds) */
-                sk->timestamp = parse_timestamp (p);
-                break;
-              case 7: /* expiration time (seconds) */
-                sk->expires_at = parse_timestamp (p);
-                break;
-              case 8: /* reserved (LID) */
-                break;
-              case 9: /* ownertrust */
-                break;
-              case 10:/* user ID n/a for a subkey */
-                break;
-              case 11:  /* signature class  */
-                break;
-              case 12: /* capability */
-                set_subkey_capability (sk, p);
-                break;
-              case 13:
-                pend = NULL;  /* we can stop here */
-                break;
-            }
-        }
-      else if (rectype == RT_UID)
+         ctx->error = mk_error (Out_Of_Core);
+         return;
+       }
+    }
+  else if (!strcmp (field[0], "pub"))
+    {
+      /* Start a new keyblock.  */
+      if (_gpgme_key_new (&key))
        {
-         switch (field)
-           {
-           case 2: /* trust info */
-             trust_info = p;  /*save for later */
-             break;
-           case 10: /* user ID */
-             if (_gpgme_key_append_name (key, p))
-               /* The only kind of error we can get*/
-               ctx->error = mk_error (Out_Of_Core);
-             else
-               {
-                 if (trust_info)
-                    set_userid_flags (key, trust_info);
-               }
-             pend = NULL;  /* we can stop here */
-             break;
-            }
-        }
-      else if (rectype == RT_FPR)
+         /* The only kind of error we can get.  */
+         ctx->error = mk_error (Out_Of_Core);
+         return;
+       }
+      rectype = RT_PUB;
+      finish_key (ctx);
+      assert (!ctx->tmp_key);
+      ctx->tmp_key = key;
+    }
+  else if (!strcmp (field[0], "sec"))
+    {
+      /* Start a new keyblock,  */
+      if (_gpgme_key_new_secret (&key))
        {
-         switch (field)
-           {
-           case 10: /* fingerprint (take only the first one)*/
-             if (!key->keys.fingerprint && *p)
-               {
-                 key->keys.fingerprint = strdup (p);
-                 if (!key->keys.fingerprint)
-                   ctx->error = mk_error (Out_Of_Core);
-                }
-             break;
-           case 13: /* gpgsm chain ID (take only the first one)*/
-             if (!key->chain_id && *p)
-               {
-                 key->chain_id = strdup (p);
-                 if (!key->chain_id)
-                   ctx->error = mk_error (Out_Of_Core);
-                }
-             pend = NULL; /* that is all we want */
-             break;
-            }
-        }
+         /* The only kind of error we can get.  */
+         ctx->error = mk_error (Out_Of_Core);
+         return;
+       }
+      rectype = RT_SEC;
+      finish_key (ctx);
+      assert (!ctx->tmp_key);
+      ctx->tmp_key = key;
     }
-}
+  else if (!strcmp (field[0], "crt"))
+    {
+      /* Start a new certificate.  */
+      if (_gpgme_key_new (&key))
+       {
+         /* The only kind of error we can get.  */
+         ctx->error = mk_error (Out_Of_Core);
+         return;
+       }
+      key->x509 = 1;
+      rectype = RT_CRT;
+      finish_key (ctx);
+      assert (!ctx->tmp_key);
+      ctx->tmp_key = key;
+    }
+  else if (!strcmp (field[0], "crs"))
+    {
+      /* Start a new certificate.  */
+      if (_gpgme_key_new_secret (&key))
+       {
+         /* The only kind of error we can get.  */
+         ctx->error = mk_error (Out_Of_Core);
+         return;
+       }
+      key->x509 = 1;
+      rectype = RT_CRS;
+      finish_key (ctx);
+      assert (!ctx->tmp_key);
+      ctx->tmp_key = key;
+    }
+  else if (!strcmp (field[0], "fpr") && key) 
+    rectype = RT_FPR;
+  else 
+    rectype = RT_NONE;
+
+  /* Only look at signatures immediately following a user ID.  For
+     this, clear the user ID pointer when encountering anything but a
+     signature.  */
+  if (rectype != RT_SIG && rectype != RT_REV)
+    ctx->tmp_uid = NULL;
+
+  switch (rectype)
+    {
+    case RT_CRT:
+    case RT_CRS:
+      /* Field 8 has the X.509 serial number.  */
+      if (fields >= 8)
+       {
+         key->issuer_serial = strdup (field[7]);
+         if (!key->issuer_serial)
+           ctx->error = mk_error (Out_Of_Core);
+       }
 
+      /* Field 10 is not used for gpg due to --fixed-list-mode option
+        but GPGSM stores the issuer name.  */
+      if (fields >= 10 && _gpgme_decode_c_string (field[9],
+                                                 &key->issuer_name, 0))
+         ctx->error = mk_error (Out_Of_Core);
+      /* Fall through!  */
+
+    case RT_PUB:
+    case RT_SEC:
+      /* Field 2 has the trust info.  */
+      if (fields >= 2)
+       set_mainkey_trust_info (key, field[1]);
+
+      /* Field 3 has the key length.  */
+      if (fields >= 3)
+       {
+         int i = atoi (field[2]);
+         /* Ignore invalid values.  */
+         if (i > 1)
+           key->keys.key_len = i; 
+       }
 
-/*
- * We have read an entire key into ctx->tmp_key and should now finish
- * it.  It is assumed that this releases ctx->tmp_key.
- */
-static void
-finish_key (GpgmeCtx ctx)
-{
-  GpgmeKey key = ctx->tmp_key;
+      /* Field 4 has the public key algorithm.  */
+      if (fields >= 4)
+       {
+         int i = atoi (field[3]);
+         if (i >= 1 && i < 128)
+           key->keys.key_algo = i;
+       }
 
-  ctx->tmp_key = NULL;
+      /* Field 5 has the long keyid.  */
+      if (fields >= 5 && strlen (field[4]) == DIM(key->keys.keyid) - 1)
+       strcpy (key->keys.keyid, field[4]);
 
-  if (key)
-    _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key);
+      /* Field 6 has the timestamp (seconds).  */
+      if (fields >= 6)
+       key->keys.timestamp = parse_timestamp (field[5]);
+
+      /* Field 7 has the expiration time (seconds).  */
+      if (fields >= 7)
+       key->keys.expires_at = parse_timestamp (field[6]);
+
+      /* Field 9 has the ownertrust.  */
+      if (fields >= 9)
+       set_ownertrust (key, field[8]);
+
+      /* Field 11 has the signature class.  */
+
+      /* Field 12 has the capabilities.  */
+      if (fields >= 12)
+       set_mainkey_capability (key, field[11]);
+      break;
+
+    case RT_SUB:
+    case RT_SSB:
+      /* Field 2 has the trust info.  */
+      if (fields >= 2)
+       set_subkey_trust_info (subkey, field[1]);
+
+      /* Field 3 has the key length.  */
+      if (fields >= 3)
+       {
+         int i = atoi (field[2]);
+         /* Ignore invalid values.  */
+         if (i > 1)
+           subkey->key_len = i;
+       }
+
+      /* Field 4 has the public key algorithm.  */
+      if (fields >= 4)
+       {
+         int i = atoi (field[3]);
+         if (i >= 1 && i < 128)
+           subkey->key_algo = i;
+       }
+
+      /* Field 5 has the long keyid.  */
+      if (fields >= 5 && strlen (field[4]) == DIM(subkey->keyid) - 1)
+       strcpy (subkey->keyid, field[4]);
+
+      /* Field 6 has the timestamp (seconds).  */
+      if (fields >= 6)
+       subkey->timestamp = parse_timestamp (field[5]);
+
+      /* Field 7 has the expiration time (seconds).  */
+      if (fields >= 7)
+       subkey->expires_at = parse_timestamp (field[6]);
+
+      /* Field 8 is reserved (LID).  */
+      /* Field 9 has the ownertrust.  */
+      /* Field 10, the user ID, is n/a for a subkey.  */
+      
+      /* Field 11 has the signature class.  */
+
+      /* Field 12 has the capabilities.  */
+      if (fields >= 12)
+       set_subkey_capability (subkey, field[11]);
+      break;
+
+    case RT_UID:
+      /* Field 2 has the trust info, and field 10 has the user ID.  */
+      if (fields >= 10)
+       {
+         if (_gpgme_key_append_name (key, field[9]))
+           ctx->error = mk_error (Out_Of_Core);
+         else
+           {
+             if (field[1])
+               set_userid_flags (key, field[1]);
+             ctx->tmp_uid = key->last_uid;
+           }
+       }
+      break;
+
+    case RT_FPR:
+      /* Field 10 has the fingerprint (take only the first one).  */
+      if (fields >= 10 && !key->keys.fingerprint && field[9] && *field[9])
+       {
+         key->keys.fingerprint = strdup (field[9]);
+         if (!key->keys.fingerprint)
+           ctx->error = mk_error (Out_Of_Core);
+       }
+
+      /* Field 13 has the gpgsm chain ID (take only the first one).  */
+      if (fields >= 13 && !key->chain_id && *field[12])
+       {
+         key->chain_id = strdup (field[12]);
+         if (!key->chain_id)
+           ctx->error = mk_error (Out_Of_Core);
+       }
+      break;
+
+    case RT_SIG:
+    case RT_REV:
+      if (!ctx->tmp_uid)
+       return;
+
+      /* Start a new (revoked) signature.  */
+      assert (ctx->tmp_uid == key->last_uid);
+      certsig = _gpgme_key_add_certsig (key, (fields >= 10) ? field[9] : NULL);
+      if (!certsig)
+       {
+         ctx->error = mk_error (Out_Of_Core);
+         return;
+       }
+
+      /* Field 2 has the calculated trust ('!', '-', '?', '%').  */
+      if (fields >= 2)
+       switch (field[1][0])
+         {
+         case '!':
+           certsig->sig_stat = GPGME_SIG_STAT_GOOD;
+           break;
+
+         case '-':
+           certsig->sig_stat = GPGME_SIG_STAT_BAD;
+           break;
+
+         case '?':
+           certsig->sig_stat = GPGME_SIG_STAT_NOKEY;
+           break;
+
+         case '%':
+           certsig->sig_stat = GPGME_SIG_STAT_ERROR;
+           break;
+
+         default:
+           certsig->sig_stat = GPGME_SIG_STAT_NONE;
+           break;
+         }
+
+      /* Field 4 has the public key algorithm.  */
+      if (fields >= 4)
+       {
+         int i = atoi (field[3]);
+         if (i >= 1 && i < 128)
+           certsig->algo = i;
+       }
+      
+      /* Field 5 has the long keyid.  */
+      if (fields >= 5 && strlen (field[4]) == DIM(certsig->keyid) - 1)
+       strcpy (certsig->keyid, field[4]);
+      
+      /* Field 6 has the timestamp (seconds).  */
+      if (fields >= 6)
+       certsig->timestamp = parse_timestamp (field[5]);
+
+      /* Field 7 has the expiration time (seconds).  */
+      if (fields >= 7)
+       certsig->expires_at = parse_timestamp (field[6]);
+
+      /* Field 11 has the signature class (eg, 0x30 means revoked).  */
+      if (fields >= 11)
+       if (field[10][0] && field[10][1])
+         {
+           int class = _gpgme_hextobyte (field[10]);
+           if (class >= 0)
+             {
+               certsig->sig_class = class;
+               if (class == 0x30)
+                 certsig->flags.revoked = 1;
+             }
+           if (field[10][2] == 'x')
+             certsig->flags.exportable = 1;
+         }
+      break;
+
+    case RT_NONE:
+      /* Unknown record.  */
+      break;
+    }
 }
 
 
@@ -593,9 +752,9 @@ gpgme_op_keylist_start (GpgmeCtx ctx, const char *pattern, int secret_only)
   if (err)
     goto leave;
 
-  /* We don't want to use the verbose mode as this will also print 
-     the key signatures which is in most cases not needed and furthermore we 
-     just ignore those lines - This should speed up things */
+  /* We don't want to use the verbose mode as this will also print the
+     key signatures which is in most cases not needed and furthermore
+     we just ignore those lines - This should speed up things.  */
   _gpgme_engine_set_verbosity (ctx->engine, 0);
 
   err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
@@ -647,15 +806,16 @@ gpgme_op_keylist_ext_start (GpgmeCtx ctx, const char *pattern[],
   if (err)
     goto leave;
 
-  /* We don't want to use the verbose mode as this will also print 
-     the key signatures which is in most cases not needed and furthermore we 
-     just ignore those lines - This should speed up things */
+  /* We don't want to use the verbose mode as this will also print the
+     key signatures which is in most cases not needed and furthermore
+     we just ignore those lines - This should speed up things.  */
   _gpgme_engine_set_verbosity (ctx->engine, 0);
 
   err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
                                      reserved, ctx->keylist_mode);
 
-  if (!err)    /* And kick off the process.  */
+  /* And kick off the process.  */
+  if (!err)
     err = _gpgme_engine_start (ctx->engine, ctx);
 
  leave:
index 9a556cee2d4cf622faa7f9eb5ae3cf3210916e97..88afaea903150fa41c21d303d8798c91aaa6d2ea 100644 (file)
@@ -1486,10 +1486,9 @@ gpg_keylist (void *engine, const char *pattern, int secret_only,
   if (!err)
     err = add_arg (gpg, "--with-fingerprint");
   if (!err)
-    err = add_arg (gpg, (keylist_mode & GPGME_KEYLIST_MODE_SIGS) ?
-                  "--check-sigs" :
-                  secret_only ? "--list-secret-keys"
-                  : "--list-keys");
+    err = add_arg (gpg, secret_only ? "--list-secret-keys"
+                  : ((keylist_mode & GPGME_KEYLIST_MODE_SIGS)
+                     ? "--check-sigs" : "--list-keys"));
   
   /* Tell the gpg object about the data.  */
   if (!err)
@@ -1519,8 +1518,10 @@ gpg_keylist_ext (void *engine, const char *pattern[], int secret_only,
   if (!err)
     err = add_arg (gpg, "--with-fingerprint");
   if (!err)
-    err = add_arg (gpg, secret_only ? "--list-secret-keys" : "--list-keys");
-  
+    err = add_arg (gpg, secret_only ? "--list-secret-keys"
+                  : ((keylist_mode & GPGME_KEYLIST_MODE_SIGS)
+                     ? "--check-sigs" : "--list-keys"));
+
   /* Tell the gpg object about the data.  */
   if (!err)
     err = add_arg (gpg, "--");
index c28c1cb72e15afaf592a1c164802c4f6b4d890b8..6d99cad380ac4a2bfd9dd7f539cc2510266392f5 100644 (file)
@@ -85,7 +85,13 @@ FILE *fopencookie (void *cookie, const char *opentype,
 
 
 /*-- conversion.c --*/
-GpgmeError _gpgme_decode_c_string (const char *src, char **destp);
+/* Decode the C formatted string SRC and store the result in the
+   buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
+   large enough buffer is allocated with malloc and *DESTP is set to
+   the result.  Currently, LEN is only used to specify if allocation
+   is desired or not, the caller is expected to make sure that *DESTP
+   is large enough if LEN is not zero.  */
+GpgmeError _gpgme_decode_c_string (const char *src, char **destp, int len);
 int _gpgme_hextobyte (const byte *str);
 
 #endif /* UTIL_H */
index 1dcc0eeccd6ceb131ed59a282d96ac6adfff06fa..bc2a787b256cfca25006a3f02e1a90189ab40e07 100644 (file)
@@ -643,42 +643,20 @@ gpgme_get_sig_ulong_attr (GpgmeCtx c, int idx, GpgmeAttr what, int reserved)
  *               indicate that there are no more signatures. 
  **/
 GpgmeError
-gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key)
+gpgme_get_sig_key (GpgmeCtx ctx, int idx, GpgmeKey *r_key)
 {
   VerifyResult result;
-  GpgmeError err = 0;
 
-  if (!c || !r_key)
+  if (!ctx || !r_key)
     return mk_error (Invalid_Value);
-  if (c->pending || !c->result.verify)
+  if (ctx->pending || !ctx->result.verify)
     return mk_error (Busy);
   
-  for (result = c->result.verify;
+  for (result = ctx->result.verify;
        result && idx > 0; result = result->next, idx--)
     ;
   if (!result)
     return mk_error (EOF);
-  
-  if (strlen(result->fpr) < 16)        /* We have at least a key ID.  */
-    return mk_error (Invalid_Key);
-  
-  *r_key = _gpgme_key_cache_get (result->fpr);
-  if (!*r_key)
-    {
-      GpgmeCtx listctx;
-      
-      /* Fixme: This can be optimized by keeping an internal context
-        used for such key listings.  */
-      err = gpgme_new (&listctx);
-      if (err)
-       return err;
-      gpgme_set_protocol (listctx, gpgme_get_protocol (c));
-      gpgme_set_keylist_mode (listctx, c->keylist_mode);
-      err = gpgme_op_keylist_start (listctx, result->fpr, 0);
-      if (!err)
-       err = gpgme_op_keylist_next (listctx, r_key);
-      gpgme_release (listctx);
-    }
-  return err;
-}
 
+  return gpgme_get_key (ctx, result->fpr, r_key, 0, 0);
+}