From 0b176661453bc01663e3a2ed9bfd7f57d7f0b99f Mon Sep 17 00:00:00 2001
From: Werner Koch <wk@gnupg.org>
Date: Mon, 19 Nov 2001 12:42:01 +0000
Subject: [PATCH] Write status output, make verify work in server mode.

---
 sm/fingerprint.c |  23 +++++
 sm/gpgsm.c       |  25 +++--
 sm/gpgsm.h       |  88 ++++++++++++++++-
 sm/import.c      |   2 +-
 sm/server.c      | 245 ++++++++++++++++++++++++++++++++++++++++++++++-
 sm/verify.c      |  52 +++++++++-
 6 files changed, 422 insertions(+), 13 deletions(-)

diff --git a/sm/fingerprint.c b/sm/fingerprint.c
index 9453a6eab..a612f3b87 100644
--- a/sm/fingerprint.c
+++ b/sm/fingerprint.c
@@ -102,3 +102,26 @@ gpgsm_get_fingerprint_string (KsbaCert cert, int algo)
   return buf;
 }
 
+/* Return an allocated buffer with the formatted fungerprint as one
+   large hexnumber */
+char *
+gpgsm_get_fingerprint_hexstring (KsbaCert cert, int algo)
+{
+  unsigned char digest[MAX_DIGEST_LEN];
+  char *buf;
+  int len, i;
+
+  if (!algo)
+    algo = GCRY_MD_SHA1;
+
+  len = gcry_md_get_algo_dlen (algo);
+  assert (len <= MAX_DIGEST_LEN );
+  gpgsm_get_fingerprint (cert, algo, digest, NULL);
+  buf = xmalloc (len*3+1);
+  *buf = 0;
+  for (i=0; i < len; i++ )
+    sprintf (buf+strlen(buf), "%02X", digest[i]);
+  return buf;
+}
+
+
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
index 329e80d9f..3a84777e1 100644
--- a/sm/gpgsm.c
+++ b/sm/gpgsm.c
@@ -319,6 +319,7 @@ static char *build_list (const char *text,
 static void set_cmd (enum cmd_and_opt_values *ret_cmd,
                      enum cmd_and_opt_values new_cmd );
 
+static int check_special_filename (const char *fname);
 static int open_read (const char *filename);
 
 
@@ -512,6 +513,7 @@ main ( int argc, char **argv)
   char *def_cipher_string = NULL;
   char *def_digest_string = NULL;
   enum cmd_and_opt_values cmd = 0;
+  struct server_control_s ctrl;
 
   /* FIXME: trap_unaligned ();*/
   set_strusage (my_strusage);
@@ -583,6 +585,12 @@ main ( int argc, char **argv)
   assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
   keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
 
+  /* Setup a default control structure */
+  memset (&ctrl, 0, sizeof ctrl);
+  ctrl.no_server = 1;
+  ctrl.status_fd = -1; /* not status output */
+
+  /* set the default option file */
   if (default_config )
     configname = make_filename (opt.homedir, "gpgsm.conf", NULL);
   
@@ -680,7 +688,7 @@ main ( int argc, char **argv)
         case oDebug: opt.debug |= pargs.r.ret_ulong; break;
         case oDebugAll: opt.debug = ~0; break;
 
-        case oStatusFD: /* fixme: set_status_fd (pargs.r.ret_int );*/ break;
+        case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break;
         case oLoggerFD: /* fixme: log_set_logfile (NULL, pargs.r.ret_int );*/ break;
         case oWithFingerprint:
           with_fpr=1; /*fall thru*/
@@ -930,11 +938,11 @@ main ( int argc, char **argv)
 
     case aVerify:
       if (!argc)
-        gpgsm_verify (0, -1); /* normal signature from stdin */
+        gpgsm_verify (&ctrl, 0, -1); /* normal signature from stdin */
       else if (argc == 1)
-        gpgsm_verify (open_read (*argv), -1); /* normal signature */
+        gpgsm_verify (&ctrl, open_read (*argv), -1); /* normal signature */
       else if (argc == 2) /* detached signature (sig, detached) */
-        gpgsm_verify (open_read (*argv), open_read (argv[1])); 
+        gpgsm_verify (&ctrl, open_read (*argv), open_read (argv[1])); 
       else
         wrong_args (_("--verify [signature [detached_data]]"));
       break;
@@ -992,8 +1000,13 @@ main ( int argc, char **argv)
       break;
 
     case aImport:
-/*        import_keys (argc? argv:NULL, argc); */
-      gpgsm_import (0);
+      if (!argc)
+        gpgsm_import (&ctrl, 0);
+      else
+        {
+          for (; argc; argc--, argv++)
+            gpgsm_import (&ctrl, open_read (*argv));
+        }
       break;
       
     case aExport:
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index 9c0c93375..5f7f56454 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -43,6 +43,78 @@ enum {
   GPGSM_Conflict = 13,
 };
 
+/* Status codes (shared with gpg) */
+enum {
+  STATUS_ENTER,
+  STATUS_LEAVE,
+  STATUS_ABORT,
+  STATUS_GOODSIG,
+  STATUS_BADSIG,
+  STATUS_ERRSIG,
+  STATUS_BADARMOR,
+  STATUS_RSA_OR_IDEA,
+  STATUS_SIGEXPIRED,
+  STATUS_KEYREVOKED,
+  STATUS_TRUST_UNDEFINED,
+  STATUS_TRUST_NEVER,
+  STATUS_TRUST_MARGINAL,
+  STATUS_TRUST_FULLY,
+  STATUS_TRUST_ULTIMATE,
+  
+  STATUS_SHM_INFO,
+  STATUS_SHM_GET,
+  STATUS_SHM_GET_BOOL,
+  STATUS_SHM_GET_HIDDEN,
+  
+  STATUS_NEED_PASSPHRASE,
+  STATUS_VALIDSIG,
+  STATUS_SIG_ID,
+  STATUS_ENC_TO,
+  STATUS_NODATA,
+  STATUS_BAD_PASSPHRASE,
+  STATUS_NO_PUBKEY,
+  STATUS_NO_SECKEY,
+  STATUS_NEED_PASSPHRASE_SYM,
+  STATUS_DECRYPTION_FAILED,
+  STATUS_DECRYPTION_OKAY,
+  STATUS_MISSING_PASSPHRASE,
+  STATUS_GOOD_PASSPHRASE,
+  STATUS_GOODMDC,
+  STATUS_BADMDC,
+  STATUS_ERRMDC,
+  STATUS_IMPORTED,
+  STATUS_IMPORT_RES,
+  STATUS_FILE_START,
+  STATUS_FILE_DONE,
+  STATUS_FILE_ERROR,
+  
+  STATUS_BEGIN_DECRYPTION,
+  STATUS_END_DECRYPTION,
+  STATUS_BEGIN_ENCRYPTION,
+  STATUS_END_ENCRYPTION,
+  
+  STATUS_DELETE_PROBLEM,
+  STATUS_GET_BOOL,
+  STATUS_GET_LINE,
+  STATUS_GET_HIDDEN,
+  STATUS_GOT_IT,
+  STATUS_PROGRESS,
+  STATUS_SIG_CREATED,
+  STATUS_SESSION_KEY,
+  STATUS_NOTATION_NAME,
+  STATUS_NOTATION_DATA,
+  STATUS_POLICY_URL,
+  STATUS_BEGIN_STREAM,
+  STATUS_END_STREAM,
+  STATUS_KEY_CREATED,
+  STATUS_USERID_HIN,
+  STATUS_UNEXPECTED,
+  STATUS_INV_RECP,
+  STATUS_NO_RECP,
+  STATUS_ALREADY_SIGNED,
+};
+
+
 #define MAX_DIGEST_LEN 24 
 
 /* A large struct name "opt" to keep global flags */
@@ -98,15 +170,27 @@ struct {
 #define DBG_CACHE   (opt.debug & DBG_CACHE_VALUE)
 #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
 
+struct server_local_s;
+
+struct server_control_s {
+  int no_server;     /* we are not running under server control */
+  int  status_fd;    /* only for non-server mode */
+  struct server_local_s *server_local;
+};
+typedef struct server_control_s *CTRL;
+
+
 /*-- gpgsm.c --*/
 void gpgsm_exit (int rc);
 
 /*-- server.c --*/
 void gpgsm_server (void);
+void gpgsm_status (CTRL ctrl, int no, const char *text);
 
 /*-- fingerprint --*/
 char *gpgsm_get_fingerprint (KsbaCert cert, int algo, char *array, int *r_len);
 char *gpgsm_get_fingerprint_string (KsbaCert cert, int algo);
+char *gpgsm_get_fingerprint_hexstring (KsbaCert cert, int algo);
 
 /*-- certdump.c --*/
 void gpgsm_dump_cert (const char *text, KsbaCert cert);
@@ -124,10 +208,10 @@ int gpgsm_validate_path (KsbaCert cert);
 
 
 /*-- import.c --*/
-int gpgsm_import (int in_fd);
+int gpgsm_import (CTRL ctrl, int in_fd);
 
 /*-- verify.c --*/
-int gpgsm_verify (int in_fd, int data_fd);
+int gpgsm_verify (CTRL ctrl, int in_fd, int data_fd);
 
 
 
diff --git a/sm/import.c b/sm/import.c
index 8913f8092..ba21312de 100644
--- a/sm/import.c
+++ b/sm/import.c
@@ -265,7 +265,7 @@ store_cert (KsbaCert cert)
 
 
 int
-gpgsm_import (int in_fd)
+gpgsm_import (CTRL ctrl, int in_fd)
 {
   int rc;
   KsbaReader reader = NULL;
diff --git a/sm/server.c b/sm/server.c
index c3b9f1559..c44de16da 100644
--- a/sm/server.c
+++ b/sm/server.c
@@ -30,6 +30,18 @@
 #include "../assuan/assuan.h"
 
 #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
+#define digitp(a) ((a) >= '0' && (a) <= '9')
+
+
+/* The filepointer for status message used in non-server mode */
+static FILE *statusfp;
+
+/* Data used to assuciate an Assuan context with local server data */
+struct server_local_s {
+  ASSUAN_CONTEXT assuan_ctx;
+  int message_fd;
+};
+
 
 
 /*  RECIPIENT <userID>
@@ -104,12 +116,13 @@ cmd_decrypt (ASSUAN_CONTEXT ctx, char *line)
 static int 
 cmd_verify (ASSUAN_CONTEXT ctx, char *line)
 {
+  CTRL ctrl = assuan_get_pointer (ctx);
   int fd = assuan_get_input_fd (ctx);
 
   if (fd == -1)
     return set_error (No_Input, NULL);
 
-  gpgsm_verify (fd, -1);
+  gpgsm_verify (assuan_get_pointer (ctx), fd, ctrl->server_local->message_fd);
 
   return 0;
 }
@@ -141,11 +154,37 @@ cmd_import (ASSUAN_CONTEXT ctx, char *line)
   if (fd == -1)
     return set_error (No_Input, NULL);
 
-  gpgsm_import (fd);
+  gpgsm_import (assuan_get_pointer (ctx), fd);
 
   return 0;
 }
 
+/* MESSAGE FD=<n>
+
+   Set the file descriptor to read a message which is used with
+   detached signatures */
+static int 
+cmd_message (ASSUAN_CONTEXT ctx, char *line)
+{
+  char *endp;
+  int fd;
+  CTRL ctrl = assuan_get_pointer (ctx);
+
+  if (strncmp (line, "FD=", 3))
+    return set_error (Syntax_Error, "FD=<n> expected");
+  line += 3;
+  if (!digitp (*line))
+    return set_error (Syntax_Error, "number required");
+  fd = strtoul (line, &endp, 10);
+  if (*endp)
+    return set_error (Syntax_Error, "garbage found");
+  if (fd == -1)
+    return set_error (No_Input, NULL);
+
+  ctrl->server_local->message_fd = fd;
+  return 0;
+}
+
 
 
 
@@ -166,6 +205,7 @@ register_commands (ASSUAN_CONTEXT ctx)
     { "IMPORT",     0,  cmd_import },
     { "",     ASSUAN_CMD_INPUT, NULL }, 
     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
+    { "MESSAGE",    0,  cmd_message },
     { NULL }
   };
   int i, j, rc;
@@ -189,6 +229,9 @@ gpgsm_server (void)
   int rc;
   int filedes[2];
   ASSUAN_CONTEXT ctx;
+  struct server_control_s ctrl;
+
+  memset (&ctrl, 0, sizeof ctrl);
 
   /* For now we use a simple pipe based server so that we can work
      from scripts.  We will later add options to run as a daemon and
@@ -210,6 +253,11 @@ gpgsm_server (void)
       gpgsm_exit (2);
     }
 
+  assuan_set_pointer (ctx, &ctrl);
+  ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
+  ctrl.server_local->assuan_ctx = ctx;
+  ctrl.server_local->message_fd = -1;
+
   log_info ("Assuan started\n");
   for (;;)
     {
@@ -238,6 +286,199 @@ gpgsm_server (void)
 }
 
 
+static const char *
+get_status_string ( int no ) 
+{
+  const char *s;
+
+  switch (no)
+    {
+    case STATUS_ENTER  : s = "ENTER"; break;
+    case STATUS_LEAVE  : s = "LEAVE"; break;
+    case STATUS_ABORT  : s = "ABORT"; break;
+    case STATUS_GOODSIG: s = "GOODSIG"; break;
+    case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
+    case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
+    case STATUS_BADSIG : s = "BADSIG"; break;
+    case STATUS_ERRSIG : s = "ERRSIG"; break;
+    case STATUS_BADARMOR : s = "BADARMOR"; break;
+    case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
+    case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
+    case STATUS_TRUST_NEVER	 : s = "TRUST_NEVER"; break;
+    case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
+    case STATUS_TRUST_FULLY	 : s = "TRUST_FULLY"; break;
+    case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
+    case STATUS_GET_BOOL	 : s = "GET_BOOL"; break;
+    case STATUS_GET_LINE	 : s = "GET_LINE"; break;
+    case STATUS_GET_HIDDEN	 : s = "GET_HIDDEN"; break;
+    case STATUS_GOT_IT	 : s = "GOT_IT"; break;
+    case STATUS_SHM_INFO	 : s = "SHM_INFO"; break;
+    case STATUS_SHM_GET	 : s = "SHM_GET"; break;
+    case STATUS_SHM_GET_BOOL	 : s = "SHM_GET_BOOL"; break;
+    case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
+    case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
+    case STATUS_VALIDSIG	 : s = "VALIDSIG"; break;
+    case STATUS_SIG_ID	 : s = "SIG_ID"; break;
+    case STATUS_ENC_TO	 : s = "ENC_TO"; break;
+    case STATUS_NODATA	 : s = "NODATA"; break;
+    case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
+    case STATUS_NO_PUBKEY	 : s = "NO_PUBKEY"; break;
+    case STATUS_NO_SECKEY	 : s = "NO_SECKEY"; break;
+    case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
+    case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
+    case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
+    case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
+    case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
+    case STATUS_GOODMDC	 : s = "GOODMDC"; break;
+    case STATUS_BADMDC	 : s = "BADMDC"; break;
+    case STATUS_ERRMDC	 : s = "ERRMDC"; break;
+    case STATUS_IMPORTED	 : s = "IMPORTED"; break;
+    case STATUS_IMPORT_RES	 : s = "IMPORT_RES"; break;
+    case STATUS_FILE_START	 : s = "FILE_START"; break;
+    case STATUS_FILE_DONE	 : s = "FILE_DONE"; break;
+    case STATUS_FILE_ERROR	 : s = "FILE_ERROR"; break;
+    case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
+    case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
+    case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
+    case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
+    case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
+    case STATUS_PROGRESS	 : s = "PROGRESS"; break;
+    case STATUS_SIG_CREATED	 : s = "SIG_CREATED"; break;
+    case STATUS_SESSION_KEY	 : s = "SESSION_KEY"; break;
+    case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
+    case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
+    case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
+    case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
+    case STATUS_END_STREAM     : s = "END_STREAM"; break;
+    case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
+    case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
+    case STATUS_INV_RECP       : s = "INV_RECP"; break;
+    case STATUS_NO_RECP        : s = "NO_RECP"; break;
+    case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
+    default: s = "?"; break;
+    }
+  return s;
+}
+
+
+
+void
+gpgsm_status (CTRL ctrl, int no, const char *text)
+{
+  if (ctrl->no_server)
+    {
+      if (ctrl->status_fd == -1)
+        return; /* no status wanted */
+      if (!statusfp)
+        {
+          if (ctrl->status_fd == 1)
+            statusfp = stdout;
+          else if (ctrl->status_fd == 2)
+            statusfp = stderr;
+          else
+            statusfp = fdopen (ctrl->status_fd, "w");
+      
+          if (!statusfp)
+            {
+              log_fatal ("can't open fd %d for status output: %s\n",
+                         ctrl->status_fd, strerror(errno));
+            }
+        }
+      
+      fputs ("[GNUPG:] ", statusfp);
+      fputs (get_status_string (no), statusfp);
+    
+      if (text)
+        {
+          putc ( ' ', statusfp );
+          for (; *text; text++) 
+            {
+              if (*text == '\n')
+                fputs ( "\\n", statusfp );
+              else if (*text == '\r')
+                fputs ( "\\r", statusfp );
+              else 
+                putc ( *(const byte *)text,  statusfp );
+            }
+        }
+      putc ('\n', statusfp);
+      fflush (statusfp);
+    }
+  else 
+    {
+      ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
+
+      assuan_write_status (ctx, get_status_string (no), text);
+    }
+}
+
+
+#if 0
+/*
+ * Write a status line with a buffer using %XX escapes.  If WRAP is >
+ * 0 wrap the line after this length.  If STRING is not NULL it will
+ * be prepended to the buffer, no escaping is done for string.
+ * A wrap of -1 forces spaces not to be encoded as %20.
+ */
+void
+write_status_text_and_buffer ( int no, const char *string,
+                               const char *buffer, size_t len, int wrap )
+{
+    const char *s, *text;
+    int esc, first;
+    int lower_limit = ' ';
+    size_t n, count, dowrap;
+
+    if( !statusfp )
+	return;  /* not enabled */
+    
+    if (wrap == -1) {
+        lower_limit--;
+        wrap = 0;
+    }
+
+    text = get_status_string (no);
+    count = dowrap = first = 1;
+    do {
+        if (dowrap) {
+            fprintf (statusfp, "[GNUPG:] %s ", text );
+            count = dowrap = 0;
+            if (first && string) {
+                fputs (string, statusfp);
+                count += strlen (string);
+            }
+            first = 0;
+        }
+        for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
+            if ( *s == '%' || *(const byte*)s <= lower_limit 
+                           || *(const byte*)s == 127 ) 
+                esc = 1;
+            if ( wrap && ++count > wrap ) {
+                dowrap=1;
+                break;
+            }
+        }
+        if (esc) {
+            s--; n++;
+        }
+        if (s != buffer) 
+            fwrite (buffer, s-buffer, 1, statusfp );
+        if ( esc ) {
+            fprintf (statusfp, "%%%02X", *(const byte*)s );
+            s++; n--;
+        }
+        buffer = s;
+        len = n;
+        if ( dowrap && len )
+            putc ( '\n', statusfp );
+    } while ( len );
+
+    putc ('\n',statusfp);
+    fflush (statusfp);
+}
+#endif
+
+
 
 
 
diff --git a/sm/verify.c b/sm/verify.c
index ecbbba4f0..6b4ef5c09 100644
--- a/sm/verify.c
+++ b/sm/verify.c
@@ -38,6 +38,31 @@ struct reader_cb_parm_s {
   FILE *fp;
 };
 
+
+/* FIXME: Move this to jnlib */
+char *
+strtimestamp (time_t atime)
+{
+  char *buffer = xmalloc (15);
+  
+  if (atime < 0) 
+    {
+      strcpy (buffer, "????" "-??" "-??");
+    }
+  else
+    {
+      struct tm *tp;
+      
+      tp = gmtime( &atime );
+      sprintf (buffer, "%04d-%02d-%02d",
+               1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday);
+    }
+  return buffer;
+}
+
+
+
+
 /* FIXME: We need to write a generic reader callback which should be able
    to detect and convert base-64 */
 static int
@@ -142,7 +167,7 @@ hash_data (int fd, GCRY_MD_HD md)
 /* Perform a verify operation.  To verify detached signatures, data_fd
    must be different than -1 */
 int
-gpgsm_verify (int in_fd, int data_fd)
+gpgsm_verify (CTRL ctrl, int in_fd, int data_fd)
 {
   int i, rc;
   KsbaError err;
@@ -289,6 +314,7 @@ gpgsm_verify (int in_fd, int data_fd)
       unsigned char *serial;
       char *msgdigest = NULL;
       size_t msgdigestlen;
+      time_t sigcreated;
 
       err = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial);
       if (err)
