2005-10-01 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / verify.c
index da23b7e..d7c3216 100644 (file)
-/* verify.c -  signature verification
- *     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
- */
+/* verify.c - Signature verification.
+   Copyright (C) 2000 Werner Koch (dd9jn)
+   Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
 
+   This file is part of GPGME.
+   GPGME is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+   
+   GPGME is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
 #include <config.h>
-#include <stdio.h>
+#endif
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
 #include <assert.h>
 
+#include "gpgme.h"
 #include "util.h"
 #include "context.h"
 #include "ops.h"
-#include "key.h"
-
 
-struct verify_result_s
+\f
+typedef struct
 {
-  struct verify_result_s *next;
-  GpgmeSigStat status;
-  GpgmeSigStat expstatus; /* only used by finish_sig */
-  GpgmeData notation;  /* We store an XML fragment here.  */
-  int collecting;      /* Private to finish_sig().  */
-  int notation_in_data;        /* Private to add_notation().  */
-  char fpr[41];                /* Fingerprint of a good signature or keyid of
-                          a bad one.  */
-  ulong timestamp;     /* Signature creation time.  */
-  ulong exptimestamp;   /* signature exipration time or 0 */
-  GpgmeValidity validity;
-  int wrong_key_usage;  
-  char trust_errtok[31]; /* error token send with the trust status */
-};
-
-
-void
-_gpgme_release_verify_result (VerifyResult result)
+  struct _gpgme_op_verify_result result;
+
+  gpgme_signature_t current_sig;
+  int did_prepare_new_sig;
+  int only_newsig_seen;
+} *op_data_t;
+
+
+static void
+release_op_data (void *hook)
 {
-  while (result)
+  op_data_t opd = (op_data_t) hook;
+  gpgme_signature_t sig = opd->result.signatures;
+
+  while (sig)
     {
-      VerifyResult next_result = result->next;
-      gpgme_data_release (result->notation);
-      xfree (result);
-      result = next_result;
+      gpgme_signature_t next = sig->next;
+      gpgme_sig_notation_t notation = sig->notations;
+
+      while (notation)
+       {
+         gpgme_sig_notation_t next_nota = notation->next;
+
+         _gpgme_sig_notation_free (notation);
+         notation = next_nota;
+       }
+
+      if (sig->fpr)
+       free (sig->fpr);
+      free (sig);
+      sig = next;
     }
+
+  if (opd->result.file_name)
+    free (opd->result.file_name);
 }
 
-/* Check whether STRING starts with TOKEN and return true in this
-   case.  This is case insensitive.  If NEXT is not NULL return the
-   number of bytes to be added to STRING to get to the next token; a
-   returned value of 0 indicates end of line. */
-static int 
-is_token (const char *string, const char *token, size_t *next)
+
+gpgme_verify_result_t
+gpgme_op_verify_result (gpgme_ctx_t ctx)
 {
-  size_t n = 0;
+  void *hook;
+  op_data_t opd;
+  gpgme_error_t err;
 
-  for (;*string && *token && *string == *token; string++, token++, n++)
-    ;
-  if (*token || (*string != ' ' && !*string))
-    return 0;
-  if (next)
+  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
+  opd = hook;
+  if (err || !opd)
+    return NULL;
+
+  return &opd->result;
+}
+
+\f
+/* Build a summary vector from RESULT. */
+static void
+calc_sig_summary (gpgme_signature_t sig)
+{
+  unsigned long sum = 0;
+  
+  /* Calculate the red/green flag.  */
+  if (sig->validity == GPGME_VALIDITY_FULL
+      || sig->validity == GPGME_VALIDITY_ULTIMATE)
     {
-      for (; *string == ' '; string++, n++)
-        ;
-      *next = n;
+      if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
+         || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
+         || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
+       sum |= GPGME_SIGSUM_GREEN;
     }
-  return 1;
+  else if (sig->validity == GPGME_VALIDITY_NEVER)
+    {
+      if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
+         || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
+         || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
+       sum |= GPGME_SIGSUM_RED;
+    }
+  else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE)
+    sum |= GPGME_SIGSUM_RED;
+
+
+  /* FIXME: handle the case when key and message are expired. */
+  switch (gpg_err_code (sig->status))
+    {
+    case GPG_ERR_SIG_EXPIRED:
+      sum |= GPGME_SIGSUM_SIG_EXPIRED;
+      break;
+
+    case GPG_ERR_KEY_EXPIRED:
+      sum |= GPGME_SIGSUM_KEY_EXPIRED;
+      break;
+
+    case GPG_ERR_NO_PUBKEY:
+      sum |= GPGME_SIGSUM_KEY_MISSING;
+      break;
+
+    case GPG_ERR_BAD_SIGNATURE:
+    case GPG_ERR_NO_ERROR:
+      break;
+
+    default:
+      sum |= GPGME_SIGSUM_SYS_ERROR;
+      break;
+    }
+  
+  /* Now look at the certain reason codes.  */
+  switch (gpg_err_code (sig->validity_reason))
+    {
+    case GPG_ERR_CRL_TOO_OLD:
+      if (sig->validity == GPGME_VALIDITY_UNKNOWN)
+        sum |= GPGME_SIGSUM_CRL_TOO_OLD;
+      break;
+        
+    case GPG_ERR_CERT_REVOKED:
+      sum |= GPGME_SIGSUM_KEY_REVOKED;
+      break;
+
+    default:
+      break;
+    }
+
+  /* Check other flags. */
+  if (sig->wrong_key_usage)
+    sum |= GPGME_SIGSUM_BAD_POLICY;
+  
+  /* Set the valid flag when the signature is unquestionable
+     valid. */
+  if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
+    sum |= GPGME_SIGSUM_VALID;
+  
+  sig->summary = sum;
 }
