2005-10-01 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / verify.c
index e94232c..d7c3216 100644 (file)
@@ -1,22 +1,23 @@
 /* verify.c - Signature verification.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003 g10 Code GmbH
+   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 General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
+   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
-   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.  */
+   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>
@@ -24,6 +25,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <assert.h>
 
 #include "gpgme.h"
 #include "util.h"
@@ -35,7 +37,9 @@ typedef struct
 {
   struct _gpgme_op_verify_result result;
 
-  GpgmeSignature current_sig;
+  gpgme_signature_t current_sig;
+  int did_prepare_new_sig;
+  int only_newsig_seen;
 } *op_data_t;
 
 
@@ -43,21 +47,18 @@ static void
 release_op_data (void *hook)
 {
   op_data_t opd = (op_data_t) hook;
-  GpgmeSignature sig = opd->result.signatures;
+  gpgme_signature_t sig = opd->result.signatures;
 
   while (sig)
     {
-      GpgmeSignature next = sig->next;
-      GpgmeSigNotation notation = sig->notations;
+      gpgme_signature_t next = sig->next;
+      gpgme_sig_notation_t notation = sig->notations;
 
       while (notation)
        {
-         GpgmeSigNotation next_nota = notation->next;
+         gpgme_sig_notation_t next_nota = notation->next;
 
-         if (notation->name)
-           free (notation->name);
-         if (notation->value)
-           free (notation->value);
+         _gpgme_sig_notation_free (notation);
          notation = next_nota;
        }
 
@@ -66,16 +67,21 @@ release_op_data (void *hook)
       free (sig);
       sig = next;
     }
+
+  if (opd->result.file_name)
+    free (opd->result.file_name);
 }
 
 
-GpgmeVerifyResult
-gpgme_op_verify_result (GpgmeCtx ctx)
+gpgme_verify_result_t
+gpgme_op_verify_result (gpgme_ctx_t ctx)
 {
+  void *hook;
   op_data_t opd;
-  GpgmeError err;
+  gpgme_error_t err;
 
-  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, (void **) &opd, -1, NULL);
+  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
+  opd = hook;
   if (err || !opd)
     return NULL;
 
@@ -85,45 +91,47 @@ gpgme_op_verify_result (GpgmeCtx ctx)
 \f
 /* Build a summary vector from RESULT. */
 static void
