agent,g10: Remove redundant SERIALNO request.
[gnupg.git] / g10 / keylist.c
index d71bf4f..32cf1e8 100644 (file)
@@ -16,7 +16,7 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #ifdef HAVE_DOSISH_SYSTEM
-#include <fcntl.h>             /* for setmode() */
+# include <fcntl.h>            /* for setmode() */
 #endif
 
 #include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "status.h"
+#include "../common/status.h"
 #include "keydb.h"
 #include "photoid.h"
-#include "util.h"
-#include "ttyio.h"
+#include "../common/util.h"
+#include "../common/ttyio.h"
 #include "trustdb.h"
 #include "main.h"
-#include "i18n.h"
-#include "status.h"
+#include "../common/i18n.h"
+#include "../common/status.h"
 #include "call-agent.h"
-#include "mbox-util.h"
-#include "zb32.h"
+#include "../common/mbox-util.h"
+#include "../common/zb32.h"
 #include "tofu.h"
 
 
@@ -60,6 +59,7 @@ struct keylist_context
   int inv_sigs;    /* Counter used if CHECK_SIGS is set.  */
   int no_key;      /* Counter used if CHECK_SIGS is set.  */
   int oth_err;     /* Counter used if CHECK_SIGS is set.  */
+  int no_validity; /* Do not show validity.  */
 };
 
 
@@ -131,10 +131,10 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
      is very bad for W32 because of a sharing violation. For real OSes
      it might lead to false results if we are later listing a keyring
      which is associated with the inode of a deleted file.  */
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
 
 #ifdef USE_TOFU
-  tofu_begin_batch_update ();
+  tofu_begin_batch_update (ctrl);
 #endif
 
   if (locate_mode)
@@ -145,7 +145,7 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
     list_one (ctrl, list, 0, opt.with_secret);
 
 #ifdef USE_TOFU
-  tofu_end_batch_update ();
+  tofu_end_batch_update (ctrl);
 #endif
 }
 
@@ -155,7 +155,7 @@ secret_key_list (ctrl_t ctrl, strlist_t list)
 {
   (void)ctrl;
 
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
 
   if (!list)
     list_all (ctrl, 1, 0);
@@ -248,7 +248,7 @@ print_card_key_info (estream_t fp, kbnode_t keyblock)
               log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
               s2k_char = '?';
             }
-          else if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
+          else if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
             s2k_char = serialno? '>':' ';
           else
             s2k_char = '#';  /* Key not found.  */
@@ -304,6 +304,7 @@ status_one_subpacket (sigsubpkttype_t type, size_t len, int flags,
 
 
 /* Print a policy URL.  Allowed values for MODE are:
+ *  -1 - print to the TTY
  *   0 - print to stdout.
  *   1 - use log_info and emit status messages.
  *   2 - emit only status messages.
@@ -314,50 +315,48 @@ show_policy_url (PKT_signature * sig, int indent, int mode)
   const byte *p;
   size_t len;
   int seq = 0, crit;
-  estream_t fp = mode ? log_get_stream () : es_stdout;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
 
   while ((p =
          enum_sig_subpkt (sig->hashed, SIGSUBPKT_POLICY, &len, &seq, &crit)))
     {
       if (mode != 2)
        {
-         int i;
          const char *str;
 
-         for (i = 0; i < indent; i++)
-           es_putc (' ', fp);
+          tty_fprintf (fp, "%*s", indent, "");
 
          if (crit)
            str = _("Critical signature policy: ");
          else
            str = _("Signature policy: ");
-         if (mode)
+         if (mode > 0)
            log_info ("%s", str);
          else
-           es_fprintf (fp, "%s", str);
-         print_utf8_buffer (fp, p, len);
-         es_fprintf (fp, "\n");
+           tty_fprintf (fp, "%s", str);
+         tty_print_utf8_string2 (fp, p, len, 0);
+         tty_fprintf (fp, "\n");
        }
 
-      if (mode)
+      if (mode > 0)
        write_status_buffer (STATUS_POLICY_URL, p, len, 0);
     }
 }
 
 
-/*
-  mode=0 for stdout.
-  mode=1 for log_info + status messages
-  mode=2 for status messages only
-*/
-/* TODO: use this */
+/* Print a keyserver URL.  Allowed values for MODE are:
+ *  -1 - print to the TTY
+ *   0 - print to stdout.
+ *   1 - use log_info and emit status messages.
+ *   2 - emit only status messages.
+ */
 void
 show_keyserver_url (PKT_signature * sig, int indent, int mode)
 {
   const byte *p;
   size_t len;
   int seq = 0, crit;
-  estream_t fp = mode ? log_get_stream () : es_stdout;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
 
   while ((p =
          enum_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &len, &seq,
@@ -365,44 +364,44 @@ show_keyserver_url (PKT_signature * sig, int indent, int mode)
     {
       if (mode != 2)
        {
-         int i;
          const char *str;
 
-         for (i = 0; i < indent; i++)
-           es_putc (' ', es_stdout);
+          tty_fprintf (fp, "%*s", indent, "");
 
          if (crit)
            str = _("Critical preferred keyserver: ");
          else
            str = _("Preferred keyserver: ");
-         if (mode)
+         if (mode > 0)
            log_info ("%s", str);
          else
-           es_fprintf (es_stdout, "%s", str);
-         print_utf8_buffer (fp, p, len);
-         es_fprintf (fp, "\n");
+           tty_fprintf (es_stdout, "%s", str);
+         tty_print_utf8_string2 (fp, p, len, 0);
+         tty_fprintf (fp, "\n");
        }
 
-      if (mode)
+      if (mode > 0)
        status_one_subpacket (SIGSUBPKT_PREF_KS, len,
                              (crit ? 0x02 : 0) | 0x01, p);
     }
 }
 
-/*
-  mode=0 for stdout.
-  mode=1 for log_info + status messages
-  mode=2 for status messages only
-
-  Defined bits in WHICH:
-    1 == standard notations
-    2 == user notations
-*/
+
+/* Print notation data.  Allowed values for MODE are:
+ *  -1 - print to the TTY
+ *   0 - print to stdout.
+ *   1 - use log_info and emit status messages.
+ *   2 - emit only status messages.
+ *
+ * Defined bits in WHICH:
+ *   1 - standard notations
+ *   2 - user notations
+ */
 void
 show_notation (PKT_signature * sig, int indent, int mode, int which)
 {
-  estream_t fp = mode ? log_get_stream () : es_stdout;
-  struct notation *nd, *notations;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
+  notation_t nd, notations;
 
   if (which == 0)
     which = 3;
@@ -418,37 +417,39 @@ show_notation (PKT_signature * sig, int indent, int mode, int which)
 
          if ((which & 1 && !has_at) || (which & 2 && has_at))
            {
-             int i;
              const char *str;
 
-             for (i = 0; i < indent; i++)
-               es_putc (' ', es_stdout);
+              tty_fprintf (fp, "%*s", indent, "");
 
              if (nd->flags.critical)
                str = _("Critical signature notation: ");
              else
                str = _("Signature notation: ");
-             if (mode)
+             if (mode > 0)
                log_info ("%s", str);
              else
-               es_fprintf (es_stdout, "%s", str);
+               tty_fprintf (es_stdout, "%s", str);
              /* This is all UTF8 */
-             print_utf8_buffer (fp, nd->name, strlen (nd->name));
-             es_fprintf (fp, "=");
-             print_utf8_buffer (fp, nd->value, strlen (nd->value));
+             tty_print_utf8_string2 (fp, nd->name, strlen (nd->name), 0);
+             tty_fprintf (fp, "=");
+             tty_print_utf8_string2 (fp, nd->value, strlen (nd->value), 0);
               /* (We need to use log_printf so that the next call to a
                   log function does not insert an extra LF.)  */
-              if (mode)
+              if (mode > 0)
                 log_printf ("\n");
               else
-                es_putc ('\n', fp);
+                tty_fprintf (fp, "\n");
            }
        }
 
-      if (mode)
+      if (mode > 0)
        {
          write_status_buffer (STATUS_NOTATION_NAME,
                               nd->name, strlen (nd->name), 0);
+          if (nd->flags.critical || nd->flags.human)
+            write_status_text (STATUS_NOTATION_FLAGS,
+                               nd->flags.critical && nd->flags.human? "1 1" :
+                               nd->flags.critical? "1 0" : "0 1");
          write_status_buffer (STATUS_NOTATION_DATA,
                               nd->value, strlen (nd->value), 50);
        }
@@ -464,6 +465,10 @@ print_signature_stats (struct keylist_context *s)
   if (!s->check_sigs)
     return;  /* Signature checking was not requested.  */
 
+  /* Better flush stdout so that the stats are always printed after
+   * the output.  */
+  es_fflush (es_stdout);
+
   if (s->good_sigs)
     log_info (ngettext("%d good signature\n",
                        "%d good signatures\n", s->good_sigs), s->good_sigs);
@@ -646,7 +651,7 @@ locate_one (ctrl_t ctrl, strlist_t names)
 
   for (sl = names; sl; sl = sl->next)
     {
-      rc = get_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, NULL, 1, 0);
+      rc = get_best_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, 1, 0);
       if (rc)
        {
          if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY)
@@ -799,7 +804,7 @@ print_subpackets_colon (PKT_signature * sig)
 {
   byte *i;
 
-  assert (opt.show_subpackets);
+  log_assert (opt.show_subpackets);
 
   for (i = opt.show_subpackets; *i; i++)
     {
@@ -848,9 +853,8 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk)
                   (ulong) uid->attribs[i].len, uid->attribs[i].type, i + 1,
                   uid->numattribs, (ulong) uid->created,
                   (ulong) uid->expiredate,
-                  ((uid->is_primary ? 0x01 : 0) | (uid->
-                                                   is_revoked ? 0x02 : 0) |
-                   (uid->is_expired ? 0x04 : 0)));
+                  ((uid->flags.primary ? 0x01 : 0) | (uid->flags.revoked ? 0x02 : 0) |
+                   (uid->flags.expired ? 0x04 : 0)));
          write_status_text (STATUS_ATTRIBUTE, buf);
        }
 
@@ -860,158 +864,8 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk)
 }
 
 
-/* Print IPGP cert records instead of a standard key listing.  */
 static void
-list_keyblock_pka (ctrl_t ctrl, kbnode_t keyblock)
-{
-  kbnode_t kbctx;
-  kbnode_t node;
-  PKT_public_key *pk;
-  char pkstrbuf[PUBKEY_STRING_SIZE];
-  char *hexfpr;
-  char *hexkeyblock = NULL;
-  unsigned int hexkeyblocklen = 0;  /* Init to avoid -Wmaybe-uninitialized. */
-  const char *s;
-
-  /* Get the keyid from the keyblock.  */
-  node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
-  if (!node)
-    {
-      log_error ("Oops; key lost!\n");
-      dump_kbnode (keyblock);
-      return;
-    }
-
-  pk = node->pkt->pkt.public_key;
-
-  /* First print an overview of the key with all userids.  */
-  es_fprintf (es_stdout, ";; pub  %s/%s %s\n;;",
-              pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-              keystr_from_pk (pk), datestr_from_pk (pk));
-  print_fingerprint (NULL, pk, 10);
-  for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
-    {
-      if (node->pkt->pkttype == PKT_USER_ID)
-       {
-         PKT_user_id *uid = node->pkt->pkt.user_id;
-
-         if (pk && (uid->is_expired || uid->is_revoked)
-             && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
-            continue;
-
-          es_fputs (";; uid  ", es_stdout);
-          print_utf8_buffer (es_stdout, uid->name, uid->len);
-          es_putc ('\n', es_stdout);
-        }
-    }
-
-
-  hexfpr = hexfingerprint (pk, NULL, 0);
-  if (opt.print_dane_records)
-    {
-      kbnode_t dummy_keyblock;
-      void *data;
-      size_t datalen;
-      gpg_error_t err;
-
-      /* We do not have an export function which allows to pass a
-         keyblock, thus we need to search the key again.  */
-      err = export_pubkey_buffer (ctrl, hexfpr,
-                                  EXPORT_DANE_FORMAT, NULL,
-                                  &dummy_keyblock, &data, &datalen);
-      release_kbnode (dummy_keyblock);
-      if (!err)
-        {
-          hexkeyblocklen = datalen;
-          hexkeyblock = bin2hex (data, datalen, NULL);
-          if (!hexkeyblock)
-            err = gpg_error_from_syserror ();
-          xfree (data);
-          ascii_strlwr (hexkeyblock);
-        }
-      if (err)
-        log_error (_("skipped \"%s\": %s\n"), hexfpr, gpg_strerror (err));
-
-    }
-
-  for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
-    {
-      if (node->pkt->pkttype == PKT_USER_ID)
-       {
-         PKT_user_id *uid = node->pkt->pkt.user_id;
-          char *mbox;
-          char *p;
-
-         if (pk && (uid->is_expired || uid->is_revoked)
-             && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
-            continue;
-
-          mbox = mailbox_from_userid (uid->name);
-          if (mbox && (p = strchr (mbox, '@')))
-            {
-              char hashbuf[32];
-              char *hash;
-              unsigned int len;
-
-              *p++ = 0;
-              if (opt.print_pka_records)
-                {
-                  es_fprintf (es_stdout, "$ORIGIN _pka.%s.\n; %s\n; ",
-                              p, hexfpr);
-                  print_utf8_buffer (es_stdout, uid->name, uid->len);
-                  es_putc ('\n', es_stdout);
-                  gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf,
-                                       mbox, strlen (mbox));
-                  hash = zb32_encode (hashbuf, 8*20);
-                  if (hash)
-                    {
-                      len = strlen (hexfpr)/2;
-                      es_fprintf (es_stdout,
-                                  "%s TYPE37 \\# %u 0006 0000 00 %02X %s\n",
-                                  hash, 6 + len, len, hexfpr);
-                      xfree (hash);
-                    }
-                }
-              if (opt.print_dane_records && hexkeyblock)
-                {
-                  es_fprintf (es_stdout, "$ORIGIN _openpgpkey.%s.\n; %s\n; ",
-                              p, hexfpr);
-                  print_utf8_buffer (es_stdout, uid->name, uid->len);
-                  es_putc ('\n', es_stdout);
-                  gcry_md_hash_buffer (GCRY_MD_SHA256, hashbuf,
-                                       mbox, strlen (mbox));
-                  hash = bin2hex (hashbuf, 28, NULL);
-                  if (hash)
-                    {
-                      ascii_strlwr (hash);
-                      es_fprintf (es_stdout, "%s TYPE61 \\# %u (\n",
-                                  hash, hexkeyblocklen);
-                      xfree (hash);
-                      s = hexkeyblock;
-                      for (;;)
-                        {
-                          es_fprintf (es_stdout, "\t%.64s\n", s);
-                          if (strlen (s) < 64)
-                            break;
-                          s += 64;
-                        }
-                      es_fputs ("\t)\n", es_stdout);
-                    }
-                }
-            }
-          xfree (mbox);
-       }
-
-    }
-  es_putc ('\n', es_stdout);
-
-  xfree (hexkeyblock);
-  xfree (hexfpr);
-}
-
-
-static void
-list_keyblock_print (KBNODE keyblock, int secret, int fpr,
+list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
                      struct keylist_context *listctx)
 {
   int rc;
@@ -1019,10 +873,8 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
   KBNODE node;
   PKT_public_key *pk;
   int skip_sigs = 0;
-  int s2k_char;
   char *hexgrip = NULL;
   char *serialno = NULL;
-  char pkstrbuf[PUBKEY_STRING_SIZE];
 
   /* Get the keyid from the keyblock.  */
   node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
@@ -1044,60 +896,18 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
 
   if (secret)
     {
-      if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
-        s2k_char = serialno? '>':' ';
+      /* Encode some info about the secret key in SECRET.  */
+      if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
+        secret = serialno? 3 : 1;
       else
-        s2k_char = '#';  /* Key not found.  */
-    }
-  else
-    s2k_char = ' ';
-
-  check_trustdb_stale ();
-
-
-  es_fprintf (es_stdout, "%s%c  %s/%s %s",
-              secret? "sec":"pub",
-              s2k_char,
-              pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-              keystr_from_pk (pk), datestr_from_pk (pk));
-
-  if ((opt.list_options & LIST_SHOW_USAGE))
-    {
-      es_fprintf (es_stdout, " [%s]", usagestr_from_pk (pk, 0));
-    }
-  if (pk->flags.revoked)
-    {
-      es_fprintf (es_stdout, " [");
-      es_fprintf (es_stdout, _("revoked: %s"), revokestr_from_pk (pk));
-      es_fprintf (es_stdout, "]");
-    }
-  else if (pk->has_expired)
-    {
-      es_fprintf (es_stdout, " [");
-      es_fprintf (es_stdout, _("expired: %s"), expirestr_from_pk (pk));
-      es_fprintf (es_stdout, "]");
-    }
-  else if (pk->expiredate)
-    {
-      es_fprintf (es_stdout, " [");
-      es_fprintf (es_stdout, _("expires: %s"), expirestr_from_pk (pk));
-      es_fprintf (es_stdout, "]");
+        secret = 2;  /* Key not found.  */
     }
 
-#if 0
-  /* I need to think about this some more.  It's easy enough to
-     include, but it looks sort of confusing in the listing... */
-  if (opt.list_options & LIST_SHOW_VALIDITY)
-    {
-      int validity = get_validity (pk, NULL, NULL, 0);
-      es_fprintf (es_stdout, " [%s]", trust_value_to_string (validity));
-    }
-#endif
+  if (!listctx->no_validity)
+    check_trustdb_stale (ctrl);
 
-  if (pk->pubkey_algo >= 100)
-    es_fprintf (es_stdout, " [experimental algorithm %d]", pk->pubkey_algo);
-
-  es_fprintf (es_stdout, "\n");
+  /* Print the "pub" line and in KF_NONE mode the fingerprint.  */
+  print_key_line (es_stdout, pk, secret);
 
   if (fpr)
     print_fingerprint (NULL, pk, 0);
@@ -1116,8 +926,10 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
       if (node->pkt->pkttype == PKT_USER_ID)
        {
          PKT_user_id *uid = node->pkt->pkt.user_id;
+          int indent;
+          int kl = opt.keyid_format == KF_NONE? 10 : keystrlen ();
 
-         if ((uid->is_expired || uid->is_revoked)
+         if ((uid->flags.expired || uid->flags.revoked)
              && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
            {
              skip_sigs = 1;
@@ -1129,31 +941,53 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
          if (attrib_fp && uid->attrib_data != NULL)
            dump_attribs (uid, pk);
 
-         if ((uid->is_revoked || uid->is_expired)
-             || (opt.list_options & LIST_SHOW_UID_VALIDITY))
+         if ((uid->flags.revoked || uid->flags.expired)
+             || ((opt.list_options & LIST_SHOW_UID_VALIDITY)
+                  && !listctx->no_validity))
            {
              const char *validity;
-             int indent;
-
-             validity = uid_trust_string_fixed (pk, uid);
-             indent =
-               (keystrlen () + (opt.legacy_list_mode? 9:11)) -
-               atoi (uid_trust_string_fixed (NULL, NULL));
 
+             validity = uid_trust_string_fixed (ctrl, pk, uid);
+             indent = ((kl + (opt.legacy_list_mode? 9:11))
+                        - atoi (uid_trust_string_fixed (ctrl, NULL, NULL)));
              if (indent < 0 || indent > 40)
                indent = 0;
 
              es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity);
            }
          else
-           es_fprintf (es_stdout, "uid%*s",
-                        (int) keystrlen () + (opt.legacy_list_mode? 10:12), "");
+            {
+              indent = kl + (opt.legacy_list_mode? 10:12);
+              es_fprintf (es_stdout, "uid%*s", indent, "");
+            }
 
          print_utf8_buffer (es_stdout, uid->name, uid->len);
          es_putc ('\n', es_stdout);
 
+          if (opt.with_wkd_hash)
+            {
+              char *mbox, *hash, *p;
+              char hashbuf[32];
+
+              mbox = mailbox_from_userid (uid->name);
+              if (mbox && (p = strchr (mbox, '@')))
+                {
+                  *p++ = 0;
+                  gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf,
+                                       mbox, strlen (mbox));
+                  hash = zb32_encode (hashbuf, 8*20);
+                  if (hash)
+                    {
+                      es_fprintf (es_stdout, "   %*s%s@%s\n",
+                                  indent, "", hash, p);
+                      xfree (hash);
+                    }
+                }
+              xfree (mbox);
+            }
+
          if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL)
-           show_photos (uid->attribs, uid->numattribs, pk, uid);
+           show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid);
        }
       else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
@@ -1179,44 +1013,15 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
             }
           if (secret)
             {
-              if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
-                s2k_char = serialno? '>':' ';
+              if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
+                secret = serialno? 3 : 1;
               else
-                s2k_char = '#';  /* Key not found.  */
+                secret = '2';  /* Key not found.  */
             }
-          else
-            s2k_char = ' ';
 
-         es_fprintf (es_stdout, "%s%c  %s/%s %s",
-                  secret? "ssb":"sub",
-                  s2k_char,
-                  pubkey_string (pk2, pkstrbuf, sizeof pkstrbuf),
-                 keystr_from_pk (pk2), datestr_from_pk (pk2));
-
-          if ((opt.list_options & LIST_SHOW_USAGE))
-            {
-              es_fprintf (es_stdout, " [%s]", usagestr_from_pk (pk2, 0));
-            }
-         if (pk2->flags.revoked)
-           {
-             es_fprintf (es_stdout, " [");
-             es_fprintf (es_stdout, _("revoked: %s"), revokestr_from_pk (pk2));
-             es_fprintf (es_stdout, "]");
-           }
-         else if (pk2->has_expired)
-           {
-             es_fprintf (es_stdout, " [");
-             es_fprintf (es_stdout, _("expired: %s"), expirestr_from_pk (pk2));
-             es_fprintf (es_stdout, "]");
-           }
-         else if (pk2->expiredate)
-           {
-             es_fprintf (es_stdout, " [");
-             es_fprintf (es_stdout, _("expires: %s"), expirestr_from_pk (pk2));
-             es_fprintf (es_stdout, "]");
-           }
-         es_putc ('\n', es_stdout);
-         if (fpr > 1)
+          /* Print the "sub" line.  */
+          print_key_line (es_stdout, pk2, secret);
+         if (fpr > 1 || opt.with_subkey_fingerprint)
             {
               print_fingerprint (NULL, pk2, 0);
               if (serialno)
@@ -1369,7 +1174,8 @@ print_revokers (estream_t fp, PKT_public_key * pk)
    record (i.e. requested via --list-secret-key).  If HAS_SECRET a
    secret key is available even if SECRET is not set.  */
 static void
-list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
+list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
+                     int secret, int has_secret)
 {
   int rc;
   KBNODE kbctx;
@@ -1377,10 +1183,12 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
   PKT_public_key *pk;
   u32 keyid[2];
   int trustletter = 0;
+  int trustletter_print;
+  int ownertrust_print;
   int ulti_hack = 0;
   int i;
-  char *p;
-  char *hexgrip = NULL;
+  char *hexgrip_buffer = NULL;
+  const char *hexgrip = NULL;
   char *serialno = NULL;
   int stubkey;
 
@@ -1396,40 +1204,52 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
   pk = node->pkt->pkt.public_key;
   if (secret || has_secret || opt.with_keygrip || opt.with_key_data)
     {
-      rc = hexkeygrip_from_pk (pk, &hexgrip);
+      rc = hexkeygrip_from_pk (pk, &hexgrip_buffer);
       if (rc)
         log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
+      /* In the error case we print an empty string so that we have a
+       * "grp" record for each and subkey - even if it is empty.  This
+       * may help to prevent sync problems.  */
+      hexgrip = hexgrip_buffer? hexgrip_buffer : "";
     }
   stubkey = 0;
-  if ((secret||has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno))
+  if ((secret || has_secret)
+      && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
     stubkey = 1;  /* Key not found.  */
 
   keyid_from_pk (pk, keyid);
-  es_fputs (secret? "sec:":"pub:", es_stdout);
   if (!pk->flags.valid)
-    es_putc ('i', es_stdout);
+    trustletter_print = 'i';
   else if (pk->flags.revoked)
-    es_putc ('r', es_stdout);
+    trustletter_print = 'r';
   else if (pk->has_expired)
-    es_putc ('e', es_stdout);
+    trustletter_print = 'e';
   else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
-    ;
+    trustletter_print = 0;
   else
     {
-      trustletter = get_validity_info (pk, NULL);
+      trustletter = get_validity_info (ctrl, keyblock, pk, NULL);
       if (trustletter == 'u')
         ulti_hack = 1;
-      es_putc (trustletter, es_stdout);
+      trustletter_print = trustletter;
     }
 
+  if (!opt.fast_list_mode && !opt.no_expensive_trust_checks)
+    ownertrust_print = get_ownertrust_info (pk, 0);
+  else
+    ownertrust_print = 0;
+
+  es_fputs (secret? "sec:":"pub:", es_stdout);
+  if (trustletter_print)
+    es_putc (trustletter_print, es_stdout);
   es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::",
           nbits_from_pk (pk),
           pk->pubkey_algo,
           (ulong) keyid[0], (ulong) keyid[1],
           colon_datestr_from_pk (pk), colon_strtime (pk->expiredate));
 
-  if (!opt.fast_list_mode && !opt.no_expensive_trust_checks)
-    es_putc (get_ownertrust_info (pk), es_stdout);
+  if (ownertrust_print)
+    es_putc (ownertrust_print, es_stdout);
   es_putc (':', es_stdout);
 
   es_putc (':', es_stdout);
@@ -1464,45 +1284,37 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
   es_putc ('\n', es_stdout);
 
   print_revokers (es_stdout, pk);
-  if (fpr)
-    print_fingerprint (NULL, pk, 0);
-  if (opt.with_key_data || opt.with_keygrip)
-    {
-      if (hexgrip)
-        es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
-      if (opt.with_key_data)
-        print_key_data (pk);
-    }
+  print_fingerprint (NULL, pk, 0);
+  if (hexgrip)
+    es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
+  if (opt.with_key_data)
+    print_key_data (pk);
 
   for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
     {
       if (node->pkt->pkttype == PKT_USER_ID)
        {
-         char *str;
          PKT_user_id *uid = node->pkt->pkt.user_id;
+          int uid_validity;
+
+         if (attrib_fp && uid->attrib_data != NULL)
+           dump_attribs (uid, pk);
 
-         if (attrib_fp && node->pkt->pkt.user_id->attrib_data != NULL)
-           dump_attribs (node->pkt->pkt.user_id, pk);
-         /*
-          * Fixme: We need a valid flag here too
-          */
-         str = uid->attrib_data ? "uat" : "uid";
-         if (uid->is_revoked)
-           es_fprintf (es_stdout, "%s:r::::", str);
-         else if (uid->is_expired)
-           es_fprintf (es_stdout, "%s:e::::", str);
+         if (uid->flags.revoked)
+           uid_validity = 'r';
+         else if (uid->flags.expired)
+           uid_validity = 'e';
          else if (opt.no_expensive_trust_checks)
-           es_fprintf (es_stdout, "%s:::::", str);
-         else
-           {
-             int uid_validity;
+           uid_validity = 0;
+         else if (ulti_hack)
+            uid_validity = 'u';
+          else
+            uid_validity = get_validity_info (ctrl, keyblock, pk, uid);
 
-             if (!ulti_hack)
-               uid_validity = get_validity_info (pk, uid);
-             else
-               uid_validity = 'u';
-             es_fprintf (es_stdout, "%s:%c::::", str, uid_validity);
-           }
+          es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout);
+          if (uid_validity)
+            es_putc (uid_validity, es_stdout);
+          es_fputs ("::::", es_stdout);
 
          es_fprintf (es_stdout, "%s:", colon_strtime (uid->created));
          es_fprintf (es_stdout, "%s:", colon_strtime (uid->expiredate));
@@ -1518,37 +1330,38 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
            es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len);
          else
            es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL);
-         es_fprintf (es_stdout, "::::::::");
-         if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
-           {
-#ifdef USE_TOFU
-             enum tofu_policy policy;
-             if (! tofu_get_policy (pk, uid, &policy)
-                 && policy != TOFU_POLICY_NONE)
-               es_fprintf (es_stdout, "%s", tofu_policy_str (policy));
-#endif /*USE_TOFU*/
-           }
          es_putc (':', es_stdout);
          es_putc ('\n', es_stdout);
+#ifdef USE_TOFU
+         if (!uid->attrib_data && opt.with_tofu_info
+              && (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP))
+           {
+              /* Print a "tfs" record.  */
+              tofu_write_tfs_record (ctrl, es_stdout, pk, uid->name);
+           }
+#endif /*USE_TOFU*/
        }
       else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
          u32 keyid2[2];
          PKT_public_key *pk2;
+          int need_hexgrip = !!hexgrip;
 
           pk2 = node->pkt->pkt.public_key;
-          xfree (hexgrip); hexgrip = NULL;
+          xfree (hexgrip_buffer); hexgrip_buffer = NULL; hexgrip = NULL;
           xfree (serialno); serialno = NULL;
-          if (secret || has_secret || opt.with_keygrip || opt.with_key_data)
+          if (need_hexgrip
+              || secret || has_secret || opt.with_keygrip || opt.with_key_data)
             {
-              rc = hexkeygrip_from_pk (pk2, &hexgrip);
+              rc = hexkeygrip_from_pk (pk2, &hexgrip_buffer);
               if (rc)
                 log_error ("error computing a keygrip: %s\n",
                            gpg_strerror (rc));
+              hexgrip = hexgrip_buffer? hexgrip_buffer : "";
             }
           stubkey = 0;
           if ((secret||has_secret)
-              && agent_get_keyinfo (NULL, hexgrip, &serialno))
+              && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
             stubkey = 1;  /* Key not found.  */
 
          keyid_from_pk (pk2, keyid2);
@@ -1588,11 +1401,11 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
             }
           es_putc (':', es_stdout);    /* End of field 15. */
           es_putc (':', es_stdout);    /* End of field 16. */
-          if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
-              || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
-              || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
+          if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA
+              || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA
+              || pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
             {
-              char *curve = openpgp_oid_to_str (pk->pkey[0]);
+              char *curve = openpgp_oid_to_str (pk2->pkey[0]);
               const char *name = openpgp_oid_to_curve (curve, 0);
               if (!name)
                 name = curve;
@@ -1601,15 +1414,11 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
             }
           es_putc (':', es_stdout);    /* End of field 17. */
          es_putc ('\n', es_stdout);
-         if (fpr > 1)
-           print_fingerprint (NULL, pk2, 0);
-         if (opt.with_key_data || opt.with_keygrip)
-            {
-              if (hexgrip)
-                es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
-              if (opt.with_key_data)
-                print_key_data (pk2);
-            }
+          print_fingerprint (NULL, pk2, 0);
+          if (hexgrip)
+            es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
+          if (opt.with_key_data)
+            print_key_data (pk2);
        }
       else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE)
        {
@@ -1618,6 +1427,8 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
          char *sigstr;
          size_t fplen;
          byte fparray[MAX_FINGERPRINT_LEN];
+          char *siguid;
+          size_t siguidlen;
 
          if (sig->sig_class == 0x20 || sig->sig_class == 0x28
              || sig->sig_class == 0x30)
@@ -1639,7 +1450,7 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
            {
              PKT_public_key *signer_pk = NULL;
 
-             fflush (stdout);
+             es_fflush (es_stdout);
              if (opt.no_sig_cache)
                signer_pk = xmalloc_clear (sizeof (PKT_public_key));
 
@@ -1677,6 +1488,16 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
              rc = 0;
              sigrc = ' ';
            }
+
+         if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode)
+            siguid = get_user_id (sig->keyid, &siguidlen);
+          else
+            {
+              siguid = NULL;
+              siguidlen = 0;
+            }
+
+
          es_fputs (sigstr, es_stdout);
          es_putc (':', es_stdout);
          if (sigrc != ' ')
@@ -1697,17 +1518,11 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
 
          if (sigrc == '%')
            es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc));
-         else if (sigrc == '?')
-           ;
-         else if (!opt.fast_list_mode)
-           {
-             size_t n;
-             p = get_user_id (sig->keyid, &n);
-             es_write_sanitized (es_stdout, p, n, ":", NULL);
-             xfree (p);
-           }
+         else if (siguid)
+            es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL);
+
          es_fprintf (es_stdout, ":%02x%c::", sig->sig_class,
-                 sig->flags.exportable ? 'x' : 'l');
+                      sig->flags.exportable ? 'x' : 'l');
 
          if (opt.no_sig_cache && opt.check_sigs && fprokay)
            {
@@ -1721,10 +1536,11 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
            print_subpackets_colon (sig);
 
          /* fixme: check or list other sigs here */
+          xfree (siguid);
        }
     }
 
-  xfree (hexgrip);
+  xfree (hexgrip_buffer);
   xfree (serialno);
 }
 
@@ -1743,7 +1559,7 @@ do_reorder_keyblock (KBNODE keyblock, int attr)
       if (node->pkt->pkttype == PKT_USER_ID &&
          ((attr && node->pkt->pkt.user_id->attrib_data) ||
           (!attr && !node->pkt->pkt.user_id->attrib_data)) &&
-         node->pkt->pkt.user_id->is_primary)
+         node->pkt->pkt.user_id->flags.primary)
        {
          primary = primary2 = node;
          for (node = node->next; node; primary2 = node, node = node->next)
@@ -1766,9 +1582,9 @@ do_reorder_keyblock (KBNODE keyblock, int attr)
       if (node->pkt->pkttype == PKT_USER_ID)
        break;
     }
-  assert (node);
-  assert (last);        /* The user ID is never the first packet.  */
-  assert (primary0);    /* Ditto (this is the node before primary).  */
+  log_assert (node);
+  log_assert (last);    /* The user ID is never the first packet.  */
+  log_assert (primary0); /* Ditto (this is the node before primary).  */
   if (node == primary)
     return; /* Already the first one.  */
 
@@ -1790,25 +1606,28 @@ list_keyblock (ctrl_t ctrl,
                struct keylist_context *listctx)
 {
   reorder_keyblock (keyblock);
-  if (opt.print_pka_records || opt.print_dane_records)
-    list_keyblock_pka (ctrl, keyblock);
-  else if (opt.with_colons)
-    list_keyblock_colon (keyblock, secret, has_secret, fpr);
+
+  if (opt.with_colons)
+    list_keyblock_colon (ctrl, keyblock, secret, has_secret);
   else
-    list_keyblock_print (keyblock, secret, fpr, listctx);
+    list_keyblock_print (ctrl, keyblock, secret, fpr, listctx);
+
   if (secret)
     es_fflush (es_stdout);
 }
 
 
-/* Public function used by keygen to list a keyblock.  */
+/* Public function used by keygen to list a keyblock.  If NO_VALIDITY
+ * is set the validity of a key is never shown.  */
 void
 list_keyblock_direct (ctrl_t ctrl,
-                      kbnode_t keyblock, int secret, int has_secret, int fpr)
+                      kbnode_t keyblock, int secret, int has_secret, int fpr,
+                      int no_validity)
 {
   struct keylist_context listctx;
 
   memset (&listctx, 0, sizeof (listctx));
+  listctx.no_validity = !!no_validity;
   list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx);
   keylist_context_release (&listctx);
 }
@@ -1835,6 +1654,7 @@ print_icao_hexdigit (estream_t fp, int c)
  *      3: direct use of tty but only primary key.
  *      4: direct use of tty but only subkey.
  *     10: Same as 0 but with_colons etc is ignored.
+ *     20: Same as 0 but using a compact format.
  *
  * Modes 1 and 2 will try and print both subkey and primary key
  * fingerprints.  A MODE with bit 7 set is used internally.  If
@@ -1852,6 +1672,7 @@ print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
   int primary = 0;
   int with_colons = opt.with_colons;
   int with_icao   = opt.with_icao_spelling;
+  int compact = 0;
 
   if (mode == 10)
     {
@@ -1859,6 +1680,16 @@ print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
       with_colons = 0;
       with_icao = 0;
     }
+  else if (mode == 20)
+    {
+      mode = 0;
+      with_colons = 0;
+      compact = 1;
+    }
+
+  if (!opt.fingerprint && !opt.with_fingerprint
+      && opt.with_subkey_fingerprint)
+    compact = 1;
 
   if (pk->main_keyid[0] == pk->keyid[0]
       && pk->main_keyid[1] == pk->keyid[1])
@@ -1912,7 +1743,13 @@ print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
   else
     {
       fp = override_fp? override_fp : es_stdout;
-      text = _("      Key fingerprint =");
+      if (opt.keyid_format == KF_NONE)
+        {
+          text = "     ";  /* To indent ICAO spelling.  */
+          compact = 1;
+        }
+      else
+        text = _("      Key fingerprint =");
     }
 
   hexfingerprint (pk, hexfpr, sizeof hexfpr);
@@ -1920,11 +1757,18 @@ print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
     {
       es_fprintf (fp, "fpr:::::::::%s:", hexfpr);
     }
+  else if (compact && !opt.fingerprint && !opt.with_fingerprint)
+    {
+      tty_fprintf (fp, "%*s%s", 6, "", hexfpr);
+    }
   else
     {
       char fmtfpr[MAX_FORMATTED_FINGERPRINT_LEN + 1];
       format_hexfingerprint (hexfpr, fmtfpr, sizeof fmtfpr);
-      tty_fprintf (fp, "%s %s", text, fmtfpr);
+      if (compact)
+        tty_fprintf (fp, "%*s%s", 6, "", fmtfpr);
+      else
+        tty_fprintf (fp, "%s %s", text, fmtfpr);
     }
   tty_fprintf (fp, "\n");
   if (!with_colons && with_icao)
@@ -1971,6 +1815,78 @@ print_card_serialno (const char *serialno)
 }
 
 
+/* Print a public or secret (sub)key line.  Example:
+ *
+ * pub   dsa2048 2007-12-31 [SC] [expires: 2018-12-31]
+ *       80615870F5BAD690333686D0F2AD85AC1E42B367
+ *
+ * Some global options may result in a different output format.  If
+ * SECRET is set, "sec" or "ssb" is used instead of "pub" or "sub" and
+ * depending on the value a flag character is shown:
+ *
+ *    1 := ' ' Regular secret key
+ *    2 := '#' Stub secret key
+ *    3 := '>' Secret key is on a token.
+ */
+void
+print_key_line (estream_t fp, PKT_public_key *pk, int secret)
+{
+  char pkstrbuf[PUBKEY_STRING_SIZE];
+
+  tty_fprintf (fp, "%s%c  %s",
+               pk->flags.primary? (secret? "sec":"pub")
+               /**/             : (secret? "ssb":"sub"),
+               secret == 2? '#' : secret == 3? '>' : ' ',
+               pubkey_string (pk, pkstrbuf, sizeof pkstrbuf));
+  if (opt.keyid_format != KF_NONE)
+    tty_fprintf (fp, "/%s", keystr_from_pk (pk));
+  tty_fprintf (fp, " %s", datestr_from_pk (pk));
+
+  if ((opt.list_options & LIST_SHOW_USAGE))
+    {
+      tty_fprintf (fp, " [%s]", usagestr_from_pk (pk, 0));
+    }
+  if (pk->flags.revoked)
+    {
+      tty_fprintf (fp, " [");
+      tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk));
+      tty_fprintf (fp, "]");
+    }
+  else if (pk->has_expired)
+    {
+      tty_fprintf (fp, " [");
+      tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk));
+      tty_fprintf (fp, "]");
+    }
+  else if (pk->expiredate)
+    {
+      tty_fprintf (fp, " [");
+      tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk));
+      tty_fprintf (fp, "]");
+    }
+
+#if 0
+  /* I need to think about this some more.  It's easy enough to
+     include, but it looks sort of confusing in the listing... */
+  if (opt.list_options & LIST_SHOW_VALIDITY)
+    {
+      int validity = get_validity (ctrl, pk, NULL, NULL, 0);
+      tty_fprintf (fp, " [%s]", trust_value_to_string (validity));
+    }
+#endif
+
+  if (pk->pubkey_algo >= 100)
+    tty_fprintf (fp, " [experimental algorithm %d]", pk->pubkey_algo);
+
+  tty_fprintf (fp, "\n");
+
+  /* if the user hasn't explicitly asked for human-readable
+     fingerprints, show compact fpr of primary key: */
+  if (pk->flags.primary &&
+      !opt.fingerprint && !opt.with_fingerprint)
+    print_fingerprint (fp, pk, 20);
+}
+
 
 void
 set_attrib_fd (int fd)
@@ -1987,6 +1903,9 @@ set_attrib_fd (int fd)
   if (fd == -1)
     return;
 
+  if (! gnupg_fd_valid (fd))
+    log_fatal ("attribute-fd is invalid: %s\n", strerror (errno));
+
 #ifdef HAVE_DOSISH_SYSTEM
   setmode (fd, O_BINARY);
 #endif