+  
 
-static int
-skip_token (const char *string, size_t *next)
+static gpgme_error_t
+prepare_new_sig (op_data_t opd)
 {
-  size_t n = 0;
+  gpgme_signature_t sig;
 
-  for (;*string && *string != ' '; string++, n++)
-    ;
-  for (;*string == ' '; string++, n++)
-    ;
-  if (!*string)
-    return 0;
-  if (next)
-    *next = n;
-  return 1;
+  if (opd->only_newsig_seen && opd->current_sig)
+    {
+      /* We have only seen the NEWSIG status and nothing else - we
+         better skip this signature therefore and reuse it for the
+         next possible signature. */
+      sig = opd->current_sig;
+      memset (sig, 0, sizeof *sig);
+      assert (opd->result.signatures == sig);
+    }
+  else
+    {
+      sig = calloc (1, sizeof (*sig));
+      if (!sig)
+        return gpg_error_from_errno (errno);
+      if (!opd->result.signatures)
+        opd->result.signatures = sig;
+      if (opd->current_sig)
+        opd->current_sig->next = sig;
+      opd->current_sig = sig;
+    }
+  opd->did_prepare_new_sig = 1;
+  opd->only_newsig_seen = 0;
+  return 0;
 }
 
-
-static size_t
-copy_token (const char *string, char *buffer, size_t length)
+static gpgme_error_t
+parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args)
 {
-  const char *s = string;
-  char *p = buffer;
-  size_t i;
-
-  for (i = 1; i < length && *s && *s != ' ' ; i++)
-    *p++ = *s++;
-  *p = 0;
-  /* continue scanning in case the copy was truncated */
-  while (*s && *s != ' ')
-    s++;
-  return s - string;
-}
+  gpgme_signature_t sig;
+  char *end = strchr (args, ' ');
+  char *tail;
+
+  if (end)
+    {
+      *end = '\0';
+      end++;
+    }
 
+  if (!opd->did_prepare_new_sig)
+    {
+      gpg_error_t err;
 
-/* FIXME: Check that we are adding this to the correct signature.  */
-static void
-add_notation (GpgmeCtx ctx, GpgmeStatusCode code, const char *data)
-{
-  GpgmeData dh = ctx->result.verify->notation;
+      err = prepare_new_sig (opd);
+      if (err)
+        return err;
+    }
+  assert (opd->did_prepare_new_sig);
+  opd->did_prepare_new_sig = 0;
+
+  assert (opd->current_sig);
+  sig = opd->current_sig;
 
-  if (!dh)
+  /* FIXME: We should set the source of the state.  */
+  switch (code)
     {
-      if (gpgme_data_new (&dh))
+    case GPGME_STATUS_GOODSIG:
+      sig->status = gpg_error (GPG_ERR_NO_ERROR);
+      break;
+
+    case GPGME_STATUS_EXPSIG:
+      sig->status = gpg_error (GPG_ERR_SIG_EXPIRED);
+      break;
+
+    case GPGME_STATUS_EXPKEYSIG:
+      sig->status = gpg_error (GPG_ERR_KEY_EXPIRED);
+      break;
+
+    case GPGME_STATUS_BADSIG:
+      sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
+      break;
+
+    case GPGME_STATUS_REVKEYSIG:
+      sig->status = gpg_error (GPG_ERR_CERT_REVOKED);
+      break;
+
+    case GPGME_STATUS_ERRSIG:
+      /* Parse the pubkey algo.  */
+      if (!end)
+       goto parse_err_sig_fail;
+      errno = 0;
+      sig->pubkey_algo = strtol (end, &tail, 0);
+      if (errno || end == tail || *tail != ' ')
+       goto parse_err_sig_fail;
+      end = tail;
+      while (*end == ' ')
+       end++;
+     
+      /* Parse the hash algo.  */
+      if (!*end)
+       goto parse_err_sig_fail;
+      errno = 0;
+      sig->hash_algo = strtol (end, &tail, 0);
+      if (errno || end == tail || *tail != ' ')
+       goto parse_err_sig_fail;
+      end = tail;
+      while (*end == ' ')
+       end++;
+
+      /* Skip the sig class.  */
+      end = strchr (end, ' ');
+      if (!end)
+       goto parse_err_sig_fail;
+      while (*end == ' ')
+       end++;
+
+      /* Parse the timestamp.  */
+      sig->timestamp = _gpgme_parse_timestamp (end, &tail);
+      if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
+       return gpg_error (GPG_ERR_INV_ENGINE);
+      end = tail;
+      while (*end == ' ')
+       end++;
+      
+      /* Parse the return code.  */
+      if (end[0] && (!end[1] || end[1] == ' '))
        {
-         ctx->error = mk_error (Out_Of_Core);
-         return;
-        }
-      ctx->result.verify->notation = dh;
-      _gpgme_data_append_string (dh, "  <notation>\n");
+         switch (end[0])
+           {
+           case '4':
+             sig->status = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+             break;
+             
+           case '9':
+             sig->status = gpg_error (GPG_ERR_NO_PUBKEY);
+             break;
+             
+           default:
+             sig->status = gpg_error (GPG_ERR_GENERAL);
+           }
+       }
+      else
+       goto parse_err_sig_fail;
+
+      goto parse_err_sig_ok;
+      
+    parse_err_sig_fail:
+      sig->status = gpg_error (GPG_ERR_GENERAL);
+    parse_err_sig_ok:
+      break;
+      
+    default:
+      return gpg_error (GPG_ERR_GENERAL);
     }
 
-  if (code == GPGME_STATUS_NOTATION_DATA)
+  if (*args)
     {
-      if (!ctx->result.verify->notation_in_data)
-       _gpgme_data_append_string (dh, "  <data>");
-      _gpgme_data_append_percentstring_for_xml (dh, data);
-      ctx->result.verify->notation_in_data = 1;
-      return;
+      sig->fpr = strdup (args);
+      if (!sig->fpr)
+       return gpg_error_from_errno (errno);
     }
+  return 0;
+}
+
 
-  if (ctx->result.verify->notation_in_data)
+static gpgme_error_t
+parse_valid_sig (gpgme_signature_t sig, char *args)
+{
+  char *end = strchr (args, ' ');
+  if (end)
     {
-      _gpgme_data_append_string (dh, "</data>\n");
-      ctx->result.verify->notation_in_data = 0;
+      *end = '\0';
+      end++;
     }
 
-  if (code == GPGME_STATUS_NOTATION_NAME)
+  if (!*args)
+    /* We require at least the fingerprint.  */
+    return gpg_error (GPG_ERR_GENERAL);
+
+  if (sig->fpr)
+    free (sig->fpr);
+  sig->fpr = strdup (args);
+  if (!sig->fpr)
+    return gpg_error_from_errno (errno);
+
+  /* Skip the creation date.  */
+  end = strchr (end, ' ');
+  if (end)
     {
-      _gpgme_data_append_string (dh, "  <name>");
-      _gpgme_data_append_percentstring_for_xml (dh, data);
-      _gpgme_data_append_string (dh, "</name>\n");
+      char *tail;
+
+      sig->timestamp = _gpgme_parse_timestamp (end, &tail);
+      if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
+       return gpg_error (GPG_ERR_INV_ENGINE);
+      end = tail;
+     
+      sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail);
+      if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' '))
+       return gpg_error (GPG_ERR_INV_ENGINE);
+      end = tail;
+
+      while (*end == ' ')
+       end++;
+      /* Skip the signature version.  */
+      end = strchr (end, ' ');
+      if (end)
+       {
+         while (*end == ' ')
+           end++;
+
+         /* Skip the reserved field.  */
+         end = strchr (end, ' ');
+         if (end)
+           {
+             /* Parse the pubkey algo.  */
+             errno = 0;
+             sig->pubkey_algo = strtol (end, &tail, 0);
+             if (errno || end == tail || *tail != ' ')
+               return gpg_error (GPG_ERR_INV_ENGINE);
+             end = tail;
+
+             while (*end == ' ')
+               end++;
+
+             if (*end)
+               {
+                 /* Parse the hash algo.  */
+
+                 errno = 0;
+                 sig->hash_algo = strtol (end, &tail, 0);
+                 if (errno || end == tail || *tail != ' ')
+                   return gpg_error (GPG_ERR_INV_ENGINE);
+                 end = tail;
+               }
+           }
+       }
     }
-  else if (code == GPGME_STATUS_POLICY_URL)
+  return 0;
+}
+
+
+static gpgme_error_t
+parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
+{
+  gpgme_error_t err;
+  gpgme_sig_notation_t *lastp = &sig->notations;
+  gpgme_sig_notation_t notation = sig->notations;
+  char *end = strchr (args, ' ');
+
+  if (end)
+    *end = '\0';
+
+  if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL)
     {
-      _gpgme_data_append_string (dh, "  <policy>");
-      _gpgme_data_append_percentstring_for_xml (dh, data);
-      _gpgme_data_append_string (dh, "</policy>\n");
+      /* FIXME: We could keep a pointer to the last notation in the list.  */
+      while (notation && notation->value)
+       {
+         lastp = &notation->next;
+         notation = notation->next;
+       }
+
+      if (notation)
+       /* There is another notation name without data for the
+          previous one.  The crypto backend misbehaves.  */
+       return gpg_error (GPG_ERR_INV_ENGINE);
+
+      err = _gpgme_sig_notation_create (&notation, NULL, 0, NULL, 0, 0);
+      if (err)
+       return err;
+
+      if (code == GPGME_STATUS_NOTATION_NAME)
+       {
+         err = _gpgme_decode_percent_string (args, &notation->name, 0);
+         if (err)
+           {
+             _gpgme_sig_notation_free (notation);
+             return err;
+           }
+
+         notation->name_len = strlen (notation->name);
+
+         /* FIXME: For now we fake the human-readable flag.  The
+            critical flag can not be reported as it is not
+            provided.  */
+         notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE;
+         notation->human_readable = 1;
+       }
+      else
+       {
+         /* This is a policy URL.  */
+
+         err = _gpgme_decode_percent_string (args, &notation->value, 0);
+         if (err)
+           {
+             _gpgme_sig_notation_free (notation);
+             return err;
+           }
+
+         notation->value_len = strlen (notation->value);
+       }
+      *lastp = notation;
+    }
+  else if (code == GPGME_STATUS_NOTATION_DATA)
+    {
+      int len = strlen (args) + 1;
+      char *dest;
+
+      /* FIXME: We could keep a pointer to the last notation in the list.  */
+      while (notation && notation->next)
+       {
+         lastp = &notation->next;
+         notation = notation->next;
+       }
+
+      if (!notation || !notation->name)
+       /* There is notation data without a previous notation
+          name.  The crypto backend misbehaves.  */
+       return gpg_error (GPG_ERR_INV_ENGINE);
+      
+      if (!notation->value)
+       {
+         dest = notation->value = malloc (len);
+         if (!dest)
+           return gpg_error_from_errno (errno);
+       }
+      else
+       {
+         int cur_len = strlen (notation->value);
+         dest = realloc (notation->value, len + strlen (notation->value));
+         if (!dest)
+           return gpg_error_from_errno (errno);
+         notation->value = dest;
+         dest += cur_len;
+       }
+      
+      err = _gpgme_decode_percent_string (args, &dest, len);
+      if (err)
+       return err;
+
+      notation->value_len += strlen (dest);
     }
   else
-    assert (0);
+    return gpg_error (GPG_ERR_INV_ENGINE);
+  return 0;
 }
 
 
-/* 
- * finish a pending signature info collection and prepare for a new
- * signature info collection
- */
-static void
-finish_sig (GpgmeCtx ctx, int stop)
+static gpgme_error_t
+parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
 {
-  if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
-    ctx->result.verify->status = ctx->result.verify->expstatus;
+  char *end = strchr (args, ' ');
 
-  if (stop)
-    return; /* nothing to do */
+  if (end)
+    *end = '\0';
 
-  if (ctx->result.verify->collecting)
+  switch (code)
     {
-      VerifyResult res2;
+    case GPGME_STATUS_TRUST_UNDEFINED:
+    default:
+      sig->validity = GPGME_VALIDITY_UNKNOWN;
+      break;
 
-      ctx->result.verify->collecting = 0;
-      /* Create a new result structure.  */
-      res2 = xtrycalloc (1, sizeof *res2);
-      if (!res2)
-       {
-         ctx->error = mk_error (Out_Of_Core);
-         return;
-        }
+    case GPGME_STATUS_TRUST_NEVER:
+      sig->validity = GPGME_VALIDITY_NEVER;
+      break;
+
+    case GPGME_STATUS_TRUST_MARGINAL:
+      sig->validity = GPGME_VALIDITY_MARGINAL;
+      break;
 
-      res2->next = ctx->result.verify;
-      ctx->result.verify = res2;
+    case GPGME_STATUS_TRUST_FULLY:
+    case GPGME_STATUS_TRUST_ULTIMATE:
+      sig->validity = GPGME_VALIDITY_FULL;
+      break;
     }
-    
-  ctx->result.verify->collecting = 1;
+
+  if (*args)
+    sig->validity_reason = _gpgme_map_gnupg_error (args);
+  else
+    sig->validity_reason = 0;
+
+  return 0;
 }
 
 
-void
-_gpgme_verify_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
+static gpgme_error_t
+parse_error (gpgme_signature_t sig, char *args)
 {
-  char *p;
-  size_t n;
-  int i;
-
-  if (ctx->error)
-    return;
-  test_and_allocate_result (ctx, verify);
-
-  if (code == GPGME_STATUS_GOODSIG
-      || code == GPGME_STATUS_EXPSIG
-      || code == GPGME_STATUS_EXPKEYSIG
-      || code == GPGME_STATUS_BADSIG
-      || code == GPGME_STATUS_ERRSIG)
+  gpgme_error_t err;
+  char *where = strchr (args, ' ');
+  char *which;
+
+  if (where)
     {
-      finish_sig (ctx,0);
-      if (ctx->error)
-       return;
+      *where = '\0';
+      which = where + 1;
+
+      where = strchr (which, ' ');
+      if (where)
+       *where = '\0';
+
+      where = args;      
     }
+  else
+    return gpg_error (GPG_ERR_INV_ENGINE);
+
+  err = _gpgme_map_gnupg_error (which);
+
+  if (!strcmp (where, "verify.findkey"))
+    sig->status = err;
+  else if (!strcmp (where, "verify.keyusage")
+          && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
+    sig->wrong_key_usage = 1;
+
+  return 0;
+}
+
+
+gpgme_error_t
+_gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+  gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+  gpgme_error_t err;
+  void *hook;
+  op_data_t opd;
+  gpgme_signature_t sig;
+
+  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
+  opd = hook;
+  if (err)
+    return err;
+
+  sig = opd->current_sig;
 
   switch (code)
     {
-    case GPGME_STATUS_NODATA:
-      ctx->result.verify->status = GPGME_SIG_STAT_NOSIG;
-      break;
+    case GPGME_STATUS_NEWSIG:
+      if (sig)
+        calc_sig_summary (sig);
+      err = prepare_new_sig (opd);
+      opd->only_newsig_seen = 1;
+      return err;
 
     case GPGME_STATUS_GOODSIG:
-      ctx->result.verify->expstatus = GPGME_SIG_STAT_GOOD;
-      break;
-    
     case GPGME_STATUS_EXPSIG:
-      ctx->result.verify->expstatus = GPGME_SIG_STAT_GOOD_EXP;
-      break;
-
     case GPGME_STATUS_EXPKEYSIG:
-      ctx->result.verify->expstatus = GPGME_SIG_STAT_GOOD_EXPKEY;
-      break;
+    case GPGME_STATUS_BADSIG:
+    case GPGME_STATUS_ERRSIG:
+    case GPGME_STATUS_REVKEYSIG:
+      if (sig && !opd->did_prepare_new_sig)
+       calc_sig_summary (sig);
+      opd->only_newsig_seen = 0;
+      return parse_new_sig (opd, code, args);
 
     case GPGME_STATUS_VALIDSIG:
-      ctx->result.verify->status = GPGME_SIG_STAT_GOOD;
-      i = copy_token (args, ctx->result.verify->fpr,
-                      DIM(ctx->result.verify->fpr));
-      /* Skip the formatted date.  */
-      while (args[i] && args[i] == ' ')
-       i++;
-      while (args[i] && args[i] != ' ')
-       i++;
-      /* And get the timestamp.  */
-      ctx->result.verify->timestamp = strtoul (args+i, &p, 10);
-      if (args[i])
-        ctx->result.verify->exptimestamp = strtoul (p, NULL, 10);
-      break;
+      opd->only_newsig_seen = 0;
+      return sig ? parse_valid_sig (sig, args)
+       : gpg_error (GPG_ERR_INV_ENGINE);
 
-    case GPGME_STATUS_BADSIG:
-      ctx->result.verify->status = GPGME_SIG_STAT_BAD;
-      /* Store the keyID in the fpr field.  */
-      copy_token (args, ctx->result.verify->fpr,
-                  DIM(ctx->result.verify->fpr));
+    case GPGME_STATUS_NODATA:
+      opd->only_newsig_seen = 0;
+      if (!sig)
+       return gpg_error (GPG_ERR_NO_DATA);
+      sig->status = gpg_error (GPG_ERR_NO_DATA);
       break;
 
-    case GPGME_STATUS_ERRSIG:
-      /* The return code is the 6th argument, if it is 9, the problem
-        is a missing key.  Note that this is not emitted by gpgsm */
-      for (p = args, i = 0; p && *p && i < 5; i++)
-        {
-          p = strchr (p, ' ');
-          if (p)
-            while (*p == ' ')
-              p++;
-        }
-      if (p && *(p++) == '9' && (*p == '\0' || *p == ' '))
-       ctx->result.verify->status = GPGME_SIG_STAT_NOKEY;
-      else
-       ctx->result.verify->status = GPGME_SIG_STAT_ERROR;
-      /* Store the keyID in the fpr field.  */
-      copy_token (args, ctx->result.verify->fpr,
-                  DIM(ctx->result.verify->fpr));
+    case GPGME_STATUS_UNEXPECTED:
+      opd->only_newsig_seen = 0;
+      if (!sig)
+       return gpg_error (GPG_ERR_GENERAL);
+      sig->status = gpg_error (GPG_ERR_NO_DATA);
       break;
 
     case GPGME_STATUS_NOTATION_NAME:
     case GPGME_STATUS_NOTATION_DATA:
     case GPGME_STATUS_POLICY_URL:
-      add_notation (ctx, code, args);
-      break;
+      opd->only_newsig_seen = 0;
+      return sig ? parse_notation (sig, code, args)
+       : gpg_error (GPG_ERR_INV_ENGINE);
 
     case GPGME_STATUS_TRUST_UNDEFINED:
-      ctx->result.verify->validity = GPGME_VALIDITY_UNKNOWN;
-      copy_token (args, ctx->result.verify->trust_errtok,
-                  DIM(ctx->result.verify->trust_errtok));
-      break;
     case GPGME_STATUS_TRUST_NEVER:
-      ctx->result.verify->validity = GPGME_VALIDITY_NEVER;
-      copy_token (args, ctx->result.verify->trust_errtok,
-                  DIM(ctx->result.verify->trust_errtok));
-      break;
     case GPGME_STATUS_TRUST_MARGINAL:
-      if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
-        ctx->result.verify->validity = GPGME_VALIDITY_MARGINAL;
-      copy_token (args, ctx->result.verify->trust_errtok,
-                  DIM(ctx->result.verify->trust_errtok));
-      break;
     case GPGME_STATUS_TRUST_FULLY:
     case GPGME_STATUS_TRUST_ULTIMATE:
-      if (ctx->result.verify->status == GPGME_SIG_STAT_GOOD)
-        ctx->result.verify->validity = GPGME_VALIDITY_FULL;
-      break;
-
-    case GPGME_STATUS_END_STREAM:
-      break;
+      opd->only_newsig_seen = 0;
+      return sig ? parse_trust (sig, code, args)
+       : gpg_error (GPG_ERR_INV_ENGINE);
 
     case GPGME_STATUS_ERROR:
-      /* Generic error, we need this for gpgsm (and maybe for gpg in future)
-         to get error descriptions. */
-      if (is_token (args, "verify.findkey", &n) && n)
-        {
-          args += n;
-          if (is_token (args, "No_Public_Key", NULL))
-            ctx->result.verify->status = GPGME_SIG_STAT_NOKEY;
-          else
-            ctx->result.verify->status = GPGME_SIG_STAT_ERROR;
+      opd->only_newsig_seen = 0;
+      /* The error status is informational, so we don't return an
+         error code if we are not ready to process this status. */
+      return sig ? parse_error (sig, args) : 0;
 
-        }
-      else if (skip_token (args, &n) && n)
+    case GPGME_STATUS_EOF:
+      if (sig && !opd->did_prepare_new_sig)
+       calc_sig_summary (sig);
+      if (opd->only_newsig_seen && sig)
         {
-          args += n;
-          if (is_token (args, "Wrong_Key_Usage", NULL))
-            ctx->result.verify->wrong_key_usage = 1;
+          gpgme_signature_t sig2;
+          /* The last signature has no valid information - remove it
+             from the list. */
+          assert (!sig->next);
+          if (sig == opd->result.signatures)
+            opd->result.signatures = NULL;
+          else
+            {
+              for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next)
+                if (sig2->next == sig)
+                  {
+                    sig2->next = NULL;
+                    break;
+                  }
+            }
+          /* Note that there is no need to release the members of SIG
+             because we won't be here if they have been set. */
+          free (sig);
+          opd->current_sig = NULL;
         }
+      opd->only_newsig_seen = 0;
       break;
 
-    case GPGME_STATUS_EOF:
-      finish_sig (ctx,1);
-
-      /* FIXME: Put all notation data into one XML fragment.  */
-      if (ctx->result.verify->notation)
-       {
-         GpgmeData dh = ctx->result.verify->notation;
+    case GPGME_STATUS_PLAINTEXT:
+      err = _gpgme_parse_plaintext (args, &opd->result.file_name);
+      if (err)
+       return err;
 
-         if (ctx->result.verify->notation_in_data)
-           {
-             _gpgme_data_append_string (dh, "</data>\n");
-             ctx->result.verify->notation_in_data = 0;
-           }
-         _gpgme_data_append_string (dh, "</notation>\n");
-         ctx->notation = dh;
-         ctx->result.verify->notation = NULL;
-       }
-      break;
     default:
-      /* Ignore all other codes.  */
       break;
     }
+  return 0;
 }
 
-static GpgmeError
-_gpgme_op_verify_start (GpgmeCtx ctx, int synchronous,
-                       GpgmeData sig, GpgmeData signed_text, GpgmeData plaintext)
+
+static gpgme_error_t
+verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
 {
-  int err = 0;
-  int pipemode = 0;     /* !!text; use pipemode for detached sigs.  */
+  gpgme_error_t err;
+
+  err = _gpgme_progress_status_handler (priv, code, args);
+  if (!err)
+    err = _gpgme_verify_status_handler (priv, code, args);
+  return err;
+}
+
+
+gpgme_error_t
+_gpgme_op_verify_init_result (gpgme_ctx_t ctx)
+{  
+  void *hook;
+  op_data_t opd;
+
+  return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
+                               sizeof (*opd), release_op_data);
+}
 
-  if (!pipemode)
-    ;  /* XXX I am not sure what should happen/not happen in
-          pipemode.  */
+
+static gpgme_error_t
+verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
+             gpgme_data_t signed_text, gpgme_data_t plaintext)
+{
+  gpgme_error_t err;
 
   err = _gpgme_op_reset (ctx, synchronous);
   if (err)
-    goto leave;
+    return err;
 
-#if 0  /* FIXME */
-  if (pipemode)
-    _gpgme_gpg_enable_pipemode (c->engine->engine.gpg);
-#endif
+  err = _gpgme_op_verify_init_result (ctx);
+  if (err)
+    return err;
 
-  _gpgme_engine_set_status_handler (ctx->engine, _gpgme_verify_status_handler,
-                                   ctx);
-  _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
+  _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
 
-  /* Check the supplied data.  */
   if (!sig)
-    {
-      err = mk_error (No_Data);
-      goto leave;
-    }
+    return gpg_error (GPG_ERR_NO_DATA);
   if (!signed_text && !plaintext)
-    {
-      err = mk_error (Invalid_Value);
-      goto leave;
-    }
-  err = _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
-  if (!err)    /* And kick off the process.  */
-    err = _gpgme_engine_start (ctx->engine, ctx);
+    return gpg_error (GPG_ERR_INV_VALUE);
 
- leave:
-  if (err)
-    {
-      ctx->pending = 0; 
-      _gpgme_engine_release (ctx->engine);
-      ctx->engine = NULL;
-    }
-  return err;
+  return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
 }
 
-GpgmeError
-gpgme_op_verify_start (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
-                      GpgmeData plaintext)
-{
-  return _gpgme_op_verify_start (ctx, 0, sig, signed_text, plaintext);
-}
 
-/* 
- * Figure out a common status value for all signatures 
- */
-GpgmeSigStat
-_gpgme_intersect_stati (VerifyResult result)
+/* Decrypt ciphertext CIPHER and make a signature verification within
+   CTX and store the resulting plaintext in PLAIN.  */
+gpgme_error_t
+gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
+                      gpgme_data_t signed_text, gpgme_data_t plaintext)
 {
-  GpgmeSigStat status = result->status;
-
-  for (result = result->next; result; result = result->next)
-    {
-      if (status != result->status) 
-       return GPGME_SIG_STAT_DIFF;
-    }
-  return status;
+  return verify_start (ctx, 0, sig, signed_text, plaintext);
 }
 
-/**
- * gpgme_op_verify:
- * @c: the context
- * @sig: the signature data
- * @text: the signed text
- * @r_stat: returns the status of the signature
- * 
- * Perform a signature check on the signature given in @sig.  If @text
- * is a new and uninitialized data object, it is assumed that @sig
- * contains a normal or cleartext signature, and the plaintext is
- * returned in @text upon successful verification.
- *
- * If @text is initialized, it is assumed that @sig is a detached
- * signature for the material given in @text.
- *
- * The result of this operation is returned in @r_stat which can take these
- * values:
- *  GPGME_SIG_STAT_NONE:  No status - should not happen
- *  GPGME_SIG_STAT_GOOD:  The signature is valid 
- *  GPGME_SIG_STAT_BAD:   The signature is not valid
- *  GPGME_SIG_STAT_NOKEY: The signature could not be checked due to a
- *                        missing key
- *  GPGME_SIG_STAT_NOSIG: This is not a signature
- *  GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done.
- *  GPGME_SIG_STAT_DIFF:  There is more than 1 signature and they have not
- *                        the same status.
- *  GPGME_SIG_STAT_GOOD_EXP:  The signature is good but has expired.
- *  GPGME_SIG_STAT_GOOD_KEYEXP:  The signature is good but the key has expired.
- *
- * Return value: 0 on success or an errorcode if something not related to
- *               the signature itself did go wrong.
- **/
-GpgmeError
-gpgme_op_verify (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
-                GpgmeData plaintext, GpgmeSigStat *r_stat)
-{
-  GpgmeError err;
 
-  if (!r_stat)
-    return mk_error (Invalid_Value);
+/* Decrypt ciphertext CIPHER and make a signature verification within
+   CTX and store the resulting plaintext in PLAIN.  */
+gpgme_error_t
+gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
+                gpgme_data_t plaintext)
+{
+  gpgme_error_t err;
 
-  gpgme_data_release (ctx->notation);
-  ctx->notation = NULL;
-    
-  *r_stat = GPGME_SIG_STAT_NONE;
-  err = _gpgme_op_verify_start (ctx, 1, sig, signed_text, plaintext);
+  err = verify_start (ctx, 1, sig, signed_text, plaintext);
   if (!err)
-    {
-      err = _gpgme_wait_one (ctx);
-      if (!err && ctx->result.verify)
-       *r_stat = _gpgme_intersect_stati (ctx->result.verify);
-    }
-    return err;
+    err = _gpgme_wait_one (ctx);
+  return err;
 }
 
+\f
+/* Compatibility interfaces.  */
 
-/**
- * gpgme_get_sig_status:
- * @c: Context
- * @idx: Index of the signature starting at 0
- * @r_stat: Returns the status
- * @r_created: Returns the creation timestamp
- * 
- * Return information about an already verified signatures. 
- * 
- * Return value: The fingerprint or NULL in case of an problem or
- *               when there are no more signatures.
- **/
-const char *
-gpgme_get_sig_status (GpgmeCtx c, int idx,
-                      GpgmeSigStat *r_stat, time_t *r_created)
+/* Get the key used to create signature IDX in CTX and return it in
+   R_KEY.  */
+gpgme_error_t
+gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
 {
-  VerifyResult result;
+  gpgme_verify_result_t result;
+  gpgme_signature_t sig;
 
-  if (!c || c->pending || !c->result.verify)
-    return NULL;       /* No results yet or verification error.  */
+  result = gpgme_op_verify_result (ctx);
+  sig = result->signatures;
 
-  for (result = c->result.verify;
-       result && idx > 0; result = result->next, idx--)
-    ;
-  if (!result)
-    return NULL;       /* No more signatures.  */
+  while (sig && idx)
+    {
+      sig = sig->next;
+      idx--;
+    }
+  if (!sig || idx)
+    return gpg_error (GPG_ERR_EOF);
 
-  if (r_stat)
-    *r_stat = result->status;
-  if (r_created)
-    *r_created = result->timestamp;
-  return result->fpr;
+  return gpgme_get_key (ctx, sig->fpr, r_key, 0);
 }
 
 
-/* Build a summary vector from RESULT. */
-static unsigned long
-calc_sig_summary (VerifyResult result)
+/* Retrieve the signature status of signature IDX in CTX after a
+   successful verify operation in R_STAT (if non-null).  The creation
+   time stamp of the signature is returned in R_CREATED (if non-null).
+   The function returns a string containing the fingerprint.  */
+const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
+                                  _gpgme_sig_stat_t *r_stat, time_t *r_created)
 {
-  unsigned long sum = 0;
+  gpgme_verify_result_t result;
+  gpgme_signature_t sig;
 
-  if (result->validity == GPGME_VALIDITY_FULL
-     || result->validity == GPGME_VALIDITY_ULTIMATE)
+  result = gpgme_op_verify_result (ctx);
+  sig = result->signatures;
+
+  while (sig && idx)
     {
-      if (result->status == GPGME_SIG_STAT_GOOD
-          || result->status == GPGME_SIG_STAT_GOOD_EXP
-          || result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
-        sum |= GPGME_SIGSUM_GREEN;
+      sig = sig->next;
+      idx--;
     }
-  else if (result->validity == GPGME_VALIDITY_NEVER)
+  if (!sig || idx)
+    return NULL;
+
+  if (r_stat)
     {
-      if (result->status == GPGME_SIG_STAT_GOOD
-          || result->status == GPGME_SIG_STAT_GOOD_EXP
-          || result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
-        sum |= GPGME_SIGSUM_RED;
+      switch (gpg_err_code (sig->status))
+       {
+       case GPG_ERR_NO_ERROR:
+         *r_stat = GPGME_SIG_STAT_GOOD;
+         break;
+         
+       case GPG_ERR_BAD_SIGNATURE:
+         *r_stat = GPGME_SIG_STAT_BAD;
+         break;
+         
+       case GPG_ERR_NO_PUBKEY:
+         *r_stat = GPGME_SIG_STAT_NOKEY;
+         break;
+         
+       case GPG_ERR_NO_DATA:
+         *r_stat = GPGME_SIG_STAT_NOSIG;
+         break;
+         
+       case GPG_ERR_SIG_EXPIRED:
+         *r_stat = GPGME_SIG_STAT_GOOD_EXP;
+         break;
+         
+       case GPG_ERR_KEY_EXPIRED:
+         *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
+         break;
+         
+       default:
+         *r_stat = GPGME_SIG_STAT_ERROR;
+         break;
+       }
     }
-  else if (result->status == GPGME_SIG_STAT_BAD)
-    sum |= GPGME_SIGSUM_RED;
-
-  /* fixme: handle the case when key and message are expired. */
-  if (result->status == GPGME_SIG_STAT_GOOD_EXP)
-    sum |= GPGME_SIGSUM_SIG_EXPIRED;
-  else if (result->status == GPGME_SIG_STAT_GOOD_EXPKEY)
-    sum |= GPGME_SIGSUM_KEY_EXPIRED;
-  else if (result->status == GPGME_SIG_STAT_NOKEY)
-    sum |= GPGME_SIGSUM_KEY_MISSING;
-  else if (result->status == GPGME_SIG_STAT_ERROR)
-    sum |= GPGME_SIGSUM_SYS_ERROR;
-
-  if ( !strcmp (result->trust_errtok, "Certificate_Revoked"))
-    sum |= GPGME_SIGSUM_KEY_REVOKED;
-  else if ( !strcmp (result->trust_errtok, "No_CRL_Known"))
-    sum |= GPGME_SIGSUM_CRL_MISSING;
-  else if ( !strcmp (result->trust_errtok, "CRL_Too_Old"))
-    sum |= GPGME_SIGSUM_CRL_TOO_OLD;
-  else if ( !strcmp (result->trust_errtok, "No_Policy_Match"))
-    sum |= GPGME_SIGSUM_BAD_POLICY;
-  else if (*result->trust_errtok)
-    sum |= GPGME_SIGSUM_SYS_ERROR;
-
-  if (result->wrong_key_usage)
-    sum |= GPGME_SIGSUM_BAD_POLICY;
-
-  /* Set the valid flag when the signature is unquestionable
-     valid. */
-  if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
-    sum |= GPGME_SIGSUM_VALID;
-
-  return sum;
+  if (r_created)
+    *r_created = sig->timestamp;
+  return sig->fpr;
 }
 
 
-const char *
-gpgme_get_sig_string_attr (GpgmeCtx c, int idx, GpgmeAttr what, int whatidx)
+/* Retrieve certain attributes of a signature.  IDX is the index
+   number of the signature after a successful verify operation.  WHAT
+   is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
+   one.  WHATIDX is to be passed as 0 for most attributes . */
+unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
+                                        _gpgme_attr_t what, int whatidx)
 {
-  VerifyResult result;
-
-  if (!c || c->pending || !c->result.verify)
-    return NULL;       /* No results yet or verification error.  */
+  gpgme_verify_result_t result;
+  gpgme_signature_t sig;
 
-  for (result = c->result.verify;
-       result && idx > 0; result = result->next, idx--)
-    ;
-  if (!result)
-    return NULL;       /* No more signatures.  */
+  result = gpgme_op_verify_result (ctx);
+  sig = result->signatures;
 
-  switch (what)
+  while (sig && idx)
     {
-    case GPGME_ATTR_FPR:
-      return result->fpr;
-    case GPGME_ATTR_ERRTOK:
-      if (whatidx == 1)
-        return result->wrong_key_usage? "Wrong_Key_Usage":"";
-      else
-        return result->trust_errtok;
-    default:
-      break;
+      sig = sig->next;
+      idx--;
     }
-  return NULL;
-}
-
-unsigned long
-gpgme_get_sig_ulong_attr (GpgmeCtx c, int idx, GpgmeAttr what, int reserved)
-{
-  VerifyResult result;
-
-  if (!c || c->pending || !c->result.verify)
-    return 0;  /* No results yet or verification error.  */
-
-  for (result = c->result.verify;
-       result && idx > 0; result = result->next, idx--)
-    ;
-  if (!result)
-    return 0;  /* No more signatures.  */
+  if (!sig || idx)
+    return 0;
 
   switch (what)
     {
     case GPGME_ATTR_CREATED:
-      return result->timestamp;
+      return sig->timestamp;
+
     case GPGME_ATTR_EXPIRE:
-      return result->exptimestamp;
+      return sig->exp_timestamp;
+
     case GPGME_ATTR_VALIDITY:
-      return (unsigned long)result->validity;
+      return (unsigned long) sig->validity;
+
     case GPGME_ATTR_SIG_STATUS:
-      return (unsigned long)result->status;
+      switch (gpg_err_code (sig->status))
+       {
+       case GPG_ERR_NO_ERROR:
+         return GPGME_SIG_STAT_GOOD;
+         
+       case GPG_ERR_BAD_SIGNATURE:
+         return GPGME_SIG_STAT_BAD;
+         
+       case GPG_ERR_NO_PUBKEY:
+         return GPGME_SIG_STAT_NOKEY;
+         
+       case GPG_ERR_NO_DATA:
+         return GPGME_SIG_STAT_NOSIG;
+         
+       case GPG_ERR_SIG_EXPIRED:
+         return GPGME_SIG_STAT_GOOD_EXP;
+         
+       case GPG_ERR_KEY_EXPIRED:
+         return GPGME_SIG_STAT_GOOD_EXPKEY;
+         
+       default:
+         return GPGME_SIG_STAT_ERROR;
+       }
+
     case GPGME_ATTR_SIG_SUMMARY:
-      return calc_sig_summary (result);
+      return sig->summary;
+
     default:
       break;
     }
@@ -639,55 +922,36 @@ gpgme_get_sig_ulong_attr (GpgmeCtx c, int idx, GpgmeAttr what, int reserved)
 }
 
 
-
-/**
- * gpgme_get_sig_key:
- * @c: context
- * @idx: Index of the signature starting at 0
- * @r_key: Returns the key object
- * 
- * Return a key object which was used to check the signature. 
- * 
- * Return value: An Errorcode or 0 for success. GPGME_EOF is returned to
- *               indicate that there are no more signatures. 
- **/
-GpgmeError
-gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key)
+const char *gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
+                                      _gpgme_attr_t what, int whatidx)
 {
-  VerifyResult result;
-  GpgmeError err = 0;
+  gpgme_verify_result_t result;
+  gpgme_signature_t sig;
 
-  if (!c || !r_key)
-    return mk_error (Invalid_Value);
-  if (c->pending || !c->result.verify)
-    return mk_error (Busy);
-  
-  for (result = c->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)
+  result = gpgme_op_verify_result (ctx);
+  sig = result->signatures;
+
+  while (sig && idx)
     {
-      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);
+      sig = sig->next;
+      idx--;
+    }
+  if (!sig || idx)
+    return NULL;
+
+  switch (what)
+    {
+    case GPGME_ATTR_FPR:
+      return sig->fpr;
+
+    case GPGME_ATTR_ERRTOK:
+      if (whatidx == 1)
+        return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
+      else
+       return "";
+    default:
+      break;
     }
-  return err;
-}
 
+  return NULL;
+}