-calc_sig_summary (GpgmeSignature sig)
+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)
     {
-      if (sig->status == GPGME_No_Error
-         || sig->status == GPGME_Sig_Expired
-         || sig->status == GPGME_Key_Expired)
+      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;
     }
   else if (sig->validity == GPGME_VALIDITY_NEVER)
     {
-      if (sig->status == GPGME_No_Error
-         || sig->status == GPGME_Sig_Expired
-         || sig->status == GPGME_Key_Expired)
+      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 (sig->status == GPGME_Bad_Signature)
+  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 (sig->status)
+  switch (gpg_err_code (sig->status))
     {
-    case GPGME_Sig_Expired:
+    case GPG_ERR_SIG_EXPIRED:
       sum |= GPGME_SIGSUM_SIG_EXPIRED;
       break;
 
-    case GPGME_Key_Expired:
+    case GPG_ERR_KEY_EXPIRED:
       sum |= GPGME_SIGSUM_KEY_EXPIRED;
       break;
 
-    case GPGME_No_Public_Key:
+    case GPG_ERR_NO_PUBKEY:
       sum |= GPGME_SIGSUM_KEY_MISSING;
       break;
 
-    case GPGME_Bad_Signature:
-    case GPGME_No_Error:
+    case GPG_ERR_BAD_SIGNATURE:
+    case GPG_ERR_NO_ERROR:
       break;
 
     default:
@@ -131,6 +139,23 @@ calc_sig_summary (GpgmeSignature sig)
       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;
   
@@ -143,11 +168,42 @@ calc_sig_summary (GpgmeSignature sig)
 }
   
 
-static GpgmeError
-parse_new_sig (op_data_t opd, GpgmeStatusCode code, char *args)
+static gpgme_error_t
+prepare_new_sig (op_data_t opd)
+{
+  gpgme_signature_t sig;
+
+  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 gpgme_error_t
+parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args)
 {
-  GpgmeSignature sig;
+  gpgme_signature_t sig;
   char *end = strchr (args, ' ');
+  char *tail;
 
   if (end)
     {
@@ -155,81 +211,126 @@ parse_new_sig (op_data_t opd, GpgmeStatusCode code, char *args)
       end++;
     }
 
-  sig = calloc (1, sizeof (*sig));
-  if (!sig)
-    return GPGME_Out_Of_Core;
-  if (!opd->result.signatures)
-    opd->result.signatures = sig;
-  if (opd->current_sig)
-    opd->current_sig->next = sig;
-  opd->current_sig = sig;
+  if (!opd->did_prepare_new_sig)
+    {
+      gpg_error_t err;
+
+      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;
 
+  /* FIXME: We should set the source of the state.  */
   switch (code)
     {
     case GPGME_STATUS_GOODSIG:
-      sig->status = GPGME_No_Error;
+      sig->status = gpg_error (GPG_ERR_NO_ERROR);
       break;
 
     case GPGME_STATUS_EXPSIG:
-      sig->status = GPGME_Sig_Expired;
+      sig->status = gpg_error (GPG_ERR_SIG_EXPIRED);
       break;
 
     case GPGME_STATUS_EXPKEYSIG:
-      sig->status = GPGME_Key_Expired;
+      sig->status = gpg_error (GPG_ERR_KEY_EXPIRED);
       break;
 
     case GPGME_STATUS_BADSIG:
-      sig->status = GPGME_Bad_Signature;
+      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:
-      if (end)
+      /* 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] == ' '))
        {
-         int i = 0;
-         /* The return code is the 6th argument, if it is 9, the
-            problem is a missing key.  */
-         while (end && i < 4)
-           end = strchr (end, ' ');
-         if (end && end[0] && (!end[1] || !end[1] == ' '))
+         switch (end[0])
            {
-             switch (end[0])
-               {
-               case '4':
-                 sig->status = GPGME_Unsupported_Algorithm;
-                 break;
-
-               case 9:
-                 sig->status = GPGME_No_Public_Key;
-                 break;
-
-               default:
-                 sig->status = GPGME_General_Error;
-               }
+           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
-       sig->status = GPGME_General_Error;
-      break;
+       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 GPGME_General_Error;
+      return gpg_error (GPG_ERR_GENERAL);
     }
 
   if (*args)
     {
       sig->fpr = strdup (args);
       if (!sig->fpr)
-       return GPGME_Out_Of_Core;
+       return gpg_error_from_errno (errno);
     }
   return 0;
 }
 
 
-static GpgmeError
-parse_valid_sig (GpgmeSignature sig, char *args)
+static gpgme_error_t
+parse_valid_sig (gpgme_signature_t sig, char *args)
 {
   char *end = strchr (args, ' ');
-
   if (end)
     {
       *end = '\0';
@@ -238,38 +339,76 @@ parse_valid_sig (GpgmeSignature sig, char *args)
 
   if (!*args)
     /* We require at least the fingerprint.  */
-    return GPGME_General_Error;
+    return gpg_error (GPG_ERR_GENERAL);
 
   if (sig->fpr)
     free (sig->fpr);
   sig->fpr = strdup (args);
   if (!sig->fpr)
-    return GPGME_Out_Of_Core;
+    return gpg_error_from_errno (errno);
 
+  /* Skip the creation date.  */
   end = strchr (end, ' ');
   if (end)
     {
       char *tail;
-      errno = 0;
-      sig->timestamp = strtol (end, &tail, 0);
-      if (errno || end == tail || (*tail && *tail != ' '))
-       return GPGME_General_Error;
+
+      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 = strtol (end, &tail, 0);
-      if (errno || end == tail || (*tail && *tail != ' '))
-       return GPGME_General_Error;
+      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;
+               }
+           }
+       }
     }
   return 0;
 }
 
 
-static GpgmeError
-parse_notation (GpgmeSignature sig, GpgmeStatusCode code, char *args)
+static gpgme_error_t
+parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
 {
-  GpgmeError err;
-  GpgmeSigNotation *lastp = &sig->notations;
-  GpgmeSigNotation notation = sig->notations;
+  gpgme_error_t err;
+  gpgme_sig_notation_t *lastp = &sig->notations;
+  gpgme_sig_notation_t notation = sig->notations;
   char *end = strchr (args, ' ');
 
   if (end)
@@ -287,43 +426,41 @@ parse_notation (GpgmeSignature sig, GpgmeStatusCode code, char *args)
       if (notation)
        /* There is another notation name without data for the
           previous one.  The crypto backend misbehaves.  */
-       return GPGME_General_Error;
+       return gpg_error (GPG_ERR_INV_ENGINE);
 
-      notation = malloc (sizeof (*sig));
-      if (!notation)
-       return GPGME_Out_Of_Core;
-      notation->next = NULL;
+      err = _gpgme_sig_notation_create (&notation, NULL, 0, NULL, 0, 0);
+      if (err)
+       return err;
 
       if (code == GPGME_STATUS_NOTATION_NAME)
        {
-         int len = strlen (args) + 1;
-
-         notation->name = malloc (len);
-         if (!notation->name)
+         err = _gpgme_decode_percent_string (args, &notation->name, 0);
+         if (err)
            {
-             free (notation);
-             return GPGME_Out_Of_Core;
+             _gpgme_sig_notation_free (notation);
+             return err;
            }
-         err = _gpgme_decode_percent_string (args, &notation->name, len);
-         if (err)
-           return err;
 
-         notation->value = NULL;
+         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
        {
-         int len = strlen (args) + 1;
+         /* This is a policy URL.  */
 
-         notation->name = NULL;
-         notation->value = malloc (len);
-         if (!notation->value)
+         err = _gpgme_decode_percent_string (args, &notation->value, 0);
+         if (err)
            {
-             free (notation);
-             return GPGME_Out_Of_Core;
+             _gpgme_sig_notation_free (notation);
+             return err;
            }
-         err = _gpgme_decode_percent_string (args, &notation->value, len);
-         if (err)
-           return err;
+
+         notation->value_len = strlen (notation->value);
        }
       *lastp = notation;
     }
@@ -342,20 +479,20 @@ parse_notation (GpgmeSignature sig, GpgmeStatusCode code, char *args)
       if (!notation || !notation->name)
        /* There is notation data without a previous notation
           name.  The crypto backend misbehaves.  */
-       return GPGME_General_Error;
+       return gpg_error (GPG_ERR_INV_ENGINE);
       
       if (!notation->value)
        {
          dest = notation->value = malloc (len);
          if (!dest)
-           return GPGME_Out_Of_Core;
+           return gpg_error_from_errno (errno);
        }
       else
        {
          int cur_len = strlen (notation->value);
          dest = realloc (notation->value, len + strlen (notation->value));
          if (!dest)
-           return GPGME_Out_Of_Core;
+           return gpg_error_from_errno (errno);
          notation->value = dest;
          dest += cur_len;
        }
@@ -363,15 +500,17 @@ parse_notation (GpgmeSignature sig, GpgmeStatusCode code, char *args)
       err = _gpgme_decode_percent_string (args, &dest, len);
       if (err)
        return err;
+
+      notation->value_len += strlen (dest);
     }
   else
-    return GPGME_General_Error;
+    return gpg_error (GPG_ERR_INV_ENGINE);
   return 0;
 }
 
 
