2004-09-30 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / verify.c
index 18c24c9..ceb84f8 100644 (file)
@@ -1,6 +1,6 @@
 /* verify.c - Signature verification.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
 
    This file is part of GPGME.
  
@@ -38,6 +38,7 @@ typedef struct
 
   gpgme_signature_t current_sig;
   int did_prepare_new_sig;
+  int only_newsig_seen;
 } *op_data_t;
 
 
@@ -151,16 +152,29 @@ static gpgme_error_t
 prepare_new_sig (op_data_t opd)
 {
   gpgme_signature_t sig;
-  
-  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;
+
+  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;
 }
 
@@ -209,6 +223,10 @@ parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args)
       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)
        {
@@ -454,7 +472,6 @@ parse_error (gpgme_signature_t sig, char *args)
   gpgme_error_t err;
   char *where = strchr (args, ' ');
   char *which;
-  char *where_last;
 
   if (where)
     {
@@ -470,16 +487,11 @@ parse_error (gpgme_signature_t sig, char *args)
   else
     return gpg_error (GPG_ERR_INV_ENGINE);
 
-  /* It is often useful to compare only the last part of the where token. */
-  where_last = strrchr (where, '.');
-  if (!where_last)
-    where_last = where;
-
   err = _gpgme_map_gnupg_error (which);
 
-  if (!strcmp (where_last, ".findkey"))
+  if (!strcmp (where, "verify.findkey"))
     sig->status = err;
-  else if (!strcmp (where_last, ".keyusage")
+  else if (!strcmp (where, "verify.keyusage")
           && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
     sig->wrong_key_usage = 1;
 
@@ -508,28 +520,35 @@ _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
     case GPGME_STATUS_NEWSIG:
       if (sig)
         calc_sig_summary (sig);
-      break;
+      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:
+    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:
+      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 gpg_error (GPG_ERR_GENERAL);
       sig->status = gpg_error (GPG_ERR_NO_DATA);
@@ -538,6 +557,7 @@ _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
     case GPGME_STATUS_NOTATION_NAME:
     case GPGME_STATUS_NOTATION_DATA:
     case GPGME_STATUS_POLICY_URL:
+      opd->only_newsig_seen = 0;
       return sig ? parse_notation (sig, code, args)
        : gpg_error (GPG_ERR_INV_ENGINE);
 
@@ -546,15 +566,42 @@ _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
     case GPGME_STATUS_TRUST_MARGINAL:
     case GPGME_STATUS_TRUST_FULLY:
     case GPGME_STATUS_TRUST_ULTIMATE:
+      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) : gpg_error (GPG_ERR_INV_ENGINE);
+      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 && !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;
 
     default: