Multiple signatures can now be verified.
authorWerner Koch <wk@gnupg.org>
Mon, 12 Feb 2001 15:23:29 +0000 (15:23 +0000)
committerWerner Koch <wk@gnupg.org>
Mon, 12 Feb 2001 15:23:29 +0000 (15:23 +0000)
TODO
gpgme/ChangeLog
gpgme/debug.c
gpgme/gpgme.h
gpgme/keylist.c
gpgme/verify.c
tests/t-verify.c

diff --git a/TODO b/TODO
index c6f650577fd989e6c19a06050c8e6fef93af48ec..1ef478dabc27c67a2e2107d58cf35959c7c5a6fc 100644 (file)
--- a/TODO
+++ b/TODO
@@ -5,4 +5,7 @@
 * Allow to use GTK's main loop instead of the select stuff in
   wait.c
 
+* a op_keylist_start should cancel a pending keylisy operation on the
+  same context
+
 * need to close a lot of handles in w32-io.c
index 6b63ca21eb581610b8edefccdf99fde27f019fa1..ea4ac7eb2851a4102a74ddc621f6720b6d6b4989 100644 (file)
@@ -1,3 +1,14 @@
+2001-02-12  Werner Koch  <wk@gnupg.org>
+
+       Enhanced the signature verification, so that it can how handle
+       more than one signature and is able to return more information on 
+       the signatures.
+       * verify.c (gpgme_get_sig_key): New.
+       (gpgme_get_sig_status): New.
+
+       * gpgme.h: Add stdio.h. 
+       (GpgmeSigStat): New status DIFF.
+
 2001-02-01  Werner Koch  <wk@gnupg.org>
 
        * w32-io.c (set_synchronize): Add EVENT_MODIFY_STATE.  Add Debug
index e700405bfe784aadd7aa13a073ad9bab639ba8f0..19de2a7d57ce9a98db5f18425d988db0d814ec11 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
+#include <unistd.h>
 #include <assert.h>
 
 #include "util.h"
index 73744922af28b39c8decfea203971183b46ec9fb..cecb912cb507e3945abfea39d08787f596d01147 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef GPGME_H
 #define GPGME_H
 
+#include <stdio.h> /* for FILE * */
 #ifdef _MSC_VER
   typedef long off_t;
 #else
@@ -103,7 +104,8 @@ typedef enum {
     GPGME_SIG_STAT_BAD  = 2,
     GPGME_SIG_STAT_NOKEY = 3,
     GPGME_SIG_STAT_NOSIG = 4,
-    GPGME_SIG_STAT_ERROR = 5
+    GPGME_SIG_STAT_ERROR = 5,
+    GPGME_SIG_STAT_DIFF  = 6
 } GpgmeSigStat;
 
 typedef enum {
@@ -165,6 +167,12 @@ void       gpgme_signers_clear (GpgmeCtx c);
 GpgmeError gpgme_signers_add (GpgmeCtx c, const GpgmeKey key);
 GpgmeKey   gpgme_signers_enum (const GpgmeCtx c, int seq);
 
+const char *gpgme_get_sig_status (GpgmeCtx c, int idx,
+                                  GpgmeSigStat *r_stat, time_t *r_created );
+GpgmeError gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key);
+
+
+
 
 /* Functions to handle recipients */
 GpgmeError   gpgme_recipients_new (GpgmeRecipients *r_rset);
index 9fea42575c0de93bec54673d7dc995d5554568de..8b360e0069a3ead423db92a5f53675567a06142f 100644 (file)
@@ -359,7 +359,6 @@ gpgme_op_keylist_start ( GpgmeCtx c,  const char *pattern, int secret_only )
     _gpgme_release_result (c);
     c->out_of_core = 0;
 
-#warning This context still keeps a gpg Zombie in some cases.
     if ( c->gpg ) {
         _gpgme_gpg_release ( c->gpg ); 
         c->gpg = NULL;
index cc2039593a4a97352b79bead9c77002d5fe964c0..7a1af98569065cdf8ce24135849a9a06e3134def 100644 (file)
 #include "ops.h"
 
 struct verify_result_s {
+    struct verify_result_s *next;
     GpgmeSigStat status;
     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 */
 };
 
 
 void
 _gpgme_release_verify_result ( VerifyResult res )
 {
-    gpgme_data_release ( res->notation );
-    xfree (res);
+    while (res) {
+        VerifyResult r2 = res->next;
+        gpgme_data_release ( res->notation );
+        xfree (res);
+        res = r2;
+    }
 }
 
-
+/* fixme: check that we are adding this to the correct signature */
 static void
 add_notation ( GpgmeCtx ctx, GpgStatusCode code, const char *data )
 {
@@ -86,9 +93,42 @@ add_notation ( GpgmeCtx ctx, GpgStatusCode code, const char *data )
     }
 }
 
+
+/* 
+ * finish a pending signature info collection and prepare for a new
+ * signature info collection
+ */
+static void
+finish_sig (GpgmeCtx ctx, int stop)
+{
+    if (stop)
+        return; /* nothing to do */
+
+    if (ctx->result.verify->collecting) {
+        VerifyResult res2;
+
+        ctx->result.verify->collecting = 0;
+        /* create a new result structure */
+        res2 = xtrycalloc ( 1, sizeof *res2 );
+        if ( !res2 ) {
+            ctx->out_of_core = 1;
+            return;
+        }
+
+        res2->next = ctx->result.verify;
+        ctx->result.verify = res2;
+    }
+    
+    ctx->result.verify->collecting = 1;
+}
+
+
 static void
 verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args )
 {
+    char *p;
+    int i;
+
     if ( ctx->out_of_core )
         return;
     if ( ctx->result_type == RESULT_TYPE_NONE ) {
@@ -102,20 +142,54 @@ verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args )
     }
     assert ( ctx->result_type == RESULT_TYPE_VERIFY );
 
-    /* FIXME: For now we handle only one signature */
-    /* FIXME: Collect useful information
-       and return them as XML */
+    if (code == STATUS_GOODSIG
+        || code == STATUS_BADSIG || code == STATUS_ERRSIG) {
+        finish_sig (ctx,0);
+        if ( ctx->out_of_core )
+            return;
+    }
+
     switch (code) {
       case STATUS_GOODSIG:
+        /* We just look at VALIDSIG */
+        break;
+
+      case STATUS_VALIDSIG:
         ctx->result.verify->status = GPGME_SIG_STAT_GOOD;
+        p = ctx->result.verify->fpr;
+        for (i=0; i < DIM(ctx->result.verify->fpr)
+                 && args[i] && args[i] != ' ' ; i++ )
+            *p++ = args[i];
+        *p = 0;
+        /* 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, NULL, 10);
         break;
+
       case STATUS_BADSIG:
         ctx->result.verify->status = GPGME_SIG_STAT_BAD;
+        /* store the keyID in the fpr field */
+        p = ctx->result.verify->fpr;
+        for (i=0; i < DIM(ctx->result.verify->fpr)
+                 && args[i] && args[i] != ' ' ; i++ )
+            *p++ = args[i];
+        *p = 0;
         break;
+
       case STATUS_ERRSIG:
         ctx->result.verify->status = GPGME_SIG_STAT_ERROR;
         /* FIXME: distinguish between a regular error and a missing key.
          * this is encoded in the args. */
+        /* store the keyID in the fpr field */
+        p = ctx->result.verify->fpr;
+        for (i=0; i < DIM(ctx->result.verify->fpr)
+                 && args[i] && args[i] != ' ' ; i++ )
+            *p++ = args[i];
+        *p = 0;
         break;
 
       case STATUS_NOTATION_NAME:
@@ -127,6 +201,10 @@ verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args )
       case STATUS_END_STREAM:
         break;
 
+      case STATUS_EOF:
+        finish_sig(ctx,1);
+        break;
+
       default:
         /* ignore all other codes */
         break;
@@ -205,6 +283,21 @@ gpgme_op_verify_start ( GpgmeCtx c,  GpgmeData sig, GpgmeData text )
 }
 
 
+/* 
+ * Figure out a common status value for all signatures 
+ */
+static GpgmeSigStat
+intersect_stati ( VerifyResult res )
+{
+    GpgmeSigStat status = res->status;
+
+    for (res=res->next; res; res = res->next) {
+        if (status != res->status ) 
+            return GPGME_SIG_STAT_DIFF;
+    }
+    return status;
+}
+
 /**
  * gpgme_op_verify:
  * @c: the context
@@ -223,7 +316,8 @@ gpgme_op_verify_start ( GpgmeCtx c,  GpgmeData sig, GpgmeData text )
  *                        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.
- *  FIXME: What do we return if only some o the signatures ae valid?
+ *  GPGME_SIG_STAT_DIFF:  There is more than 1 signature and they have not
+ *                        the same status.
  *
  * Return value: 0 on success or an errorcode if something not related to
  *               the signature itself did go wrong.
@@ -250,6 +344,7 @@ gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text,
             rc = mk_error (Out_Of_Core);
         else {
             assert ( c->result.verify );
+            /* fixme: Put all notation data into one XML fragment */
             if ( c->result.verify->notation ) {
                 GpgmeData dh = c->result.verify->notation;
                 
@@ -261,7 +356,7 @@ gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text,
                 c->notation = dh;
                 c->result.verify->notation = NULL;
             }
-            *r_stat = c->result.verify->status;
+            *r_stat = intersect_stati (c->result.verify);
         }
         c->pending = 0;
     }
@@ -269,6 +364,82 @@ gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text,
 }
 
 
+/**
+ * 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 )
+{
+    VerifyResult res;
+
+    if (!c || c->pending || c->result_type != RESULT_TYPE_VERIFY )
+        return NULL; /* No results yet or verification error */
+
+    for (res = c->result.verify; res && idx>0 ; res = res->next, idx--)
+        ;
+    if (!res)
+        return NULL; /* No more signatures */
+
+    if (r_stat)
+        *r_stat = res->status;
+    if (r_created)
+        *r_created = res->timestamp;
+    return res->fpr;
+}
+
+
+/**
+ * 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. GPG<ME_EOF is returned to
+ *               indicate that there are no more signatures. 
+ **/
+GpgmeError
+gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key)
+{
+    VerifyResult res;
+    GpgmeCtx listctx;
+    GpgmeError err;
+
+    if (!c || !r_key)
+        return mk_error (Invalid_Value);
+    if (c->pending || c->result_type != RESULT_TYPE_VERIFY )
+        return mk_error (Busy);
+
+    for (res = c->result.verify; res && idx>0 ; res = res->next, idx--)
+        ;
+    if (!res)
+        return mk_error (EOF);
+
+    if (strlen(res->fpr) < 16) /* we have at least an key ID */
+        return mk_error (Invalid_Key);
+
+    /* Fixme: This can me optimized keeping
+     *        an internal context used for such key listings */
+    if ( (err=gpgme_new (&listctx)) )
+        return err;
+    if ( !(err=gpgme_op_keylist_start (listctx, res->fpr, 0 )) )
+        err=gpgme_op_keylist_next ( listctx, r_key );
+    gpgme_release (listctx);
+
+    return err;
+}
+
 
 
 
index 2b91a99ac5c3b41a4e66a41474f3abfc9fc8f79e..22ff1589e3646f92744263f7b166ed52b70a30d7 100644 (file)
@@ -67,28 +67,59 @@ static const char test_sig1[] =
                                 exit (1); }                               \
                              } while(0)
 
-static void
-print_sig_stat ( GpgmeSigStat status )
+
+static const char *
+status_string (GpgmeSigStat status)
 {
+    const char *s = "?";
+
     switch ( status ) {
       case GPGME_SIG_STAT_NONE:
-        fputs ("Verification Status: None\n", stdout);
+        s = "None";
         break;
       case GPGME_SIG_STAT_NOSIG:
-        fputs ("Verification Status: No Signature\n", stdout);
+        s = "No Signature";
         break;
       case GPGME_SIG_STAT_GOOD:
-        fputs ("Verification Status: Good\n", stdout);
+        s = "Good";
         break;
       case GPGME_SIG_STAT_BAD:
-        fputs ("Verification Status: Bad\n", stdout);
+        s = "Bad";
         break;
       case GPGME_SIG_STAT_NOKEY:
-        fputs ("Verification Status: No Key\n", stdout);
+        s = "No Key";
         break;
       case GPGME_SIG_STAT_ERROR:
-        fputs ("Verification Status: Error\n", stdout);
+        s = "Error";
         break;
+      case GPGME_SIG_STAT_DIFF:
+        s = "More than one signature";
+        break;
+    }
+    return s;
+}
+
+
+static void
+print_sig_stat ( GpgmeCtx ctx, GpgmeSigStat status )
+{
+    const char *s;
+    time_t created;
+    int idx;
+    GpgmeKey key;
+
+    printf ("Verification Status: %s\n", status_string (status));
+    
+    for(idx=0; (s=gpgme_get_sig_status (ctx, idx, &status, &created)); idx++ ) {
+        printf ("sig %d: created: %lu status: %s\n", idx, (ulong)created,
+                status_string(status) );
+        printf ("sig %d: fpr/keyid=`%s'\n", idx, s );
+        if ( !gpgme_get_sig_key (ctx, idx, &key) ) {
+            char *p = gpgme_key_get_as_xml ( key );
+            printf ("sig %d: key object:\n%s\n", idx, p );
+            free (p);
+            gpgme_key_release (key);
+        }
     }
 }
 
@@ -118,7 +149,7 @@ main (int argc, char **argv )
 
     puts ("checking a valid message:\n");
     err = gpgme_op_verify (ctx, sig, text, &status );
-    print_sig_stat ( status );
+    print_sig_stat ( ctx, status );
     fail_if_err (err);
 
     if ( (nota=gpgme_get_notation (ctx)) )
@@ -131,7 +162,7 @@ main (int argc, char **argv )
     fail_if_err (err);
     gpgme_data_rewind ( sig );
     err = gpgme_op_verify (ctx, sig, text, &status );
-    print_sig_stat ( status );
+    print_sig_stat ( ctx, status );
     fail_if_err (err);
     if ( (nota=gpgme_get_notation (ctx)) )
         printf ("---Begin Notation---\n%s---End Notation---\n", nota );