-static GpgmeError
-parse_trust (GpgmeSignature sig, GpgmeStatusCode code, char *args)
+static gpgme_error_t
+parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
 {
   char *end = strchr (args, ' ');
 
@@ -401,15 +540,17 @@ parse_trust (GpgmeSignature sig, GpgmeStatusCode code, char *args)
 
   if (*args)
     sig->validity_reason = _gpgme_map_gnupg_error (args);
+  else
+    sig->validity_reason = 0;
 
   return 0;
 }
 
 
-static GpgmeError
-parse_error (GpgmeSignature sig, char *args)
+static gpgme_error_t
+parse_error (gpgme_signature_t sig, char *args)
 {
-  GpgmeError err;
+  gpgme_error_t err;
   char *where = strchr (args, ' ');
   char *which;
 
@@ -425,28 +566,31 @@ parse_error (GpgmeSignature sig, char *args)
       where = args;      
     }
   else
-    return GPGME_General_Error;
+    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") && err == GPGME_Wrong_Key_Usage)
+  else if (!strcmp (where, "verify.keyusage")
+          && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
     sig->wrong_key_usage = 1;
 
   return 0;
 }
 
 
-GpgmeError
-_gpgme_verify_status_handler (void *priv, GpgmeStatusCode code, char *args)
+gpgme_error_t
+_gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
 {
-  GpgmeCtx ctx = (GpgmeCtx) priv;
-  GpgmeError err;
+  gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+  gpgme_error_t err;
+  void *hook;
   op_data_t opd;
-  GpgmeSignature sig;
+  gpgme_signature_t sig;
 
-  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, (void **) &opd, -1, NULL);
+  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
+  opd = hook;
   if (err)
     return err;
 