@@ -346,6 +372,7 @@ gpgsm_verify (int in_fd, int data_fd)
             {
               log_error ("message digest attribute does not "
                          "match calculated one\n");
+              gpgsm_status (ctrl, STATUS_BADSIG, NULL);
               goto next_signer; 
             }
             
@@ -355,7 +382,7 @@ gpgsm_verify (int in_fd, int data_fd)
               log_error ("md_open failed: %s\n", gcry_strerror (-1));
               goto next_signer;
             }
-          ksba_cms_set_hash_function (cms, gcry_md_write, md);
+          ksba_cms_set_hash_function (cms, HASH_FNC, md);
           rc = ksba_cms_hash_signed_attrs (cms, signer);
           if (rc)
             {
@@ -375,16 +402,37 @@ gpgsm_verify (int in_fd, int data_fd)
       if (rc)
         {
           log_error ("invalid signature: %s\n", gpgsm_strerror (rc));
+          gpgsm_status (ctrl, STATUS_BADSIG, NULL);
           goto next_signer;
         }
       log_debug ("signature okay - checking certs\n");
+      gpgsm_status (ctrl, STATUS_GOODSIG, NULL);
+      {
+        char *buf, *fpr, *tstr;
+
+        fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+        tstr = strtimestamp ( 42 /*fixme: get right time */);
+        buf = xmalloc ( strlen(fpr) + strlen (tstr) + 100);
+        sprintf (buf, "%s %s %lu", fpr, tstr, (unsigned long)42 );
+        xfree (tstr);
+        xfree (fpr);
+        gpgsm_status (ctrl, STATUS_VALIDSIG, buf);
+        xfree (buf);
+      }
+
       rc = gpgsm_validate_path (cert);
       if (rc)
         {
           log_error ("invalid certification path: %s\n", gpgsm_strerror (rc));
+          if (rc == GPGSM_Bad_Certificate_Path
+              || rc == GPGSM_Bad_Certificate)
+            gpgsm_status (ctrl, STATUS_TRUST_NEVER, NULL);
+          else
+            gpgsm_status (ctrl, STATUS_TRUST_UNDEFINED, NULL);
           goto next_signer;
         }
       log_info ("signature is good\n");
+      gpgsm_status (ctrl, STATUS_TRUST_FULLY, NULL);
           
 
     next_signer: