gpg: Prepare for listing last_update and key origin data.
[gnupg.git] / g10 / keyedit.c
index 432ba86..9a7fe13 100644 (file)
@@ -1,6 +1,7 @@
 /* keyedit.c - Edit properties of a key
  * Copyright (C) 1998-2010 Free Software Foundation, Inc.
 /* keyedit.c - Edit properties of a key
  * Copyright (C) 1998-2010 Free Software Foundation, Inc.
- * Copyright (C) 1998-2015 Werner Koch
+ * Copyright (C) 1998-2017 Werner Koch
+ * Copyright (C) 2015, 2016 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * 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 <config.h>
@@ -23,7 +24,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #include <ctype.h>
 #ifdef HAVE_LIBREADLINE
 # define GNUPG_LIBREADLINE_H_INCLUDED
 #include <ctype.h>
 #ifdef HAVE_LIBREADLINE
 # define GNUPG_LIBREADLINE_H_INCLUDED
 #include "gpg.h"
 #include "options.h"
 #include "packet.h"
 #include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "status.h"
-#include "iobuf.h"
+#include "../common/status.h"
+#include "../common/iobuf.h"
 #include "keydb.h"
 #include "photoid.h"
 #include "keydb.h"
 #include "photoid.h"
-#include "util.h"
+#include "../common/util.h"
 #include "main.h"
 #include "trustdb.h"
 #include "filter.h"
 #include "main.h"
 #include "trustdb.h"
 #include "filter.h"
-#include "ttyio.h"
-#include "status.h"
-#include "i18n.h"
+#include "../common/ttyio.h"
+#include "../common/status.h"
+#include "../common/i18n.h"
 #include "keyserver-internal.h"
 #include "call-agent.h"
 #include "keyserver-internal.h"
 #include "call-agent.h"
-#include "host2net.h"
+#include "../common/host2net.h"
 #include "tofu.h"
 
 static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig,
                        int verbose);
 #include "tofu.h"
 
 static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig,
                        int verbose);
-static void show_names (estream_t fp, KBNODE keyblock, PKT_public_key * pk,
+static void show_names (ctrl_t ctrl, estream_t fp,
+                        kbnode_t keyblock, PKT_public_key * pk,
                        unsigned int flag, int with_prefs);
 static void show_key_with_all_names (ctrl_t ctrl, estream_t fp,
                                      KBNODE keyblock, int only_marked,
                        unsigned int flag, int with_prefs);
 static void show_key_with_all_names (ctrl_t ctrl, estream_t fp,
                                      KBNODE keyblock, int only_marked,
@@ -61,14 +62,16 @@ static void show_key_with_all_names (ctrl_t ctrl, estream_t fp,
 static void show_key_and_fingerprint (kbnode_t keyblock, int with_subkeys);
 static void show_key_and_grip (kbnode_t keyblock);
 static void subkey_expire_warning (kbnode_t keyblock);
 static void show_key_and_fingerprint (kbnode_t keyblock, int with_subkeys);
 static void show_key_and_grip (kbnode_t keyblock);
 static void subkey_expire_warning (kbnode_t keyblock);
-static int menu_adduid (KBNODE keyblock, int photo, const char *photo_name,
-                        const char *uidstr);
+static int menu_adduid (ctrl_t ctrl, kbnode_t keyblock,
+                        int photo, const char *photo_name, const char *uidstr);
 static void menu_deluid (KBNODE pub_keyblock);
 static int menu_delsig (KBNODE pub_keyblock);
 static int menu_clean (KBNODE keyblock, int self_only);
 static void menu_delkey (KBNODE pub_keyblock);
 static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive);
 static void menu_deluid (KBNODE pub_keyblock);
 static int menu_delsig (KBNODE pub_keyblock);
 static int menu_clean (KBNODE keyblock, int self_only);
 static void menu_delkey (KBNODE pub_keyblock);
 static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive);
-static int menu_expire (KBNODE pub_keyblock);
+static gpg_error_t menu_expire (kbnode_t pub_keyblock,
+                                int force_mainkey, u32 newexpiration);
+static int menu_changeusage (kbnode_t keyblock);
 static int menu_backsign (KBNODE pub_keyblock);
 static int menu_set_primary_uid (KBNODE pub_keyblock);
 static int menu_set_preferences (KBNODE pub_keyblock);
 static int menu_backsign (KBNODE pub_keyblock);
 static int menu_set_primary_uid (KBNODE pub_keyblock);
 static int menu_set_preferences (KBNODE pub_keyblock);
@@ -76,7 +79,7 @@ static int menu_set_keyserver_url (const char *url, KBNODE pub_keyblock);
 static int menu_set_notation (const char *string, KBNODE pub_keyblock);
 static int menu_select_uid (KBNODE keyblock, int idx);
 static int menu_select_uid_namehash (KBNODE keyblock, const char *namehash);
 static int menu_set_notation (const char *string, KBNODE pub_keyblock);
 static int menu_select_uid (KBNODE keyblock, int idx);
 static int menu_select_uid_namehash (KBNODE keyblock, const char *namehash);
-static int menu_select_key (KBNODE keyblock, int idx);
+static int menu_select_key (KBNODE keyblock, int idx, char *p);
 static int count_uids (KBNODE keyblock);
 static int count_uids_with_flag (KBNODE keyblock, unsigned flag);
 static int count_keys_with_flag (KBNODE keyblock, unsigned flag);
 static int count_uids (KBNODE keyblock);
 static int count_uids_with_flag (KBNODE keyblock, unsigned flag);
 static int count_keys_with_flag (KBNODE keyblock, unsigned flag);
@@ -84,13 +87,16 @@ static int count_selected_uids (KBNODE keyblock);
 static int real_uids_left (KBNODE keyblock);
 static int count_selected_keys (KBNODE keyblock);
 static int menu_revsig (KBNODE keyblock);
 static int real_uids_left (KBNODE keyblock);
 static int count_selected_keys (KBNODE keyblock);
 static int menu_revsig (KBNODE keyblock);
-static int menu_revuid (KBNODE keyblock);
+static int menu_revuid (ctrl_t ctrl, kbnode_t keyblock);
+static int core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node,
+                        const struct revocation_reason_info *reason,
+                        int *modified);
 static int menu_revkey (KBNODE pub_keyblock);
 static int menu_revsubkey (KBNODE pub_keyblock);
 #ifndef NO_TRUST_MODELS
 static int enable_disable_key (KBNODE keyblock, int disable);
 #endif /*!NO_TRUST_MODELS*/
 static int menu_revkey (KBNODE pub_keyblock);
 static int menu_revsubkey (KBNODE pub_keyblock);
 #ifndef NO_TRUST_MODELS
 static int enable_disable_key (KBNODE keyblock, int disable);
 #endif /*!NO_TRUST_MODELS*/
-static void menu_showphoto (KBNODE keyblock);
+static void menu_showphoto (ctrl_t ctrl, kbnode_t keyblock);
 
 static int update_trust = 0;
 
 
 static int update_trust = 0;
 
@@ -187,24 +193,23 @@ print_and_check_one_sig_colon (KBNODE keyblock, KBNODE node,
 
 
 /*
 
 
 /*
- * Print information about a signature, check it and return true if
- * the signature is okay.  NODE must be a signature packet.  With
- * EXTENDED set all possible signature list options will always be
- * printed.
+ * Print information about a signature (rc is its status), check it
+ * and return true if the signature is okay.  NODE must be a signature
+ * packet.  With EXTENDED set all possible signature list options will
+ * always be printed.
  */
 static int
  */
 static int
-print_and_check_one_sig (KBNODE keyblock, KBNODE node,
-                        int *inv_sigs, int *no_key, int *oth_err,
-                        int *is_selfsig, int print_without_key, int extended)
+print_one_sig (int rc, KBNODE keyblock, KBNODE node,
+               int *inv_sigs, int *no_key, int *oth_err,
+               int is_selfsig, int print_without_key, int extended)
 {
   PKT_signature *sig = node->pkt->pkt.signature;
 {
   PKT_signature *sig = node->pkt->pkt.signature;
-  int rc, sigrc;
+  int sigrc;
   int is_rev = sig->sig_class == 0x30;
 
   /* TODO: Make sure a cached sig record here still has the pk that
      issued it.  See also keylist.c:list_keyblock_print */
 
   int is_rev = sig->sig_class == 0x30;
 
   /* TODO: Make sure a cached sig record here still has the pk that
      issued it.  See also keylist.c:list_keyblock_print */
 
-  rc = check_key_signature (keyblock, node, is_selfsig);
   switch (gpg_err_code (rc))
     {
     case 0:
   switch (gpg_err_code (rc))
     {
     case 0:
@@ -255,7 +260,7 @@ print_and_check_one_sig (KBNODE keyblock, KBNODE node,
        tty_printf ("[%s] ", gpg_strerror (rc));
       else if (sigrc == '?')
        ;
        tty_printf ("[%s] ", gpg_strerror (rc));
       else if (sigrc == '?')
        ;
-      else if (*is_selfsig)
+      else if (is_selfsig)
        {
          tty_printf (is_rev ? _("[revocation]") : _("[self-signature]"));
           if (extended && sig->flags.chosen_selfsig)
        {
          tty_printf (is_rev ? _("[revocation]") : _("[self-signature]"));
           if (extended && sig->flags.chosen_selfsig)
@@ -276,11 +281,11 @@ print_and_check_one_sig (KBNODE keyblock, KBNODE node,
 
       if (sig->flags.policy_url
           && ((opt.list_options & LIST_SHOW_POLICY_URLS) || extended))
 
       if (sig->flags.policy_url
           && ((opt.list_options & LIST_SHOW_POLICY_URLS) || extended))
-       show_policy_url (sig, 3, 0);
+       show_policy_url (sig, 3, -1);
 
       if (sig->flags.notation
           && ((opt.list_options & LIST_SHOW_NOTATIONS) || extended))
 
       if (sig->flags.notation
           && ((opt.list_options & LIST_SHOW_NOTATIONS) || extended))
-       show_notation (sig, 3, 0,
+       show_notation (sig, 3, -1,
                       ((opt.
                         list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) +
                       ((opt.
                       ((opt.
                         list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) +
                       ((opt.
@@ -288,7 +293,7 @@ print_and_check_one_sig (KBNODE keyblock, KBNODE node,
 
       if (sig->flags.pref_ks
           && ((opt.list_options & LIST_SHOW_KEYSERVER_URLS) || extended))
 
       if (sig->flags.pref_ks
           && ((opt.list_options & LIST_SHOW_KEYSERVER_URLS) || extended))
-       show_keyserver_url (sig, 3, 0);
+       show_keyserver_url (sig, 3, -1);
 
       if (extended)
         {
 
       if (extended)
         {
@@ -310,90 +315,625 @@ print_and_check_one_sig (KBNODE keyblock, KBNODE node,
 }
 
 
 }
 
 
+static int
+print_and_check_one_sig (KBNODE keyblock, KBNODE node,
+                        int *inv_sigs, int *no_key, int *oth_err,
+                        int *is_selfsig, int print_without_key, int extended)
+{
+  int rc;
+
+  rc = check_key_signature (keyblock, node, is_selfsig);
+  return print_one_sig (rc,
+                        keyblock, node, inv_sigs, no_key, oth_err,
+                        *is_selfsig, print_without_key, extended);
+}
 
 
-/*
- * Check the keysigs and set the flags to indicate errors.
- * Returns true if error found.
- */
+
+
+/* Order two signatures.  The actual ordering isn't important.  Our
  goal is to ensure that identical signatures occur together.  */
 static int
 static int
-check_all_keysigs (KBNODE keyblock, int only_selected, int only_selfsigs)
+sig_comparison (const void *av, const void *bv)
 {
 {
-  KBNODE kbctx;
-  KBNODE node;
-  int inv_sigs = 0;
-  int no_key = 0;
-  int oth_err = 0;
-  int has_selfsig = 0;
-  int mis_selfsig = 0;
-  int selected = !only_selected;
-  int anyuid = 0;
-  u32 keyid[2];
+  const KBNODE an = *(const KBNODE *) av;
+  const KBNODE bn = *(const KBNODE *) bv;
+  const PKT_signature *a;
+  const PKT_signature *b;
+  int ndataa;
+  int ndatab;
+  int i;
+
+  log_assert (an->pkt->pkttype == PKT_SIGNATURE);
+  log_assert (bn->pkt->pkttype == PKT_SIGNATURE);
+
+  a = an->pkt->pkt.signature;
+  b = bn->pkt->pkt.signature;
+
+  if (a->digest_algo < b->digest_algo)
+    return -1;
+  if (a->digest_algo > b->digest_algo)
+    return 1;
 
 
-  for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
+  ndataa = pubkey_get_nsig (a->pubkey_algo);
+  ndatab = pubkey_get_nsig (b->pubkey_algo);
+  if (ndataa != ndatab)
+    return (ndataa < ndatab)? -1 : 1;
+
+  for (i = 0; i < ndataa; i ++)
     {
     {
-      if (node->pkt->pkttype == PKT_PUBLIC_KEY)
+      int c = gcry_mpi_cmp (a->data[i], b->data[i]);
+      if (c != 0)
+        return c;
+    }
+
+  /* Okay, they are equal.  */
+  return 0;
+}
+
+/* Perform a few sanity checks on a keyblock is okay and possibly
+   repair some damage.  Concretely:
+
+     - Detect duplicate signatures and remove them.
+
+     - Detect out of order signatures and relocate them (e.g., a sig
+       over user id X located under subkey Y).
+
+   Note: this function does not remove signatures that don't belong or
+   components that are not signed!  (Although it would be trivial to
+   do so.)
+
+   If ONLY_SELFSIGS is true, then this function only reorders self
+   signatures (it still checks all signatures for duplicates,
+   however).
+
+   Returns 1 if the keyblock was modified, 0 otherwise.  */
+static int
+check_all_keysigs (KBNODE kb, int only_selected, int only_selfsigs)
+{
+  gpg_error_t err;
+  PKT_public_key *pk;
+  KBNODE n, n_next, *n_prevp, n2;
+  char *pending_desc = NULL;
+  PKT_public_key *issuer;
+  KBNODE last_printed_component;
+  KBNODE current_component = NULL;
+  int dups = 0;
+  int missing_issuer = 0;
+  int reordered = 0;
+  int bad_signature = 0;
+  int missing_selfsig = 0;
+  int modified = 0;
+
+  log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY);
+  pk = kb->pkt->pkt.public_key;
+
+  /* First we look for duplicates.  */
+  {
+    int nsigs;
+    kbnode_t *sigs;
+    int i;
+    int last_i;
+
+    /* Count the sigs.  */
+    for (nsigs = 0, n = kb; n; n = n->next)
+      {
+        if (is_deleted_kbnode (n))
+          continue;
+        else if (n->pkt->pkttype == PKT_SIGNATURE)
+          nsigs ++;
+      }
+
+    if (!nsigs)
+      return 0; /* No signatures at all.  */
+
+    /* Add them all to the SIGS array.  */
+    sigs = xtrycalloc (nsigs, sizeof *sigs);
+    if (!sigs)
+      {
+        log_error (_("error allocating memory: %s\n"),
+                   gpg_strerror (gpg_error_from_syserror ()));
+        return 0;
+      }
+
+    i = 0;
+    for (n = kb; n; n = n->next)
+      {
+        if (is_deleted_kbnode (n))
+          continue;
+
+        if (n->pkt->pkttype != PKT_SIGNATURE)
+          continue;
+
+        sigs[i] = n;
+        i ++;
+      }
+    log_assert (i == nsigs);
+
+    qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison);
+
+    last_i = 0;
+    for (i = 1; i < nsigs; i ++)
+      {
+        log_assert (sigs[last_i]);
+        log_assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE);
+        log_assert (sigs[i]);
+        log_assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE);
+
+        if (sig_comparison (&sigs[last_i], &sigs[i]) == 0)
+          /* They are the same.  Kill the latter.  */
+          {
+            if (DBG_PACKET)
+              {
+                PKT_signature *sig = sigs[i]->pkt->pkt.signature;
+
+                log_debug ("Signature appears multiple times, "
+                           "deleting duplicate:\n");
+                log_debug ("  sig: class 0x%x, issuer: %s,"
+                           " timestamp: %s (%lld), digest: %02x %02x\n",
+                           sig->sig_class, keystr (sig->keyid),
+                           isotimestamp (sig->timestamp),
+                           (long long) sig->timestamp,
+                           sig->digest_start[0], sig->digest_start[1]);
+              }
+
+            /* Remove sigs[i] from the keyblock.  */
+            {
+              KBNODE z, *prevp;
+              int to_kill = last_i;
+              last_i = i;
+
+              for (prevp = &kb, z = kb; z; prevp = &z->next, z = z->next)
+                if (z == sigs[to_kill])
+                  break;
+
+              *prevp = sigs[to_kill]->next;
+
+              sigs[to_kill]->next = NULL;
+              release_kbnode (sigs[to_kill]);
+              sigs[to_kill] = NULL;
+
+              dups ++;
+              modified = 1;
+            }
+          }
+        else
+          last_i = i;
+      }
+
+    xfree (sigs);
+  }
+
+  /* Make sure the sigs occur after the component (public key, subkey,
+     user id) that they sign.  */
+  issuer = NULL;
+  last_printed_component = NULL;
+  for (n_prevp = &kb, n = kb;
+       n;
+       /* If we moved n, then n_prevp is need valid.  */
+       n_prevp = (n->next == n_next ? &n->next : n_prevp), n = n_next)
+    {
+      PACKET *p;
+      int processed_current_component;
+      PKT_signature *sig;
+      int rc;
+      int dump_sig_params = 0;
+
+      n_next = n->next;
+
+      if (is_deleted_kbnode (n))
+        continue;
+
+      p = n->pkt;
+
+      if (issuer && issuer != pk)
         {
         {
-          if (only_selfsigs)
-            keyid_from_pk (node->pkt->pkt.public_key, keyid);
+          free_public_key (issuer);
+          issuer = NULL;
         }
         }
-      else if (node->pkt->pkttype == PKT_USER_ID)
-       {
-         PKT_user_id *uid = node->pkt->pkt.user_id;
 
 
-         if (only_selected)
-           selected = (node->flag & NODFLG_SELUID);
-         if (selected)
-           {
-             tty_printf ("uid  ");
-             tty_print_utf8_string (uid->name, uid->len);
-             tty_printf ("\n");
-             if (anyuid && !has_selfsig)
-               mis_selfsig++;
-             has_selfsig = 0;
-             anyuid = 1;
-           }
-       }
-      else if (selected && node->pkt->pkttype == PKT_SIGNATURE
-              && ((node->pkt->pkt.signature->sig_class & ~3) == 0x10
-                  || node->pkt->pkt.signature->sig_class == 0x30))
-       {
-         int selfsig;
-          PKT_signature *sig = node->pkt->pkt.signature;
-
-          if (only_selfsigs
-              && !(keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]))
-            ;  /* Not a selfsig but we want only selfsigs - skip.  */
-         else if (print_and_check_one_sig (keyblock, node, &inv_sigs,
-                                            &no_key, &oth_err, &selfsig,
-                                            0, only_selfsigs))
-           {
-             if (selfsig)
-               has_selfsig = 1;
-           }
-         /* Hmmm: should we update the trustdb here? */
-       }
+      xfree (pending_desc);
+      pending_desc = NULL;
+
+      switch (p->pkttype)
+        {
+        case PKT_PUBLIC_KEY:
+          log_assert (p->pkt.public_key == pk);
+          if (only_selected && ! (n->flag & NODFLG_SELKEY))
+            {
+              current_component = NULL;
+              break;
+            }
+
+          if (DBG_PACKET)
+            log_debug ("public key %s: timestamp: %s (%lld)\n",
+                       pk_keyid_str (pk),
+                       isotimestamp (pk->timestamp),
+                       (long long) pk->timestamp);
+          current_component = n;
+          break;
+        case PKT_PUBLIC_SUBKEY:
+          if (only_selected && ! (n->flag & NODFLG_SELKEY))
+            {
+              current_component = NULL;
+              break;
+            }
+
+          if (DBG_PACKET)
+            log_debug ("subkey %s: timestamp: %s (%lld)\n",
+                       pk_keyid_str (p->pkt.public_key),
+                       isotimestamp (p->pkt.public_key->timestamp),
+                       (long long) p->pkt.public_key->timestamp);
+          current_component = n;
+          break;
+        case PKT_USER_ID:
+          if (only_selected && ! (n->flag & NODFLG_SELUID))
+            {
+              current_component = NULL;
+              break;
+            }
+
+          if (DBG_PACKET)
+            log_debug ("user id: %s\n",
+                       p->pkt.user_id->attrib_data
+                       ? "[ photo id ]"
+                       : p->pkt.user_id->name);
+          current_component = n;
+          break;
+        case PKT_SIGNATURE:
+          if (! current_component)
+            /* The current component is not selected, don't check the
+               sigs under it.  */
+            break;
+
+          sig = n->pkt->pkt.signature;
+
+          pending_desc = xasprintf ("  sig: class: 0x%x, issuer: %s,"
+                                    " timestamp: %s (%lld), digest: %02x %02x",
+                                    sig->sig_class,
+                                    keystr (sig->keyid),
+                                    isotimestamp (sig->timestamp),
+                                    (long long) sig->timestamp,
+                                    sig->digest_start[0], sig->digest_start[1]);
+
+
+          if (keyid_cmp (pk_keyid (pk), sig->keyid) == 0)
+            issuer = pk;
+          else
+            /* Issuer is a different key.  */
+            {
+              if (only_selfsigs)
+                continue;
+
+              issuer = xmalloc (sizeof (*issuer));
+              err = get_pubkey (issuer, sig->keyid);
+              if (err)
+                {
+                  xfree (issuer);
+                  issuer = NULL;
+                  if (DBG_PACKET)
+                    {
+                      if (pending_desc)
+                        log_debug ("%s", pending_desc);
+                      log_debug ("    Can't check signature allegedly"
+                                 " issued by %s: %s\n",
+                                 keystr (sig->keyid), gpg_strerror (err));
+                    }
+                  missing_issuer ++;
+                  break;
+                }
+            }
+
+          if ((err = openpgp_pk_test_algo (sig->pubkey_algo)))
+            {
+              if (DBG_PACKET && pending_desc)
+                log_debug ("%s", pending_desc);
+              tty_printf (_("can't check signature with unsupported"
+                            " public-key algorithm (%d): %s.\n"),
+                          sig->pubkey_algo, gpg_strerror (err));
+              break;
+            }
+          if ((err = openpgp_md_test_algo (sig->digest_algo)))
+            {
+              if (DBG_PACKET && pending_desc)
+                log_debug ("%s", pending_desc);
+              tty_printf (_("can't check signature with unsupported"
+                            " message-digest algorithm %d: %s.\n"),
+                          sig->digest_algo, gpg_strerror (err));
+              break;
+            }
+
+          /* We iterate over the keyblock.  Most likely, the matching
+             component is the current component so always try that
+             first.  */
+          processed_current_component = 0;
+          for (n2 = current_component;
+               n2;
+               n2 = (processed_current_component ? n2->next : kb),
+                 processed_current_component = 1)
+            if (is_deleted_kbnode (n2))
+              continue;
+            else if (processed_current_component && n2 == current_component)
+              /* Don't process it twice.  */
+              continue;
+            else
+              {
+                err = check_signature_over_key_or_uid (issuer, sig, kb, n2->pkt,
+                                                       NULL, NULL);
+                if (! err)
+                  break;
+              }
+
+          /* n/sig is a signature and n2 is the component (public key,
+             subkey or user id) that it signs, if any.
+             current_component is that component that it appears to
+             apply to (according to the ordering).  */
+
+          if (current_component == n2)
+            {
+              if (DBG_PACKET)
+                {
+                  log_debug ("%s", pending_desc);
+                  log_debug ("    Good signature over last key or uid!\n");
+                }
+
+              rc = 0;
+            }
+          else if (n2)
+            {
+              log_assert (n2->pkt->pkttype == PKT_USER_ID
+                          || n2->pkt->pkttype == PKT_PUBLIC_KEY
+                          || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY);
+
+              if (DBG_PACKET)
+                {
+                  log_debug ("%s", pending_desc);
+                  log_debug ("    Good signature out of order!"
+                             "  (Over %s (%d) '%s')\n",
+                             n2->pkt->pkttype == PKT_USER_ID
+                             ? "user id"
+                             : n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                             ? "subkey"
+                             : "primary key",
+                             n2->pkt->pkttype,
+                             n2->pkt->pkttype == PKT_USER_ID
+                             ? n2->pkt->pkt.user_id->name
+                             : pk_keyid_str (n2->pkt->pkt.public_key));
+                }
+
+              /* Reorder the packets: move the signature n to be just
+                 after n2.  */
+
+              /* Unlink the signature.  */
+              log_assert (n_prevp);
+              *n_prevp = n->next;
+
+              /* Insert the sig immediately after the component.  */
+              n->next = n2->next;
+              n2->next = n;
+
+              reordered ++;
+              modified = 1;
+
+              rc = 0;
+            }
+          else
+            {
+              if (DBG_PACKET)
+                {
+                  log_debug ("%s", pending_desc);
+                  log_debug ("    Bad signature.\n");
+                }
+
+              if (DBG_PACKET)
+                dump_sig_params = 1;
+
+              bad_signature ++;
+
+              rc = GPG_ERR_BAD_SIGNATURE;
+            }
+
+          /* We don't cache the result here, because we haven't
+             completely checked that the signature is legitimate.  For
+             instance, if we have a revocation certificate on Alice's
+             key signed by Bob, the signature may be good, but we
+             haven't checked that Bob is a designated revoker.  */
+          /* cache_sig_result (sig, rc); */
+
+          {
+            int has_selfsig = 0;
+            if (! rc && issuer == pk)
+              {
+                if (n2->pkt->pkttype == PKT_PUBLIC_KEY
+                    && (/* Direct key signature.  */
+                        sig->sig_class == 0x1f
+                        /* Key revocation signature.  */
+                        || sig->sig_class == 0x20))
+                  has_selfsig = 1;
+                if (n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                    && (/* Subkey binding sig.  */
+                        sig->sig_class == 0x18
+                        /* Subkey revocation sig.  */
+                        || sig->sig_class == 0x28))
+                  has_selfsig = 1;
+                if (n2->pkt->pkttype == PKT_USER_ID
+                    && (/* Certification sigs.  */
+                        sig->sig_class == 0x10
+                        || sig->sig_class == 0x11
+                        || sig->sig_class == 0x12
+                        || sig->sig_class == 0x13
+                        /* Certification revocation sig.  */
+                        || sig->sig_class == 0x30))
+                  has_selfsig = 1;
+              }
+
+            if ((n2 && n2 != last_printed_component)
+                || (! n2 && last_printed_component != current_component))
+              {
+                int is_reordered = n2 && n2 != current_component;
+                if (n2)
+                  last_printed_component = n2;
+                else
+                  last_printed_component = current_component;
+
+                if (!modified)
+                  ;
+                else if (last_printed_component->pkt->pkttype == PKT_USER_ID)
+                  {
+                    tty_printf ("uid  ");
+                    tty_print_utf8_string (last_printed_component
+                                           ->pkt->pkt.user_id->name,
+                                           last_printed_component
+                                           ->pkt->pkt.user_id->len);
+                  }
+                else if (last_printed_component->pkt->pkttype
+                         == PKT_PUBLIC_KEY)
+                  tty_printf ("pub  %s",
+                              pk_keyid_str (last_printed_component
+                                            ->pkt->pkt.public_key));
+                else
+                  tty_printf ("sub  %s",
+                              pk_keyid_str (last_printed_component
+                                            ->pkt->pkt.public_key));
+
+                if (modified)
+                  {
+                    if (is_reordered)
+                      tty_printf (_(" (reordered signatures follow)"));
+                    tty_printf ("\n");
+                  }
+              }
+
+            if (modified)
+              print_one_sig (rc, kb, n, NULL, NULL, NULL, has_selfsig,
+                             0, only_selfsigs);
+          }
+
+          if (dump_sig_params)
+            {
+              int i;
+
+              for (i = 0; i < pubkey_get_nsig (sig->pubkey_algo); i ++)
+                {
+                  char buffer[1024];
+                  size_t len;
+                  char *printable;
+                  gcry_mpi_print (GCRYMPI_FMT_USG,
+                                  buffer, sizeof (buffer), &len,
+                                  sig->data[i]);
+                  printable = bin2hex (buffer, len, NULL);
+                  log_info ("        %d: %s\n", i, printable);
+                  xfree (printable);
+                }
+            }
+          break;
+        default:
+          if (DBG_PACKET)
+            log_debug ("unhandled packet: %d\n", p->pkttype);
+          break;
+        }
     }
     }
-  if (!has_selfsig)
-    mis_selfsig++;
-  if (inv_sigs == 1)
-    tty_printf (_("1 bad signature\n"));
-  else if (inv_sigs)
-    tty_printf (_("%d bad signatures\n"), inv_sigs);
-  if (no_key == 1)
-    tty_printf (_("1 signature not checked due to a missing key\n"));
-  else if (no_key)
-    tty_printf (_("%d signatures not checked due to missing keys\n"), no_key);
-  if (oth_err == 1)
-    tty_printf (_("1 signature not checked due to an error\n"));
-  else if (oth_err)
-    tty_printf (_("%d signatures not checked due to errors\n"), oth_err);
-  if (mis_selfsig == 1)
-    tty_printf (_("1 user ID without valid self-signature detected\n"));
-  else if (mis_selfsig)
-    tty_printf (_("%d user IDs without valid self-signatures detected\n"),
-               mis_selfsig);
-
-  return inv_sigs || no_key || oth_err || mis_selfsig;
+
+  xfree (pending_desc);
+  pending_desc = NULL;
+
+  if (issuer != pk)
+    free_public_key (issuer);
+  issuer = NULL;
+
+  /* Identify keys / uids that don't have a self-sig.  */
+  {
+    int has_selfsig = 0;
+    PACKET *p;
+    PKT_signature *sig;
+
+    current_component = NULL;
+    for (n = kb; n; n = n->next)
+      {
+        if (is_deleted_kbnode (n))
+          continue;
+
+        p = n->pkt;
+
+        switch (p->pkttype)
+          {
+          case PKT_PUBLIC_KEY:
+          case PKT_PUBLIC_SUBKEY:
+          case PKT_USER_ID:
+            if (current_component && ! has_selfsig)
+              missing_selfsig ++;
+            current_component = n;
+            has_selfsig = 0;
+            break;
+
+          case PKT_SIGNATURE:
+            if (! current_component || has_selfsig)
+              break;
+
+            sig = n->pkt->pkt.signature;
+
+            if (! (sig->flags.checked && sig->flags.valid))
+              break;
+
+            if (keyid_cmp (pk_keyid (pk), sig->keyid) != 0)
+              /* Different issuer, couldn't be a self-sig.  */
+              break;
+
+            if (current_component->pkt->pkttype == PKT_PUBLIC_KEY
+                && (/* Direct key signature.  */
+                    sig->sig_class == 0x1f
+                    /* Key revocation signature.  */
+                    || sig->sig_class == 0x20))
+              has_selfsig = 1;
+            if (current_component->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                && (/* Subkey binding sig.  */
+                    sig->sig_class == 0x18
+                    /* Subkey revocation sig.  */
+                    || sig->sig_class == 0x28))
+              has_selfsig = 1;
+            if (current_component->pkt->pkttype == PKT_USER_ID
+                && (/* Certification sigs.  */
+                    sig->sig_class == 0x10
+                    || sig->sig_class == 0x11
+                    || sig->sig_class == 0x12
+                    || sig->sig_class == 0x13
+                    /* Certification revocation sig.  */
+                    || sig->sig_class == 0x30))
+              has_selfsig = 1;
+
+            break;
+
+          default:
+            if (current_component && ! has_selfsig)
+              missing_selfsig ++;
+            current_component = NULL;
+          }
+      }
+  }
+
+  if (dups || missing_issuer || bad_signature || reordered)
+    tty_printf (_("key %s:\n"), pk_keyid_str (pk));
+
+  if (dups)
+    tty_printf (ngettext ("%d duplicate signature removed\n",
+                          "%d duplicate signatures removed\n", dups), dups);
+  if (missing_issuer)
+    tty_printf (ngettext ("%d signature not checked due to a missing key\n",
+                          "%d signatures not checked due to missing keys\n",
+                          missing_issuer), missing_issuer);
+  if (bad_signature)
+    tty_printf (ngettext ("%d bad signature\n",
+                          "%d bad signatures\n",
+                          bad_signature), bad_signature);
+  if (reordered)
+    tty_printf (ngettext ("%d signature reordered\n",
+                          "%d signatures reordered\n",
+                          reordered), reordered);
+
+  if (only_selfsigs && (bad_signature || reordered))
+    tty_printf (_("Warning: errors found and only checked self-signatures,"
+                  " run '%s' to check all signatures.\n"), "check");
+
+  return modified;
 }
 
 
 }
 
 
@@ -474,7 +1014,8 @@ trustsig_prompt (byte * trust_value, byte * trust_depth, char **regexp)
   tty_printf ("\n");
 
   tty_printf (_("Please enter the depth of this trust signature.\n"
   tty_printf ("\n");
 
   tty_printf (_("Please enter the depth of this trust signature.\n"
-               "A depth greater than 1 allows the key you are signing to make\n"
+               "A depth greater than 1 allows the key you are"
+                " signing to make\n"
                "trust signatures on your behalf.\n"));
   tty_printf ("\n");
 
                "trust signatures on your behalf.\n"));
   tty_printf ("\n");
 
@@ -539,7 +1080,7 @@ trustsig_prompt (byte * trust_value, byte * trust_depth, char **regexp)
 
 
 /*
 
 
 /*
- * Loop over all LOCUSR and and sign the uids after asking.  If no
+ * Loop over all LOCUSR and sign the uids after asking.  If no
  * user id is marked, all user ids will be signed; if some user_ids
  * are marked only those will be signed.  If QUICK is true the
  * function won't ask the user and use sensible defaults.
  * user id is marked, all user ids will be signed; if some user_ids
  * are marked only those will be signed.  If QUICK is true the
  * function won't ask the user and use sensible defaults.
@@ -565,7 +1106,7 @@ sign_uids (ctrl_t ctrl, estream_t fp,
    * why to sign keys using a subkey.  Implementation of USAGE_CERT
    * is just a hack in getkey.c and does not mean that a subkey
    * marked as certification capable will be used. */
    * why to sign keys using a subkey.  Implementation of USAGE_CERT
    * is just a hack in getkey.c and does not mean that a subkey
    * marked as certification capable will be used. */
-  rc = build_sk_list (locusr, &sk_list, PUBKEY_USAGE_CERT);
+  rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT);
   if (rc)
     goto leave;
 
   if (rc)
     goto leave;
 
@@ -614,7 +1155,16 @@ sign_uids (ctrl_t ctrl, estream_t fp,
                   user = utf8_to_native (uidnode->pkt->pkt.user_id->name,
                                          uidnode->pkt->pkt.user_id->len, 0);
 
                   user = utf8_to_native (uidnode->pkt->pkt.user_id->name,
                                          uidnode->pkt->pkt.user_id->len, 0);
 
-                 if (uidnode->pkt->pkt.user_id->is_revoked)
+                  if (opt.only_sign_text_ids
+                      && uidnode->pkt->pkt.user_id->attribs)
+                    {
+                      tty_fprintf (fp, _("Skipping user ID \"%s\","
+                                         " which is not a text ID.\n"),
+                                   user);
+                      uidnode->flag &= ~NODFLG_MARK_A;
+                      uidnode = NULL;
+                    }
+                 else if (uidnode->pkt->pkt.user_id->flags.revoked)
                    {
                      tty_fprintf (fp, _("User ID \"%s\" is revoked."), user);
 
                    {
                      tty_fprintf (fp, _("User ID \"%s\" is revoked."), user);
 
@@ -642,7 +1192,7 @@ sign_uids (ctrl_t ctrl, estream_t fp,
                          tty_fprintf (fp, _("  Unable to sign.\n"));
                        }
                    }
                          tty_fprintf (fp, _("  Unable to sign.\n"));
                        }
                    }
-                 else if (uidnode->pkt->pkt.user_id->is_expired)
+                 else if (uidnode->pkt->pkt.user_id->flags.expired)
                    {
                      tty_fprintf (fp, _("User ID \"%s\" is expired."), user);
 
                    {
                      tty_fprintf (fp, _("User ID \"%s\" is expired."), user);
 
@@ -800,7 +1350,7 @@ sign_uids (ctrl_t ctrl, estream_t fp,
                    }
 
                  /* Fixme: see whether there is a revocation in which
                    }
 
                  /* Fixme: see whether there is a revocation in which
-                  * case we should allow to sign it again. */
+                  * case we should allow signing it again. */
                  if (!node->pkt->pkt.signature->flags.exportable && local)
                    tty_fprintf ( fp,
                        _("\"%s\" was already locally signed by key %s\n"),
                  if (!node->pkt->pkt.signature->flags.exportable && local)
                    tty_fprintf ( fp,
                        _("\"%s\" was already locally signed by key %s\n"),
@@ -846,6 +1396,14 @@ sign_uids (ctrl_t ctrl, estream_t fp,
 
       if (primary_pk->expiredate && !selfsig)
        {
 
       if (primary_pk->expiredate && !selfsig)
        {
+          /* Static analyzer note: A claim that PRIMARY_PK might be
+             NULL is not correct because it set from the public key
+             packet which is always the first packet in a keyblock and
+             parsed in the above loop over the keyblock.  In case the
+             keyblock has no packets at all and thus the loop was not
+             entered the above count_uids_with_flag would have
+             detected this case.  */
+
          u32 now = make_timestamp ();
 
          if (primary_pk->expiredate <= now)
          u32 now = make_timestamp ();
 
          if (primary_pk->expiredate <= now)
@@ -1044,7 +1602,7 @@ sign_uids (ctrl_t ctrl, estream_t fp,
              PKT_signature *sig;
              struct sign_attrib attrib;
 
              PKT_signature *sig;
              struct sign_attrib attrib;
 
-             assert (primary_pk);
+             log_assert (primary_pk);
              memset (&attrib, 0, sizeof attrib);
              attrib.non_exportable = local;
              attrib.non_revocable = nonrevocable;
              memset (&attrib, 0, sizeof attrib);
              attrib.non_exportable = local;
              attrib.non_revocable = nonrevocable;
@@ -1149,7 +1707,7 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
           err = hexkeygrip_from_pk (pk, &hexgrip);
           if (err)
             goto leave;
           err = hexkeygrip_from_pk (pk, &hexgrip);
           if (err)
             goto leave;
-          err = agent_get_keyinfo (ctrl, hexgrip, &serialno);
+          err = agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL);
           if (!err && serialno)
             ; /* Key on card.  */
           else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
           if (!err && serialno)
             ; /* Key on card.  */
           else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
@@ -1171,7 +1729,7 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
     }
 
   /* Change the passphrase for all keys.  */
     }
 
   /* Change the passphrase for all keys.  */
-  for (any = 0, node = keyblock; node; node = node->next)
+  for (node = keyblock; node; node = node->next)
     {
       if (node->pkt->pkttype == PKT_PUBLIC_KEY
          || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
     {
       if (node->pkt->pkttype == PKT_PUBLIC_KEY
          || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
@@ -1187,7 +1745,8 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
             goto leave;
 
           desc = gpg_format_keydesc (pk, FORMAT_KEYDESC_NORMAL, 1);
             goto leave;
 
           desc = gpg_format_keydesc (pk, FORMAT_KEYDESC_NORMAL, 1);
-          err = agent_passwd (ctrl, hexgrip, desc, &cache_nonce, &passwd_nonce);
+          err = agent_passwd (ctrl, hexgrip, desc, 0,
+                              &cache_nonce, &passwd_nonce);
           xfree (desc);
 
           if (err)
           xfree (desc);
 
           if (err)
@@ -1211,54 +1770,6 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
 
 
 \f
 
 
 \f
-/*
- * There are some keys out (due to a bug in gnupg), where the sequence
- * of the packets is wrong.  This function fixes that.
- * Returns: true if the keyblock has been fixed.
- *
- * Note:  This function does not work if there is more than one user ID.
- */
-static int
-fix_key_signature_order (KBNODE keyblock)
-{
-  KBNODE node, last, subkey;
-  int fixed = 0;
-
-  /* Locate key signatures of class 0x10..0x13 behind sub key packets.  */
-  for (subkey = last = NULL, node = keyblock; node;
-       last = node, node = node->next)
-    {
-      switch (node->pkt->pkttype)
-       {
-       case PKT_PUBLIC_SUBKEY:
-       case PKT_SECRET_SUBKEY:
-         if (!subkey)
-           subkey = last; /* Actually it is the one before the subkey.  */
-         break;
-       case PKT_SIGNATURE:
-         if (subkey)
-           {
-             PKT_signature *sig = node->pkt->pkt.signature;
-             if (sig->sig_class >= 0x10 && sig->sig_class <= 0x13)
-               {
-                 log_info (_("moving a key signature to the correct place\n"));
-                 last->next = node->next;
-                 node->next = subkey->next;
-                 subkey->next = node;
-                 node = last;
-                 fixed = 1;
-               }
-           }
-         break;
-       default:
-         break;
-       }
-    }
-
-  return fixed;
-}
-
-
 /* Fix various problems in the keyblock.  Returns true if the keyblock
    was changed.  Note that a pointer to the keyblock must be given and
    the function may change it (i.e. replacing the first node).  */
 /* Fix various problems in the keyblock.  Returns true if the keyblock
    was changed.  Note that a pointer to the keyblock must be given and
    the function may change it (i.e. replacing the first node).  */
@@ -1267,10 +1778,10 @@ fix_keyblock (kbnode_t *keyblockp)
 {
   int changed = 0;
 
 {
   int changed = 0;
 
-  if (fix_key_signature_order (*keyblockp))
-    changed++;
   if (collapse_uids (keyblockp))
     changed++;
   if (collapse_uids (keyblockp))
     changed++;
+  if (check_all_keysigs (*keyblockp, 0, 1))
+    changed++;
   reorder_keyblock (*keyblockp);
   /* If we modified the keyblock, make sure the flags are right. */
   if (changed)
   reorder_keyblock (*keyblockp);
   /* If we modified the keyblock, make sure the flags are right. */
   if (changed)
@@ -1336,13 +1847,13 @@ enum cmdids
   cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG,
   cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY,
   cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF,
   cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG,
   cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY,
   cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF,
-  cmdEXPIRE, cmdBACKSIGN,
+  cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN,
 #ifndef NO_TRUST_MODELS
   cmdENABLEKEY, cmdDISABLEKEY,
 #endif /*!NO_TRUST_MODELS*/
   cmdSHOWPREF,
   cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
 #ifndef NO_TRUST_MODELS
   cmdENABLEKEY, cmdDISABLEKEY,
 #endif /*!NO_TRUST_MODELS*/
   cmdSHOWPREF,
   cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
-  cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCHECKBKUPKEY,
+  cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD,
   cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP
 };
 
   cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP
 };
 
@@ -1367,6 +1878,7 @@ static struct
   { "key", cmdSELKEY, 0, N_("select subkey N")},
   { "check", cmdCHECK, 0, N_("check signatures")},
   { "c", cmdCHECK, 0, NULL},
   { "key", cmdSELKEY, 0, N_("select subkey N")},
   { "check", cmdCHECK, 0, N_("check signatures")},
   { "c", cmdCHECK, 0, NULL},
+  { "change-usage", cmdCHANGEUSAGE, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "cross-certify", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "backsign", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "sign", cmdSIGN, KEYEDIT_NOT_SK | KEYEDIT_TAIL_MATCH,
   { "cross-certify", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "backsign", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "sign", cmdSIGN, KEYEDIT_NOT_SK | KEYEDIT_TAIL_MATCH,
@@ -1394,7 +1906,6 @@ static struct
     N_("move a key to a smartcard")},
   { "bkuptocard", cmdBKUPTOCARD, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK,
     N_("move a backup key to a smartcard")},
     N_("move a key to a smartcard")},
   { "bkuptocard", cmdBKUPTOCARD, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK,
     N_("move a backup key to a smartcard")},
-  { "checkbkupkey", cmdCHECKBKUPKEY, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK, NULL},
 #endif /*ENABLE_CARD_SUPPORT */
   { "delkey", cmdDELKEY, KEYEDIT_NOT_SK, N_("delete selected subkeys")},
   { "addrevoker", cmdADDREVOKER, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK,
 #endif /*ENABLE_CARD_SUPPORT */
   { "delkey", cmdDELKEY, KEYEDIT_NOT_SK, N_("delete selected subkeys")},
   { "addrevoker", cmdADDREVOKER, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK,
@@ -1530,7 +2041,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
      and run the stale check as early as possible.  Note, that for
      non- W32 platforms it is run indirectly trough a call to
      get_validity ().  */
      and run the stale check as early as possible.  Note, that for
      non- W32 platforms it is run indirectly trough a call to
      get_validity ().  */
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
 #endif
 
   /* Get the public key */
 #endif
 
   /* Get the public key */
@@ -1709,14 +2220,15 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            if (*arg_string == '*'
                && (!arg_string[1] || spacep (arg_string + 1)))
              arg_number = -1;  /* Select all. */
            if (*arg_string == '*'
                && (!arg_string[1] || spacep (arg_string + 1)))
              arg_number = -1;  /* Select all. */
-           if (menu_select_key (keyblock, arg_number))
+           if (menu_select_key (keyblock, arg_number, p))
              redisplay = 1;
          }
          break;
 
        case cmdCHECK:
              redisplay = 1;
          }
          break;
 
        case cmdCHECK:
-         check_all_keysigs (keyblock, count_selected_uids (keyblock),
-                             !strcmp (arg_string, "selfsig"));
+         if (check_all_keysigs (keyblock, count_selected_uids (keyblock),
+                                 !strcmp (arg_string, "selfsig")))
+            modified = 1;
          break;
 
        case cmdSIGN:
          break;
 
        case cmdSIGN:
@@ -1742,21 +2254,31 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                  }
              }
 
                  }
              }
 
-           if (count_uids (keyblock) > 1 && !count_selected_uids (keyblock)
-               && !cpr_get_answer_is_yes ("keyedit.sign_all.okay",
-                                          _("Really sign all user IDs?"
-                                            " (y/N) ")))
-             {
-               if (opt.interactive)
-                 interactive = 1;
-               else
-                 {
-                   tty_printf (_("Hint: Select the user IDs to sign\n"));
-                   have_commands = 0;
-                   break;
-                 }
-
-             }
+           if (count_uids (keyblock) > 1 && !count_selected_uids (keyblock))
+              {
+                int result;
+                if (opt.only_sign_text_ids)
+                  result = cpr_get_answer_is_yes
+                    ("keyedit.sign_all.okay",
+                     _("Really sign all user IDs? (y/N) "));
+                else
+                  result = cpr_get_answer_is_yes
+                    ("keyedit.sign_all.okay",
+                     _("Really sign all text user IDs? (y/N) "));
+
+                if (! result)
+                  {
+                    if (opt.interactive)
+                      interactive = 1;
+                    else
+                      {
+                        tty_printf (_("Hint: Select the user IDs to sign\n"));
+                        have_commands = 0;
+                        break;
+                      }
+
+                  }
+              }
            /* What sort of signing are we doing? */
            if (!parse_sign_type
                (answer, &localsig, &nonrevokesig, &trustsig))
            /* What sort of signing are we doing? */
            if (!parse_sign_type
                (answer, &localsig, &nonrevokesig, &trustsig))
@@ -1792,7 +2314,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          photo = 1;
          /* fall through */
        case cmdADDUID:
          photo = 1;
          /* fall through */
        case cmdADDUID:
-         if (menu_adduid (keyblock, photo, arg_string, NULL))
+         if (menu_adduid (ctrl, keyblock, photo, arg_string, NULL))
            {
              update_trust = 1;
              redisplay = 1;
            {
              update_trust = 1;
              redisplay = 1;
@@ -1845,7 +2367,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          break;
 
        case cmdADDKEY:
          break;
 
        case cmdADDKEY:
-         if (!generate_subkeypair (ctrl, keyblock))
+         if (!generate_subkeypair (ctrl, keyblock, NULL, NULL, NULL))
            {
              redisplay = 1;
              modified = 1;
            {
              redisplay = 1;
              modified = 1;
@@ -1901,24 +2423,28 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          break;
 
        case cmdBKUPTOCARD:
          break;
 
        case cmdBKUPTOCARD:
-       case cmdCHECKBKUPKEY:
-          log_debug ("FIXME: This needs to be changed\n");
          {
            /* Ask for a filename, check whether this is really a
               backup key as generated by the card generation, parse
               that key and store it on card. */
            KBNODE node;
          {
            /* Ask for a filename, check whether this is really a
               backup key as generated by the card generation, parse
               that key and store it on card. */
            KBNODE node;
-           const char *fname;
+           char *fname;
            PACKET *pkt;
            IOBUF a;
 
            PACKET *pkt;
            IOBUF a;
 
-           fname = arg_string;
-           if (!*fname)
+            if (!*arg_string)
              {
                tty_printf (_("Command expects a filename argument\n"));
                break;
              }
 
              {
                tty_printf (_("Command expects a filename argument\n"));
                break;
              }
 
+            if (*arg_string == DIRSEP_C)
+              fname = xstrdup (arg_string);
+            else if (*arg_string == '~')
+              fname = make_filename (arg_string, NULL);
+            else
+              fname = make_filename (gnupg_homedir (), arg_string, NULL);
+
            /* Open that file.  */
            a = iobuf_open (fname);
            if (a && is_secured_file (iobuf_get_fd (a)))
            /* Open that file.  */
            a = iobuf_open (fname);
            if (a && is_secured_file (iobuf_get_fd (a)))
@@ -1927,12 +2453,13 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                a = NULL;
                gpg_err_set_errno (EPERM);
              }
                a = NULL;
                gpg_err_set_errno (EPERM);
              }
-           if (!a)
-             {
-               tty_printf (_("Can't open '%s': %s\n"),
-                           fname, strerror (errno));
-               break;
-             }
+            if (!a)
+              {
+                tty_printf (_("Can't open '%s': %s\n"),
+                            fname, strerror (errno));
+                xfree (fname);
+                break;
+              }
 
            /* Parse and check that file.  */
            pkt = xmalloc (sizeof *pkt);
 
            /* Parse and check that file.  */
            pkt = xmalloc (sizeof *pkt);
@@ -1943,54 +2470,39 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            if (!err && pkt->pkttype != PKT_SECRET_KEY
                && pkt->pkttype != PKT_SECRET_SUBKEY)
              err = GPG_ERR_NO_SECKEY;
            if (!err && pkt->pkttype != PKT_SECRET_KEY
                && pkt->pkttype != PKT_SECRET_SUBKEY)
              err = GPG_ERR_NO_SECKEY;
-           if (err)
-             {
-               tty_printf (_("Error reading backup key from '%s': %s\n"),
-                           fname, gpg_strerror (err));
-               free_packet (pkt);
-               xfree (pkt);
-               break;
-             }
+            if (err)
+              {
+                tty_printf (_("Error reading backup key from '%s': %s\n"),
+                            fname, gpg_strerror (err));
+                xfree (fname);
+                free_packet (pkt);
+                xfree (pkt);
+                break;
+              }
+
+           xfree (fname);
            node = new_kbnode (pkt);
 
            node = new_kbnode (pkt);
 
-           if (cmd == cmdCHECKBKUPKEY)
-             {
-               /* PKT_public_key *sk = node->pkt->pkt.secret_key; */
-               /* switch (is_secret_key_protected (sk)) */
-               /*   { */
-               /*   case 0:    /\* Not protected. *\/ */
-               /*     tty_printf (_("This key is not protected.\n")); */
-               /*     break; */
-               /*   case -1: */
-               /*     log_error (_("unknown key protection algorithm\n")); */
-               /*     break; */
-               /*   default: */
-               /*     if (sk->protect.s2k.mode == 1001) */
-               /*       tty_printf (_("Secret parts of key" */
-               /*                  " are not available.\n")); */
-               /*     if (sk->protect.s2k.mode == 1002) */
-               /*       tty_printf (_("Secret parts of key" */
-               /*                  " are stored on-card.\n")); */
-                   /* else */
-                   /*   check_secret_key (sk, 0); */
-                 /* } */
-             }
-           else                /* Store it.  */
-             {
-               if (card_store_subkey (node, 0))
-                 {
-                   redisplay = 1;
-                   sec_shadowing = 1;
-                 }
-             }
-           release_kbnode (node);
-         }
-         break;
+            /* Transfer it to gpg-agent which handles secret keys.  */
+            err = transfer_secret_keys (ctrl, NULL, node, 1, 1);
 
 
-#endif /* ENABLE_CARD_SUPPORT */
+            /* Treat the pkt as a public key.  */
+            pkt->pkttype = PKT_PUBLIC_KEY;
 
 
-       case cmdDELKEY:
-         {
+            /* Ask gpg-agent to store the secret key to card.  */
+            if (card_store_subkey (node, 0))
+              {
+                redisplay = 1;
+                sec_shadowing = 1;
+              }
+            release_kbnode (node);
+          }
+          break;
+
+#endif /* ENABLE_CARD_SUPPORT */
+
+       case cmdDELKEY:
+         {
            int n1;
 
            if (!(n1 = count_selected_keys (keyblock)))
            int n1;
 
            if (!(n1 = count_selected_keys (keyblock)))
@@ -2044,7 +2556,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                       n1 > 1 ? _("Really revoke all selected user IDs? (y/N) ")
                      :        _("Really revoke this user ID? (y/N) ")))
              {
                       n1 > 1 ? _("Really revoke all selected user IDs? (y/N) ")
                      :        _("Really revoke this user ID? (y/N) ")))
              {
-               if (menu_revuid (keyblock))
+               if (menu_revuid (ctrl, keyblock))
                  {
                    modified = 1;
                    redisplay = 1;
                  {
                    modified = 1;
                    redisplay = 1;
@@ -2088,7 +2600,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          break;
 
        case cmdEXPIRE:
          break;
 
        case cmdEXPIRE:
-         if (menu_expire (keyblock))
+         if (gpg_err_code (menu_expire (keyblock, 0, 0)) == GPG_ERR_TRUE)
            {
              merge_keys_and_selfsig (keyblock);
               run_subkey_warnings = 1;
            {
              merge_keys_and_selfsig (keyblock);
               run_subkey_warnings = 1;
@@ -2097,6 +2609,15 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            }
          break;
 
            }
          break;
 
+       case cmdCHANGEUSAGE:
+         if (menu_changeusage (keyblock))
+           {
+             merge_keys_and_selfsig (keyblock);
+             modified = 1;
+             redisplay = 1;
+           }
+         break;
+
        case cmdBACKSIGN:
          if (menu_backsign (keyblock))
            {
        case cmdBACKSIGN:
          if (menu_backsign (keyblock))
            {
@@ -2129,7 +2650,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
 
          show_key_with_all_names (ctrl, NULL, keyblock, 0, 0, 0, 1, 0, 0);
          tty_printf ("\n");
 
          show_key_with_all_names (ctrl, NULL, keyblock, 0, 0, 0, 1, 0, 0);
          tty_printf ("\n");
-         if (edit_ownertrust (find_kbnode (keyblock,
+         if (edit_ownertrust (ctrl, find_kbnode (keyblock,
                                            PKT_PUBLIC_KEY)->pkt->pkt.
                               public_key, 1))
            {
                                            PKT_PUBLIC_KEY)->pkt->pkt.
                               public_key, 1))
            {
@@ -2145,8 +2666,8 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
        case cmdPREF:
          {
            int count = count_selected_uids (keyblock);
        case cmdPREF:
          {
            int count = count_selected_uids (keyblock);
-           assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
-           show_names (NULL, keyblock, keyblock->pkt->pkt.public_key,
+           log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+           show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key,
                        count ? NODFLG_SELUID : 0, 1);
          }
          break;
                        count ? NODFLG_SELUID : 0, 1);
          }
          break;
@@ -2154,8 +2675,8 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
        case cmdSHOWPREF:
          {
            int count = count_selected_uids (keyblock);
        case cmdSHOWPREF:
          {
            int count = count_selected_uids (keyblock);
-           assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
-           show_names (NULL, keyblock, keyblock->pkt->pkt.public_key,
+           log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+           show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key,
                        count ? NODFLG_SELUID : 0, 2);
          }
          break;
                        count ? NODFLG_SELUID : 0, 2);
          }
          break;
@@ -2231,7 +2752,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
 #endif /*!NO_TRUST_MODELS*/
 
        case cmdSHOWPHOTO:
 #endif /*!NO_TRUST_MODELS*/
 
        case cmdSHOWPHOTO:
-         menu_showphoto (keyblock);
+         menu_showphoto (ctrl, keyblock);
          break;
 
        case cmdCLEAN:
          break;
 
        case cmdCLEAN:
@@ -2258,11 +2779,11 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                goto leave;
              break;
            }
                goto leave;
              break;
            }
-         /* fall thru */
+         /* fall through */
        case cmdSAVE:
          if (modified)
            {
        case cmdSAVE:
          if (modified)
            {
-              err = keydb_update_keyblock (kdbhd, keyblock);
+              err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
               if (err)
                 {
                   log_error (_("update failed: %s\n"), gpg_strerror (err));
               if (err)
                 {
                   log_error (_("update failed: %s\n"), gpg_strerror (err));
@@ -2319,7 +2840,7 @@ keyedit_passwd (ctrl_t ctrl, const char *username)
       err = gpg_error_from_syserror ();
       goto leave;
     }
       err = gpg_error_from_syserror ();
       goto leave;
     }
-  err = getkey_byname (NULL, pk, username, 1, &keyblock);
+  err = getkey_byname (ctrl, NULL, pk, username, 1, &keyblock);
   if (err)
     goto leave;
 
   if (err)
     goto leave;
 
@@ -2339,33 +2860,31 @@ leave:
 }
 
 
 }
 
 
-/* Unattended adding of a new keyid.  USERNAME specifies the
-   key. NEWUID is the new user id to add to the key.  */
-void
-keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
+/* Helper for quick commands to find the keyblock for USERNAME.
+ * Returns on success the key database handle at R_KDBHD and the
+ * keyblock at R_KEYBLOCK.  */
+static gpg_error_t
+quick_find_keyblock (ctrl_t ctrl, const char *username,
+                     KEYDB_HANDLE *r_kdbhd, kbnode_t *r_keyblock)
 {
   gpg_error_t err;
   KEYDB_HANDLE kdbhd = NULL;
 {
   gpg_error_t err;
   KEYDB_HANDLE kdbhd = NULL;
-  KEYDB_SEARCH_DESC desc;
   kbnode_t keyblock = NULL;
   kbnode_t keyblock = NULL;
+  KEYDB_SEARCH_DESC desc;
   kbnode_t node;
   kbnode_t node;
-  char *uidstring = NULL;
 
 
-  uidstring = xstrdup (newuid);
-  trim_spaces (uidstring);
-  if (!*uidstring)
+  *r_kdbhd = NULL;
+  *r_keyblock = NULL;
+
+  /* Search the key; we don't want the whole getkey stuff here.  */
+  kdbhd = keydb_new ();
+  if (!kdbhd)
     {
     {
-      log_error ("%s\n", gpg_strerror (GPG_ERR_INV_USER_ID));
+      /* Note that keydb_new has already used log_error.  */
+      err = gpg_error_from_syserror ();
       goto leave;
     }
 
       goto leave;
     }
 
-#ifdef HAVE_W32_SYSTEM
-  /* See keyedit_menu for why we need this.  */
-  check_trustdb_stale ();
-#endif
-
-  /* Search the key; we don't want the whole getkey stuff here.  */
-  kdbhd = keydb_new ();
   err = classify_user_id (username, &desc, 1);
   if (!err)
     err = keydb_search (kdbhd, &desc, 1, NULL);
   err = classify_user_id (username, &desc, 1);
   if (!err)
     err = keydb_search (kdbhd, &desc, 1, NULL);
@@ -2390,25 +2909,68 @@ keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
 
       if (!err)
         {
 
       if (!err)
         {
-          /* We require the secret primary key to add a UID.  */
+          /* We require the secret primary key to set the primary UID.  */
           node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
           node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
-          if (!node)
-            BUG ();
+          log_assert (node);
           err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key);
         }
     }
           err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key);
         }
     }
+  else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    err = gpg_error (GPG_ERR_NO_PUBKEY);
+
   if (err)
     {
   if (err)
     {
-      log_error (_("secret key \"%s\" not found: %s\n"),
+      log_error (_("key \"%s\" not found: %s\n"),
                  username, gpg_strerror (err));
       goto leave;
     }
 
   fix_keyblock (&keyblock);
                  username, gpg_strerror (err));
       goto leave;
     }
 
   fix_keyblock (&keyblock);
+  merge_keys_and_selfsig (keyblock);
+
+  *r_keyblock = keyblock;
+  keyblock = NULL;
+  *r_kdbhd = kdbhd;
+  kdbhd = NULL;
 
 
-  if (menu_adduid (keyblock, 0, NULL, uidstring))
+ leave:
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+  return err;
+}
+
+
+/* Unattended adding of a new keyid.  USERNAME specifies the
+   key. NEWUID is the new user id to add to the key.  */
+void
+keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
+{
+  gpg_error_t err;
+  KEYDB_HANDLE kdbhd = NULL;
+  kbnode_t keyblock = NULL;
+  char *uidstring = NULL;
+
+  uidstring = xstrdup (newuid);
+  trim_spaces (uidstring);
+  if (!*uidstring)
+    {
+      log_error ("%s\n", gpg_strerror (GPG_ERR_INV_USER_ID));
+      goto leave;
+    }
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale (ctrl);
+#endif
+
+  /* Search the key; we don't want the whole getkey stuff here.  */
+  err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock);
+  if (err)
+    goto leave;
+
+  if (menu_adduid (ctrl, keyblock, 0, NULL, uidstring))
     {
     {
-      err = keydb_update_keyblock (kdbhd, keyblock);
+      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
       if (err)
         {
           log_error (_("update failed: %s\n"), gpg_strerror (err));
       if (err)
         {
           log_error (_("update failed: %s\n"), gpg_strerror (err));
@@ -2426,41 +2988,175 @@ keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
 }
 
 
 }
 
 
-/* Unattended key signing function.  If the key specifified by FPR is
-   availabale and FPR is the primary fingerprint all user ids of the
-   user ids of the key are signed using the default signing key.  If
-   UIDS is an empty list all usable UIDs are signed, if it is not
-   empty, only those user ids matching one of the entries of the loist
-   are signed.  With LOCAL being true kthe signatures are marked as
-   non-exportable.  */
+/* Unattended revocation of a keyid.  USERNAME specifies the
+   key. UIDTOREV is the user id revoke from the key.  */
 void
 void
-keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
-                    strlist_t locusr, int local)
+keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev)
 {
   gpg_error_t err;
 {
   gpg_error_t err;
-  kbnode_t keyblock = NULL;
   KEYDB_HANDLE kdbhd = NULL;
   KEYDB_HANDLE kdbhd = NULL;
+  kbnode_t keyblock = NULL;
+  kbnode_t node;
   int modified = 0;
   int modified = 0;
-  KEYDB_SEARCH_DESC desc;
-  PKT_public_key *pk;
+  size_t revlen;
+  size_t valid_uids;
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale (ctrl);
+#endif
+
+  /* Search the key; we don't want the whole getkey stuff here.  */
+  err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock);
+  if (err)
+    goto leave;
+
+  /* Too make sure that we do not revoke the last valid UID, we first
+     count how many valid UIDs there are.  */
+  valid_uids = 0;
+  for (node = keyblock; node; node = node->next)
+    valid_uids += (node->pkt->pkttype == PKT_USER_ID
+                   && !node->pkt->pkt.user_id->flags.revoked
+                   && !node->pkt->pkt.user_id->flags.expired);
+
+  /* Find the right UID. */
+  revlen = strlen (uidtorev);
+  for (node = keyblock; node; node = node->next)
+    {
+      if (node->pkt->pkttype == PKT_USER_ID
+          && revlen == node->pkt->pkt.user_id->len
+          && !memcmp (node->pkt->pkt.user_id->name, uidtorev, revlen))
+        {
+          struct revocation_reason_info *reason;
+
+          /* Make sure that we do not revoke the last valid UID.  */
+          if (valid_uids == 1
+              && ! node->pkt->pkt.user_id->flags.revoked
+              && ! node->pkt->pkt.user_id->flags.expired)
+            {
+              log_error (_("cannot revoke the last valid user ID.\n"));
+              err = gpg_error (GPG_ERR_INV_USER_ID);
+              goto leave;
+            }
+
+          reason = get_default_uid_revocation_reason ();
+          err = core_revuid (ctrl, keyblock, node, reason, &modified);
+          release_revocation_reason_info (reason);
+          if (err)
+            goto leave;
+          err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
+          if (err)
+            {
+              log_error (_("update failed: %s\n"), gpg_strerror (err));
+              goto leave;
+            }
+
+          revalidation_mark ();
+          goto leave;
+        }
+    }
+  err = gpg_error (GPG_ERR_NO_USER_ID);
+
+
+ leave:
+  if (err)
+    log_error (_("revoking the user ID failed: %s\n"), gpg_strerror (err));
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+}
+
+
+/* Unattended setting of the primary uid.  USERNAME specifies the key.
+   PRIMARYUID is the user id which shall be primary.  */
+void
+keyedit_quick_set_primary (ctrl_t ctrl, const char *username,
+                           const char *primaryuid)
+{
+  gpg_error_t err;
+  KEYDB_HANDLE kdbhd = NULL;
+  kbnode_t keyblock = NULL;
   kbnode_t node;
   kbnode_t node;
-  strlist_t sl;
+  size_t primaryuidlen;
   int any;
 
 #ifdef HAVE_W32_SYSTEM
   /* See keyedit_menu for why we need this.  */
   int any;
 
 #ifdef HAVE_W32_SYSTEM
   /* See keyedit_menu for why we need this.  */
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
 #endif
 
 #endif
 
-  /* We require a fingerprint because only this uniquely identifies a
-     key and may thus be used to select a key for unattended key
-     signing.  */
+  err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock);
+  if (err)
+    goto leave;
+
+  /* Find and mark the UID - we mark only the first valid one. */
+  primaryuidlen = strlen (primaryuid);
+  any = 0;
+  for (node = keyblock; node; node = node->next)
+    {
+      if (node->pkt->pkttype == PKT_USER_ID
+          && !any
+          && !node->pkt->pkt.user_id->flags.revoked
+          && !node->pkt->pkt.user_id->flags.expired
+          && primaryuidlen == node->pkt->pkt.user_id->len
+          && !memcmp (node->pkt->pkt.user_id->name, primaryuid, primaryuidlen))
+        {
+          node->flag |= NODFLG_SELUID;
+          any = 1;
+        }
+      else
+        node->flag &= ~NODFLG_SELUID;
+    }
+
+  if (!any)
+    err = gpg_error (GPG_ERR_NO_USER_ID);
+  else if (menu_set_primary_uid (keyblock))
+    {
+      merge_keys_and_selfsig (keyblock);
+      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
+      if (err)
+        {
+          log_error (_("update failed: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+      revalidation_mark ();
+    }
+  else
+    err = gpg_error (GPG_ERR_GENERAL);
+
+  if (err)
+    log_error (_("setting the primary user ID failed: %s\n"),
+               gpg_strerror (err));
+
+ leave:
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+}
+
+
+/* Find a keyblock by fingerprint because only this uniquely
+ * identifies a key and may thus be used to select a key for
+ * unattended subkey creation os key signing.  */
+static gpg_error_t
+find_by_primary_fpr (ctrl_t ctrl, const char *fpr,
+                     kbnode_t *r_keyblock, KEYDB_HANDLE *r_kdbhd)
+{
+  gpg_error_t err;
+  kbnode_t keyblock = NULL;
+  KEYDB_HANDLE kdbhd = NULL;
+  KEYDB_SEARCH_DESC desc;
+  byte fprbin[MAX_FINGERPRINT_LEN];
+  size_t fprlen;
+
+  *r_keyblock = NULL;
+  *r_kdbhd = NULL;
+
   if (classify_user_id (fpr, &desc, 1)
       || !(desc.mode == KEYDB_SEARCH_MODE_FPR
            || desc.mode == KEYDB_SEARCH_MODE_FPR16
            || desc.mode == KEYDB_SEARCH_MODE_FPR20))
     {
       log_error (_("\"%s\" is not a fingerprint\n"), fpr);
   if (classify_user_id (fpr, &desc, 1)
       || !(desc.mode == KEYDB_SEARCH_MODE_FPR
            || desc.mode == KEYDB_SEARCH_MODE_FPR16
            || desc.mode == KEYDB_SEARCH_MODE_FPR20))
     {
       log_error (_("\"%s\" is not a fingerprint\n"), fpr);
+      err = gpg_error (GPG_ERR_INV_NAME);
       goto leave;
     }
   err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1);
       goto leave;
     }
   err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1);
@@ -2471,108 +3167,333 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
     }
 
   /* Check that the primary fingerprint has been given. */
     }
 
   /* Check that the primary fingerprint has been given. */
-  {
-    byte fprbin[MAX_FINGERPRINT_LEN];
-    size_t fprlen;
-
-    fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen);
-    if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16
-        && !memcmp (fprbin, desc.u.fpr, 16))
-      ;
-    else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR
-             && !memcmp (fprbin, desc.u.fpr, 16)
-             && !desc.u.fpr[16]
-             && !desc.u.fpr[17]
-             && !desc.u.fpr[18]
-             && !desc.u.fpr[19])
-      ;
-    else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20
-                              || desc.mode == KEYDB_SEARCH_MODE_FPR)
-             && !memcmp (fprbin, desc.u.fpr, 20))
-      ;
-    else
-      {
-        log_error (_("\"%s\" is not the primary fingerprint\n"), fpr);
-        goto leave;
-      }
-  }
+  fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen);
+  if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16
+      && !memcmp (fprbin, desc.u.fpr, 16))
+    ;
+  else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR
+           && !memcmp (fprbin, desc.u.fpr, 16)
+           && !desc.u.fpr[16]
+           && !desc.u.fpr[17]
+           && !desc.u.fpr[18]
+           && !desc.u.fpr[19])
+    ;
+  else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20
+                            || desc.mode == KEYDB_SEARCH_MODE_FPR)
+           && !memcmp (fprbin, desc.u.fpr, 20))
+    ;
+  else
+    {
+      log_error (_("\"%s\" is not the primary fingerprint\n"), fpr);
+      err = gpg_error (GPG_ERR_INV_NAME);
+      goto leave;
+    }
+
+  *r_keyblock = keyblock;
+  keyblock = NULL;
+  *r_kdbhd = kdbhd;
+  kdbhd = NULL;
+  err = 0;
+
+ leave:
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+  return err;
+}
+
+
+/* Unattended key signing function.  If the key specifified by FPR is
+   available and FPR is the primary fingerprint all user ids of the
+   key are signed using the default signing key.  If UIDS is an empty
+   list all usable UIDs are signed, if it is not empty, only those
+   user ids matching one of the entries of the list are signed.  With
+   LOCAL being true the signatures are marked as non-exportable.  */
+void
+keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
+                    strlist_t locusr, int local)
+{
+  gpg_error_t err;
+  kbnode_t keyblock = NULL;
+  KEYDB_HANDLE kdbhd = NULL;
+  int modified = 0;
+  PKT_public_key *pk;
+  kbnode_t node;
+  strlist_t sl;
+  int any;
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale (ctrl);
+#endif
+
+  /* We require a fingerprint because only this uniquely identifies a
+     key and may thus be used to select a key for unattended key
+     signing.  */
+  if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd))
+    goto leave;
+
+  if (fix_keyblock (&keyblock))
+    modified++;
+
+  /* Give some info in verbose.  */
+  if (opt.verbose)
+    {
+      show_key_with_all_names (ctrl, es_stdout, keyblock, 0,
+                               1/*with_revoker*/, 1/*with_fingerprint*/,
+                               0, 0, 1);
+      es_fflush (es_stdout);
+    }
+
+  pk = keyblock->pkt->pkt.public_key;
+  if (pk->flags.revoked)
+    {
+      if (!opt.verbose)
+        show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
+      log_error ("%s%s", _("Key is revoked."), _("  Unable to sign.\n"));
+      goto leave;
+    }
+
+  /* Set the flags according to the UIDS list.  Fixme: We may want to
+     use classify_user_id along with dedicated compare functions so
+     that we match the same way as in the key lookup. */
+  any = 0;
+  menu_select_uid (keyblock, 0);   /* Better clear the flags first. */
+  for (sl=uids; sl; sl = sl->next)
+    {
+      const char *name = sl->d;
+      int count = 0;
+
+      sl->flags &= ~(1|2);  /* Clear flags used for error reporting.  */
+
+      for (node = keyblock; node; node = node->next)
+        {
+          if (node->pkt->pkttype == PKT_USER_ID)
+            {
+              PKT_user_id *uid = node->pkt->pkt.user_id;
+
+              if (uid->attrib_data)
+                ;
+              else if (*name == '='
+                       && strlen (name+1) == uid->len
+                       && !memcmp (uid->name, name + 1, uid->len))
+                { /* Exact match - we don't do a check for ambiguity
+                   * in this case.  */
+                  node->flag |= NODFLG_SELUID;
+                  if (any != -1)
+                    {
+                      sl->flags |= 1;  /* Report as found.  */
+                      any = 1;
+                    }
+                }
+              else if (ascii_memistr (uid->name, uid->len,
+                                      *name == '*'? name+1:name))
+                {
+                  node->flag |= NODFLG_SELUID;
+                  if (any != -1)
+                    {
+                      sl->flags |= 1;  /* Report as found.  */
+                      any = 1;
+                    }
+                  count++;
+                }
+            }
+        }
+
+      if (count > 1)
+        {
+          any = -1;        /* Force failure at end.  */
+          sl->flags |= 2;  /* Report as ambiguous.  */
+        }
+    }
+
+  /* Check whether all given user ids were found.  */
+  for (sl=uids; sl; sl = sl->next)
+    if (!(sl->flags & 1))
+      any = -1;  /* That user id was not found.  */
+
+  /* Print an error if there was a problem with the user ids.  */
+  if (uids && any < 1)
+    {
+      if (!opt.verbose)
+        show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
+      es_fflush (es_stdout);
+      for (sl=uids; sl; sl = sl->next)
+        {
+          if ((sl->flags & 2))
+            log_info (_("Invalid user ID '%s': %s\n"),
+                      sl->d, gpg_strerror (GPG_ERR_AMBIGUOUS_NAME));
+          else if (!(sl->flags & 1))
+            log_info (_("Invalid user ID '%s': %s\n"),
+                      sl->d, gpg_strerror (GPG_ERR_NOT_FOUND));
+        }
+      log_error ("%s  %s", _("No matching user IDs."), _("Nothing to sign.\n"));
+      goto leave;
+    }
+
+  /* Sign. */
+  sign_uids (ctrl, es_stdout, keyblock, locusr, &modified, local, 0, 0, 0, 1);
+  es_fflush (es_stdout);
+
+  if (modified)
+    {
+      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
+      if (err)
+        {
+          log_error (_("update failed: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+    }
+  else
+    log_info (_("Key not changed so no update needed.\n"));
+
+  if (update_trust)
+    revalidation_mark ();
+
+
+ leave:
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+}
+
+
+/* Unattended subkey creation function.
+ *
+ */
+void
+keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr,
+                      const char *usagestr, const char *expirestr)
+{
+  gpg_error_t err;
+  kbnode_t keyblock;
+  KEYDB_HANDLE kdbhd;
+  int modified = 0;
+  PKT_public_key *pk;
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale (ctrl);
+#endif
+
+  /* We require a fingerprint because only this uniquely identifies a
+   * key and may thus be used to select a key for unattended subkey
+   * creation.  */
+  if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd))
+    goto leave;
+
+  if (fix_keyblock (&keyblock))
+    modified++;
+
+  pk = keyblock->pkt->pkt.public_key;
+  if (pk->flags.revoked)
+    {
+      if (!opt.verbose)
+        show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
+      log_error ("%s%s", _("Key is revoked."), "\n");
+      goto leave;
+    }
+
+  /* Create the subkey.  Note that the called function already prints
+   * an error message. */
+  if (!generate_subkeypair (ctrl, keyblock, algostr, usagestr, expirestr))
+    modified = 1;
+  es_fflush (es_stdout);
+
+  /* Store.  */
+  if (modified)
+    {
+      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
+      if (err)
+        {
+          log_error (_("update failed: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+    }
+  else
+    log_info (_("Key not changed so no update needed.\n"));
+
+ leave:
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+}
+
+
+/* Unattended expiration setting function for the main key.
+ *
+ */
+void
+keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr)
+{
+  gpg_error_t err;
+  kbnode_t keyblock;
+  KEYDB_HANDLE kdbhd;
+  int modified = 0;
+  PKT_public_key *pk;
+  u32 expire;
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale (ctrl);
+#endif
+
+  /* We require a fingerprint because only this uniquely identifies a
+   * key and may thus be used to select a key for unattended
+   * expiration setting.  */
+  err = find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd);
+  if (err)
+    goto leave;
 
   if (fix_keyblock (&keyblock))
     modified++;
 
 
   if (fix_keyblock (&keyblock))
     modified++;
 
-  /* Give some info in verbose.  */
-  if (opt.verbose)
-    {
-      show_key_with_all_names (ctrl, es_stdout, keyblock, 0,
-                               1/*with_revoker*/, 1/*with_fingerprint*/,
-                               0, 0, 1);
-      es_fflush (es_stdout);
-    }
-
   pk = keyblock->pkt->pkt.public_key;
   if (pk->flags.revoked)
     {
       if (!opt.verbose)
         show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
   pk = keyblock->pkt->pkt.public_key;
   if (pk->flags.revoked)
     {
       if (!opt.verbose)
         show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
-      log_error ("%s%s", _("Key is revoked."), _("  Unable to sign.\n"));
+      log_error ("%s%s", _("Key is revoked."), "\n");
+      err = gpg_error (GPG_ERR_CERT_REVOKED);
       goto leave;
     }
 
       goto leave;
     }
 
-  /* Set the flags according to the UIDS list.  Fixme: We may want to
-     use classify_user_id along with dedicated compare functions so
-     that we match the same way as in the key lookup. */
-  any = 0;
-  menu_select_uid (keyblock, 0);   /* Better clear the flags first. */
-  for (sl=uids; sl; sl = sl->next)
-    {
-      for (node = keyblock; node; node = node->next)
-        {
-          if (node->pkt->pkttype == PKT_USER_ID)
-            {
-              PKT_user_id *uid = node->pkt->pkt.user_id;
-
-              if (!uid->attrib_data
-                  && ascii_memistr (uid->name, uid->len, sl->d))
-                {
-                  node->flag |= NODFLG_SELUID;
-                  any = 1;
-                }
-            }
-        }
-    }
 
 
-  if (uids && !any)
+  expire = parse_expire_string (expirestr);
+  if (expire == (u32)-1 )
     {
     {
-      if (!opt.verbose)
-        show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
-      es_fflush (es_stdout);
-      log_error ("%s  %s", _("No matching user IDs."), _("Nothing to sign.\n"));
+      log_error (_("'%s' is not a valid expiration time\n"), expirestr);
+      err = gpg_error (GPG_ERR_INV_VALUE);
       goto leave;
     }
       goto leave;
     }
-
-  /* Sign. */
-  sign_uids (ctrl, es_stdout, keyblock, locusr, &modified, local, 0, 0, 0, 1);
+  if (expire)
+    expire += make_timestamp ();
+
+  /* Set the new expiration date.  */
+  err = menu_expire (keyblock, 1, expire);
+  if (gpg_err_code (err) == GPG_ERR_TRUE)
+    modified = 1;
+  else if (err)
+    goto leave;
   es_fflush (es_stdout);
 
   es_fflush (es_stdout);
 
+  /* Store.  */
   if (modified)
     {
   if (modified)
     {
-      err = keydb_update_keyblock (kdbhd, keyblock);
+      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
       if (err)
         {
           log_error (_("update failed: %s\n"), gpg_strerror (err));
           goto leave;
         }
       if (err)
         {
           log_error (_("update failed: %s\n"), gpg_strerror (err));
           goto leave;
         }
+      if (update_trust)
+        revalidation_mark ();
     }
   else
     log_info (_("Key not changed so no update needed.\n"));
 
     }
   else
     log_info (_("Key not changed so no update needed.\n"));
 
-  if (update_trust)
-    revalidation_mark ();
-
-
  leave:
   release_kbnode (keyblock);
   keydb_release (kdbhd);
  leave:
   release_kbnode (keyblock);
   keydb_release (kdbhd);
+  if (err)
+    write_status_error ("set_expire", err);
 }
 
 
 }
 
 
@@ -2819,7 +3740,7 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
            es_putc ('e', fp);
          else if (!(opt.fast_list_mode || opt.no_expensive_trust_checks))
            {
            es_putc ('e', fp);
          else if (!(opt.fast_list_mode || opt.no_expensive_trust_checks))
            {
-             int trust = get_validity_info (pk, NULL);
+             int trust = get_validity_info (ctrl, keyblock, pk, NULL);
              if (trust == 'u')
                ulti_hack = 1;
              es_putc (trust, fp);
              if (trust == 'u')
                ulti_hack = 1;
              es_putc (trust, fp);
@@ -2832,7 +3753,7 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
                       (ulong) pk->timestamp, (ulong) pk->expiredate);
          if (node->pkt->pkttype == PKT_PUBLIC_KEY
              && !(opt.fast_list_mode || opt.no_expensive_trust_checks))
                       (ulong) pk->timestamp, (ulong) pk->expiredate);
          if (node->pkt->pkttype == PKT_PUBLIC_KEY
              && !(opt.fast_list_mode || opt.no_expensive_trust_checks))
-           es_putc (get_ownertrust_info (pk), fp);
+           es_putc (get_ownertrust_info (pk, 0), fp);
          es_putc (':', fp);
          es_putc (':', fp);
          es_putc (':', fp);
          es_putc (':', fp);
          es_putc (':', fp);
          es_putc (':', fp);
@@ -2867,9 +3788,9 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
          else
            es_fputs ("uid:", fp);
 
          else
            es_fputs ("uid:", fp);
 
-         if (uid->is_revoked)
+         if (uid->flags.revoked)
            es_fputs ("r::::::::", fp);
            es_fputs ("r::::::::", fp);
-         else if (uid->is_expired)
+         else if (uid->flags.expired)
            es_fputs ("e::::::::", fp);
          else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
            es_fputs ("::::::::", fp);
            es_fputs ("e::::::::", fp);
          else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
            es_fputs ("::::::::", fp);
@@ -2878,7 +3799,7 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
              int uid_validity;
 
              if (primary && !ulti_hack)
              int uid_validity;
 
              if (primary && !ulti_hack)
-               uid_validity = get_validity_info (primary, uid);
+               uid_validity = get_validity_info (ctrl, keyblock, primary, uid);
              else
                uid_validity = 'u';
              es_fprintf (fp, "%c::::::::", uid_validity);
              else
                uid_validity = 'u';
              es_fprintf (fp, "%c::::::::", uid_validity);
@@ -2917,11 +3838,11 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
          es_putc (':', fp);
          /* flags */
          es_fprintf (fp, "%d,", i);
          es_putc (':', fp);
          /* flags */
          es_fprintf (fp, "%d,", i);
-         if (uid->is_primary)
+         if (uid->flags.primary)
            es_putc ('p', fp);
            es_putc ('p', fp);
-         if (uid->is_revoked)
+         if (uid->flags.revoked)
            es_putc ('r', fp);
            es_putc ('r', fp);
-         if (uid->is_expired)
+         if (uid->flags.expired)
            es_putc ('e', fp);
          if ((node->flag & NODFLG_SELUID))
            es_putc ('s', fp);
            es_putc ('e', fp);
          if ((node->flag & NODFLG_SELUID))
            es_putc ('s', fp);
@@ -2930,10 +3851,12 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
          es_putc (':', fp);
          if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
            {
          es_putc (':', fp);
          if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
            {
+#ifdef USE_TOFU
              enum tofu_policy policy;
              enum tofu_policy policy;
-             if (! tofu_get_policy (primary, uid, &policy)
+             if (! tofu_get_policy (ctrl, primary, uid, &policy)
                  && policy != TOFU_POLICY_NONE)
                es_fprintf (fp, "%s", tofu_policy_str (policy));
                  && policy != TOFU_POLICY_NONE)
                es_fprintf (fp, "%s", tofu_policy_str (policy));
+#endif /*USE_TOFU*/
            }
          es_putc (':', fp);
          es_putc ('\n', fp);
            }
          es_putc (':', fp);
          es_putc ('\n', fp);
@@ -2943,8 +3866,8 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
 
 
 static void
 
 
 static void
-show_names (estream_t fp,
-            KBNODE keyblock, PKT_public_key * pk, unsigned int flag,
+show_names (ctrl_t ctrl, estream_t fp,
+            kbnode_t keyblock, PKT_public_key * pk, unsigned int flag,
            int with_prefs)
 {
   KBNODE node;
            int with_prefs)
 {
   KBNODE node;
@@ -2959,13 +3882,13 @@ show_names (estream_t fp,
          if (!flag || (flag && (node->flag & flag)))
            {
              if (!(flag & NODFLG_MARK_A) && pk)
          if (!flag || (flag && (node->flag & flag)))
            {
              if (!(flag & NODFLG_MARK_A) && pk)
-               tty_fprintf (fp, "%s ", uid_trust_string_fixed (pk, uid));
+               tty_fprintf (fp, "%s ", uid_trust_string_fixed (ctrl, pk, uid));
 
              if (flag & NODFLG_MARK_A)
                tty_fprintf (fp, "     ");
              else if (node->flag & NODFLG_SELUID)
                tty_fprintf (fp, "(%d)* ", i);
 
              if (flag & NODFLG_MARK_A)
                tty_fprintf (fp, "     ");
              else if (node->flag & NODFLG_SELUID)
                tty_fprintf (fp, "(%d)* ", i);
-             else if (uid->is_primary)
+             else if (uid->flags.primary)
                tty_fprintf (fp, "(%d). ", i);
              else
                tty_fprintf (fp, "(%d)  ", i);
                tty_fprintf (fp, "(%d). ", i);
              else
                tty_fprintf (fp, "(%d)  ", i);
@@ -3046,12 +3969,12 @@ show_key_with_all_names (ctrl_t ctrl, estream_t fp,
               * output */
              static int did_warn = 0;
 
               * output */
              static int did_warn = 0;
 
-             trust = get_validity_string (pk, NULL);
-             otrust = get_ownertrust_string (pk);
+             trust = get_validity_string (ctrl, pk, NULL);
+             otrust = get_ownertrust_string (pk, 0);
 
              /* Show a warning once */
              if (!did_warn
 
              /* Show a warning once */
              if (!did_warn
-                 && (get_validity (pk, NULL, NULL, 0)
+                 && (get_validity (ctrl, keyblock, pk, NULL, NULL, 0)
                      & TRUST_FLAG_PENDING_CHECK))
                {
                  did_warn = 1;
                      & TRUST_FLAG_PENDING_CHECK))
                {
                  did_warn = 1;
@@ -3118,7 +4041,7 @@ show_key_with_all_names (ctrl_t ctrl, estream_t fp,
                 have_seckey = 0;
               }
             else
                 have_seckey = 0;
               }
             else
-              have_seckey = !agent_get_keyinfo (ctrl, hexgrip, &serialno);
+              have_seckey = !agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL);
             xfree (hexgrip);
           }
 
             xfree (hexgrip);
           }
 
@@ -3205,9 +4128,10 @@ show_key_with_all_names (ctrl_t ctrl, estream_t fp,
                                opt.legacy_list_mode?
                                ((int) keystrlen () + 13):5, "");
                  /* Ownertrust is only meaningful for the PGP or
                                opt.legacy_list_mode?
                                ((int) keystrlen () + 13):5, "");
                  /* Ownertrust is only meaningful for the PGP or
-                    classic trust models */
+                    classic trust models, or PGP combined with TOFU */
                  if (opt.trust_model == TM_PGP
                  if (opt.trust_model == TM_PGP
-                     || opt.trust_model == TM_CLASSIC)
+                     || opt.trust_model == TM_CLASSIC
+                     || opt.trust_model == TM_TOFU_PGP)
                    {
                      int width = 14 - strlen (otrust);
                      if (width <= 0)
                    {
                      int width = 14 - strlen (otrust);
                      if (width <= 0)
@@ -3237,7 +4161,7 @@ show_key_with_all_names (ctrl_t ctrl, estream_t fp,
        }
     }
 
        }
     }
 
-  show_names (fp,
+  show_names (ctrl, fp,
               keyblock, primary, only_marked ? NODFLG_MARK_A : 0, with_prefs);
 
   if (do_warn && !nowarn)
               keyblock, primary, only_marked ? NODFLG_MARK_A : 0, with_prefs);
 
   if (do_warn && !nowarn)
@@ -3296,9 +4220,9 @@ show_basic_key_info (KBNODE keyblock)
          ++i;
 
          tty_printf ("     ");
          ++i;
 
          tty_printf ("     ");
-         if (uid->is_revoked)
+         if (uid->flags.revoked)
            tty_printf ("[%s] ", _("revoked"));
            tty_printf ("[%s] ", _("revoked"));
-         else if (uid->is_expired)
+         else if (uid->flags.expired)
            tty_printf ("[%s] ", _("expired"));
          tty_print_utf8_string (uid->name, uid->len);
          tty_printf ("\n");
            tty_printf ("[%s] ", _("expired"));
          tty_print_utf8_string (uid->name, uid->len);
          tty_printf ("\n");
@@ -3406,7 +4330,7 @@ no_primary_warning (KBNODE keyblock)
        {
          uid_count++;
 
        {
          uid_count++;
 
-         if (node->pkt->pkt.user_id->is_primary == 2)
+         if (node->pkt->pkt.user_id->flags.primary == 2)
            {
              have_primary = 1;
              break;
            {
              have_primary = 1;
              break;
@@ -3482,8 +4406,8 @@ subkey_expire_warning (kbnode_t keyblock)
  * user id.
  */
 static int
  * user id.
  */
 static int
-menu_adduid (kbnode_t pub_keyblock, int photo, const char *photo_name,
-             const char *uidstring)
+menu_adduid (ctrl_t ctrl, kbnode_t pub_keyblock,
+             int photo, const char *photo_name, const char *uidstring)
 {
   PKT_user_id *uid;
   PKT_public_key *pk = NULL;
 {
   PKT_user_id *uid;
   PKT_public_key *pk = NULL;
@@ -3505,7 +4429,7 @@ menu_adduid (kbnode_t pub_keyblock, int photo, const char *photo_name,
     }
   if (!node) /* No subkey.  */
     pub_where = NULL;
     }
   if (!node) /* No subkey.  */
     pub_where = NULL;
-  assert (pk);
+  log_assert (pk);
 
   if (photo)
     {
 
   if (photo)
     {
@@ -3545,14 +4469,17 @@ menu_adduid (kbnode_t pub_keyblock, int photo, const char *photo_name,
            }
        }
 
            }
        }
 
-      uid = generate_photo_id (pk, photo_name);
+      uid = generate_photo_id (ctrl, pk, photo_name);
     }
   else
     uid = generate_user_id (pub_keyblock, uidstring);
   if (!uid)
     {
       if (uidstring)
     }
   else
     uid = generate_user_id (pub_keyblock, uidstring);
   if (!uid)
     {
       if (uidstring)
-        log_error ("%s", _("Such a user ID already exists on this key!\n"));
+        {
+          write_status_error ("adduid", gpg_error (304));
+          log_error ("%s", _("Such a user ID already exists on this key!\n"));
+        }
       return 0;
     }
 
       return 0;
     }
 
@@ -3577,7 +4504,7 @@ menu_adduid (kbnode_t pub_keyblock, int photo, const char *photo_name,
     add_kbnode (pub_keyblock, node);
   pkt = xmalloc_clear (sizeof *pkt);
   pkt->pkttype = PKT_SIGNATURE;
     add_kbnode (pub_keyblock, node);
   pkt = xmalloc_clear (sizeof *pkt);
   pkt->pkttype = PKT_SIGNATURE;
-  pkt->pkt.signature = copy_signature (NULL, sig);
+  pkt->pkt.signature = sig;
   if (pub_where)
     insert_kbnode (node, new_kbnode (pkt), 0);
   else
   if (pub_where)
     insert_kbnode (node, new_kbnode (pkt), 0);
   else
@@ -3604,7 +4531,7 @@ menu_deluid (KBNODE pub_keyblock)
            {
              /* Only cause a trust update if we delete a
                 non-revoked user id */
            {
              /* Only cause a trust update if we delete a
                 non-revoked user id */
-             if (!node->pkt->pkt.user_id->is_revoked)
+             if (!node->pkt->pkt.user_id->flags.revoked)
                update_trust = 1;
              delete_kbnode (node);
            }
                update_trust = 1;
              delete_kbnode (node);
            }
@@ -3690,8 +4617,8 @@ menu_delsig (KBNODE pub_keyblock)
   if (changed)
     {
       commit_kbnode (&pub_keyblock);
   if (changed)
     {
       commit_kbnode (&pub_keyblock);
-      tty_printf (changed == 1 ? _("Deleted %d signature.\n")
-                 : _("Deleted %d signatures.\n"), changed);
+      tty_printf (ngettext("Deleted %d signature.\n",
+                           "Deleted %d signatures.\n", changed), changed);
     }
   else
     tty_printf (_("Nothing deleted.\n"));
     }
   else
     tty_printf (_("Nothing deleted.\n"));
@@ -3724,9 +4651,9 @@ menu_clean (KBNODE keyblock, int self_only)
            {
              const char *reason;
 
            {
              const char *reason;
 
-             if (uidnode->pkt->pkt.user_id->is_revoked)
+             if (uidnode->pkt->pkt.user_id->flags.revoked)
                reason = _("revoked");
                reason = _("revoked");
-             else if (uidnode->pkt->pkt.user_id->is_expired)
+             else if (uidnode->pkt->pkt.user_id->flags.expired)
                reason = _("expired");
              else
                reason = _("invalid");
                reason = _("expired");
              else
                reason = _("invalid");
@@ -3737,11 +4664,9 @@ menu_clean (KBNODE keyblock, int self_only)
            }
          else if (sigs)
            {
            }
          else if (sigs)
            {
-             tty_printf (sigs == 1 ?
-                         _("User ID \"%s\": %d signature removed\n") :
-                         _("User ID \"%s\": %d signatures removed\n"),
-                         user, sigs);
-
+             tty_printf (ngettext("User ID \"%s\": %d signature removed\n",
+                                   "User ID \"%s\": %d signatures removed\n",
+                                   sigs), user, sigs);
              modified = 1;
            }
          else
              modified = 1;
            }
          else
@@ -3804,7 +4729,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
   size_t fprlen;
   int rc;
 
   size_t fprlen;
   int rc;
 
-  assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+  log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
 
   pk = pub_keyblock->pkt->pkt.public_key;
 
 
   pk = pub_keyblock->pkt->pkt.public_key;
 
@@ -3910,8 +4835,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
                  log_error (_("this key has already been designated "
                               "as a revoker\n"));
 
                  log_error (_("this key has already been designated "
                               "as a revoker\n"));
 
-                 sprintf (buf, "%08lX%08lX",
-                          (ulong) pk->keyid[0], (ulong) pk->keyid[1]);
+                  format_keyid (pk_keyid (pk), KF_LONG, buf, sizeof (buf));
                  write_status_text (STATUS_ALREADY_SIGNED, buf);
 
                  break;
                  write_status_text (STATUS_ALREADY_SIGNED, buf);
 
                  break;
@@ -3967,33 +4891,50 @@ fail:
 }
 
 
 }
 
 
-static int
-menu_expire (KBNODE pub_keyblock)
+/* With FORCE_MAINKEY cleared this function handles the interactive
+ * menu option "expire".  With FORCE_MAINKEY set this functions only
+ * sets the expiration date of the primary key to NEWEXPIRATION and
+ * avoid all interactivity.  Retirns 0 if nothing was done,
+ * GPG_ERR_TRUE if the key was modified, or any other error code. */
+static gpg_error_t
+menu_expire (kbnode_t pub_keyblock, int force_mainkey, u32 newexpiration)
 {
 {
-  int n1, signumber, rc;
+  int signumber, rc;
   u32 expiredate;
   int mainkey = 0;
   PKT_public_key *main_pk, *sub_pk;
   PKT_user_id *uid;
   u32 expiredate;
   int mainkey = 0;
   PKT_public_key *main_pk, *sub_pk;
   PKT_user_id *uid;
-  KBNODE node;
+  kbnode_t node;
   u32 keyid[2];
 
   u32 keyid[2];
 
-  n1 = count_selected_keys (pub_keyblock);
-  if (n1 > 1)
+  if (force_mainkey)
     {
     {
-      tty_printf (_("Please select at most one subkey.\n"));
-      return 0;
+      mainkey = 1;
+      expiredate = newexpiration;
     }
     }
-  else if (n1)
-    tty_printf (_("Changing expiration time for a subkey.\n"));
   else
     {
   else
     {
-      tty_printf (_("Changing expiration time for the primary key.\n"));
-      mainkey = 1;
-      no_primary_warning (pub_keyblock);
+      int n1 = count_selected_keys (pub_keyblock);
+      if (n1 > 1)
+        {
+          if (!cpr_get_answer_is_yes
+              ("keyedit.expire_multiple_subkeys.okay",
+               _("Are you sure you want to change the"
+                 " expiration time for multiple subkeys? (y/N) ")))
+            return gpg_error (GPG_ERR_CANCELED);;
+        }
+      else if (n1)
+        tty_printf (_("Changing expiration time for a subkey.\n"));
+      else
+        {
+          tty_printf (_("Changing expiration time for the primary key.\n"));
+          mainkey = 1;
+          no_primary_warning (pub_keyblock);
+        }
+
+      expiredate = ask_expiredate ();
     }
 
     }
 
-  expiredate = ask_expiredate ();
 
   /* Now we can actually change the self-signature(s) */
   main_pk = sub_pk = NULL;
 
   /* Now we can actually change the self-signature(s) */
   main_pk = sub_pk = NULL;
@@ -4007,11 +4948,15 @@ menu_expire (KBNODE pub_keyblock)
          keyid_from_pk (main_pk, keyid);
          main_pk->expiredate = expiredate;
        }
          keyid_from_pk (main_pk, keyid);
          main_pk->expiredate = expiredate;
        }
-      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
-              && (node->flag & NODFLG_SELKEY))
+      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
        {
-         sub_pk = node->pkt->pkt.public_key;
-         sub_pk->expiredate = expiredate;
+          if ((node->flag & NODFLG_SELKEY) && !force_mainkey)
+            {
+              sub_pk = node->pkt->pkt.public_key;
+              sub_pk->expiredate = expiredate;
+            }
+          else
+            sub_pk = NULL;
        }
       else if (node->pkt->pkttype == PKT_USER_ID)
        uid = node->pkt->pkt.user_id;
        }
       else if (node->pkt->pkttype == PKT_USER_ID)
        uid = node->pkt->pkt.user_id;
@@ -4019,6 +4964,7 @@ menu_expire (KBNODE pub_keyblock)
               && (mainkey || sub_pk))
        {
          PKT_signature *sig = node->pkt->pkt.signature;
               && (mainkey || sub_pk))
        {
          PKT_signature *sig = node->pkt->pkt.signature;
+
          if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
              && ((mainkey && uid
                   && uid->created && (sig->sig_class & ~3) == 0x10)
          if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
              && ((mainkey && uid
                   && uid->created && (sig->sig_class & ~3) == 0x10)
@@ -4036,7 +4982,7 @@ menu_expire (KBNODE pub_keyblock)
                {
                  log_info
                     (_("You can't change the expiration date of a v3 key\n"));
                {
                  log_info
                     (_("You can't change the expiration date of a v3 key\n"));
-                 return 0;
+                 return gpg_error (GPG_ERR_LEGACY_KEY);
                }
 
              if (mainkey)
                }
 
              if (mainkey)
@@ -4051,7 +4997,9 @@ menu_expire (KBNODE pub_keyblock)
                {
                  log_error ("make_keysig_packet failed: %s\n",
                             gpg_strerror (rc));
                {
                  log_error ("make_keysig_packet failed: %s\n",
                             gpg_strerror (rc));
-                 return 0;
+                  if (gpg_err_code (rc) == GPG_ERR_TRUE)
+                    rc = GPG_ERR_GENERAL;
+                 return rc;
                }
 
              /* Replace the packet.  */
                }
 
              /* Replace the packet.  */
@@ -4067,6 +5015,112 @@ menu_expire (KBNODE pub_keyblock)
     }
 
   update_trust = 1;
     }
 
   update_trust = 1;
+  return gpg_error (GPG_ERR_TRUE);
+}
+
+
+/* Change the capability of a selected key.  This command should only
+ * be used to rectify badly created keys and as such is not suggested
+ * for general use.  */
+static int
+menu_changeusage (kbnode_t keyblock)
+{
+  int n1, rc;
+  int mainkey = 0;
+  PKT_public_key *main_pk, *sub_pk;
+  PKT_user_id *uid;
+  kbnode_t node;
+  u32 keyid[2];
+
+  n1 = count_selected_keys (keyblock);
+  if (n1 > 1)
+    {
+      tty_printf (_("You must select exactly one key.\n"));
+      return 0;
+    }
+  else if (n1)
+    tty_printf ("Changing usage of a subkey.\n");
+  else
+    {
+      tty_printf ("Changing usage of the primary key.\n");
+      mainkey = 1;
+    }
+
+  /* Now we can actually change the self-signature(s) */
+  main_pk = sub_pk = NULL;
+  uid = NULL;
+  for (node = keyblock; node; node = node->next)
+    {
+      if (node->pkt->pkttype == PKT_PUBLIC_KEY)
+       {
+         main_pk = node->pkt->pkt.public_key;
+         keyid_from_pk (main_pk, keyid);
+       }
+      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+       {
+          if (node->flag & NODFLG_SELKEY)
+            sub_pk = node->pkt->pkt.public_key;
+          else
+            sub_pk = NULL;
+       }
+      else if (node->pkt->pkttype == PKT_USER_ID)
+       uid = node->pkt->pkt.user_id;
+      else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE
+              && (mainkey || sub_pk))
+       {
+         PKT_signature *sig = node->pkt->pkt.signature;
+         if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+             && ((mainkey && uid
+                  && uid->created && (sig->sig_class & ~3) == 0x10)
+                 || (!mainkey && sig->sig_class == 0x18))
+             && sig->flags.chosen_selfsig)
+           {
+             /* This is the self-signature which is to be replaced.  */
+             PKT_signature *newsig;
+             PACKET *newpkt;
+
+             if ((mainkey && main_pk->version < 4)
+                 || (!mainkey && sub_pk->version < 4))
+               {
+                 log_info ("You can't change the capabilities of a v3 key\n");
+                 return 0;
+               }
+
+              if (mainkey)
+                main_pk->pubkey_usage = ask_key_flags (main_pk->pubkey_algo, 0,
+                                                       main_pk->pubkey_usage);
+              else
+                sub_pk->pubkey_usage  = ask_key_flags (sub_pk->pubkey_algo, 1,
+                                                       sub_pk->pubkey_usage);
+
+             if (mainkey)
+               rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL,
+                                          main_pk, keygen_add_key_flags,
+                                          main_pk);
+             else
+               rc =
+                 update_keysig_packet (&newsig, sig, main_pk, NULL, sub_pk,
+                                       main_pk, keygen_add_key_flags, sub_pk);
+             if (rc)
+               {
+                 log_error ("make_keysig_packet failed: %s\n",
+                            gpg_strerror (rc));
+                 return 0;
+               }
+
+             /* Replace the packet.  */
+             newpkt = xmalloc_clear (sizeof *newpkt);
+             newpkt->pkttype = PKT_SIGNATURE;
+             newpkt->pkt.signature = newsig;
+             free_packet (node->pkt);
+             xfree (node->pkt);
+             node->pkt = newpkt;
+             sub_pk = NULL;
+              break;
+           }
+       }
+    }
+
   return 1;
 }
 
   return 1;
 }
 
@@ -4079,7 +5133,7 @@ menu_backsign (KBNODE pub_keyblock)
   KBNODE node;
   u32 timestamp;
 
   KBNODE node;
   u32 timestamp;
 
-  assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+  log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
 
   merge_keys_and_selfsig (pub_keyblock);
   main_pk = pub_keyblock->pkt->pkt.public_key;
 
   merge_keys_and_selfsig (pub_keyblock);
   main_pk = pub_keyblock->pkt->pkt.public_key;
@@ -4204,9 +5258,9 @@ change_primary_uid_cb (PKT_signature * sig, void *opaque)
 
 /*
  * Set the primary uid flag for the selected UID.  We will also reset
 
 /*
  * Set the primary uid flag for the selected UID.  We will also reset
- * all other primary uid flags.  For this to work with have to update
+ * all other primary uid flags.  For this to work we have to update
  * all the signature timestamps.  If we would do this with the current
  * all the signature timestamps.  If we would do this with the current
- * time, we lose quite a lot of information, so we use a kludge to
+ * time, we lose quite a lot of information, so we use a kludge to
  * do this: Just increment the timestamp by one second which is
  * sufficient to updated a signature during import.
  */
  * do this: Just increment the timestamp by one second which is
  * sufficient to updated a signature during import.
  */
@@ -4828,7 +5882,7 @@ menu_select_uid_namehash (KBNODE keyblock, const char *namehash)
   KBNODE node;
   int i;
 
   KBNODE node;
   int i;
 
-  assert (strlen (namehash) == NAMEHASH_LEN * 2);
+  log_assert (strlen (namehash) == NAMEHASH_LEN * 2);
 
   for (i = 0; i < NAMEHASH_LEN; i++)
     hash[i] = hextobyte (&namehash[i * 2]);
 
   for (i = 0; i < NAMEHASH_LEN; i++)
     hash[i] = hextobyte (&namehash[i * 2]);
@@ -4866,10 +5920,97 @@ menu_select_uid_namehash (KBNODE keyblock, const char *namehash)
  * Returns: True if the selection changed.
  */
 static int
  * Returns: True if the selection changed.
  */
 static int
-menu_select_key (KBNODE keyblock, int idx)
+menu_select_key (KBNODE keyblock, int idx, char *p)
 {
   KBNODE node;
 {
   KBNODE node;
-  int i;
+  int i, j;
+  int is_hex_digits;
+
+  is_hex_digits = p && strlen (p) >= 8;
+  if (is_hex_digits)
+    {
+      /* Skip initial spaces.  */
+      while (spacep (p))
+        p ++;
+      /* If the id starts with 0x accept and ignore it.  */
+      if (p[0] == '0' && p[1] == 'x')
+        p += 2;
+
+      for (i = 0, j = 0; p[i]; i ++)
+        if (hexdigitp (&p[i]))
+          {
+            p[j] = toupper (p[i]);
+            j ++;
+          }
+        else if (spacep (&p[i]))
+          /* Skip spaces.  */
+          {
+          }
+        else
+          {
+            is_hex_digits = 0;
+            break;
+          }
+      if (is_hex_digits)
+        /* In case we skipped some spaces, add a new NUL terminator.  */
+        {
+          p[j] = 0;
+          /* If we skipped some spaces, make sure that we still have
+             at least 8 characters.  */
+          is_hex_digits = (/* Short keyid.  */
+                           strlen (p) == 8
+                           /* Long keyid.  */
+                           || strlen (p) == 16
+                           /* Fingerprints are (currently) 32 or 40
+                              characters.  */
+                           || strlen (p) >= 32);
+        }
+    }
+
+  if (is_hex_digits)
+    {
+      int found_one = 0;
+      for (node = keyblock; node; node = node->next)
+       if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+           || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+          {
+            int match = 0;
+            if (strlen (p) == 8 || strlen (p) == 16)
+              {
+                u32 kid[2];
+                char kid_str[17];
+                keyid_from_pk (node->pkt->pkt.public_key, kid);
+                format_keyid (kid, strlen (p) == 8 ? KF_SHORT : KF_LONG,
+                              kid_str, sizeof (kid_str));
+
+                if (strcmp (p, kid_str) == 0)
+                  match = 1;
+              }
+            else
+              {
+                char fp[2*MAX_FINGERPRINT_LEN + 1];
+                hexfingerprint (node->pkt->pkt.public_key, fp, sizeof (fp));
+                if (strcmp (fp, p) == 0)
+                  match = 1;
+              }
+
+            if (match)
+              {
+                if ((node->flag & NODFLG_SELKEY))
+                  node->flag &= ~NODFLG_SELKEY;
+                else
+                  node->flag |= NODFLG_SELKEY;
+
+                found_one = 1;
+              }
+          }
+
+      if (found_one)
+        return 1;
+
+      tty_printf (_("No subkey with key ID '%s'.\n"), p);
+      return 0;
+    }
 
   if (idx == -1)               /* Select all.  */
     {
 
   if (idx == -1)               /* Select all.  */
     {
@@ -5075,7 +6216,7 @@ menu_revsig (KBNODE keyblock)
   int rc, any, skip = 1, all = !count_selected_uids (keyblock);
   struct revocation_reason_info *reason = NULL;
 
   int rc, any, skip = 1, all = !count_selected_uids (keyblock);
   struct revocation_reason_info *reason = NULL;
 
-  assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+  log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
 
   /* First check whether we have any signatures at all.  */
   any = 0;
 
   /* First check whether we have any signatures at all.  */
   any = 0;
@@ -5216,7 +6357,7 @@ reloop:                   /* (must use this, because we are modifing the list) */
          || node->pkt->pkttype != PKT_SIGNATURE)
        continue;
       unode = find_prev_kbnode (keyblock, node, PKT_USER_ID);
          || node->pkt->pkttype != PKT_SIGNATURE)
        continue;
       unode = find_prev_kbnode (keyblock, node, PKT_USER_ID);
-      assert (unode);          /* we already checked this */
+      log_assert (unode); /* we already checked this */
 
       memset (&attrib, 0, sizeof attrib);
       attrib.reason = reason;
 
       memset (&attrib, 0, sizeof attrib);
       attrib.reason = reason;
@@ -5247,7 +6388,7 @@ reloop:                   /* (must use this, because we are modifing the list) */
       /* Are we revoking our own uid? */
       if (primary_pk->keyid[0] == sig->keyid[0] &&
          primary_pk->keyid[1] == sig->keyid[1])
       /* Are we revoking our own uid? */
       if (primary_pk->keyid[0] == sig->keyid[0] &&
          primary_pk->keyid[1] == sig->keyid[1])
-       unode->pkt->pkt.user_id->is_revoked = 1;
+       unode->pkt->pkt.user_id->flags.revoked = 1;
       pkt = xmalloc_clear (sizeof *pkt);
       pkt->pkttype = PKT_SIGNATURE;
       pkt->pkt.signature = sig;
       pkt = xmalloc_clear (sizeof *pkt);
       pkt->pkttype = PKT_SIGNATURE;
       pkt->pkt.signature = sig;
@@ -5260,16 +6401,107 @@ reloop:                        /* (must use this, because we are modifing the list) */
 }
 
 
 }
 
 
+/* return 0 if revocation of NODE (which must be a User ID) was
+   successful, non-zero if there was an error.  *modified will be set
+   to 1 if a change was made. */
+static int
+core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node,
+             const struct revocation_reason_info *reason, int *modified)
+{
+  PKT_public_key *pk = keyblock->pkt->pkt.public_key;
+  gpg_error_t rc;
+
+  if (node->pkt->pkttype != PKT_USER_ID)
+    {
+      rc = gpg_error (GPG_ERR_NO_USER_ID);
+      write_status_error ("keysig", rc);
+      log_error (_("tried to revoke a non-user ID: %s\n"), gpg_strerror (rc));
+      return 1;
+    }
+  else
+    {
+      PKT_user_id *uid = node->pkt->pkt.user_id;
+
+      if (uid->flags.revoked)
+        {
+          char *user = utf8_to_native (uid->name, uid->len, 0);
+          log_info (_("user ID \"%s\" is already revoked\n"), user);
+          xfree (user);
+        }
+      else
+        {
+          PACKET *pkt;
+          PKT_signature *sig;
+          struct sign_attrib attrib;
+          u32 timestamp = make_timestamp ();
+
+          if (uid->created >= timestamp)
+            {
+              /* Okay, this is a problem.  The user ID selfsig was
+                 created in the future, so we need to warn the user and
+                 set our revocation timestamp one second after that so
+                 everything comes out clean. */
+
+              log_info (_("WARNING: a user ID signature is dated %d"
+                          " seconds in the future\n"),
+                        uid->created - timestamp);
+
+              timestamp = uid->created + 1;
+            }
+
+          memset (&attrib, 0, sizeof attrib);
+          /* should not need to cast away const here; but
+             revocation_reason_build_cb needs to take a non-const
+             void* in order to meet the function signtuare for the
+             mksubpkt argument to make_keysig_packet */
+          attrib.reason = (struct revocation_reason_info *)reason;
+
+          rc = make_keysig_packet (&sig, pk, uid, NULL, pk, 0x30, 0,
+                                   timestamp, 0,
+                                   sign_mk_attrib, &attrib, NULL);
+          if (rc)
+            {
+              write_status_error ("keysig", rc);
+              log_error (_("signing failed: %s\n"), gpg_strerror (rc));
+              return 1;
+            }
+          else
+            {
+              pkt = xmalloc_clear (sizeof *pkt);
+              pkt->pkttype = PKT_SIGNATURE;
+              pkt->pkt.signature = sig;
+              insert_kbnode (node, new_kbnode (pkt), 0);
+
+#ifndef NO_TRUST_MODELS
+              /* If the trustdb has an entry for this key+uid then the
+                 trustdb needs an update. */
+              if (!update_trust
+                  && ((get_validity (ctrl, keyblock, pk, uid, NULL, 0)
+                       & TRUST_MASK)
+                      >= TRUST_UNDEFINED))
+                update_trust = 1;
+#endif /*!NO_TRUST_MODELS*/
+
+              node->pkt->pkt.user_id->flags.revoked = 1;
+              if (modified)
+                *modified = 1;
+            }
+        }
+      return 0;
+    }
+}
+
 /* Revoke a user ID (i.e. revoke a user ID selfsig).  Return true if
    keyblock changed.  */
 static int
 /* Revoke a user ID (i.e. revoke a user ID selfsig).  Return true if
    keyblock changed.  */
 static int
-menu_revuid (KBNODE pub_keyblock)
+menu_revuid (ctrl_t ctrl, kbnode_t pub_keyblock)
 {
   PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key;
   KBNODE node;
   int changed = 0;
   int rc;
   struct revocation_reason_info *reason = NULL;
 {
   PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key;
   KBNODE node;
   int changed = 0;
   int rc;
   struct revocation_reason_info *reason = NULL;
+  size_t valid_uids;
 
   /* Note that this is correct as per the RFCs, but nevertheless
      somewhat meaningless in the real world.  1991 did define the 0x30
 
   /* Note that this is correct as per the RFCs, but nevertheless
      somewhat meaningless in the real world.  1991 did define the 0x30
@@ -5286,75 +6518,39 @@ menu_revuid (KBNODE pub_keyblock)
          goto leave;
       }
 
          goto leave;
       }
 
- reloop: /* (better this way because we are modifing the keyring) */
+  /* Too make sure that we do not revoke the last valid UID, we first
+     count how many valid UIDs there are.  */
+  valid_uids = 0;
+  for (node = pub_keyblock; node; node = node->next)
+    valid_uids +=
+      node->pkt->pkttype == PKT_USER_ID
+      && ! node->pkt->pkt.user_id->flags.revoked
+      && ! node->pkt->pkt.user_id->flags.expired;
+
+ reloop: /* (better this way because we are modifying the keyring) */
   for (node = pub_keyblock; node; node = node->next)
     if (node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_SELUID))
       {
   for (node = pub_keyblock; node; node = node->next)
     if (node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_SELUID))
       {
-       PKT_user_id *uid = node->pkt->pkt.user_id;
-
-       if (uid->is_revoked)
-         {
-           char *user = utf8_to_native (uid->name, uid->len, 0);
-           log_info (_("user ID \"%s\" is already revoked\n"), user);
-           xfree (user);
-         }
-       else
-         {
-           PACKET *pkt;
-           PKT_signature *sig;
-           struct sign_attrib attrib;
-           u32 timestamp = make_timestamp ();
+        int modified = 0;
 
 
-           if (uid->created >= timestamp)
-             {
-               /* Okay, this is a problem.  The user ID selfsig was
-                  created in the future, so we need to warn the user and
-                  set our revocation timestamp one second after that so
-                  everything comes out clean. */
-
-               log_info (_("WARNING: a user ID signature is dated %d"
-                           " seconds in the future\n"),
-                         uid->created - timestamp);
-
-               timestamp = uid->created + 1;
-             }
-
-           memset (&attrib, 0, sizeof attrib);
-           attrib.reason = reason;
+        /* Make sure that we do not revoke the last valid UID.  */
+        if (valid_uids == 1
+            && ! node->pkt->pkt.user_id->flags.revoked
+            && ! node->pkt->pkt.user_id->flags.expired)
+          {
+            log_error (_("Cannot revoke the last valid user ID.\n"));
+            goto leave;
+          }
 
 
+        rc = core_revuid (ctrl, pub_keyblock, node, reason, &modified);
+        if (rc)
+          goto leave;
+        if (modified)
+          {
            node->flag &= ~NODFLG_SELUID;
            node->flag &= ~NODFLG_SELUID;
-
-           rc = make_keysig_packet (&sig, pk, uid, NULL, pk, 0x30, 0,
-                                    timestamp, 0,
-                                    sign_mk_attrib, &attrib, NULL);
-           if (rc)
-             {
-                write_status_error ("keysig", rc);
-               log_error (_("signing failed: %s\n"), gpg_strerror (rc));
-               goto leave;
-             }
-           else
-             {
-               pkt = xmalloc_clear (sizeof *pkt);
-               pkt->pkttype = PKT_SIGNATURE;
-               pkt->pkt.signature = sig;
-               insert_kbnode (node, new_kbnode (pkt), 0);
-
-#ifndef NO_TRUST_MODELS
-               /* If the trustdb has an entry for this key+uid then the
-                  trustdb needs an update. */
-               if (!update_trust
-                   && (get_validity (pk, uid, NULL, 0) & TRUST_MASK) >=
-                   TRUST_UNDEFINED)
-                 update_trust = 1;
-#endif /*!NO_TRUST_MODELS*/
-
-               changed = 1;
-               node->pkt->pkt.user_id->is_revoked = 1;
-
-               goto reloop;
-             }
-         }
+            changed = 1;
+            goto reloop;
+          }
       }
 
   if (changed)
       }
 
   if (changed)
@@ -5506,7 +6702,7 @@ enable_disable_key (KBNODE keyblock, int disable)
 
 
 static void
 
 
 static void
-menu_showphoto (KBNODE keyblock)
+menu_showphoto (ctrl_t ctrl, kbnode_t keyblock)
 {
   KBNODE node;
   int select_all = !count_selected_uids (keyblock);
 {
   KBNODE node;
   int select_all = !count_selected_uids (keyblock);
@@ -5543,7 +6739,7 @@ menu_showphoto (KBNODE keyblock)
                                    "key %s (uid %d)\n"),
                                  image_type_to_string (type, 1),
                                  (ulong) size, keystr_from_pk (pk), count);
                                    "key %s (uid %d)\n"),
                                  image_type_to_string (type, 1),
                                  (ulong) size, keystr_from_pk (pk), count);
-                     show_photos (&uid->attribs[i], 1, pk, uid);
+                     show_photos (ctrl, &uid->attribs[i], 1, pk, uid);
                    }
                }
            }
                    }
                }
            }