@@ -454,45 +598,98 @@ _gpgme_verify_status_handler (void *priv, GpgmeStatusCode code, char *args)
 
   switch (code)
     {
+    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:
     case GPGME_STATUS_EXPSIG:
     case GPGME_STATUS_EXPKEYSIG:
     case GPGME_STATUS_BADSIG:
     case GPGME_STATUS_ERRSIG:
-      if (sig)
+    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:
-      return sig ? parse_valid_sig (sig, args) : GPGME_General_Error;
+      opd->only_newsig_seen = 0;
+      return sig ? parse_valid_sig (sig, args)
+       : gpg_error (GPG_ERR_INV_ENGINE);
 
     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_UNEXPECTED:
+      opd->only_newsig_seen = 0;
       if (!sig)
-       return GPGME_General_Error;
-      sig->status = GPGME_No_Data;
+       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:
-      return sig ? parse_notation (sig, code, args) : GPGME_General_Error;
+      opd->only_newsig_seen = 0;
+      return sig ? parse_notation (sig, code, args)
+       : gpg_error (GPG_ERR_INV_ENGINE);
 
     case GPGME_STATUS_TRUST_UNDEFINED:
     case GPGME_STATUS_TRUST_NEVER:
     case GPGME_STATUS_TRUST_MARGINAL:
     case GPGME_STATUS_TRUST_FULLY:
     case GPGME_STATUS_TRUST_ULTIMATE:
-      return sig ? parse_trust (sig, code, args) : GPGME_General_Error;
+      opd->only_newsig_seen = 0;
+      return sig ? parse_trust (sig, code, args)
+       : gpg_error (GPG_ERR_INV_ENGINE);
 
     case GPGME_STATUS_ERROR:
-      return sig ? parse_error (sig, args) : GPGME_General_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;
 
     case GPGME_STATUS_EOF:
-      if (sig)
+      if (sig && !opd->did_prepare_new_sig)
        calc_sig_summary (sig);
+      if (opd->only_newsig_seen && sig)
+        {
+          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_PLAINTEXT:
+      err = _gpgme_parse_plaintext (args, &opd->result.file_name);
+      if (err)
+       return err;
+
     default:
       break;
     }
@@ -500,21 +697,34 @@ _gpgme_verify_status_handler (void *priv, GpgmeStatusCode code, char *args)
 }
 
 
-GpgmeError
-_gpgme_op_verify_init_result (GpgmeCtx ctx)
+static gpgme_error_t
+verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+  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, (void **) &opd,
+  return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
                                sizeof (*opd), release_op_data);
 }
 
 
-static GpgmeError
-_gpgme_op_verify_start (GpgmeCtx ctx, int synchronous, GpgmeData sig,
-                       GpgmeData signed_text, GpgmeData plaintext)
+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)
 {
-  GpgmeError err;
+  gpgme_error_t err;
 
   err = _gpgme_op_reset (ctx, synchronous);
   if (err)
@@ -524,13 +734,12 @@ _gpgme_op_verify_start (GpgmeCtx ctx, int synchronous, GpgmeData sig,
   if (err)
     return err;
 
-  _gpgme_engine_set_status_handler (ctx->engine, _gpgme_verify_status_handler,
-                                   ctx);
+  _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
 
   if (!sig)
-    return GPGME_No_Data;
+    return gpg_error (GPG_ERR_NO_DATA);
   if (!signed_text && !plaintext)
-    return GPGME_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
 }
@@ -538,23 +747,23 @@ _gpgme_op_verify_start (GpgmeCtx ctx, int synchronous, GpgmeData sig,
 
 /* Decrypt ciphertext CIPHER and make a signature verification within
    CTX and store the resulting plaintext in PLAIN.  */
-GpgmeError
-gpgme_op_verify_start (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
-                      GpgmeData plaintext)
+gpgme_error_t
+gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
+                      gpgme_data_t signed_text, gpgme_data_t plaintext)
 {
-  return _gpgme_op_verify_start (ctx, 0, sig, signed_text, plaintext);
+  return verify_start (ctx, 0, sig, signed_text, plaintext);
 }
 
 
 /* Decrypt ciphertext CIPHER and make a signature verification within
    CTX and store the resulting plaintext in PLAIN.  */
-GpgmeError
-gpgme_op_verify (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
-                GpgmeData plaintext)
+gpgme_error_t
+gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
+                gpgme_data_t plaintext)
 {
-  GpgmeError err;
+  gpgme_error_t err;
 
-  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);
   return err;
@@ -565,11 +774,11 @@ gpgme_op_verify (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text,
 
 /* Get the key used to create signature IDX in CTX and return it in
    R_KEY.  */
-GpgmeError
-gpgme_get_sig_key (GpgmeCtx ctx, int idx, GpgmeKey *r_key)
+gpgme_error_t
+gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
 {
-  GpgmeVerifyResult result;
-  GpgmeSignature sig;
+  gpgme_verify_result_t result;
+  gpgme_signature_t sig;
 
   result = gpgme_op_verify_result (ctx);
   sig = result->signatures;
@@ -580,7 +789,7 @@ gpgme_get_sig_key (GpgmeCtx ctx, int idx, GpgmeKey *r_key)
       idx--;
     }
   if (!sig || idx)
-    return GPGME_EOF;
+    return gpg_error (GPG_ERR_EOF);
 
   return gpgme_get_key (ctx, sig->fpr, r_key, 0);
 }
@@ -590,11 +799,11 @@ gpgme_get_sig_key (GpgmeCtx ctx, int idx, GpgmeKey *r_key)
    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 (GpgmeCtx ctx, int idx,
-                                  GpgmeSigStat *r_stat, time_t *r_created)
+const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
+                                  _gpgme_sig_stat_t *r_stat, time_t *r_created)
 {
-  GpgmeVerifyResult result;
-  GpgmeSignature sig;
+  gpgme_verify_result_t result;
+  gpgme_signature_t sig;
 
   result = gpgme_op_verify_result (ctx);
   sig = result->signatures;
@@ -609,29 +818,29 @@ const char *gpgme_get_sig_status (GpgmeCtx ctx, int idx,
 
   if (r_stat)
     {
-      switch (sig->status)
+      switch (gpg_err_code (sig->status))
        {
-       case GPGME_No_Error:
+       case GPG_ERR_NO_ERROR:
          *r_stat = GPGME_SIG_STAT_GOOD;
          break;
          
-       case GPGME_Bad_Signature:
+       case GPG_ERR_BAD_SIGNATURE:
          *r_stat = GPGME_SIG_STAT_BAD;
          break;
          
-       case GPGME_No_Public_Key:
+       case GPG_ERR_NO_PUBKEY:
          *r_stat = GPGME_SIG_STAT_NOKEY;
          break;
          
-       case GPGME_No_Data:
+       case GPG_ERR_NO_DATA:
          *r_stat = GPGME_SIG_STAT_NOSIG;
          break;
          
-       case GPGME_Sig_Expired:
+       case GPG_ERR_SIG_EXPIRED:
          *r_stat = GPGME_SIG_STAT_GOOD_EXP;
          break;
          
-       case GPGME_Key_Expired:
+       case GPG_ERR_KEY_EXPIRED:
          *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
          break;
          
@@ -650,11 +859,11 @@ const char *gpgme_get_sig_status (GpgmeCtx ctx, int idx,
    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 (GpgmeCtx ctx, int idx,
-                                        GpgmeAttr what, int whatidx)
+unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
+                                        _gpgme_attr_t what, int whatidx)
 {
-  GpgmeVerifyResult result;
-  GpgmeSignature sig;
+  gpgme_verify_result_t result;
+  gpgme_signature_t sig;
 
   result = gpgme_op_verify_result (ctx);
   sig = result->signatures;
@@ -679,24 +888,24 @@ unsigned long gpgme_get_sig_ulong_attr (GpgmeCtx ctx, int idx,
       return (unsigned long) sig->validity;
 
     case GPGME_ATTR_SIG_STATUS:
-      switch (sig->status)
+      switch (gpg_err_code (sig->status))
        {
-       case GPGME_No_Error:
+       case GPG_ERR_NO_ERROR:
          return GPGME_SIG_STAT_GOOD;
          
-       case GPGME_Bad_Signature:
+       case GPG_ERR_BAD_SIGNATURE:
          return GPGME_SIG_STAT_BAD;
          
-       case GPGME_No_Public_Key:
+       case GPG_ERR_NO_PUBKEY:
          return GPGME_SIG_STAT_NOKEY;
          
-       case GPGME_No_Data:
+       case GPG_ERR_NO_DATA:
          return GPGME_SIG_STAT_NOSIG;
          
-       case GPGME_Sig_Expired:
+       case GPG_ERR_SIG_EXPIRED:
          return GPGME_SIG_STAT_GOOD_EXP;
          
-       case GPGME_Key_Expired:
+       case GPG_ERR_KEY_EXPIRED:
          return GPGME_SIG_STAT_GOOD_EXPKEY;
          
        default:
@@ -713,11 +922,11 @@ unsigned long gpgme_get_sig_ulong_attr (GpgmeCtx ctx, int idx,
 }
 
 
-const char *gpgme_get_sig_string_attr (GpgmeCtx ctx, int idx,
-                                      GpgmeAttr what, int whatidx)
+const char *gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
+                                      _gpgme_attr_t what, int whatidx)
 {
-  GpgmeVerifyResult result;
-  GpgmeSignature sig;
+  gpgme_verify_result_t result;
+  gpgme_signature_t sig;
 
   result = gpgme_op_verify_result (ctx);
   sig = result->signatures;